已修改24个文件
已添加7个文件
4312 ■■■■ 文件已修改
src/api/collaborativeApproval/noticeManagement.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/energyManagement/index.js 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 392 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index1.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index2.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index3.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index4.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/meetingBoard/index.vue 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/noticeManagement/index.vue 705 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/warningSystem/index.vue 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/fakePage/index.vue 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/dynamicEnergySaving/index.vue 659 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyArea/index.vue 387 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyPower/components/formDia.vue 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/formDia.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/filesDia.vue 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/index.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/revenueManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/index.vue 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/filesDia.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/formDia.vue 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/index.vue 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/index.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/collaborativeApproval/noticeManagement.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
import request from '@/utils/request'
// æŸ¥è¯¢å…¬å‘Šåˆ—表
export function listNotice(query) {
  return request({
    url: '/collaborativeApproval/notice/list',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢å…¬å‘Šè¯¦ç»†
export function getNotice(noticeId) {
  return request({
    url: '/collaborativeApproval/notice/' + noticeId,
    method: 'get'
  })
}
// æ–°å¢žå…¬å‘Š
export function addNotice(data) {
  return request({
    url: '/collaborativeApproval/notice',
    method: 'post',
    data: data
  })
}
// ä¿®æ”¹å…¬å‘Š
export function updateNotice(data) {
  return request({
    url: '/collaborativeApproval/notice',
    method: 'put',
    data: data
  })
}
// åˆ é™¤å…¬å‘Š
export function delNotice(noticeId) {
  return request({
    url: '/collaborativeApproval/notice/' + noticeId,
    method: 'delete'
  })
}
// æ‰¹é‡åˆ é™¤å…¬å‘Š
export function delNoticeBatch(noticeIds) {
  return request({
    url: '/collaborativeApproval/notice/batch',
    method: 'delete',
    data: noticeIds
  })
}
// å‘布公告
export function publishNotice(noticeId) {
  return request({
    url: '/collaborativeApproval/notice/publish/' + noticeId,
    method: 'put'
  })
}
// ä¸‹çº¿å…¬å‘Š
export function offlineNotice(noticeId) {
  return request({
    url: '/collaborativeApproval/notice/offline/' + noticeId,
    method: 'put'
  })
}
src/api/energyManagement/index.js
@@ -4,117 +4,124 @@
// è®¾å¤‡èƒ½è€—-分页查询
export function equipmentEnergyListPage(query) {
  return request({
    url: '/equipmentEnergyConsumption/listPage',
    method: 'get',
    url: "/equipmentEnergyConsumption/listPage",
    method: "get",
    params: query,
  })
  });
}
// -能源趋势-分页查询
export function listPageByTrend(query) {
  return request({
    url: '/equipmentEnergyConsumption/listPageByTrend',
    method: 'get',
    url: "/equipmentEnergyConsumption/listPageByTrend",
    method: "get",
    params: query,
  })
  });
}
// åŒºåŸŸ-分页查询
export function areaListPage(query) {
  return request({
    url: '/electricityConsumptionArea/listPage',
    method: 'get',
    url: "/electricityConsumptionArea/listPage",
    method: "get",
    params: query,
  })
  });
}
// åŒºåŸŸ-树
export function areaListTree(query) {
  return request({
    url: "/electricityConsumptionArea/list",
    method: "get",
    params: query,
  });
}
// æ—¶é—´å‘¨æœŸ-分页查询
export function periodListPage(query) {
  return request({
    url: '/energyPeriod/listPage',
    method: 'get',
    url: "/energyPeriod/listPage",
    method: "get",
    params: query,
  })
  });
}
// è®¾å¤‡èƒ½è€—-删除
export function equipmentEnergyDelete(query) {
  return request({
    url: '/equipmentEnergyConsumption/delete',
    method: 'delete',
    url: "/equipmentEnergyConsumption/delete",
    method: "delete",
    data: query,
  })
  });
}
// åŒºåŸŸ-删除
export function areaDelete(query) {
  return request({
    url: '/electricityConsumptionArea/delete',
    method: 'delete',
    url: "/electricityConsumptionArea/delete",
    method: "delete",
    data: query,
  })
  });
}
// æ—¶é—´å‘¨æœŸ-删除
export function periodDelete(query) {
  return request({
    url: '/energyPeriod/delete',
    method: 'delete',
    url: "/energyPeriod/delete",
    method: "delete",
    data: query,
  })
  });
}
// è®¾å¤‡èƒ½è€—-新增
export function equipmentEnergyAdd(query) {
  return request({
    url: '/equipmentEnergyConsumption/add',
    method: 'post',
    url: "/equipmentEnergyConsumption/add",
    method: "post",
    data: query,
  })
  });
}
// åŒºåŸŸ-新增
export function areaAdd(query) {
  return request({
    url: '/electricityConsumptionArea/add',
    method: 'post',
    url: "/electricityConsumptionArea/add",
    method: "post",
    data: query,
  })
  });
}
// æ—¶é—´å‘¨æœŸ-新增
export function periodAdd(query) {
  return request({
    url: '/energyPeriod/add',
    method: 'post',
    url: "/energyPeriod/add",
    method: "post",
    data: query,
  })
  });
}
// è®¾å¤‡èƒ½è€—-修改
export function equipmentEnergyUpdate(query) {
  return request({
    url: '/equipmentEnergyConsumption/update',
    method: 'post',
    url: "/equipmentEnergyConsumption/update",
    method: "post",
    data: query,
  })
  });
}
//区域-修改
export function areaUpdate(query) {
  return request({
    url: '/electricityConsumptionArea/update',
    method: 'post',
    url: "/electricityConsumptionArea/update",
    method: "post",
    data: query,
  })
  });
}
// æ—¶é—´å‘¨æœŸ-修改
export function periodUpdate(query) {
  return request({
    url: '/energyPeriod/update',
    method: 'post',
    url: "/energyPeriod/update",
    method: "post",
    data: query,
  })
  });
}
// è®¾å¤‡ä¸‹æ‹‰æ¡†æŸ¥è¯¢
export function deviceList(query) {
  return request({
    url: '/equipmentEnergyConsumption/deviceList',
    method: 'get',
  })
}
    url: "/equipmentEnergyConsumption/deviceList",
    method: "get",
  });
}
src/router/index.js
@@ -1,202 +1,190 @@
import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
/**
 * Note: è·¯ç”±é…ç½®é¡¹
 *
 * hidden: true                     // å½“设置 true çš„æ—¶å€™è¯¥è·¯ç”±ä¸ä¼šå†ä¾§è¾¹æ å‡ºçް å¦‚401,login等页面,或者如一些编辑页面/edit/1
 * alwaysShow: true                 // å½“你一个路由下面的 children å£°æ˜Žçš„路由大于1个时,自动会变成嵌套的模式--如组件页面
 *                                  // åªæœ‰ä¸€ä¸ªæ—¶ï¼Œä¼šå°†é‚£ä¸ªå­è·¯ç”±å½“做根路由显示在侧边栏--如引导页面
 *                                  // è‹¥ä½ æƒ³ä¸ç®¡è·¯ç”±ä¸‹é¢çš„ children å£°æ˜Žçš„个数都显示你的根路由
 *                                  // ä½ å¯ä»¥è®¾ç½® alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
 * redirect: noRedirect             // å½“设置 noRedirect çš„æ—¶å€™è¯¥è·¯ç”±åœ¨é¢åŒ…屑导航中不可被点击
 * name:'router-name'               // è®¾å®šè·¯ç”±çš„名字,一定要填写不然使用<keep-alive>时会出现各种问题
 * query: '{"id": 1, "name": "ry"}' // è®¿é—®è·¯ç”±çš„默认传递参数
 * roles: ['admin', 'common']       // è®¿é—®è·¯ç”±çš„角色权限
 * permissions: ['a:a:a', 'b:b:b']  // è®¿é—®è·¯ç”±çš„菜单权限
 * meta : {
    noCache: true                   // å¦‚果设置为true,则不会被 <keep-alive> ç¼“å­˜(默认 false)
    title: 'title'                  // è®¾ç½®è¯¥è·¯ç”±åœ¨ä¾§è¾¹æ å’Œé¢åŒ…屑中展示的名字
    icon: 'svg-name'                // è®¾ç½®è¯¥è·¯ç”±çš„图标,对应路径src/assets/icons/svg
    breadcrumb: false               // å¦‚果设置为false,则不会在breadcrumb面包屑中显示
    activeMenu: '/system/user'      // å½“路由设置了该属性,则会高亮相对应的侧边栏。
  }
 */
// å…¬å…±è·¯ç”±
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login'),
    hidden: true
  },
  {
    path: '/register',
    component: () => import('@/views/register'),
    hidden: true
  },
  {
    path: "/:pathMatch(.*)*",
    component: () => import('@/views/error/404'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error/401'),
    hidden: true
  },
  {
    path: '',
    component: Layout,
    redirect: '/index',
    children: [
      {
        path: '/index',
        component: () => import('@/views/index'),
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true }
      }
    ]
  },
  // {
  //   path: '/equipment',
  //   component: Layout,
  //   redirect: '/equipment/iot-monitor',
  //   children: [
  //     {
  //       path: 'iot-monitor',
  //       component: () => import('@/views/equipmentManagement/iotMonitor/index.vue'),
  //       name: 'IoTMonitor',
  //       meta: { title: 'IoT监控', icon: 'monitor', noCache: true }
  //     }
  //   ]
  // },
  // {
  //   path: '/main/MobileChat',
  //   component: Layout,
  //   redirect: '',
  //   hidden: true,
  //   children: [
  //     {
  //       path: '',
  //       component: () => import('@/views/chatHome/chatHomeIndex/MobileChat'),
  //       name: 'MobileChat',
  //       meta: { title: 'AI对话', icon: 'dashboard', affix: true}
  //     }
  //   ]
  // },
  {
    path: '/user',
    component: Layout,
    hidden: true,
    redirect: 'noredirect',
    children: [
      {
        path: 'profile',
        component: () => import('@/views/system/user/profile/index'),
        name: 'Profile',
        meta: { title: '个人中心', icon: 'user' }
      }
    ]
  }
]
// åŠ¨æ€è·¯ç”±ï¼ŒåŸºäºŽç”¨æˆ·æƒé™åŠ¨æ€åŽ»åŠ è½½
export const dynamicRoutes = [
  {
    path: '/system/user-auth',
    component: Layout,
    hidden: true,
    permissions: ['system:user:edit'],
    children: [
      {
        path: 'role/:userId(\\d+)',
        component: () => import('@/views/system/user/authRole'),
        name: 'AuthRole',
        meta: { title: '分配角色', activeMenu: '/system/user' }
      }
    ]
  },
  {
    path: '/system/role-auth',
    component: Layout,
    hidden: true,
    permissions: ['system:role:edit'],
    children: [
      {
        path: 'user/:roleId(\\d+)',
        component: () => import('@/views/system/role/authUser'),
        name: 'AuthUser',
        meta: { title: '分配用户', activeMenu: '/system/role' }
      }
    ]
  },
  {
    path: '/system/dict-data',
    component: Layout,
    hidden: true,
    permissions: ['system:dict:list'],
    children: [
      {
        path: 'index/:dictId(\\d+)',
        component: () => import('@/views/system/dict/data'),
        name: 'Data',
        meta: { title: '字典数据', activeMenu: '/system/dict' }
      }
    ]
  },
  {
    path: '/monitor/job-log',
    component: Layout,
    hidden: true,
    permissions: ['monitor:job:list'],
    children: [
      {
        path: 'index/:jobId(\\d+)',
        component: () => import('@/views/monitor/job/log'),
        name: 'JobLog',
        meta: { title: '调度日志', activeMenu: '/monitor/job' }
      }
    ]
  },
  {
    path: '/tool/gen-edit',
    component: Layout,
    hidden: true,
    permissions: ['tool:gen:edit'],
    children: [
      {
        path: 'index/:tableId(\\d+)',
        component: () => import('@/views/tool/gen/editTable'),
        name: 'GenEdit',
        meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
      }
    ]
  }
]
const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    }
    return { top: 0 }
  },
})
export default router
import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
/**
 * Note: è·¯ç”±é…ç½®é¡¹
 *
 * hidden: true                     // å½“设置 true çš„æ—¶å€™è¯¥è·¯ç”±ä¸ä¼šå†ä¾§è¾¹æ å‡ºçް å¦‚401,login等页面,或者如一些编辑页面/edit/1
 * alwaysShow: true                 // å½“你一个路由下面的 children å£°æ˜Žçš„路由大于1个时,自动会变成嵌套的模式--如组件页面
 *                                  // åªæœ‰ä¸€ä¸ªæ—¶ï¼Œä¼šå°†é‚£ä¸ªå­è·¯ç”±å½“做根路由显示在侧边栏--如引导页面
 *                                  // è‹¥ä½ æƒ³ä¸ç®¡è·¯ç”±ä¸‹é¢çš„ children å£°æ˜Žçš„个数都显示你的根路由
 *                                  // ä½ å¯ä»¥è®¾ç½® alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
 * redirect: noRedirect             // å½“设置 noRedirect çš„æ—¶å€™è¯¥è·¯ç”±åœ¨é¢åŒ…屑导航中不可被点击
 * name:'router-name'               // è®¾å®šè·¯ç”±çš„名字,一定要填写不然使用<keep-alive>时会出现各种问题
 * query: '{"id": 1, "name": "ry"}' // è®¿é—®è·¯ç”±çš„默认传递参数
 * roles: ['admin', 'common']       // è®¿é—®è·¯ç”±çš„角色权限
 * permissions: ['a:a:a', 'b:b:b']  // è®¿é—®è·¯ç”±çš„菜单权限
 * meta : {
    noCache: true                   // å¦‚果设置为true,则不会被 <keep-alive> ç¼“å­˜(默认 false)
    title: 'title'                  // è®¾ç½®è¯¥è·¯ç”±åœ¨ä¾§è¾¹æ å’Œé¢åŒ…屑中展示的名字
    icon: 'svg-name'                // è®¾ç½®è¯¥è·¯ç”±çš„图标,对应路径src/assets/icons/svg
    breadcrumb: false               // å¦‚果设置为false,则不会在breadcrumb面包屑中显示
    activeMenu: '/system/user'      // å½“路由设置了该属性,则会高亮相对应的侧边栏。
  }
 */
// å…¬å…±è·¯ç”±
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login'),
    hidden: true
  },
  {
    path: '/register',
    component: () => import('@/views/register'),
    hidden: true
  },
  {
    path: "/:pathMatch(.*)*",
    component: () => import('@/views/error/404'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error/401'),
    hidden: true
  },
  {
    path: '',
    component: Layout,
    redirect: '/index',
    children: [
      {
        path: '/index',
        component: () => import('@/views/index'),
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true }
      }
    ]
  },
  {
    path: '/user',
    component: Layout,
    hidden: true,
    redirect: 'noredirect',
    children: [
      {
        path: 'profile',
        component: () => import('@/views/system/user/profile/index'),
        name: 'Profile',
        meta: { title: '个人中心', icon: 'user' }
      }
    ]
  }
]
// åŠ¨æ€è·¯ç”±ï¼ŒåŸºäºŽç”¨æˆ·æƒé™åŠ¨æ€åŽ»åŠ è½½
export const dynamicRoutes = [
  {
    path: '/system/user-auth',
    component: Layout,
    hidden: true,
    permissions: ['system:user:edit'],
    children: [
      {
        path: 'role/:userId(\\d+)',
        component: () => import('@/views/system/user/authRole'),
        name: 'AuthRole',
        meta: { title: '分配角色', activeMenu: '/system/user' }
      }
    ]
  },
  {
    path: '/main/MobileChat',
    component: Layout,
    redirect: '',
    hidden: true,
    permissions: ['MobileChat:edit'],
    children: [
      {
        path: '',
        component: () => import('@/views/chatHome/chatHomeIndex/MobileChat'),
        name: 'MobileChat',
        meta: { title: 'AI对话', activeMenu: '/chatHome/chatHomeIndex'}
      }
    ]
  },
  {
    path: '/system/role-auth',
    component: Layout,
    hidden: true,
    permissions: ['system:role:edit'],
    children: [
      {
        path: 'user/:roleId(\\d+)',
        component: () => import('@/views/system/role/authUser'),
        name: 'AuthUser',
        meta: { title: '分配用户', activeMenu: '/system/role' }
      }
    ]
  },
  {
    path: '/system/dict-data',
    component: Layout,
    hidden: true,
    permissions: ['system:dict:list'],
    children: [
      {
        path: 'index/:dictId(\\d+)',
        component: () => import('@/views/system/dict/data'),
        name: 'Data',
        meta: { title: '字典数据', activeMenu: '/system/dict' }
      }
    ]
  },
  {
    path: '/monitor/job-log',
    component: Layout,
    hidden: true,
    permissions: ['monitor:job:list'],
    children: [
      {
        path: 'index/:jobId(\\d+)',
        component: () => import('@/views/monitor/job/log'),
        name: 'JobLog',
        meta: { title: '调度日志', activeMenu: '/monitor/job' }
      }
    ]
  },
  {
    path: '/tool/gen-edit',
    component: Layout,
    hidden: true,
    permissions: ['tool:gen:edit'],
    children: [
      {
        path: 'index/:tableId(\\d+)',
        component: () => import('@/views/tool/gen/editTable'),
        name: 'GenEdit',
        meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
      }
    ]
  }
]
const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    }
    return { top: 0 }
  },
})
export default router
src/views/collaborativeApproval/approvalProcess/index1.vue
@@ -1,5 +1,5 @@
<template>
  <div class="app-container">
  <div class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="1" />
  </div>
@@ -15,7 +15,7 @@
</script>
<style scoped>
.app-container {
.container {
  width: 100%;
  height: 100%;
}
src/views/collaborativeApproval/approvalProcess/index2.vue
@@ -1,5 +1,5 @@
<template>
  <div class="app-container">
  <div class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="2" />
  </div>
@@ -15,7 +15,7 @@
</script>
<style scoped>
.app-container {
.container {
  width: 100%;
  height: 100%;
}
src/views/collaborativeApproval/approvalProcess/index3.vue
@@ -1,5 +1,5 @@
<template>
  <div class="app-container">
  <div class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="3" />
  </div>
@@ -15,7 +15,7 @@
</script>
<style scoped>
.app-container {
.container {
  width: 100%;
  height: 100%;
}
src/views/collaborativeApproval/approvalProcess/index4.vue
@@ -1,5 +1,5 @@
<template>
  <div class="app-container">
  <div class="container">
    <!-- å¼•å…¥index.vue组件并传递参数 -->
    <ApprovalProcessIndex :approveType="4" />
  </div>
@@ -15,7 +15,7 @@
</script>
<style scoped>
.app-container {
.container {
  width: 100%;
  height: 100%;
}
src/views/collaborativeApproval/meetingBoard/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,498 @@
<template>
  <div class="app-container">
    <!-- é¡µé¢æ ‡é¢˜ -->
    <div class="page-header">
      <h2>会议看板</h2>
<!--      <el-button type="primary" @click="createMeeting">创建会议</el-button>-->
    </div>
    <!-- ä¼šè®®ç»Ÿè®¡å¡ç‰‡ -->
    <div class="stats-cards">
      <el-card class="stat-card">
        <div class="stat-content">
          <div class="stat-number">{{ stats.total }}</div>
          <div class="stat-label">总会议数</div>
        </div>
      </el-card>
      <el-card class="stat-card">
        <div class="stat-content">
          <div class="stat-number">{{ stats.ongoing }}</div>
          <div class="stat-label">进行中</div>
        </div>
      </el-card>
      <el-card class="stat-card">
        <div class="stat-content">
          <div class="stat-number">{{ stats.completed }}</div>
          <div class="stat-label">已完成</div>
        </div>
      </el-card>
      <el-card class="stat-card">
        <div class="stat-content">
          <div class="stat-number">{{ stats.upcoming }}</div>
          <div class="stat-label">即将开始</div>
        </div>
      </el-card>
    </div>
    <!-- ä¼šè®®åˆ—表 -->
    <div class="meeting-list">
      <el-card v-for="meeting in meetings" :key="meeting.id" class="meeting-card">
        <div class="meeting-header">
          <div class="meeting-title">
            <h3>{{ meeting.title }}</h3>
            <el-tag :type="getStatusType(meeting.status)" size="small">
              {{ getStatusText(meeting.status) }}
            </el-tag>
          </div>
          <div class="meeting-time">
            <el-icon><Clock /></el-icon>
            {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }}
          </div>
        </div>
        <div class="meeting-info">
          <div class="info-item">
            <el-icon><Location /></el-icon>
            <span>{{ meeting.location }}</span>
          </div>
          <div class="info-item">
            <el-icon><User /></el-icon>
            <span>主持人: {{ meeting.host }}</span>
          </div>
          <div class="info-item">
            <el-icon><UserFilled /></el-icon>
            <span>参会人数: {{ meeting.participants.length }}人</span>
          </div>
        </div>
        <div class="meeting-agenda">
          <h4>议程安排</h4>
          <div class="agenda-list">
            <div
              v-for="(agenda, index) in meeting.agenda"
              :key="index"
              class="agenda-item"
              :class="{ 'active': agenda.status === 'active', 'completed': agenda.status === 'completed' }"
            >
              <span class="agenda-time">{{ agenda.time }}</span>
              <span class="agenda-content">{{ agenda.content }}</span>
              <el-tag
                :type="getAgendaStatusType(agenda.status)"
                size="small"
              >
                {{ getAgendaStatusText(agenda.status) }}
              </el-tag>
            </div>
          </div>
        </div>
<!--        <div class="meeting-actions">-->
<!--          <el-button type="primary" size="small" @click="joinMeeting(meeting)">-->
<!--            åŠ å…¥ä¼šè®®-->
<!--          </el-button>-->
<!--          <el-button type="info" size="small" @click="viewDetails(meeting)">-->
<!--            æŸ¥çœ‹è¯¦æƒ…-->
<!--          </el-button>-->
<!--          <el-button type="warning" size="small" @click="editMeeting(meeting)">-->
<!--            ç¼–辑-->
<!--          </el-button>-->
<!--        </div>-->
      </el-card>
    </div>
    <!-- åˆ›å»ºä¼šè®®å¯¹è¯æ¡† -->
    <el-dialog v-model="dialogVisible" title="创建会议" width="600px">
      <el-form :model="meetingForm" label-width="100px">
        <el-form-item label="会议标题">
          <el-input v-model="meetingForm.title" placeholder="请输入会议标题" />
        </el-form-item>
        <el-form-item label="会议时间">
          <el-date-picker
            v-model="meetingForm.timeRange"
            type="datetimerange"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            format="YYYY-MM-DD HH:mm"
            value-format="YYYY-MM-DD HH:mm:ss"
          />
        </el-form-item>
        <el-form-item label="会议地点">
          <el-input v-model="meetingForm.location" placeholder="请输入会议地点" />
        </el-form-item>
        <el-form-item label="主持人">
          <el-input v-model="meetingForm.host" placeholder="请输入主持人姓名" />
        </el-form-item>
        <el-form-item label="会议描述">
          <el-input
            v-model="meetingForm.description"
            type="textarea"
            :rows="3"
            placeholder="请输入会议描述"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="submitMeeting">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue'
// ç»Ÿè®¡æ•°æ®
const stats = reactive({
  total: 12,
  ongoing: 3,
  completed: 7,
  upcoming: 2
})
// ä¼šè®®æ•°æ®
const meetings = ref([
  {
    id: 1,
    title: '产品开发周会',
    status: 'ongoing',
    startTime: '2024-01-15 09:00:00',
    endTime: '2024-01-15 10:30:00',
    location: '会议室A',
    host: '张经理',
    participants: ['张经理', '李工程师', '王设计师', '赵测试员'],
    agenda: [
      { time: '09:00-09:15', content: '上周工作总结', status: 'completed' },
      { time: '09:15-09:45', content: '本周开发计划', status: 'active' },
      { time: '09:45-10:00', content: '技术难点讨论', status: 'pending' },
      { time: '10:00-10:30', content: '问题反馈与解决', status: 'pending' }
    ]
  },
  {
    id: 2,
    title: '客户需求评审会',
    status: 'upcoming',
    startTime: '2024-01-15 14:00:00',
    endTime: '2024-01-15 15:00:00',
    location: '线上会议',
    host: '陈总监',
    participants: ['陈总监', '刘产品经理', '孙客户经理', '客户代表'],
    agenda: [
      { time: '14:00-14:20', content: '需求背景介绍', status: 'pending' },
      { time: '14:20-14:40', content: '功能需求分析', status: 'pending' },
      { time: '14:40-15:00', content: '技术可行性评估', status: 'pending' }
    ]
  },
  {
    id: 3,
    title: '团队建设活动',
    status: 'completed',
    startTime: '2024-01-14 16:00:00',
    endTime: '2024-01-14 18:00:00',
    location: '公司大厅',
    host: '人事部',
    participants: ['全体员工'],
    agenda: [
      { time: '16:00-16:30', content: '团队游戏', status: 'completed' },
      { time: '16:30-17:00', content: '经验分享', status: 'completed' },
      { time: '17:00-18:00', content: '自由交流', status: 'completed' }
    ]
  }
])
// å¯¹è¯æ¡†ç›¸å…³
const dialogVisible = ref(false)
const meetingForm = reactive({
  title: '',
  timeRange: [],
  location: '',
  host: '',
  description: ''
})
// èŽ·å–çŠ¶æ€ç±»åž‹
const getStatusType = (status) => {
  const statusMap = {
    'ongoing': 'success',
    'upcoming': 'warning',
    'completed': 'info'
  }
  return statusMap[status] || 'info'
}
// èŽ·å–çŠ¶æ€æ–‡æœ¬
const getStatusText = (status) => {
  const statusMap = {
    'ongoing': '进行中',
    'upcoming': '即将开始',
    'completed': '已完成'
  }
  return statusMap[status] || '未知'
}
// èŽ·å–è®®ç¨‹çŠ¶æ€ç±»åž‹
const getAgendaStatusType = (status) => {
  const statusMap = {
    'completed': 'success',
    'active': 'warning',
    'pending': 'info'
  }
  return statusMap[status] || 'info'
}
// èŽ·å–è®®ç¨‹çŠ¶æ€æ–‡æœ¬
const getAgendaStatusText = (status) => {
  const statusMap = {
    'completed': '已完成',
    'active': '进行中',
    'pending': '待开始'
  }
  return statusMap[status] || '未知'
}
// æ ¼å¼åŒ–æ—¶é—´
const formatTime = (timeStr) => {
  const date = new Date(timeStr)
  return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
}
// åˆ›å»ºä¼šè®®
const createMeeting = () => {
  dialogVisible.value = true
  // é‡ç½®è¡¨å•
  Object.assign(meetingForm, {
    title: '',
    timeRange: [],
    location: '',
    host: '',
    description: ''
  })
}
// æäº¤ä¼šè®®
const submitMeeting = () => {
  if (!meetingForm.title || !meetingForm.timeRange.length || !meetingForm.location || !meetingForm.host) {
    ElMessage.warning('请填写完整的会议信息')
    return
  }
  // åˆ›å»ºæ–°ä¼šè®®
  const newMeeting = {
    id: Date.now(),
    title: meetingForm.title,
    status: 'upcoming',
    startTime: meetingForm.timeRange[0],
    endTime: meetingForm.timeRange[1],
    location: meetingForm.location,
    host: meetingForm.host,
    participants: [meetingForm.host],
    agenda: [
      { time: '待定', content: '议程待定', status: 'pending' }
    ]
  }
  meetings.value.unshift(newMeeting)
  stats.total++
  stats.upcoming++
  ElMessage.success('会议创建成功')
  dialogVisible.value = false
}
// åŠ å…¥ä¼šè®®
const joinMeeting = (meeting) => {
  ElMessage.success(`已加入会议:${meeting.title}`)
}
// æŸ¥çœ‹è¯¦æƒ…
const viewDetails = (meeting) => {
  ElMessage.info(`查看会议详情:${meeting.title}`)
}
// ç¼–辑会议
const editMeeting = (meeting) => {
  ElMessage.info(`编辑会议:${meeting.title}`)
}
onMounted(() => {
  console.log('会议看板页面加载完成')
})
</script>
<style scoped>
.app-container {
  padding: 20px;
}
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
.page-header h2 {
  margin: 0;
  color: #303133;
}
.stats-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  margin-bottom: 30px;
}
.stat-card {
  text-align: center;
}
.stat-content {
  padding: 10px;
}
.stat-number {
  font-size: 32px;
  font-weight: bold;
  color: #409eff;
  margin-bottom: 8px;
}
.stat-label {
  font-size: 14px;
  color: #606266;
}
.meeting-list {
  display: grid;
  gap: 20px;
}
.meeting-card {
  border-radius: 8px;
}
.meeting-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 15px;
}
.meeting-title {
  display: flex;
  align-items: center;
  gap: 10px;
}
.meeting-title h3 {
  margin: 0;
  color: #303133;
}
.meeting-time {
  display: flex;
  align-items: center;
  gap: 5px;
  color: #606266;
  font-size: 14px;
}
.meeting-info {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.info-item {
  display: flex;
  align-items: center;
  gap: 5px;
  color: #606266;
  font-size: 14px;
}
.meeting-agenda {
  margin-bottom: 20px;
}
.meeting-agenda h4 {
  margin: 0 0 15px 0;
  color: #303133;
  font-size: 16px;
}
.agenda-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.agenda-item {
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 10px;
  border-radius: 6px;
  background-color: #f5f7fa;
}
.agenda-item.active {
  background-color: #fdf6ec;
  border-left: 3px solid #e6a23c;
}
.agenda-item.completed {
  background-color: #f0f9ff;
  border-left: 3px solid #409eff;
}
.agenda-time {
  font-weight: bold;
  color: #606266;
  min-width: 80px;
}
.agenda-content {
  flex: 1;
  color: #303133;
}
.meeting-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
@media (max-width: 768px) {
  .stats-cards {
    grid-template-columns: repeat(2, 1fr);
  }
  .meeting-header {
    flex-direction: column;
    gap: 10px;
  }
  .meeting-info {
    flex-direction: column;
    gap: 10px;
  }
  .meeting-actions {
    flex-direction: column;
  }
}
</style>
src/views/collaborativeApproval/noticeManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,705 @@
<template>
  <div class="app-container">
    <!-- æœç´¢è¡¨å• -->
    <div class="search_form">
      <div>
        <span class="search_title">公告标题:</span>
        <el-input
          v-model="searchForm.noticeTitle"
          style="width: 240px"
          placeholder="请输入公告标题搜索"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
        <span class="search_title ml10">公告类型:</span>
        <el-select v-model="searchForm.noticeType" clearable @change="handleQuery" style="width: 240px">
          <el-option label="放假通知" value="1" />
          <el-option label="设备维修通知" value="2" />
        </el-select>
        <span class="search_title ml10">状态:</span>
        <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px">
          <el-option label="草稿" value="0" />
          <el-option label="已发布" value="1" />
          <el-option label="已下线" value="2" />
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
        <el-button @click="resetQuery" style="margin-left: 10px">重置</el-button>
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')">新增公告</el-button>
        <el-button type="danger" plain @click="handleDelete" :disabled="!selectedIds.length">删除</el-button>
      </div>
    </div>
    <!-- é€šçŸ¥å…¬å‘Šæ¿ -->
    <div class="notice-board">
      <!-- æ”¾å‡é€šçŸ¥åŒºåŸŸ -->
      <div class="notice-section" v-if="holidayNotices.length > 0">
        <div class="section-header">
          <h3>📅 æ”¾å‡é€šçŸ¥</h3>
          <span class="section-count">{{ holidayNotices.length }}条</span>
        </div>
        <div class="notice-cards">
          <div
            v-for="notice in holidayNotices"
            :key="notice.id"
            class="notice-card holiday-card"
            :class="{ 'urgent': notice.priority === '3' }"
          >
            <div class="card-header">
              <div class="card-title">
                <el-icon class="holiday-icon"><Calendar /></el-icon>
                {{ notice.noticeTitle }}
              </div>
              <div class="card-actions">
                <el-button link type="primary" @click="handleEdit(notice)">编辑</el-button>
                <el-button link type="danger" @click="handleDelete(notice.id)">删除</el-button>
              </div>
            </div>
            <div class="card-content">
              <p>{{ notice.noticeContent }}</p>
            </div>
            <div class="card-footer">
              <div class="card-meta">
                <span class="priority" :class="'priority-' + notice.priority">
                  {{ getPriorityText(notice.priority) }}
                </span>
                <span class="status" :class="'status-' + notice.status">
                  {{ getStatusText(notice.status) }}
                </span>
              </div>
              <div class="card-info">
                <span class="creator">{{ notice.createBy }}</span>
                <span class="time">{{ notice.createTime }}</span>
              </div>
            </div>
            <div class="card-remark" v-if="notice.remark">
              <el-icon><InfoFilled /></el-icon>
              <span>{{ notice.remark }}</span>
            </div>
          </div>
        </div>
      </div>
      <!-- è®¾å¤‡ç»´ä¿®é€šçŸ¥åŒºåŸŸ -->
      <div class="notice-section" v-if="maintenanceNotices.length > 0">
        <div class="section-header">
          <h3>🔧 è®¾å¤‡ç»´ä¿®é€šçŸ¥</h3>
          <span class="section-count">{{ maintenanceNotices.length }}条</span>
        </div>
        <div class="notice-cards">
          <div
            v-for="notice in maintenanceNotices"
            :key="notice.id"
            class="notice-card maintenance-card"
            :class="{ 'urgent': notice.priority === '3' }"
          >
            <div class="card-header">
              <div class="card-title">
                <el-icon class="maintenance-icon"><Tools /></el-icon>
                {{ notice.noticeTitle }}
              </div>
              <div class="card-actions">
                <el-button link type="primary" @click="handleEdit(notice)">编辑</el-button>
                <el-button link type="danger" @click="handleDelete(notice.id)">删除</el-button>
              </div>
            </div>
            <div class="card-content">
              <p>{{ notice.noticeContent }}</p>
            </div>
            <div class="card-footer">
              <div class="card-meta">
                <span class="priority" :class="'priority-' + notice.priority">
                  {{ getPriorityText(notice.priority) }}
                </span>
                <span class="status" :class="'status-' + notice.status">
                  {{ getStatusText(notice.status) }}
                </span>
              </div>
              <div class="card-info">
                <span class="creator">{{ notice.createBy }}</span>
                <span class="time">{{ notice.createTime }}</span>
              </div>
            </div>
            <div class="card-remark" v-if="notice.remark">
              <el-icon><InfoFilled /></el-icon>
              <span>{{ notice.remark }}</span>
            </div>
          </div>
        </div>
      </div>
      <!-- ç©ºçŠ¶æ€ -->
      <div class="empty-state" v-if="filteredNotices.length === 0">
        <el-empty description="暂无通知公告" />
      </div>
    </div>
    <!-- æ–°å¢ž/编辑对话框 -->
    <el-dialog
      :title="dialogTitle"
      v-model="dialogVisible"
      width="800px"
      append-to-body
      @close="resetForm"
    >
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="公告标题" prop="noticeTitle">
              <el-input v-model="form.noticeTitle" placeholder="请输入公告标题" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公告类型" prop="noticeType">
              <el-select v-model="form.noticeType" placeholder="请选择公告类型" style="width: 100%">
                <el-option label="放假通知" value="1" />
                <el-option label="设备维修通知" value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="状态">
              <el-radio-group v-model="form.status">
                <el-radio value="0">草稿</el-radio>
                <el-radio value="1">已发布</el-radio>
                <el-radio value="2">已下线</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="优先级">
              <el-select v-model="form.priority" placeholder="请选择优先级" style="width: 100%">
                <el-option label="普通" value="1" />
                <el-option label="重要" value="2" />
                <el-option label="紧急" value="3" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="公告内容" prop="noticeContent">
              <el-input
                v-model="form.noticeContent"
                type="textarea"
                :rows="6"
                placeholder="请输入公告内容"
                maxlength="500"
                show-word-limit
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="备注">
              <el-input
                v-model="form.remark"
                type="textarea"
                :rows="3"
                placeholder="请输入备注信息"
                maxlength="200"
                show-word-limit
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
          <el-button @click="dialogVisible = false">取 æ¶ˆ</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { Search, Calendar, Tools, InfoFilled } from "@element-plus/icons-vue";
import { onMounted, ref, reactive, toRefs, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
// å“åº”式数据
const data = reactive({
  searchForm: {
    noticeTitle: "",
    noticeType: "",
    status: "",
  },
  form: {
    id: undefined,
    noticeTitle: "",
    noticeType: "",
    noticeContent: "",
    status: "0",
    priority: "1",
    remark: "",
    createBy: "",
    createTime: "",
  },
  rules: {
    noticeTitle: [
      { required: true, message: "公告标题不能为空", trigger: "blur" }
    ],
    noticeType: [
      { required: true, message: "请选择公告类型", trigger: "change" }
    ],
    noticeContent: [
      { required: true, message: "公告内容不能为空", trigger: "blur" }
    ]
  }
});
const { searchForm, form, rules } = toRefs(data);
// é¡µé¢çŠ¶æ€
const dialogVisible = ref(false);
const dialogTitle = ref("");
const selectedIds = ref([]);
const formRef = ref();
// æ¨¡æ‹Ÿæ•°æ® - æ ¹æ®æ³•定节假日设计
const mockData = [
  {
    id: 1,
    noticeTitle: "2024年春节放假通知",
    noticeType: "1",
    priority: "2",
    status: "1",
    noticeContent: "根据国务院办公厅通知,2024年春节放假安排如下:2月10日(初一)至2月17日(初八)放假调休,共8天。2月4日(星期日)、2月18日(星期日)上班。请各部门提前做好工作安排。",
    remark: "放假期间请保持手机畅通,如有紧急事务及时联系",
    createBy: "人事部",
    createTime: "2024-01-15 10:30:00"
  },
  {
    id: 2,
    noticeTitle: "2024年清明节放假通知",
    noticeType: "1",
    priority: "1",
    status: "1",
    noticeContent: "根据国务院办公厅通知,2024年清明节放假安排如下:4月4日(星期四)至4月6日(星期六)放假调休,共3天。4月7日(星期日)上班。",
    remark: "请各部门做好值班安排,确保节日期间各项工作正常运转",
    createBy: "行政部",
    createTime: "2024-01-14 14:20:00"
  },
  {
    id: 3,
    noticeTitle: "2024年劳动节放假通知",
    noticeType: "1",
    priority: "1",
    status: "1",
    noticeContent: "根据国务院办公厅通知,2024年劳动节放假安排如下:5月1日(星期三)至5月5日(星期日)放假调休,共5天。4月28日(星期日)、5月11日(星期六)上班。",
    remark: "放假前请关闭电源,锁好门窗,注意安全",
    createBy: "行政部",
    createTime: "2024-01-13 09:15:00"
  },
  {
    id: 4,
    noticeTitle: "2024年端午节放假通知",
    noticeType: "1",
    priority: "1",
    status: "1",
    noticeContent: "根据国务院办公厅通知,2024年端午节放假安排如下:6月8日(星期六)至6月10日(星期一)放假调休,共3天。6月11日(星期二)上班。",
    remark: "祝大家端午节快乐,阖家幸福!",
    createBy: "行政部",
    createTime: "2024-01-12 16:30:00"
  },
  {
    id: 5,
    noticeTitle: "2024年中秋节放假通知",
    noticeType: "1",
    priority: "1",
    status: "1",
    noticeContent: "根据国务院办公厅通知,2024年中秋节放假安排如下:9月15日(星期日)至9月17日(星期二)放假调休,共3天。9月14日(星期六)上班。",
    remark: "中秋佳节,祝大家团圆美满,幸福安康!",
    createBy: "行政部",
    createTime: "2024-01-11 11:20:00"
  },
  {
    id: 6,
    noticeTitle: "2024年国庆节放假通知",
    noticeType: "1",
    priority: "2",
    status: "1",
    noticeContent: "根据国务院办公厅通知,2024年国庆节放假安排如下:10月1日(星期二)至10月7日(星期一)放假调休,共7天。9月29日(星期日)、10月12日(星期六)上班。",
    remark: "国庆期间请各部门做好值班安排,确保安全稳定",
    createBy: "行政部",
    createTime: "2024-01-10 15:45:00"
  },
  {
    id: 7,
    noticeTitle: "A车间生产线年度检修通知",
    noticeType: "2",
    priority: "2",
    status: "1",
    noticeContent: "A车间生产线将于2024å¹´1月20日(周六)进行年度检修维护,预计停工8小时。检修内容包括:设备清洁、润滑保养、安全装置检查等。请生产部门提前调整生产计划。",
    remark: "维修期间请相关人员配合,确保检修工作安全顺利进行",
    createBy: "设备部",
    createTime: "2024-01-14 14:20:00"
  },
  {
    id: 8,
    noticeTitle: "B车间设备预防性维护通知",
    noticeType: "2",
    priority: "1",
    status: "1",
    noticeContent: "B车间关键设备将于2024å¹´1月25日进行预防性维护,预计停工4小时。维护内容包括:设备检查、零件更换、性能测试等。请相关部门配合。",
    remark: "维护完成后将进行试运行,确保设备正常运行",
    createBy: "设备部",
    createTime: "2024-01-13 09:15:00"
  }
];
// è®¡ç®—属性
const filteredNotices = computed(() => {
  let filtered = [...mockData];
  if (searchForm.value.noticeTitle) {
    filtered = filtered.filter(item =>
      item.noticeTitle.includes(searchForm.value.noticeTitle)
    );
  }
  if (searchForm.value.noticeType) {
    filtered = filtered.filter(item =>
      item.noticeType === searchForm.value.noticeType
    );
  }
  if (searchForm.value.status !== "") {
    filtered = filtered.filter(item =>
      item.status === searchForm.value.status
    );
  }
  return filtered;
});
const holidayNotices = computed(() => {
  return filteredNotices.value.filter(notice => notice.noticeType === "1");
});
const maintenanceNotices = computed(() => {
  return filteredNotices.value.filter(notice => notice.noticeType === "2");
});
// æ–¹æ³•定义
const handleQuery = () => {
  // æœç´¢åŠŸèƒ½ä¿æŒä¸å˜ï¼Œä½†æ•°æ®é€šè¿‡è®¡ç®—å±žæ€§è‡ªåŠ¨è¿‡æ»¤
};
const resetQuery = () => {
  searchForm.value = {
    noticeTitle: "",
    noticeType: "",
    status: ""
  };
};
const getPriorityText = (priority) => {
  const priorityMap = { "1": "普通", "2": "重要", "3": "紧急" };
  return priorityMap[priority] || "普通";
};
const getStatusText = (status) => {
  const statusMap = { "0": "草稿", "1": "已发布", "2": "已下线" };
  return statusMap[status] || "未知";
};
const openForm = (type) => {
  if (type === 'add') {
    dialogTitle.value = "新增公告";
    form.value = {
      id: undefined,
      noticeTitle: "",
      noticeType: "",
      noticeContent: "",
      status: "0",
      priority: "1",
      remark: "",
      createBy: userStore.name || "当前用户",
      createTime: new Date().toLocaleString()
    };
  }
  dialogVisible.value = true;
};
const handleEdit = (row) => {
  dialogTitle.value = "编辑公告";
  form.value = { ...row };
  dialogVisible.value = true;
};
const handleSelectionChange = (selection) => {
  selectedIds.value = selection.map(item => item.id);
};
const handleDelete = (id) => {
  ElMessageBox.confirm(
    "确认删除这条公告吗?",
    "提示",
    {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning"
    }
  ).then(() => {
    const index = mockData.findIndex(item => item.id === id);
    if (index > -1) {
      mockData.splice(index, 1);
      ElMessage.success("删除成功");
    }
  });
};
const submitForm = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      if (form.value.id) {
        // ç¼–辑模式
        const index = mockData.findIndex(item => item.id === form.value.id);
        if (index > -1) {
          mockData[index] = { ...form.value };
        }
        ElMessage.success("修改成功");
      } else {
        // æ–°å¢žæ¨¡å¼
        const newId = Math.max(...mockData.map(item => item.id)) + 1;
        const newNotice = {
          ...form.value,
          id: newId,
          createTime: new Date().toLocaleString()
        };
        mockData.unshift(newNotice);
        ElMessage.success("新增成功");
      }
      dialogVisible.value = false;
    }
  });
};
const resetForm = () => {
  formRef.value?.resetFields();
};
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
  // é¡µé¢åŠ è½½å®Œæˆ
});
</script>
<style scoped>
.search_form {
  background: #fff;
  padding: 20px;
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.search_title {
  font-weight: 500;
  color: #333;
  margin-right: 8px;
}
.ml10 {
  margin-left: 10px;
}
.notice-board {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 8px;
}
.notice-section {
  margin-bottom: 30px;
}
.section-header {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  padding: 0 10px;
}
.section-header h3 {
  margin: 0;
  color: #303133;
  font-size: 18px;
  font-weight: 600;
}
.section-count {
  margin-left: 10px;
  background: #409eff;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
}
.notice-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: 20px;
}
.notice-card {
  background: white;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  border-left: 4px solid transparent;
}
.notice-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
.holiday-card {
  border-left-color: #67c23a;
}
.maintenance-card {
  border-left-color: #e6a23c;
}
.urgent {
  border-left-color: #f56c6c;
  background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 15px;
}
.card-title {
  display: flex;
  align-items: center;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
  flex: 1;
}
.holiday-icon {
  color: #67c23a;
  margin-right: 8px;
  font-size: 18px;
}
.maintenance-icon {
  color: #e6a23c;
  margin-right: 8px;
  font-size: 18px;
}
.card-actions {
  display: flex;
  gap: 8px;
}
.card-content {
  margin-bottom: 15px;
}
.card-content p {
  margin: 0;
  color: #606266;
  line-height: 1.6;
  font-size: 14px;
}
.card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
.card-meta {
  display: flex;
  gap: 8px;
}
.priority, .status {
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
}
.priority-1 { background: #f0f9ff; color: #0369a1; }
.priority-2 { background: #fef3c7; color: #d97706; }
.priority-3 { background: #fef2f2; color: #dc2626; }
.status-0 { background: #f3f4f6; color: #6b7280; }
.status-1 { background: #d1fae5; color: #059669; }
.status-2 { background: #fef3c7; color: #d97706; }
.card-info {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-size: 12px;
  color: #909399;
}
.creator {
  font-weight: 500;
  margin-bottom: 2px;
}
.card-remark {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: #f8f9fa;
  border-radius: 6px;
  font-size: 12px;
  color: #606266;
  border-left: 3px solid #409eff;
}
.empty-state {
  text-align: center;
  padding: 60px 20px;
}
.dialog-footer {
  text-align: right;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .notice-cards {
    grid-template-columns: 1fr;
  }
  .search_form {
    flex-direction: column;
    gap: 15px;
  }
  .search_form > div {
    width: 100%;
  }
}
</style>
src/views/collaborativeApproval/warningSystem/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,307 @@
<template>
  <div class="warning-system">
    <h2>预警联动机制</h2>
    <!-- ç»Ÿè®¡å¡ç‰‡ -->
    <div class="stats">
      <div class="stat-card red">
        <span class="number">2</span>
        <span class="label">红色预警</span>
      </div>
      <div class="stat-card orange">
        <span class="number">1</span>
        <span class="label">橙色预警</span>
      </div>
      <div class="stat-card yellow">
        <span class="number">1</span>
        <span class="label">黄色预警</span>
      </div>
      <div class="stat-card green">
        <span class="number">1</span>
        <span class="label">绿色预警</span>
      </div>
    </div>
    <!-- é¢„警列表 -->
    <div class="warning-list">
      <h3>预警列表</h3>
      <table>
        <thead>
          <tr>
            <th>编号</th>
            <th>标题</th>
            <th>类型</th>
            <th>等级</th>
            <th>状态</th>
            <th>责任人</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="warning in warnings" :key="warning.id">
            <td>{{ warning.id }}</td>
            <td>{{ warning.title }}</td>
            <td>{{ warning.type }}</td>
            <td>
              <span :class="['level-tag', warning.level]">
                {{ warning.levelText }}
              </span>
            </td>
            <td>
              <span :class="['status-tag', warning.status]">
                {{ warning.statusText }}
              </span>
            </td>
            <td>{{ warning.responsible }}</td>
            <td>
              <button @click="viewDetail(warning)">查看详情</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    <!-- è¯¦æƒ…对话框 -->
    <div v-if="showDetail" class="modal">
      <div class="modal-content">
        <h3>预警详情</h3>
        <div v-if="currentWarning">
          <p><strong>编号:</strong>{{ currentWarning.id }}</p>
          <p><strong>标题:</strong>{{ currentWarning.title }}</p>
          <p><strong>类型:</strong>{{ currentWarning.type }}</p>
          <p><strong>等级:</strong>{{ currentWarning.levelText }}</p>
          <p><strong>描述:</strong>{{ currentWarning.description }}</p>
          <p><strong>影响:</strong>{{ currentWarning.impact }}</p>
          <p><strong>建议:</strong>{{ currentWarning.suggestions }}</p>
        </div>
        <button @click="showDetail = false">关闭</button>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'WarningSystem',
  data() {
    return {
      showDetail: false,
      currentWarning: null,
      warnings: [
        {
          id: 'W001',
          title: '项目预算超支预警',
          type: '财务预警',
          level: 'red',
          levelText: '红色预警',
          status: 'pending',
          statusText: '待处理',
          responsible: '张经理',
          description: 'A项目预算执行率已达95%,预计将超出预算范围。',
          impact: '影响项目整体财务指标,可能导致项目亏损',
          suggestions: '暂停非必要支出,优化资源配置,申请预算调整'
        },
        {
          id: 'W002',
          title: '合同到期预警',
          type: '合规预警',
          level: 'orange',
          levelText: '橙色预警',
          status: 'processing',
          statusText: '处理中',
          responsible: '李主管',
          description: '与供应商B的合同将于2024å¹´1月25日到期。',
          impact: '影响供应链稳定性,可能导致服务中断',
          suggestions: '评估供应商表现,准备续签材料,制定备选方案'
        },
        {
          id: 'W003',
          title: '设备维护预警',
          type: '运营预警',
          level: 'yellow',
          levelText: '黄色预警',
          status: 'pending',
          statusText: '待处理',
          responsible: '王工程师',
          description: '生产线设备C已运行8000小时,接近维护周期。',
          impact: '可能影响生产效率和产品质量',
          suggestions: '安排维护时间,准备备件,制定维护计划'
        },
        {
          id: 'W004',
          title: '人员配置预警',
          type: '运营预警',
          level: 'green',
          levelText: '绿色预警',
          status: 'resolved',
          statusText: '已解决',
          responsible: 'èµµHR',
          description: '技术部门人员配置充足,项目进度正常。',
          impact: '无负面影响',
          suggestions: '继续监控人员配置情况'
        },
        {
          id: 'W005',
          title: '质量事故预警',
          type: '运营预警',
          level: 'red',
          levelText: '红色预警',
          status: 'pending',
          statusText: '待处理',
          responsible: '陈总监',
          description: '产品D在客户现场出现质量问题。',
          impact: '影响客户满意度,可能造成经济损失',
          suggestions: '立即召回问题产品,分析原因,制定改进措施'
        }
      ]
    }
  },
  methods: {
    viewDetail(warning) {
      this.currentWarning = warning
      this.showDetail = true
    }
  }
}
</script>
<style scoped>
.warning-system {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}
h2 {
  color: #333;
  margin-bottom: 30px;
}
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
  margin-bottom: 30px;
}
.stat-card {
  padding: 20px;
  border-radius: 8px;
  color: white;
  text-align: center;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.stat-card.red { background: linear-gradient(135deg, #ff6b6b, #ee5a52); }
.stat-card.orange { background: linear-gradient(135deg, #ffa726, #ff9800); }
.stat-card.yellow { background: linear-gradient(135deg, #ffd54f, #ffc107); }
.stat-card.green { background: linear-gradient(135deg, #66bb6a, #4caf50); }
.stat-card .number {
  display: block;
  font-size: 32px;
  font-weight: bold;
  margin-bottom: 8px;
}
.stat-card .label {
  font-size: 14px;
  opacity: 0.9;
}
.warning-list h3 {
  margin-bottom: 20px;
  color: #333;
}
table {
  width: 100%;
  border-collapse: collapse;
  background: white;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
th, td {
  padding: 12px;
  text-align: left;
  border-bottom: 1px solid #eee;
}
th {
  background: #f8f9fa;
  font-weight: 600;
  color: #333;
}
.level-tag, .status-tag {
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  color: white;
}
.level-tag.red { background: #f56c6c; }
.level-tag.orange { background: #e6a23c; }
.level-tag.yellow { background: #e6a23c; }
.level-tag.green { background: #67c23a; }
.status-tag.pending { background: #f56c6c; }
.status-tag.processing { background: #e6a23c; }
.status-tag.resolved { background: #67c23a; }
button {
  padding: 6px 12px;
  margin: 0 4px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  background: #409eff;
  color: white;
}
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-content {
  background: white;
  padding: 30px;
  border-radius: 8px;
  max-width: 600px;
  width: 90%;
  max-height: 80vh;
  overflow-y: auto;
}
.modal-content h3 {
  margin-bottom: 20px;
  color: #333;
}
.modal-content p {
  margin-bottom: 15px;
  line-height: 1.6;
}
.modal-content strong {
  color: #333;
}
.modal-content button {
  background: #409eff;
  color: white;
  padding: 10px 20px;
  font-size: 14px;
}
</style>
src/views/demo/fakePage/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,248 @@
<template>
  <div class="app-container">
    <el-card shadow="never">
      <div class="toolbar">
        <el-input
          v-model="query.keyword"
          placeholder="搜索名称/类别"
          clearable
          style="width: 240px"
          @keyup.enter="handleSearch"
        />
        <el-select
          v-model="query.status"
          placeholder="状态"
          clearable
          style="width: 140px; margin-left: 12px"
        >
          <el-option label="启用" value="启用" />
          <el-option label="停用" value="停用" />
        </el-select>
        <el-button type="primary" style="margin-left: 12px" @click="handleSearch">查询</el-button>
        <el-button @click="resetQuery">重置</el-button>
        <el-button type="success" plain style="float: right" @click="openCreate">新增</el-button>
      </div>
      <el-table :data="pagedList" border style="width: 100%" height="480">
        <el-table-column prop="id" label="编号" width="90" sortable />
        <el-table-column prop="name" label="名称" min-width="140" />
        <el-table-column prop="category" label="类别" width="120" />
        <el-table-column prop="stock" label="库存" width="100" sortable />
        <el-table-column prop="price" label="单价(Â¥)" width="120">
          <template #default="scope">{{ formatPrice(scope.row.price) }}</template>
        </el-table-column>
        <el-table-column label="状态" width="120">
          <template #default="scope">
            <el-tag :type="scope.row.status === '启用' ? 'success' : 'info'">{{ scope.row.status }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="updatedAt" label="更新时间" min-width="160" />
        <el-table-column label="操作" width="180" fixed="right">
          <template #default="scope">
            <el-button link type="primary" @click="openEdit(scope.row)">编辑</el-button>
            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination">
        <el-pagination
          background
          layout="total, sizes, prev, pager, next, jumper"
          :total="filteredList.length"
          :page-sizes="[5, 10, 20, 50]"
          :page-size="pager.pageSize"
          :current-page="pager.pageNum"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </el-card>
    <el-dialog v-model="dialogVisible" :title="isEdit ? '编辑' : '新增'" width="520px">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="90px">
        <el-form-item label="名称" prop="name">
          <el-input v-model="form.name" placeholder="请输入名称" />
        </el-form-item>
        <el-form-item label="类别" prop="category">
          <el-select v-model="form.category" placeholder="请选择类别" style="width: 100%">
            <el-option label="原料" value="原料" />
            <el-option label="半成品" value="半成品" />
            <el-option label="成品" value="成品" />
          </el-select>
        </el-form-item>
        <el-form-item label="库存" prop="stock">
          <el-input v-model.number="form.stock" type="number" min="0" />
        </el-form-item>
        <el-form-item label="单价(Â¥)" prop="price">
          <el-input v-model.number="form.price" type="number" min="0" step="0.01" />
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="form.status">
            <el-radio label="启用">启用</el-radio>
            <el-radio label="停用">停用</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive, computed, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
defineOptions({ name: 'FakePage' })
const query = reactive({
  keyword: '',
  status: ''
})
const pager = reactive({
  pageNum: 1,
  pageSize: 10
})
const allList = ref(generateMockData())
const filteredList = computed(() => {
  const keyword = (query.keyword || '').trim()
  const status = query.status
  return allList.value.filter(item => {
    const hitKeyword = !keyword || item.name.includes(keyword) || item.category.includes(keyword)
    const hitStatus = !status || item.status === status
    return hitKeyword && hitStatus
  })
})
const pagedList = computed(() => {
  const start = (pager.pageNum - 1) * pager.pageSize
  const end = start + pager.pageSize
  return filteredList.value.slice(start, end)
})
function handleSearch() {
  pager.pageNum = 1
}
function resetQuery() {
  query.keyword = ''
  query.status = ''
  pager.pageNum = 1
}
function handleSizeChange(size) {
  pager.pageSize = size
  pager.pageNum = 1
}
function handleCurrentChange(page) {
  pager.pageNum = page
}
function formatPrice(val) {
  return Number(val || 0).toFixed(2)
}
// æ–°å¢ž/编辑
const dialogVisible = ref(false)
const isEdit = ref(false)
const formRef = ref()
const form = reactive({ id: null, name: '', category: '', stock: 0, price: 0, status: '启用' })
const rules = {
  name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
  category: [{ required: true, message: '请选择类别', trigger: 'change' }],
  stock: [{ required: true, message: '请输入库存', trigger: 'blur' }],
  price: [{ required: true, message: '请输入单价', trigger: 'blur' }]
}
function openCreate() {
  isEdit.value = false
  Object.assign(form, { id: null, name: '', category: '', stock: 0, price: 0, status: '启用' })
  dialogVisible.value = true
  nextTick(() => formRef.value?.clearValidate?.())
}
function openEdit(row) {
  isEdit.value = true
  Object.assign(form, JSON.parse(JSON.stringify(row)))
  dialogVisible.value = true
  nextTick(() => formRef.value?.clearValidate?.())
}
function submitForm() {
  formRef.value?.validate?.((valid) => {
    if (!valid) return
    if (isEdit.value) {
      const index = allList.value.findIndex(x => x.id === form.id)
      if (index > -1) {
        allList.value[index] = { ...form, updatedAt: nowString() }
        ElMessage.success('已保存')
      }
    } else {
      const newId = Date.now()
      allList.value.unshift({ ...form, id: newId, updatedAt: nowString() })
      ElMessage.success('已新增')
    }
    dialogVisible.value = false
  })
}
function handleDelete(row) {
  ElMessageBox.confirm(`确认删除【${row.name}】吗?`, '提示', { type: 'warning' })
    .then(() => {
      allList.value = allList.value.filter(x => x.id !== row.id)
      ElMessage.success('已删除')
    })
    .catch(() => {})
}
function generateMockData() {
  const categories = ['原料', '半成品', '成品']
  const statusOptions = ['启用', '停用']
  const list = []
  for (let i = 1; i <= 36; i++) {
    list.push({
      id: i,
      name: `物料-${i.toString().padStart(3, '0')}`,
      category: categories[i % categories.length],
      stock: Math.floor(Math.random() * 1000),
      price: (Math.random() * 500 + 10).toFixed(2),
      status: statusOptions[i % 2],
      updatedAt: nowString()
    })
  }
  return list
}
function nowString() {
  const d = new Date()
  const yyyy = d.getFullYear()
  const MM = String(d.getMonth() + 1).padStart(2, '0')
  const dd = String(d.getDate()).padStart(2, '0')
  const hh = String(d.getHours()).padStart(2, '0')
  const mm = String(d.getMinutes()).padStart(2, '0')
  const ss = String(d.getSeconds()).padStart(2, '0')
  return `${yyyy}-${MM}-${dd} ${hh}:${mm}:${ss}`
}
</script>
<style scoped>
.toolbar {
  margin-bottom: 12px;
}
.pagination {
  margin-top: 12px;
  text-align: right;
}
</style>
src/views/energyManagement/dynamicEnergySaving/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,659 @@
<template>
  <div class="app-container">
    <!-- è¾¹ç¼˜è®¡ç®—状态监控 -->
    <el-row :gutter="20" class="status-section">
      <el-col :span="8">
        <el-card class="status-card">
          <div class="status-item">
            <div class="status-icon">
              <el-icon><Monitor /></el-icon>
            </div>
            <div class="status-info">
              <div class="status-title">边缘服务器状态</div>
              <div class="status-value" :class="edgeServerStatus.status">
                {{ edgeServerStatus.status === 'online' ? '在线' : '离线' }}
              </div>
              <div class="status-detail">最后心跳: {{ formatTime(edgeServerStatus.lastHeartbeat) }}</div>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card class="status-card">
          <div class="status-item">
            <div class="status-icon">
              <el-icon><Cpu /></el-icon>
            </div>
            <div class="status-info">
              <div class="status-title">模型运行状态</div>
              <div class="status-value" :class="modelStatus.status">
                {{ modelStatus.status === 'running' ? '运行中' : '已停止' }}
              </div>
              <div class="status-detail">运行模型: {{ modelStatus.modelCount }}个</div>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card class="status-card">
          <div class="status-item">
            <div class="status-icon">
              <el-icon><TrendCharts /></el-icon>
            </div>
            <div class="status-info">
              <div class="status-title">节能效果</div>
              <div class="status-value success">{{ energySavingRate.toFixed(1) }}%</div>
              <div class="status-detail">累计节能: {{ totalEnergySaved.toFixed(1) }}kWh</div>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>
    <!-- æ³¨æ°´æ³µé¢‘率优化控制 -->
    <el-card class="control-section">
      <template #header>
        <span>注水泵频率优化控制</span>
      </template>
      <el-row :gutter="20">
        <el-col :span="12">
          <div class="pump-control">
            <h4>实时参数监控</h4>
            <el-form label-width="120px">
              <el-form-item label="地层压力 (MPa)">
                <el-input v-model="pumpData.formationPressure" readonly>
                  <template #append>MPa</template>
                </el-input>
              </el-form-item>
              <el-form-item label="当前泵速 (Hz)">
                <el-input v-model="pumpData.currentFrequency" readonly>
                  <template #append>Hz</template>
                </el-input>
              </el-form-item>
              <el-form-item label="优化后泵速 (Hz)">
                <el-input v-model="pumpData.optimizedFrequency" readonly>
                  <template #append>Hz</template>
                </el-input>
              </el-form-item>
              <el-form-item label="能耗降低">
                <el-progress
                  :percentage="pumpData.energyReduction"
                  :color="getProgressColor"
                  :format="format => `${format}%`"
                />
              </el-form-item>
              <el-form-item label="流量 (m³/h)">
                <el-input v-model="pumpData.flowRate" readonly>
                  <template #append>m³/h</template>
                </el-input>
              </el-form-item>
              <el-form-item label="功率 (kW)">
                <el-input v-model="pumpData.power" readonly>
                  <template #append>kW</template>
                </el-input>
              </el-form-item>
            </el-form>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="pump-chart">
            <h4>频率优化趋势</h4>
            <div ref="frequencyChart" style="height: 300px;"></div>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="20" class="control-buttons">
        <el-col :span="24">
          <el-button
            type="primary"
            :disabled="!canControl"
            @click="applyOptimization"
          >
            åº”用优化设置
          </el-button>
          <el-button
            type="warning"
            :disabled="!canControl"
            @click="emergencyStop"
          >
            ç´§æ€¥åœæ­¢
          </el-button>
          <el-button
            type="info"
            @click="showOptimizationHistory"
          >
            ä¼˜åŒ–历史
          </el-button>
          <el-button
            type="success"
            @click="toggleAutoRefresh"
          >
            {{ autoRefreshStatus ? '停止自动刷新' : '开启自动刷新' }}
          </el-button>
        </el-col>
      </el-row>
    </el-card>
    <!-- è¾¹ç¼˜è®¡ç®—模型配置 -->
    <el-card class="model-section">
      <template #header>
        <span>边缘计算模型配置</span>
      </template>
      <el-table :data="modelConfigs" style="width: 100%">
        <el-table-column prop="modelName" label="模型名称" />
        <el-table-column prop="version" label="版本" />
        <el-table-column prop="status" label="状态">
          <template #default="scope">
            <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'">
              {{ scope.row.status === 'active' ? '激活' : '待机' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="accuracy" label="准确率" />
        <el-table-column prop="lastUpdate" label="最后更新" />
        <el-table-column label="操作">
          <template #default="scope">
            <el-button
              size="small"
              @click="updateModel(scope.row)"
            >
              æ›´æ–°æ¨¡åž‹
            </el-button>
            <el-button
              size="small"
              type="danger"
              @click="deleteModel(scope.row)"
            >
              åˆ é™¤
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
    <!-- èƒ½è€—分析图表 -->
    <el-card class="analysis-section">
      <template #header>
        <span>能耗分析</span>
      </template>
      <el-row :gutter="20">
        <el-col :span="12">
          <div ref="energyChart" style="height: 400px;"></div>
        </el-col>
        <el-col :span="12">
          <div ref="savingChart" style="height: 400px;"></div>
        </el-col>
      </el-row>
    </el-card>
    <!-- ä¼˜åŒ–历史对话框 -->
    <el-dialog v-model="historyDialogVisible" title="优化历史记录" width="80%">
      <el-table :data="optimizationHistory" style="width: 100%">
        <el-table-column prop="timestamp" label="时间" />
        <el-table-column prop="formationPressure" label="地层压力 (MPa)" />
        <el-table-column prop="oldFrequency" label="原频率 (Hz)" />
        <el-table-column prop="newFrequency" label="新频率 (Hz)" />
        <el-table-column prop="energySaved" label="节能 (kWh)" />
        <el-table-column prop="status" label="状态">
          <template #default="scope">
            <el-tag :type="scope.row.status === 'success' ? 'success' : 'warning'">
              {{ scope.row.status === 'success' ? '成功' : '失败' }}
            </el-tag>
          </template>
        </el-table-column>
      </el-table>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Monitor, Cpu, TrendCharts } from '@element-plus/icons-vue'
import * as echarts from 'echarts'
// å“åº”式数据
const edgeServerStatus = ref({ status: 'online', lastHeartbeat: Date.now() })
const modelStatus = ref({ status: 'running', modelCount: 3 })
const energySavingRate = ref(15.8)
const totalEnergySaved = ref(1250.5)
const pumpData = ref({
  formationPressure: 25.6,
  currentFrequency: 45.2,
  optimizedFrequency: 42.1,
  energyReduction: 23,
  flowRate: 180.5,
  power: 85.3
})
const modelConfigs = ref([
  {
    modelName: '注水泵频率优化模型',
    version: 'v2.1.0',
    status: 'active',
    accuracy: '94.2%',
    lastUpdate: '2024-01-15 14:30:00'
  },
  {
    modelName: '地层压力预测模型',
    version: 'v1.8.5',
    status: 'active',
    accuracy: '91.7%',
    lastUpdate: '2024-01-14 09:15:00'
  },
  {
    modelName: '能耗分析模型',
    version: 'v2.0.3',
    status: 'standby',
    accuracy: '89.3%',
    lastUpdate: '2024-01-13 16:45:00'
  }
])
const historyDialogVisible = ref(false)
const optimizationHistory = ref([])
// å›¾è¡¨å¼•用
const frequencyChart = ref(null)
const energyChart = ref(null)
const savingChart = ref(null)
// è‡ªåŠ¨åˆ·æ–°ç›¸å…³
const autoRefreshStatus = ref(true)
const autoRefreshTimer = ref(null)
const chartInstances = ref([])
// è®¡ç®—属性
const canControl = computed(() => {
  return edgeServerStatus.value.status === 'online' && modelStatus.value.status === 'running'
})
const getProgressColor = computed(() => {
  return (percentage) => {
    if (percentage < 20) return '#909399'
    if (percentage < 40) return '#E6A23C'
    if (percentage < 60) return '#409EFF'
    return '#67C23A'
  }
})
// ç”Ÿæˆæ¨¡æ‹Ÿæ•°æ®
const generateMockData = () => {
  // ç”Ÿæˆéšæœºåœ°å±‚压力 (20-30 MPa)
  const formationPressure = 20 + Math.random() * 10
  // æ ¹æ®åœ°å±‚压力计算优化频率
  const baseFrequency = 40 + (formationPressure - 25) * 2
  const currentFrequency = baseFrequency + (Math.random() - 0.5) * 4
  const optimizedFrequency = Math.max(35, baseFrequency - Math.random() * 3)
  // è®¡ç®—能耗降低
  const energyReduction = Math.round((currentFrequency - optimizedFrequency) / currentFrequency * 100)
  // è®¡ç®—流量和功率
  const flowRate = 150 + Math.random() * 60
  const power = 70 + Math.random() * 30
  // æ›´æ–°æ³µæ•°æ®
  pumpData.value = {
    formationPressure: parseFloat(formationPressure.toFixed(1)),
    currentFrequency: parseFloat(currentFrequency.toFixed(1)),
    optimizedFrequency: parseFloat(optimizedFrequency.toFixed(1)),
    energyReduction: Math.min(energyReduction, 35),
    flowRate: parseFloat(flowRate.toFixed(1)),
    power: parseFloat(power.toFixed(1))
  }
  // æ›´æ–°èŠ‚èƒ½æ•ˆæžœ
  energySavingRate.value = 12 + Math.random() * 8
  totalEnergySaved.value += Math.random() * 2
  // æ›´æ–°è¾¹ç¼˜æœåŠ¡å™¨çŠ¶æ€
  edgeServerStatus.value.lastHeartbeat = Date.now()
  // éšæœºæ›´æ–°æ¨¡åž‹çŠ¶æ€
  if (Math.random() > 0.95) {
    modelStatus.value.modelCount = Math.max(1, modelStatus.value.modelCount + (Math.random() > 0.5 ? 1 : -1))
  }
  // æ·»åŠ ä¼˜åŒ–åŽ†å²è®°å½•
  if (Math.random() > 0.7) {
    addOptimizationHistory()
  }
  // æ›´æ–°å›¾è¡¨æ•°æ®
  updateCharts()
}
// æ·»åŠ ä¼˜åŒ–åŽ†å²è®°å½•
const addOptimizationHistory = () => {
  const timestamp = new Date().toLocaleString()
  const record = {
    timestamp,
    formationPressure: pumpData.value.formationPressure,
    oldFrequency: pumpData.value.currentFrequency,
    newFrequency: pumpData.value.optimizedFrequency,
    energySaved: parseFloat((Math.random() * 5 + 1).toFixed(2)),
    status: Math.random() > 0.1 ? 'success' : 'failed'
  }
  optimizationHistory.value.unshift(record)
  // ä¿æŒæœ€å¤š100条记录
  if (optimizationHistory.value.length > 100) {
    optimizationHistory.value = optimizationHistory.value.slice(0, 100)
  }
}
// æ›´æ–°å›¾è¡¨æ•°æ®
const updateCharts = () => {
  chartInstances.value.forEach(instance => {
    if (instance && instance.setOption) {
      // è¿™é‡Œå¯ä»¥æ›´æ–°å›¾è¡¨æ•°æ®
      // ä¸ºäº†ç®€åŒ–,我们只是重新初始化图表
    }
  })
}
// æ–¹æ³•
const refreshData = () => {
  generateMockData()
  ElMessage.success('数据刷新成功')
}
const applyOptimization = async () => {
  try {
    await ElMessageBox.confirm('确定要应用当前的优化设置吗?', '确认操作', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
    // åº”用优化设置
    pumpData.value.currentFrequency = pumpData.value.optimizedFrequency
    ElMessage.success('优化设置应用成功')
    refreshData()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('应用优化设置失败')
    }
  }
}
const emergencyStop = async () => {
  try {
    await ElMessageBox.confirm('确定要紧急停止所有注水泵吗?', '紧急操作', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'error'
    })
    // æ‰§è¡Œç´§æ€¥åœæ­¢é€»è¾‘
    pumpData.value.currentFrequency = 0
    pumpData.value.optimizedFrequency = 0
    ElMessage.success('紧急停止执行成功')
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('紧急停止执行失败')
    }
  }
}
const showOptimizationHistory = () => {
  historyDialogVisible.value = true
}
const updateModel = (model) => {
  ElMessage.info(`更新模型: ${model.modelName}`)
}
const deleteModel = async (model) => {
  try {
    await ElMessageBox.confirm(`确定要删除模型 ${model.modelName} å—?`, '确认删除', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
    const index = modelConfigs.value.findIndex(m => m.modelName === model.modelName)
    if (index > -1) {
      modelConfigs.value.splice(index, 1)
      ElMessage.success('模型删除成功')
    }
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('模型删除失败')
    }
  }
}
const toggleAutoRefresh = () => {
  autoRefreshStatus.value = !autoRefreshStatus.value
  if (autoRefreshStatus.value) {
    startAutoRefresh()
    ElMessage.success('自动刷新已开启')
  } else {
    stopAutoRefresh()
    ElMessage.info('自动刷新已关闭')
  }
}
const startAutoRefresh = () => {
  stopAutoRefresh() // å…ˆåœæ­¢ä¹‹å‰çš„定时器
  autoRefreshTimer.value = setInterval(() => {
    generateMockData()
  }, 60000) // 1分钟 = 60000毫秒
}
const stopAutoRefresh = () => {
  if (autoRefreshTimer.value) {
    clearInterval(autoRefreshTimer.value)
    autoRefreshTimer.value = null
  }
}
const formatTime = (timestamp) => {
  return new Date(timestamp).toLocaleTimeString()
}
// åˆå§‹åŒ–图表
const initCharts = () => {
  // é¢‘率优化趋势图
  const frequencyChartInstance = echarts.init(frequencyChart.value)
  const frequencyOption = {
    title: { text: '泵频率优化趋势' },
    tooltip: { trigger: 'axis' },
    legend: { data: ['当前频率', '优化频率', '地层压力'] },
    xAxis: { type: 'category', data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00'] },
    yAxis: [
      { type: 'value', name: '频率 (Hz)' },
      { type: 'value', name: '压力 (MPa)' }
    ],
    series: [
      {
        name: '当前频率',
        type: 'line',
        data: [45.2, 44.8, 45.5, 45.1, 44.9, 45.2]
      },
      {
        name: '优化频率',
        type: 'line',
        data: [42.1, 41.8, 42.3, 41.9, 41.7, 42.1]
      },
      {
        name: '地层压力',
        type: 'line',
        yAxisIndex: 1,
        data: [25.6, 25.8, 26.1, 25.9, 25.7, 25.6]
      }
    ]
  }
  frequencyChartInstance.setOption(frequencyOption)
  chartInstances.value.push(frequencyChartInstance)
  // èƒ½è€—分析图
  const energyChartInstance = echarts.init(energyChart.value)
  const energyOption = {
    title: { text: '日能耗对比' },
    tooltip: { trigger: 'item' },
    legend: { orient: 'vertical', left: 'left',top: 'center' },
    series: [
      {
        name: '能耗分布',
        type: 'pie',
        radius: '50%',
        data: [
          { value: 45, name: '注水泵' },
          { value: 25, name: '照明系统' },
          { value: 20, name: '通风系统' },
          { value: 10, name: '其他设备' }
        ]
      }
    ]
  }
  energyChartInstance.setOption(energyOption)
  chartInstances.value.push(energyChartInstance)
  // èŠ‚èƒ½æ•ˆæžœå›¾
  const savingChartInstance = echarts.init(savingChart.value)
  const savingOption = {
    title: { text: '节能效果趋势' },
    tooltip: { trigger: 'axis' },
    xAxis: { type: 'category', data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] },
    yAxis: { type: 'value', name: '节能率 (%)' },
    series: [
      {
        name: '节能率',
        type: 'bar',
        data: [12.5, 15.2, 18.7, 16.3, 19.1, 17.8, 15.8]
      }
    ]
  }
  savingChartInstance.setOption(savingOption)
  chartInstances.value.push(savingChartInstance)
}
// ç”Ÿæˆåˆå§‹åŽ†å²æ•°æ®
const generateInitialHistory = () => {
  for (let i = 0; i < 20; i++) {
    const timestamp = new Date(Date.now() - i * 3600000).toLocaleString()
    const record = {
      timestamp,
      formationPressure: parseFloat((20 + Math.random() * 10).toFixed(1)),
      oldFrequency: parseFloat((40 + Math.random() * 10).toFixed(1)),
      newFrequency: parseFloat((35 + Math.random() * 8).toFixed(1)),
      energySaved: parseFloat((Math.random() * 5 + 1).toFixed(2)),
      status: Math.random() > 0.1 ? 'success' : 'failed'
    }
    optimizationHistory.value.push(record)
  }
}
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
  initCharts()
  generateInitialHistory()
  refreshData()
  if (autoRefreshStatus.value) {
    startAutoRefresh()
  }
})
onUnmounted(() => {
  stopAutoRefresh()
  chartInstances.value.forEach(instance => {
    if (instance && instance.dispose) {
      instance.dispose()
    }
  })
})
</script>
<style scoped>
.app-container {
  padding: 20px;
}
.status-section {
  margin-bottom: 20px;
}
.status-card {
  height: 140px;
}
.status-item {
  display: flex;
  align-items: center;
  height: 100%;
}
.status-icon {
  font-size: 48px;
  margin-right: 20px;
  color: #409EFF;
}
.status-info {
  flex: 1;
}
.status-title {
  font-size: 14px;
  color: #909399;
  margin-bottom: 8px;
}
.status-value {
  font-size: 24px;
  font-weight: bold;
  margin-bottom: 8px;
}
.status-detail {
  font-size: 12px;
  color: #909399;
}
.status-value.online,
.status-value.running {
  color: #67C23A;
}
.status-value.offline,
.status-value.stopped {
  color: #F56C6C;
}
.status-value.success {
  color: #67C23A;
}
.control-section,
.model-section,
.analysis-section {
  margin-bottom: 20px;
}
.pump-control h4,
.pump-chart h4 {
  margin-bottom: 20px;
  color: #303133;
}
.control-buttons {
  margin-top: 20px;
  text-align: center;
}
.control-buttons .el-button {
  margin: 0 10px;
}
</style>
src/views/energyManagement/energyArea/index.vue
@@ -2,39 +2,72 @@
  <div class="app-container product-view">
    <div class="left">
      <div>
        <el-input v-model="search" style="width: 210px" placeholder="输入关键字进行搜索" @change="searchFilter"
          @clear="searchFilter" clearable prefix-icon="Search" />
        <el-button type="primary" @click="openProDia('add')" style="margin-left: 10px">新增父区域</el-button>
        <el-input
          v-model="search"
          style="width: 210px"
          placeholder="输入关键字进行搜索"
          @change="searchFilter"
          @clear="searchFilter"
          clearable
          prefix-icon="Search"
        />
        <el-button
          type="primary"
          @click="openProDia('addOne')"
          style="margin-left: 10px"
          >新增父区域</el-button
        >
      </div>
      <div ref="containerRef">
        <el-tree ref="tree" v-loading="treeLoad" :data="list" @node-click="handleNodeClick"
          :expand-on-click-node="false" default-expand-all :default-expanded-keys="expandedKeys" :draggable="true"
          :filter-node-method="filterNode" :props="{ children: 'children', label: 'label' }" highlight-current
          node-key="id" style="
        <el-tree
          ref="tree"
          v-loading="treeLoad"
          :data="list"
          @node-click="handleNodeClick"
          :expand-on-click-node="false"
          default-expand-all
          :default-expanded-keys="expandedKeys"
          :draggable="true"
          :filter-node-method="filterNode"
          :props="{ children: 'children', label: 'label' }"
          highlight-current
          node-key="id"
          style="
            height: calc(100vh - 190px);
            overflow-y: scroll;
            scrollbar-width: none;
          ">
            margin-top: 10px;
          "
        >
          <template #default="{ node, data }">
            <div class="custom-tree-node">
              <span class="tree-node-content">
                <el-icon class="orange-icon">
                  <component :is="data.children && data.children.length > 0
                    ? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" />
                  ? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" />
                </el-icon>
                {{ data.fuId==null ? data.id : '' || data.areaName }}
                {{ data.label }}
              </span>
              <div>
                <el-button v-if="node.level < 2" type="primary" link @click="openProDia('edit', data)">
                <el-button
                  type="primary"
                  link
                  @click="openProDia('edit', data)"
                >
                  ç¼–辑
                </el-button>
                <!-- <el-button v-if="node.level < 2" type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
                <el-button type="primary" link @click="openModelDia('add','', data.id)">
                  æ·»åŠ å­åŒºåŸŸ
                </el-button> -->
                <!-- <el-button v-if="!node.childNodes.length" style="margin-left: 4px" type="danger" link
                  @click="remove(node, data)">
                </el-button>
                <el-button
                  v-if="!node.childNodes.length"
                  style="margin-left: 4px"
                  type="danger"
                  link
                  @click="remove(node, data)"
                >
                  åˆ é™¤
                </el-button> -->
                </el-button>
              </div>
            </div>
          </template>
@@ -42,26 +75,47 @@
      </div>
    </div>
    <div class="right">
      <div style="margin-bottom: 10px">
      <div style="margin-bottom: 10px" v-if="isShowButton">
        <el-button type="primary" @click="openModelDia('add')">
          æ–°å¢žåŒºåŸŸ
          æ–°å¢žå­åŒºåŸŸ
        </el-button>
        <ImportExcel @uploadSuccess="getModelList" />
        <el-button type="danger" @click="handleDelete" style="margin-left: 10px" plain>
        <el-button
          type="danger"
          @click="handleDelete"
          style="margin-left: 10px"
          plain
        >
          åˆ é™¤
        </el-button>
      </div>
      <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true"
        @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination"></PIMTable>
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
      ></PIMTable>
    </div>
    <el-dialog v-model="productDia" title="区域" width="400px" @keydown.enter.prevent>
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row>
      <el-form
        :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="区域类型:" prop="areaType">
              <el-select v-model="form.areaType" placeholder="请选择区域类型" clearable @keydown.enter.prevent>
                <el-option v-for="item in areaTypeList" :key="item" :label="item" :value="item" />
              </el-select>
            <el-form-item label="区域名称:" prop="areaName">
              <el-input
                v-model="form.areaName"
                placeholder="请输入产品名称"
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -69,38 +123,45 @@
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
           <!-- <el-button type="primary" @click="submitModelForm">确认</el-button> -->
          <el-button @click="closeProDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog v-model="modelDia" title="区域" width="400px" @close="closeModelDia" @keydown.enter.prevent>
      <el-form :model="form" label-width="140px" label-position="top" :rules="modelRules" ref="modelFormRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="区域名称:" prop="areaName">
              <el-input v-model="form.areaName" placeholder="请输入区域名称" clearable @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="区域类型:" prop="areaType">
              <el-select v-model="form.areaType" placeholder="请选择区域类型" clearable @keydown.enter.prevent>
                <el-option v-for="item in areaTypeList" :key="item" :label="item" :value="item" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="父区域:" prop="fuId">
              <el-select v-model="form.fuId"  placeholder="请选择父区域" clearable @keydown.enter.prevent>
                <el-option v-for="item in list" :key="item.id" :label="item.label" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
    <el-dialog
      v-model="modelDia"
      title="子区域"
      width="400px"
      @close="closeModelDia"
      @keydown.enter.prevent
    >
      <el-form
        :model="modelForm"
        label-width="140px"
        label-position="top"
        :rules="modelRules"
        ref="modelFormRef"
      >
      <el-form-item label="父区域:" prop="fuId">
        <el-cascader v-model="modelForm.fuId" :options="list" :props="{
          value: 'id',
          label: 'label',
          children: 'children',
          checkStrictly: true,
        }" />
        </el-form-item>
        <el-form-item label="区域类型:" prop="areaType">
          <el-select v-model="modelForm.areaType" placeholder="请选择">
            <el-option v-for="item in area_type" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="区域名称:" prop="areaName">
          <el-input
            v-model="modelForm.areaName"
            placeholder="请输入单位"
            clearable
            @keydown.enter.prevent
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
@@ -113,10 +174,14 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ref } from "vue";
import { ElMessageBox } from "element-plus";
import { areaListPage, areaDelete, areaAdd, areaUpdate } from "@/api/energyManagement/index.js";
// import ImportExcel from "../../../ImportExcel/index.vue";
import {
  areaAdd,
  areaDelete,
  areaListPage,
  areaListTree,
} from "@/api/energyManagement/index.js";
const { proxy } = getCurrentInstance();
const tree = ref(null);
@@ -132,37 +197,24 @@
const treeLoad = ref(false);
const list = ref([]);
const expandedKeys = ref([]);
const areaTypeList = ref([
  "居民用电区域",
  "商业用电区域",
  "工业用电区域",
  "农业用电区域",
  "公共事业用电区域",
  "交通用电区域",
  "特殊用电区域"
])
const {area_type} = proxy.useDict("area_type")
const tableColumn = ref([
  {
    label: "区域编号",
    prop: "id"
  },
  {
    label: "区域名称",
    prop: "areaName"
    prop: "areaName",
  },
  {
    label: "区域类型",
    prop: "areaType"
  },
  {
    label: "父区域",
    prop: "fuId",
    prop: "areaType",
    dataType: "tag",
    formatData: (row) => {
      return area_type.value.find(item => item.value == row)?.label;
    }
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    operation: [
      {
        name: "编辑",
@@ -180,49 +232,36 @@
const selectedRows = ref([]);
const page = reactive({
  current: 1,
  size: 30,
  size: 10,
  total: 0,
});
const data = reactive({
  form: {
    id: "",
    areaName: "",
    areaType: "",
  },
  rules: {
    areaName: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  modelForm: {
    areaName: "",
    fuId: "",
    sort: ""
  },
  modelRules: {
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    areaName: [{ required: true, message: "请输入", trigger: "blur" }],
    fuId: [{ required: true, message: "请输入", trigger: "change" }],
  },
});
const { form, rules, modelRules } = toRefs(data);
// const originalRecords = ref([]);
// const parentItems = ref([]);
const { form, rules, modelForm, modelRules } = toRefs(data);
// æŸ¥è¯¢äº§å“æ ‘
const getProductTeeList = () => {
const getProductTreeList = () => {
  treeLoad.value = true;
  areaListPage({
    current: page.current,
    size: page.size,
  })
  areaListTree()
    .then((res) => {
      const originalRecords = res.data.records;
      console.log(originalRecords);
      // ç­›é€‰fuId为空的项作为父节点
      list.value = originalRecords.filter(item => !item.fuId);
      // ä¸ºæ¯ä¸ªçˆ¶èŠ‚ç‚¹æ·»åŠ children并匹配子节点
      list.value.forEach(parent => {
        parent.children = originalRecords.filter(child => child.fuId === parent.id);
      list.value = res;
      list.value.forEach((a) => {
        expandedKeys.value.push(a.label);
      });
      // // æ›´æ–°åˆ—表数据为树形结构
      // list.value = parentItems.value;
      console.log('树形结构数据:', list.value);
      // list.value.forEach((a) => {
      //   expandedKeys.value.push(a.label);
      // });
      treeLoad.value = false;
    })
    .catch((err) => {
@@ -236,28 +275,23 @@
// æ‰“开产品弹框
const openProDia = (type, data) => {
  operationType.value = type;
  modelOperationType.value = type;
  productDia.value = true;
  form.value.areaType = "";
  form.value.areaName = "";
  if (type === "edit") {
    form.value.id = data.id;
    form.value.areaName = data.areaName;
    form.value.areaType = data.areaType;
    form.value.fuId = data.fuId;
    form.value.sort = data.sort;
  }
};
// æ‰“开规格型号弹框
const openModelDia = (type, data) => {
const openModelDia = (type, data,fatherId) => {
  modelOperationType.value = type;
  modelDia.value = true;
  form.value.areaName = "";
  form.value.areaType = "";
  form.value.fuId = "";
  form.value.sort = "";
  modelForm.value.fuId = "";
  modelForm.value.areaType = "";
  modelForm.value.areaName = "";
  modelForm.value.id = "";
  modelForm.value.fuId = fatherId;
  if (type === "edit") {
    form.value = { ...data };
    modelForm.value = { ...data };
  }
};
// æäº¤äº§å“åç§°ä¿®æ”¹
@@ -265,24 +299,20 @@
  proxy.$refs.formRef.validate((valid) => {
    if (valid) {
      if (operationType.value === "add") {
        // form.value.fuId = currentId.value;
        // form.value.id = "";
        areaAdd(form.value).then((res) => {
          proxy.$modal.msgSuccess("提交成功");
          closeProDia();
          getProductTeeList();
          getModelList();
        });
      }else {
        form.value.parentId = currentId.value;
        form.value.id = "";
      } else if (operationType.value === "addOne") {
        form.value.id = "";
        form.value.parentId = "";
      } else {
        form.value.id = currentId.value;
        areaUpdate(form.value).then((res) => {
          proxy.$modal.msgSuccess("提交成功");
          closeProDia();
          getModelList();
          getProductTeeList();
        });
        form.value.parentId = "";
      }
      areaAdd(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeProDia();
        getProductTreeList();
      });
    }
  });
};
@@ -306,8 +336,7 @@
      areaDelete(ids)
        .then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getProductTeeList();
          getModelList();
          getProductTreeList();
        })
        .finally(() => {
          tableLoading.value = false;
@@ -320,32 +349,24 @@
// é€‰æ‹©äº§å“
const handleNodeClick = (val, node, el) => {
  // åˆ¤æ–­æ˜¯å¦ä¸ºå¶å­èŠ‚ç‚¹
  // isShowButton.value = !(val.children && val.children.length > 0);
  isShowButton.value = !(val.children && val.children.length > 0);
  // åªæœ‰å¶å­èŠ‚ç‚¹æ‰æ‰§è¡Œä»¥ä¸‹é€»è¾‘
  currentId.value = val.id;
  currentParentId.value = val.parentId;
  getModelList();
  getModelList(true);
};
// æäº¤åŒºåŸŸ
// æäº¤è§„格型号修改
const submitModelForm = () => {
  proxy.$refs.modelFormRef.validate((valid) => {
    if (valid) {
      if (modelOperationType.value === "add") {
        form.value.fuId = currentId.value;
        areaAdd(form.value).then((res) => {
          proxy.$modal.msgSuccess("提交成功");
        });
      }
      else {
        // form.value.id = currentId.value;
        areaUpdate(form.value).then((res) => {
          proxy.$modal.msgSuccess("提交成功");
        });
      }
      closeModelDia();
      getModelList();
      getProductTeeList();
      modelForm.value.fuId = currentId.value;
      areaAdd(modelForm.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeModelDia();
        getModelList();
        getProductTreeList();
      });
    }
  });
};
@@ -359,19 +380,29 @@
  selectedRows.value = selection;
};
// æŸ¥è¯¢åŒºåŸŸ
const getModelList = () => {
// æŸ¥è¯¢è§„格型号
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getModelList();
};
const getModelList = (val = false) => {
  tableLoading.value = true;
  areaListPage({
    fuId: currentId.value,
  let obj = {
    id: currentId.value,
    fuId:currentId.value,
    current: page.current,
    size: page.size,
  }).then((res) => {
    size: page.size
  }
  if(val){
    delete obj.id;
  }else{
    delete obj.fuId
  }
  areaListPage(obj).then((res) => {
    console.log("res", res);
    const originalRecords = res.data.records;
    // ç­›é€‰fuId为空的项作为父节点
    tableData.value = originalRecords.filter(item => item.fuId === currentId.value);
    page.total = res.total;
    tableData.value = res.data.records;
    page.total = res.data.total;
    tableLoading.value = false;
  });
};
@@ -394,8 +425,8 @@
      areaDelete(ids)
        .then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getModelList();
          getProductTeeList();
          getModelList();
          getProductTreeList();
        })
        .finally(() => {
          tableLoading.value = false;
@@ -441,30 +472,24 @@
  // æ²¡åŒ¹é…åˆ°è¿”回false
  return false;
};
onMounted(() => {
    getProductTeeList();
});
getProductTreeList();
</script>
<style scoped>
.product-view {
  display: flex;
}
.left {
  width: 380px;
  padding: 16px;
  background: #ffffff;
}
.right {
  width: calc(100% - 380px);
  padding: 16px;
  margin-left: 20px;
  background: #ffffff;
}
.custom-tree-node {
  flex: 1;
  display: flex;
@@ -473,18 +498,14 @@
  font-size: 14px;
  padding-right: 8px;
}
.tree-node-content {
  display: flex;
  align-items: center;
  /* åž‚直居中 */
  align-items: center; /* åž‚直居中 */
  height: 100%;
}
.orange-icon {
  color: orange;
  font-size: 18px;
  margin-right: 8px;
  /* å›¾æ ‡ä¸Žæ–‡å­—之间加点间距 */
  margin-right: 8px; /* å›¾æ ‡ä¸Žæ–‡å­—之间加点间距 */
}
</style>
</style>
src/views/energyManagement/energyPower/components/formDia.vue
@@ -35,6 +35,25 @@
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="用电消耗区域:" prop="electricityConsumptionAreaId">
                            <el-cascader
                                v-model="form.electricityConsumptionAreaId"
                                :options="areaList"
                                :props="{
                                    value: 'id',
                                    label: 'label',
                                    children: 'children',
                                    checkStrictly: true,
                                }"
                                placeholder="请选择区域"
                                clearable
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="每日限制电量:" prop="everyNum">
                            <el-input
                                v-model="form.everyNum"
@@ -43,8 +62,6 @@
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="额定功率:" prop="powerRating">
                            <el-input
@@ -54,6 +71,8 @@
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="实际功率:" prop="powerActual">
                            <el-input
@@ -63,8 +82,6 @@
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="运行时间:" prop="runDate">
                            <el-date-picker
@@ -78,6 +95,8 @@
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="当日用电量:" prop="dayNum">
                            <el-input
@@ -102,7 +121,7 @@
<script setup>
import {ref} from "vue";
import useUserStore from "@/store/modules/user.js";
import {deviceList, equipmentEnergyAdd, equipmentEnergyUpdate} from "@/api/energyManagement/index.js";
import {deviceList, equipmentEnergyAdd, equipmentEnergyUpdate, areaListTree} from "@/api/energyManagement/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
@@ -118,6 +137,7 @@
        powerActual: "",
        runDate: "",
        dayNum: "",
        electricityConsumptionAreaId: "",
    },
    rules: {
        code: [{ required: true, message: "请选择", trigger: "change" }],
@@ -126,10 +146,12 @@
        powerRating: [{ required: true, message: "请输入", trigger: "blur" }],
        powerActual: [{ required: true, message: "请输入", trigger: "blur" }],
        dayNum: [{ required: true, message: "请输入", trigger: "blur" }],
        electricityConsumptionAreaId: [{ required: true, message: "请选择区域", trigger: "change" }],
    },
})
const { form, rules } = toRefs(data);
const codeList = ref([])
const areaList = ref([])
// æ‰“开弹框
const openDialog = (type, row) => {
@@ -139,11 +161,24 @@
    // form.value.maintenanceTime = getCurrentDate();
    form.value = {}
    proxy.resetForm("formRef");
    // èŽ·å–è®¾å¤‡åˆ—è¡¨
    deviceList().then((res) => {
        codeList.value = res.data;
    });
    // èŽ·å–åŒºåŸŸåˆ—è¡¨
    areaListTree().then((res) => {
        areaList.value = res;
        console.log("areaList", res);
    });
    if (type === "edit") {
        form.value = {...row}
        // ç¼–辑时,将单个ID转换为数组格式用于回显
        if (row.electricityConsumptionAreaId) {
            form.value.electricityConsumptionAreaId = [row.electricityConsumptionAreaId];
        }
    }
}
const setName = (code) => {
@@ -156,13 +191,19 @@
const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
        if (valid) {
            // æäº¤å‰å¤„理 electricityConsumptionAreaId,取数组的最后一个值
            const submitData = { ...form.value };
            if (Array.isArray(submitData.electricityConsumptionAreaId) && submitData.electricityConsumptionAreaId.length > 0) {
                submitData.electricityConsumptionAreaId = submitData.electricityConsumptionAreaId[submitData.electricityConsumptionAreaId.length - 1];
            }
            if (operationType.value === "add") {
                equipmentEnergyAdd(form.value).then(response => {
                equipmentEnergyAdd(submitData).then(response => {
                    proxy.$modal.msgSuccess("新增成功")
                    closeDia()
                })
            } else {
                equipmentEnergyUpdate(form.value).then(response => {
                equipmentEnergyUpdate(submitData).then(response => {
                    proxy.$modal.msgSuccess("修改成功")
                    closeDia()
                })
src/views/equipmentManagement/ledger/index.vue
@@ -127,7 +127,12 @@
} = usePaginationApi(
  getLedgerPage,
  {
    searchText: undefined,
    deviceName: undefined,
    deviceModel: undefined,
    supplierName: undefined,
    unit: undefined,
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  [
    {
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -94,23 +94,23 @@
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="附件材料:" prop="remark">
                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">
                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                                <template #tip v-if="operationType !== 'view'">
                                    <div class="el-upload__tip">
                                        æ–‡ä»¶æ ¼å¼æ”¯æŒ
                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                                    </div>
                                </template>
                            </el-upload>
                        </el-form-item>
                    </el-col>
                </el-row>
<!--                <el-row :gutter="30">-->
<!--                    <el-col :span="24">-->
<!--                        <el-form-item label="附件材料:" prop="remark">-->
<!--                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload-->
<!--                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"-->
<!--                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">-->
<!--                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>-->
<!--                                <template #tip v-if="operationType !== 'view'">-->
<!--                                    <div class="el-upload__tip">-->
<!--                                        æ–‡ä»¶æ ¼å¼æ”¯æŒ-->
<!--                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z-->
<!--                                    </div>-->
<!--                                </template>-->
<!--                            </el-upload>-->
<!--                        </el-form-item>-->
<!--                    </el-col>-->
<!--                </el-row>-->
            </el-form>
            <template #footer>
                <div class="dialog-footer">
src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -90,23 +90,23 @@
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="附件材料:" prop="remark">
                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">
                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                                <template #tip v-if="operationType !== 'view'">
                                    <div class="el-upload__tip">
                                        æ–‡ä»¶æ ¼å¼æ”¯æŒ
                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                                    </div>
                                </template>
                            </el-upload>
                        </el-form-item>
                    </el-col>
                </el-row>
<!--                <el-row :gutter="30">-->
<!--                    <el-col :span="24">-->
<!--                        <el-form-item label="附件材料:" prop="remark">-->
<!--                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload-->
<!--                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"-->
<!--                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">-->
<!--                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>-->
<!--                                <template #tip v-if="operationType !== 'view'">-->
<!--                                    <div class="el-upload__tip">-->
<!--                                        æ–‡ä»¶æ ¼å¼æ”¯æŒ-->
<!--                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z-->
<!--                                    </div>-->
<!--                                </template>-->
<!--                            </el-upload>-->
<!--                        </el-form-item>-->
<!--                    </el-col>-->
<!--                </el-row>-->
            </el-form>
            <template #footer>
                <div class="dialog-footer">
src/views/equipmentManagement/measurementEquipment/filesDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,202 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        title="上传附件"
        width="50%"
        @close="closeDia"
    >
      <div style="margin-bottom: 10px;text-align: right">
        <el-upload
            v-model:file-list="fileList"
            class="upload-demo"
            :action="uploadUrl"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            name="file"
            :show-file-list="false"
            :headers="headers"
            style="display: inline;margin-right: 10px"
        >
          <el-button type="primary">上传附件</el-button>
        </el-upload>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="500"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <filePreview ref="filePreviewRef" />
  </div>
</template>
<script setup>
import {ref} from "vue";
import {ElMessageBox} from "element-plus";
import {getToken} from "@/utils/auth.js";
import filePreview from '@/components/filePreview/index.vue'
import {
  fileAdd,
  fileDel,
  fileListPage
} from "@/api/financialManagement/revenueManagement.js";
import Pagination from "@/components/PIMTable/Pagination.vue";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const currentId = ref('')
const selectedRows = ref([]);
const filePreviewRef = ref()
const tableColumn = ref([
  {
    label: "文件名称",
    prop: "name",
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    operation: [
      {
        name: "下载",
        type: "text",
        clickFun: (row) => {
          downLoadFile(row);
        },
      },
      {
        name: "预览",
        type: "text",
        clickFun: (row) => {
          lookFile(row);
        },
      }
    ],
  },
]);
const page = reactive({
    current: 1,
    size: 100,
});
const total = ref(0);
const tableData = ref([]);
const fileList = ref([]);
const tableLoading = ref(false);
const accountType = ref('')
const headers = ref({
  Authorization: "Bearer " + getToken(),
});
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // ä¸Šä¼ çš„图片服务器地址
// æ‰“开弹框
const openDialog = (row,type) => {
  accountType.value = type;
  dialogFormVisible.value = true;
  currentId.value = row.id;
  getList()
}
const paginationSearch = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
  fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => {
    tableData.value = res.data.records;
        total.value = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  dialogFormVisible.value = false;
  emit('close')
};
// ä¸Šä¼ æˆåŠŸå¤„ç†
function handleUploadSuccess(res, file) {
  // å¦‚果上传成功
  if (res.code == 200) {
    const fileRow = {}
    fileRow.name = res.data.originalName
    fileRow.url = res.data.tempPath
    uploadFile(fileRow)
  } else {
    proxy.$modal.msgError("文件上传失败");
  }
}
function uploadFile(file) {
  file.accountId = currentId.value;
  file.accountType = accountType.value;
  fileAdd(file).then(res => {
    proxy.$modal.msgSuccess("文件上传成功");
    getList()
  })
}
// ä¸Šä¼ å¤±è´¥å¤„理
function handleUploadError() {
  proxy.$modal.msgError("文件上传失败");
}
// ä¸‹è½½é™„ä»¶
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    fileDel(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
    });
  }).catch(() => {
    proxy.$modal.msg("已取消");
  });
};
// é¢„览附件
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
src/views/equipmentManagement/measurementEquipment/index.vue
@@ -44,6 +44,7 @@
        </div>
        <form-dia ref="formDia" @close="handleQuery"></form-dia>
        <calibration-dia ref="calibrationDia" @close="handleQuery"></calibration-dia>
    <files-dia ref="filesDia"></files-dia>
    </div>
</template>
@@ -57,6 +58,7 @@
    measuringInstrumentDelete,
    measuringInstrumentListPage
} from "@/api/equipmentManagement/measurementEquipment.js";
import FilesDia from "./filesDia.vue";
const { proxy } = getCurrentInstance();
const userStore = useUserStore()
@@ -136,6 +138,7 @@
        dataType: "action",
        label: "操作",
        align: "center",
        width: '130',
        fixed: 'right',
        operation: [
            {
@@ -149,7 +152,7 @@
                name: "附件",
                type: "text",
                clickFun: (row) => {
                    openCalibrationDia("add", row);
          openFilesFormDia(row);
                },
            },
        ],
@@ -157,6 +160,7 @@
]);
const tableData = ref([]);
const tableLoading = ref(false);
const filesDia = ref()
const page = reactive({
    current: 1,
    size: 100,
@@ -164,6 +168,14 @@
});
const selectedRows = ref([]);
// æ‰“开附件弹框
const openFilesFormDia = (row) => {
  console.log(row)
  nextTick(() => {
    filesDia.value?.openDialog( row,'计量器具台账')
  })
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
src/views/equipmentManagement/repair/index.vue
@@ -163,7 +163,12 @@
} = usePaginationApi(
  getRepairPage,
  {
    searchText: undefined,
    deviceName: undefined,
    deviceModel: undefined,
    remark: undefined,
    maintenanceName: undefined,
    repairTimeStr: undefined,
    maintenanceTimeStr: undefined,
  },
  [
    {
src/views/equipmentManagement/upkeep/index.vue
@@ -154,7 +154,12 @@
  getTableData,
  resetFilters,
  onCurrentChange,
} = usePaginationApi(getUpkeepPage, {}, [
} = usePaginationApi(getUpkeepPage, {
  deviceName: undefined,
  maintenancePlanTime: undefined,
  maintenanceActuallyTime: undefined,
  maintenanceActuallyName: undefined,
}, [
  {
    label: "设备名称",
    align: "center",
src/views/financialManagement/expenseManagement/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="录入日期:">
      <el-form-item label="支出日期:">
        <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                        placeholder="请选择" clearable @change="changeDaterange" />
      </el-form-item>
src/views/financialManagement/revenueManagement/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="录入日期:">
      <el-form-item label="收入日期:">
        <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                        placeholder="请选择" clearable @change="changeDaterange" />
      </el-form-item>
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -58,8 +58,10 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-input v-model="form.checkName" placeholder="请输入" clearable/>
                            <el-select v-model="form.checkName" placeholder="请选择" clearable>
                                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                                     :value="item.nickName"/>
                            </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -77,6 +79,17 @@
          </el-col>
        </el-row>
      </el-form>
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :tableLoading="tableLoading"
                height="400"
            >
                <template #slot="{ row }">
                    <el-input v-model="row.testValue" clearable/>
                </template>
            </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
@@ -92,6 +105,9 @@
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {productTreeList} from "@/api/basicData/product.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {userListNoPage} from "@/api/system/user.js";
import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js";
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -125,17 +141,50 @@
const { form, rules } = toRefs(data);
const supplierList = ref([]);
const productOptions = ref([]);
const tableColumn = ref([
    {
        label: "指标",
        prop: "parameterItem",
    },
    {
        label: "单位",
        prop: "unit",
    },
    {
        label: "标准值",
        prop: "standardValue",
    },
    {
        label: "内控值",
        prop: "controlValue",
    },
    {
        label: "检验值",
        prop: "testValue",
        dataType: 'slot',
        slot: 'slot',
    },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const userList = ref([]);
const currentProductId = ref(0);
// æ‰“开弹框
const openDialog = (type, row) => {
const openDialog = async (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  getOptions().then((res) => {
    supplierList.value = res.data;
  });
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    form.value = {}
  getProductOptions();
  if (operationType.value === 'edit') {
    form.value = {...row}
        currentProductId.value = row.productId || 0
        getQualityInspectParamList(row.id)
  }
}
const getProductOptions = () => {
@@ -144,7 +193,11 @@
  });
};
const getModels = (value) => {
    currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
    if (currentProductId) {
        getList();
    }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
@@ -179,13 +232,19 @@
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.inspectType = 2
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
            const data = {...form.value, qualityInspectParams: tableData.value}
      if (operationType.value === "add") {
        qualityInspectAdd(form.value).then(res => {
        qualityInspectAdd(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        qualityInspectUpdate(form.value).then(res => {
        qualityInspectUpdate(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
@@ -193,6 +252,16 @@
    }
  })
}
const getList = () => {
    qualityInspectDetailByProductId(currentProductId.value).then(res => {
        tableData.value = res.data;
    })
}
const getQualityInspectParamList = (id) => {
    qualityInspectParamInfo(id).then(res => {
        tableData.value = res.data;
    })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
src/views/qualityManagement/finalInspection/index.vue
@@ -40,6 +40,23 @@
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
        <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
                             @close="closeDia">
            <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <el-form-item label="检验员:" prop="checkName">
                    <el-select v-model="form.checkName" placeholder="请选择" clearable>
                        <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                             :value="item.nickName"/>
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">确认</el-button>
                    <el-button @click="closeDia">取消</el-button>
                </div>
            </template>
        </el-dialog>
  </div>
</template>
@@ -49,9 +66,15 @@
import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {qualityInspectDel, qualityInspectListPage} from "@/api/qualityManagement/rawMaterialInspection.js";
import {
    downloadQualityInspect,
    qualityInspectDel,
    qualityInspectListPage, qualityInspectUpdate,
    submitQualityInspect
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue";
import dayjs from "dayjs";
import {userListNoPage} from "@/api/system/user.js";
const data = reactive({
  searchForm: {
@@ -63,6 +86,9 @@
    entryDateStart: dayjs().format("YYYY-MM-DD"),
    entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"),
  },
    rules: {
        checkName: [{required: true, message: "请选择", trigger: "change"}],
    },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
@@ -111,12 +137,23 @@
      }
    },
  },
    {
        label: "提交状态",
        prop: "inspectState",
        formatData: (params) => {
            if (params) {
                return "已提交";
            } else {
                return "未提交";
            }
        },
    },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 190,
    width: 280,
    operation: [
      {
        name: "编辑",
@@ -124,13 +161,9 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
      },
      {
        name: "新增检验记录",
        type: "text",
        clickFun: (row) => {
          openInspectionForm("edit", row);
        },
                disabled: (row) => {
                    return row.inspectState == 1;
                }
      },
      {
        name: "附件",
@@ -139,12 +172,44 @@
          openFilesFormDia(row);
        },
      },
            {
                name: "提交",
                type: "text",
                clickFun: (row) => {
                    submit(row.id);
                },
                disabled: (row) => {
                    return row.inspectState == 1;
                }
            },
            {
                name: "分配检验员",
                type: "text",
                clickFun: (row) => {
                    if (!row.checkName) {
                        open(row)
                    } else {
                        proxy.$modal.msgError("检验员已存在");
                    }
                },
                disabled: (row) => {
                    return row.inspectState == 1 || row.checkName;
                }
            },
            {
                name: "下载",
                type: "text",
                clickFun: (row) => {
                    downLoadFile(row);
                },
            },
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const currentRow = ref(null)
const page = reactive({
  current: 1,
  size: 100,
@@ -154,6 +219,11 @@
const filesDia = ref()
const inspectionFormDia = ref()
const { proxy } = getCurrentInstance()
const userList = ref([]);
const form = ref({
    checkName: ""
});
const dialogFormVisible = ref(false);
const changeDaterange = (value) => {
  searchForm.value.entryDateStart = undefined;
@@ -249,6 +319,60 @@
        proxy.$modal.msg("已取消");
      });
};
// æä»·
const submit = async (id) => {
    const res = await submitQualityInspect({id: id})
    if (res.code === 200) {
        proxy.$modal.msgSuccess("提交成功");
        getList();
    }
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
};
const submitForm = () => {
    if (currentRow.value) {
        const data = {
            ...form.value,
            id: currentRow.value.id
        }
        qualityInspectUpdate(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
        })
    }
};
const open = async (row) => {
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    currentRow.value = row
    dialogFormVisible.value = true
}
const downLoadFile = (row) => {
    downloadQualityInspect({ id: row.id }).then((blobData) => {
        const blob = new Blob([blobData], {
            type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        })
        const downloadUrl = window.URL.createObjectURL(blob)
        const link = document.createElement('a')
        link.href = downloadUrl
        link.download = '原材料检验报告.docx'
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        window.URL.revokeObjectURL(downloadUrl)
    })
};
onMounted(() => {
  getList();
});
src/views/qualityManagement/processInspection/components/filesDia.vue
@@ -46,18 +46,13 @@
        </div>
      </template>
    </el-dialog>
        <filePreview ref="filePreviewRef" />
  </div>
</template>
<script setup>
import {ref} from "vue";
import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
import {Search} from "@element-plus/icons-vue";
import {
  qualityInspectParamDel,
  qualityInspectParamInfo,
  qualityInspectParamUpdate
} from "@/api/qualityManagement/qualityInspectParam.js";
import filePreview from '@/components/filePreview/index.vue'
import {ElMessageBox} from "element-plus";
import {getToken} from "@/utils/auth.js";
import {
@@ -72,6 +67,7 @@
const dialogFormVisible = ref(false);
const currentId = ref('')
const selectedRows = ref([]);
const filePreviewRef = ref()
const tableColumn = ref([
  {
    label: "文件名称",
@@ -88,7 +84,14 @@
        clickFun: (row) => {
          downLoadFile(row);
        },
      }
      },
            {
                name: "预览",
                type: "text",
                clickFun: (row) => {
                    lookFile(row);
                },
            }
    ],
  },
]);
@@ -158,6 +161,10 @@
function handleUploadError() {
  proxy.$modal.msgError("文件上传失败");
}
// é¢„览附件
const lookFile = (row) => {
    filePreviewRef.value.open(row.url)
}
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
src/views/qualityManagement/processInspection/components/formDia.vue
@@ -65,8 +65,10 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-input v-model="form.checkName" placeholder="请输入" clearable/>
                            <el-select v-model="form.checkName" placeholder="请选择" clearable>
                                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                                     :value="item.nickName"/>
                            </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -84,6 +86,17 @@
          </el-col>
        </el-row>
      </el-form>
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :tableLoading="tableLoading"
                height="400"
            >
                <template #slot="{ row }">
                    <el-input v-model="row.testValue" clearable/>
                </template>
            </PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
@@ -99,6 +112,9 @@
import {getOptions} from "@/api/procurementManagement/procurementLedger.js";
import {productTreeList} from "@/api/basicData/product.js";
import {qualityInspectAdd, qualityInspectUpdate} from "@/api/qualityManagement/rawMaterialInspection.js";
import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js";
import {userListNoPage} from "@/api/system/user.js";
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -129,21 +145,54 @@
    checkResult: [{ required: true, message: "请输入", trigger: "change" }],
  },
});
const userList = ref([]);
const { form, rules } = toRefs(data);
const supplierList = ref([]);
const productOptions = ref([]);
const tableColumn = ref([
    {
        label: "指标",
        prop: "parameterItem",
    },
    {
        label: "单位",
        prop: "unit",
    },
    {
        label: "标准值",
        prop: "standardValue",
    },
    {
        label: "内控值",
        prop: "controlValue",
    },
    {
        label: "检验值",
        prop: "testValue",
        dataType: 'slot',
        slot: 'slot',
    },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const currentProductId = ref(0);
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  getOptions().then((res) => {
    supplierList.value = res.data;
  });
  getProductOptions();
  if (operationType.value === 'edit') {
    form.value = {...row}
  }
const openDialog = async (type, row) => {
    operationType.value = type;
    dialogFormVisible.value = true;
    getOptions().then((res) => {
        supplierList.value = res.data;
    });
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    form.value = {}
    getProductOptions();
    if (operationType.value === 'edit') {
        form.value = {...row}
        currentProductId.value = row.productId || 0
        getQualityInspectParamList(row.id)
    }
}
const getProductOptions = () => {
  productTreeList().then((res) => {
@@ -151,7 +200,11 @@
  });
};
const getModels = (value) => {
    currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
    if (currentProductId) {
        getList();
    }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
@@ -186,13 +239,19 @@
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.inspectType = 1
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
            const data = {...form.value, qualityInspectParams: tableData.value}
      if (operationType.value === "add") {
        qualityInspectAdd(form.value).then(res => {
        qualityInspectAdd(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        qualityInspectUpdate(form.value).then(res => {
        qualityInspectUpdate(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
@@ -200,6 +259,16 @@
    }
  })
}
const getList = () => {
    qualityInspectDetailByProductId(currentProductId.value).then(res => {
        tableData.value = res.data;
    })
}
const getQualityInspectParamList = (id) => {
    qualityInspectParamInfo(id).then(res => {
        tableData.value = res.data;
    })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
src/views/qualityManagement/processInspection/index.vue
@@ -40,6 +40,23 @@
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
        <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
                             @close="closeDia">
            <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <el-form-item label="检验员:" prop="checkName">
                    <el-select v-model="form.checkName" placeholder="请选择" clearable>
                        <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                             :value="item.nickName"/>
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">确认</el-button>
                    <el-button @click="closeDia">取消</el-button>
                </div>
            </template>
        </el-dialog>
  </div>
</template>
@@ -49,9 +66,15 @@
import InspectionFormDia from "@/views/qualityManagement/processInspection/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/processInspection/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {qualityInspectDel, qualityInspectListPage} from "@/api/qualityManagement/rawMaterialInspection.js";
import {
    downloadQualityInspect,
    qualityInspectDel,
    qualityInspectListPage, qualityInspectUpdate,
    submitQualityInspect
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/processInspection/components/filesDia.vue";
import dayjs from "dayjs";
import {userListNoPage} from "@/api/system/user.js";
const data = reactive({
  searchForm: {
@@ -63,6 +86,9 @@
    entryDateStart: dayjs().format("YYYY-MM-DD"),
    entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"),
  },
    rules: {
        checkName: [{required: true, message: "请选择", trigger: "change"}],
    },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
@@ -116,12 +142,23 @@
      }
    },
  },
    {
        label: "提交状态",
        prop: "inspectState",
        formatData: (params) => {
            if (params) {
                return "已提交";
            } else {
                return "未提交";
            }
        },
    },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 190,
    width: 280,
    operation: [
      {
        name: "编辑",
@@ -129,13 +166,9 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
      },
      {
        name: "新增检验记录",
        type: "text",
        clickFun: (row) => {
          openInspectionForm("edit", row);
        },
                disabled: (row) => {
                    return row.inspectState == 1;
                }
      },
      {
        name: "附件",
@@ -144,12 +177,49 @@
          openFilesFormDia(row);
        },
      },
            {
                name: "提交",
                type: "text",
                clickFun: (row) => {
                    submit(row.id);
                },
                disabled: (row) => {
                    return row.inspectState == 1;
                }
            },
            {
                name: "分配检验员",
                type: "text",
                clickFun: (row) => {
                    if (!row.checkName) {
                        open(row)
                    } else {
                        proxy.$modal.msgError("检验员已存在");
                    }
                },
                disabled: (row) => {
                    return row.inspectState == 1 || row.checkName;
                }
            },
            {
                name: "下载",
                type: "text",
                clickFun: (row) => {
                    downLoadFile(row);
                },
            },
    ],
  },
]);
const userList = ref([]);
const currentRow = ref(null)
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const dialogFormVisible = ref(false);
const form = ref({
    checkName: ""
});
const page = reactive({
  current: 1,
  size: 100,
@@ -214,6 +284,38 @@
    filesDia.value?.openDialog(type, row)
  })
};
// æä»·
const submit = async (id) => {
    const res = await submitQualityInspect({id: id})
    if (res.code === 200) {
        proxy.$modal.msgSuccess("提交成功");
        getList();
    }
}
const open = async (row) => {
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    currentRow.value = row
    dialogFormVisible.value = true
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
};
const submitForm = () => {
    if (currentRow.value) {
        const data = {
            ...form.value,
            id: currentRow.value.id
        }
        qualityInspectUpdate(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
        })
    }
};
// åˆ é™¤
const handleDelete = () => {
@@ -239,6 +341,23 @@
        proxy.$modal.msg("已取消");
      });
};
const downLoadFile = (row) => {
    downloadQualityInspect({ id: row.id }).then((blobData) => {
        const blob = new Blob([blobData], {
            type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        })
        const downloadUrl = window.URL.createObjectURL(blob)
        const link = document.createElement('a')
        link.href = downloadUrl
        link.download = '过程检验报告.docx'
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        window.URL.revokeObjectURL(downloadUrl)
    })
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue
@@ -46,18 +46,13 @@
        </div>
      </template>
    </el-dialog>
        <filePreview ref="filePreviewRef" />
  </div>
</template>
<script setup>
import {ref} from "vue";
import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
import {Search} from "@element-plus/icons-vue";
import {
  qualityInspectParamDel,
  qualityInspectParamInfo,
  qualityInspectParamUpdate
} from "@/api/qualityManagement/qualityInspectParam.js";
import filePreview from '@/components/filePreview/index.vue'
import {ElMessageBox} from "element-plus";
import {getToken} from "@/utils/auth.js";
import {
@@ -88,7 +83,14 @@
        clickFun: (row) => {
          downLoadFile(row);
        },
      }
      },
            {
                name: "预览",
                type: "text",
                clickFun: (row) => {
                    lookFile(row);
                },
            }
    ],
  },
]);
@@ -100,6 +102,7 @@
const tableData = ref([]);
const fileList = ref([]);
const tableLoading = ref(false);
const filePreviewRef = ref()
const headers = ref({
  Authorization: "Bearer " + getToken(),
});
@@ -159,6 +162,10 @@
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
// é¢„览附件
const lookFile = (row) => {
    filePreviewRef.value.open(row.url)
}
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -96,16 +96,14 @@
          </el-col>
        </el-row>
      </el-form>
      <div style="margin-bottom: 10px;text-align: right">
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
<!--      <div style="margin-bottom: 10px;text-align: right">-->
<!--        <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
<!--      </div>-->
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="400"
      >
        <template #slot="{ row }">
@@ -200,6 +198,7 @@
  getOptions().then((res) => {
    supplierList.value = res.data;
  });
    form.value = {}
  getProductOptions();
  if (operationType.value === 'edit') {
    form.value = {...row}
@@ -254,6 +253,11 @@
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.inspectType = 0
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
      const data = {...form.value, qualityInspectParams: tableData.value}
      if (operationType.value === "add") {
        qualityInspectAdd(data).then(res => {
@@ -269,34 +273,6 @@
    }
  })
}
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        qualityInspectParamDel(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
const getList = () => {
  qualityInspectDetailByProductId(currentProductId.value).then(res => {
src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -41,7 +41,7 @@
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="70%"
    <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
               @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="检验员:" prop="checkName">
@@ -160,7 +160,7 @@
    label: "操作",
    align: "center",
    fixed: "right",
    width: 250,
    width: 280,
    operation: [
      {
        name: "编辑",
@@ -168,9 +168,9 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
        disabled: (row) => {
          return row.inspectState;
        }
                disabled: (row) => {
                    return row.inspectState == 1;
                }
      },
      {
        name: "附件",
@@ -185,6 +185,9 @@
        clickFun: (row) => {
          submit(row.id);
        },
                disabled: (row) => {
                    return row.inspectState == 1;
                }
      },
      {
        name: "分配检验员",
@@ -196,9 +199,9 @@
            proxy.$modal.msgError("检验员已存在");
          }
        },
        disabled: (row) => {
          return row.inspectState;
        }
                disabled: (row) => {
                    return row.inspectState == 1 || row.checkName;
                }
      },
      {
        name: "下载",
@@ -269,12 +272,6 @@
const openForm = (type, row) => {
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
};
// æ‰“开新增检验弹框
const openInspectionForm = (type, row) => {
  nextTick(() => {
    inspectionFormDia.value?.openDialog(type, row)
  })
};
// æ‰“开附件弹框
@@ -368,7 +365,7 @@
    const link = document.createElement('a')
    link.href = downloadUrl
    link.download = '检验报告.docx'
    link.download = '原材料检验报告.docx'
    document.body.appendChild(link)
    link.click()