From 989d547833c94ffc4e7395b0023ec24ee09f5ab6 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期五, 14 十一月 2025 15:06:18 +0800
Subject: [PATCH] Merge branch 'dev_7004' into dev_tide_mom
---
src/views/qualityManagement/rawMaterialInspection/index.vue | 30
src/views/inventoryManagement/stockWarning/index.vue | 1
src/views/system/user/authRole.vue | 2
src/views/qualityManagement/finalInspection/index.vue | 142
src/views/monitor/logininfor/index.vue | 2
src/views/monitor/operlog/index.vue | 2
src/views/qualityManagement/processInspection/index.vue | 137
src/views/salesManagement/invoiceLedger/index.vue | 8
src/views/procurementManagement/invoiceEntry/indexOld.vue | 3
src/views/inventoryManagement/receiptManagement/index.vue | 3
src/views/energyManagement/energyPower/components/formDia.vue | 55
src/views/inventoryManagement/stockManagement/index.vue | 2
src/views/monitor/job/log.vue | 2
src/views/equipmentManagement/ledger/index.vue | 25
src/views/inventoryManagement/dispatchLog/index.vue | 7
src/views/personnelManagement/onboarding/index.vue | 66
src/views/collaborativeApproval/meetingBoard/index.vue | 498 ++
src/views/reportAnalysis/projectProfit/index.vue | 11
src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue | 4
src/views/financialManagement/revenueManagement/index.vue | 2
.env.development | 4
src/views/collaborativeApproval/warningSystem/index.vue | 307 +
src/views/energyManagement/waterManagement/components/formDia.vue | 62
src/views/tool/gen/index.vue | 1
src/views/collaborativeApproval/approvalProcess/fileList.vue | 43
public/HYSNico.ico | 0
src/views/collaborativeApproval/noticeManagement/index.vue | 705 +++
src/views/procurementManagement/procurementInvoiceLedger/index.vue | 18
src/views/system/role/index.vue | 2
src/views/collaborativeApproval/approvalProcess/index4.vue | 22
src/views/procurementManagement/invoiceEntry/index.vue | 4
src/views/salesManagement/receiptPaymentLedger/index.vue | 2
src/views/collaborativeApproval/approvalProcess/index.vue | 32
src/views/collaborativeApproval/approvalProcess/index3.vue | 22
src/views/procurementManagement/paymentEntry/index.vue | 47
src/views/salesManagement/salesLedger/index.vue | 18
src/views/system/menu/index.vue | 1
src/views/system/dict/data.vue | 2
src/views/tool/gen/editTable.vue | 2
src/views/tool/gen/importTable.vue | 2
src/views/salesManagement/salesLedger/fileList.vue | 2
src/api/collaborativeApproval/rpaManagement.js | 77
src/views/collaborativeApproval/knowledgeBase/index.vue | 848 +++
src/views/collaborativeApproval/attendanceManagement/index.vue | 714 +++
src/views/chatHome/chatHomeIndex/MobileChat.vue | 2
src/views/energyManagement/energyPeriodTime/index.vue | 444 +
src/views/salesManagement/receiptPayment/index.vue | 32
src/views/system/dept/index.vue | 37
src/views/collaborativeApproval/approvalProcess/index1.vue | 22
src/components/PIMTable/PIMTable.vue | 1
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue | 82
src/views/equipmentManagement/iotMonitor/indexWD.vue | 317 +
src/views/salesManagement/invoiceRegistration/index.vue | 25
src/assets/indexViews/HYSNLogo.png | 0
src/api/collaborativeApproval/noticeManagement.js | 69
src/views/collaborativeApproval/notificationManagement/index.vue | 1187 +++++
src/views/collaborativeApproval/rpaManagement/index.vue | 400 +
src/views/energyManagement/energyCockpit/index.vue | 1380 ++++++
src/views/index.vue | 51
src/views/equipmentManagement/measurementEquipment/components/formDia.vue | 34
src/views/energyManagement/meterCollection/index.vue | 556 ++
src/views/personnelManagement/contractManagement/index.vue | 160
src/views/equipmentManagement/ledger/Form.vue | 6
src/views/basicData/customerFile/index.vue | 37
src/views/system/role/authUser.vue | 2
src/views/equipmentManagement/upkeep/index.vue | 8
src/views/qualityManagement/processInspection/components/formDia.vue | 97
src/views/personnelManagement/onboarding/components/formDia.vue | 23
src/views/productionManagement/operationScheduling/components/formDia.vue | 2
src/views/equipmentManagement/repair/Modal/MaintainModal.vue | 4
src/views/energyManagement/energyArea/index.vue | 511 ++
src/views/system/post/index.vue | 2
src/views/inventoryManagement/issueManagement/index.vue | 2
src/views/productionManagement/safetyMonitoring/index.vue | 873 +++
src/views/energyManagement/gasManagement/index.vue | 624 ++
src/router/index.js | 29
src/views/financialManagement/expenseManagement/index.vue | 2
src/views/procurementManagement/procurementLedger/index.vue | 428 +
src/views/equipmentManagement/measurementEquipment/index.vue | 14
src/views/productionManagement/productionReporting/index.vue | 1
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue | 42
src/views/qualityManagement/processInspection/components/filesDia.vue | 23
src/views/system/notice/index.vue | 2
src/views/system/dict/index.vue | 2
bin/build.bat | 22
src/views/procurementManagement/paymentHistory/index.vue | 4
src/views/system/config/index.vue | 2
src/views/equipmentManagement/iotMonitor/index.vue | 317 +
src/api/energyManagement/index.js | 115
.env.staging | 4
src/views/equipmentManagement/measurementEquipment/filesDia.vue | 202
src/views/collaborativeApproval/approvalProcess/index2.vue | 22
src/views/system/role/selectUser.vue | 2
src/assets/indexViews/HYSNView.png | 0
src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue | 13
src/views/energyManagement/waterManagement/index.vue | 20
src/views/monitor/job/index.vue | 2
src/views/demo/fakePage/index.vue | 248 +
src/views/qualityManagement/finalInspection/components/formDia.vue | 79
src/views/equipmentManagement/repair/index.vue | 8
src/views/monitor/online/index.vue | 1
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue | 23
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue | 34
src/views/procurementManagement/paymentLedger/index.vue | 1
src/views/system/user/index.vue | 1
src/views/login.vue | 2
src/views/procurementManagement/procurementInvoiceLedger/indexOld.vue | 2
package.json | 2
src/views/personnelManagement/contractManagement/filesDia.vue | 202
src/api/energyManagement/waterManagement.js | 14
src/views/energyManagement/dynamicEnergySaving/index.vue | 659 ++
111 files changed, 13,044 insertions(+), 429 deletions(-)
diff --git a/.env.development b/.env.development
index b5f6a61..1a63995 100644
--- a/.env.development
+++ b/.env.development
@@ -1,8 +1,8 @@
# 椤甸潰鏍囬
-VITE_APP_TITLE = MIS绯荤粺锛堢鐞嗕俊鎭郴缁燂級
+VITE_APP_TITLE = 鑺浜戯紙绠$悊淇℃伅绯荤粺锛�
# 寮�鍙戠幆澧冮厤缃�
VITE_APP_ENV = 'development'
-# MIS绯荤粺锛堢鐞嗕俊鎭郴缁燂級/寮�鍙戠幆澧�
+# 鑺浜戯紙绠$悊淇℃伅绯荤粺锛�/寮�鍙戠幆澧�
VITE_APP_BASE_API = '/dev-api'
diff --git a/.env.staging b/.env.staging
index f759f9a..468abb3 100644
--- a/.env.staging
+++ b/.env.staging
@@ -1,10 +1,10 @@
# 椤甸潰鏍囬
-VITE_APP_TITLE = MIS绯荤粺锛堢鐞嗕俊鎭郴缁燂級
+VITE_APP_TITLE = 鑺浜戯紙绠$悊淇℃伅绯荤粺锛�
# 鐢熶骇鐜閰嶇疆
VITE_APP_ENV = 'staging'
-# MIS绯荤粺锛堢鐞嗕俊鎭郴缁燂級/鐢熶骇鐜
+# 鑺浜戯紙绠$悊淇℃伅绯荤粺锛�/鐢熶骇鐜
VITE_APP_BASE_API = '/stage-api'
# 鏄惁鍦ㄦ墦鍖呮椂寮�鍚帇缂╋紝鏀寔 gzip 鍜� brotli
diff --git a/bin/build.bat b/bin/build.bat
index 8868727..a4cc0df 100644
--- a/bin/build.bat
+++ b/bin/build.bat
@@ -1,12 +1,12 @@
-@echo off
-echo.
-echo [信息] 打包Web工程,生成dist文件。
-echo.
-
-%~d0
-cd %~dp0
-
-cd ..
-yarn build:prod
-
+@echo off
+echo.
+echo [锟斤拷息] 锟斤拷锟絎eb锟斤拷锟教o拷锟斤拷锟斤拷dist锟侥硷拷锟斤拷
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+yarn build:prod
+
pause
\ No newline at end of file
diff --git a/package.json b/package.json
index 3fd8e3d..2ffdd34 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ruoyi",
"version": "3.8.9",
- "description": "MES",
+ "description": "MIS绯荤粺锛堢鐞嗕俊鎭郴缁燂級",
"author": "鑻ヤ緷",
"license": "MIT",
"type": "module",
diff --git a/public/HYSNico.ico b/public/HYSNico.ico
new file mode 100644
index 0000000..70148cc
--- /dev/null
+++ b/public/HYSNico.ico
Binary files differ
diff --git a/src/api/collaborativeApproval/noticeManagement.js b/src/api/collaborativeApproval/noticeManagement.js
new file mode 100644
index 0000000..fa1caec
--- /dev/null
+++ b/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'
+ })
+}
diff --git a/src/api/collaborativeApproval/rpaManagement.js b/src/api/collaborativeApproval/rpaManagement.js
new file mode 100644
index 0000000..6fc3368
--- /dev/null
+++ b/src/api/collaborativeApproval/rpaManagement.js
@@ -0,0 +1,77 @@
+import request from "@/utils/request";
+
+// 鏌ヨRPA鍒楄〃
+export function listRpa(query) {
+ return request({
+ url: "/collaborativeApproval/rpa/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨRPA璇︾粏
+export function getRpa(rpaId) {
+ return request({
+ url: "/collaborativeApproval/rpa/" + rpaId,
+ method: "get",
+ });
+}
+
+// 鏂板RPA
+export function addRpa(data) {
+ return request({
+ url: "/collaborativeApproval/rpa",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼RPA
+export function updateRpa(data) {
+ return request({
+ url: "/collaborativeApproval/rpa",
+ method: "put",
+ data: data,
+ });
+}
+
+// 鍒犻櫎RPA
+export function delRpa(rpaId) {
+ return request({
+ url: "/collaborativeApproval/rpa/" + rpaId,
+ method: "delete",
+ });
+}
+
+// 鎵归噺鍒犻櫎RPA
+export function delRpaBatch(rpaIds) {
+ return request({
+ url: "/collaborativeApproval/rpa/batch",
+ method: "delete",
+ data: rpaIds,
+ });
+}
+
+// 鍚姩RPA
+export function startRpa(rpaId) {
+ return request({
+ url: "/collaborativeApproval/rpa/start/" + rpaId,
+ method: "post",
+ });
+}
+
+// 鍋滄RPA
+export function stopRpa(rpaId) {
+ return request({
+ url: "/collaborativeApproval/rpa/stop/" + rpaId,
+ method: "post",
+ });
+}
+
+// 鑾峰彇RPA鐘舵��
+export function getRpaStatus(rpaId) {
+ return request({
+ url: "/collaborativeApproval/rpa/status/" + rpaId,
+ method: "get",
+ });
+}
diff --git a/src/api/energyManagement/index.js b/src/api/energyManagement/index.js
index 4c061e6..f2c5494 100644
--- a/src/api/energyManagement/index.js
+++ b/src/api/energyManagement/index.js
@@ -4,47 +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",
+ 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",
+ 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",
+ data: query,
+ });
+}
+// 鏃堕棿鍛ㄦ湡-鍒犻櫎
+export function periodDelete(query) {
+ return request({
+ 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",
+ data: query,
+ });
+}
+
+// 鏃堕棿鍛ㄦ湡-鏂板
+export function periodAdd(query) {
+ return request({
+ 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",
+ data: query,
+ });
+}
+// 鏃堕棿鍛ㄦ湡-淇敼
+export function periodUpdate(query) {
+ return request({
+ url: "/energyPeriod/update",
+ method: "post",
+ data: query,
+ });
+}
+
// 璁惧涓嬫媺妗嗘煡璇�
export function deviceList(query) {
return request({
- url: '/equipmentEnergyConsumption/deviceList',
- method: 'get',
- })
-}
\ No newline at end of file
+ url: "/equipmentEnergyConsumption/deviceList",
+ method: "get",
+ });
+}
diff --git a/src/api/energyManagement/waterManagement.js b/src/api/energyManagement/waterManagement.js
index aef7465..6dbf115 100644
--- a/src/api/energyManagement/waterManagement.js
+++ b/src/api/energyManagement/waterManagement.js
@@ -4,7 +4,7 @@
// 鐢ㄦ按璁惧-鍒嗛〉鏌ヨ
export function waterEquipmentListPage(query) {
return request({
- url: '/waterEquipmentConsumption/listPage',
+ url: '/waterRecord/listPage',
method: 'get',
params: query,
})
@@ -13,7 +13,7 @@
// 鐢ㄦ按瓒嬪娍-鍒嗛〉鏌ヨ
export function listPageByWaterTrend(query) {
return request({
- url: '/waterEquipmentConsumption/listPageByTrend',
+ url: '/waterRecord/listPageByTrend',
method: 'get',
params: query,
})
@@ -22,7 +22,7 @@
// 鐢ㄦ按璁惧-鍒犻櫎
export function waterEquipmentDelete(query) {
return request({
- url: '/waterEquipmentConsumption/delete',
+ url: '/waterRecord/delete',
method: 'delete',
data: query,
})
@@ -31,7 +31,7 @@
// 鐢ㄦ按璁惧-鏂板
export function waterEquipmentAdd(query) {
return request({
- url: '/waterEquipmentConsumption/add',
+ url: '/waterRecord/add',
method: 'post',
data: query,
})
@@ -40,7 +40,7 @@
// 鐢ㄦ按璁惧-淇敼
export function waterEquipmentUpdate(query) {
return request({
- url: '/waterEquipmentConsumption/update',
+ url: '/waterRecord/update',
method: 'post',
data: query,
})
@@ -49,8 +49,9 @@
// 鐢ㄦ按璁惧涓嬫媺妗嗘煡璇�
export function waterDeviceList(query) {
return request({
- url: '/waterEquipmentConsumption/deviceList',
+ url: '/device/ledger/page',
method: 'get',
+ params: query,
})
}
@@ -89,3 +90,4 @@
data: query,
})
}
+
diff --git a/src/assets/indexViews/HYSNLogo.png b/src/assets/indexViews/HYSNLogo.png
new file mode 100644
index 0000000..70148cc
--- /dev/null
+++ b/src/assets/indexViews/HYSNLogo.png
Binary files differ
diff --git a/src/assets/indexViews/HYSNView.png b/src/assets/indexViews/HYSNView.png
new file mode 100644
index 0000000..ccd5fcc
--- /dev/null
+++ b/src/assets/indexViews/HYSNView.png
Binary files differ
diff --git a/src/components/PIMTable/PIMTable.vue b/src/components/PIMTable/PIMTable.vue
index 955173d..a89aa96 100644
--- a/src/components/PIMTable/PIMTable.vue
+++ b/src/components/PIMTable/PIMTable.vue
@@ -15,6 +15,7 @@
:expand-row-keys="expandRowKeys"
:show-summary="isShowSummary"
:summary-method="summaryMethod"
+ stripe
@row-click="rowClick"
@current-change="currentChange"
@selection-change="handleSelectionChange"
diff --git a/src/router/index.js b/src/router/index.js
index b37fa51..d190347 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -77,20 +77,6 @@
]
},
{
- 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,
@@ -123,6 +109,21 @@
]
},
{
+ 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,
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index a706616..8043d1a 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -92,25 +92,6 @@
</el-form-item>
</el-col>
</el-row>
- <el-row :gutter="30" v-for="(contact, index) in formYYs.contactList" :key="index">
- <el-col :span="12">
- <el-form-item label="鑱旂郴浜猴細" prop="contactPerson">
- <el-input v-model="contact.contactPerson" placeholder="璇疯緭鍏�" clearable />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactPhone">
- <div style="display: flex; align-items: center;width: 100%;">
- <el-input v-model="contact.contactPhone" placeholder="璇疯緭鍏�" clearable />
- <el-button @click="removeContact(index)" type="danger" circle style="margin-left: 5px;">
- <el-icon><Close /></el-icon>
- </el-button>
- </div>
- </el-form-item>
- </el-col>
- </el-row>
- <el-button @click="addNewContact" style="margin-bottom: 10px;">+ 鏂板鑱旂郴浜�</el-button>
-
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="閾惰鍩烘湰鎴凤細" prop="basicBankAccount">
@@ -142,6 +123,24 @@
</el-form-item>
</el-col>
</el-row>
+ <el-row :gutter="30" v-for="(contact, index) in formYYs.contactList" :key="index">
+ <el-col :span="12">
+ <el-form-item label="鑱旂郴浜猴細" prop="contactPerson">
+ <el-input v-model="contact.contactPerson" placeholder="璇疯緭鍏�" clearable />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鑱旂郴鐢佃瘽锛�" prop="contactPhone">
+ <div style="display: flex; align-items: center;width: 100%;">
+ <el-input v-model="contact.contactPhone" placeholder="璇疯緭鍏�" clearable />
+ <el-button @click="removeContact(index)" type="danger" circle style="margin-left: 5px;">
+ <el-icon><Close /></el-icon>
+ </el-button>
+ </div>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-button @click="addNewContact" style="margin-bottom: 10px;">+ 鏂板鑱旂郴浜�</el-button>
<el-row :gutter="30">
<el-col :span="12">
<el-form-item label="缁存姢浜猴細" prop="maintainer">
diff --git a/src/views/chatHome/chatHomeIndex/MobileChat.vue b/src/views/chatHome/chatHomeIndex/MobileChat.vue
index f8bc625..5b06e76 100644
--- a/src/views/chatHome/chatHomeIndex/MobileChat.vue
+++ b/src/views/chatHome/chatHomeIndex/MobileChat.vue
@@ -203,7 +203,7 @@
}
chatList.value.push(replyMsg)
scrollBottom()
-
+ loading.value = false
// 濡傛灉鏈夋煡璇㈠叧閿瓧锛屽垯妯℃嫙娴佸紡杈撳嚭
if (route.query.keyWord) {
simulateStreamingOutput(replyMsg, route.query.keyWord)
diff --git a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
index 3aa58c5..83585a1 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -112,6 +112,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锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
+ </div>
+ </template>
+ </el-upload>
+ </el-form-item>
+ </el-col>
+ </el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
@@ -130,7 +147,11 @@
approveProcessUpdate,
getDept
} from "@/api/collaborativeApproval/approvalProcess.js";
+import {
+ delLedgerFile,
+} from "@/api/salesManagement/salesLedger.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
+import { getToken } from "@/utils/auth";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
import useUserStore from "@/store/modules/user";
@@ -138,6 +159,13 @@
const dialogFormVisible = ref(false);
const operationType = ref('')
+const fileList = ref([]);
+const upload = reactive({
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+});
const data = reactive({
form: {
approveTime: "",
@@ -146,6 +174,7 @@
approveDeptId: "",
approveReason: "",
checkResult: "",
+ tempFileIds: [],
approverList: [] // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
},
rules: {
@@ -160,6 +189,12 @@
const { form, rules } = toRefs(data);
const productOptions = ref([]);
const currentApproveStatus = ref(0)
+const props = defineProps({
+ approveType: {
+ type: [Number, String],
+ default: 0
+ }
+})
// 瀹℃壒浜鸿妭鐐圭浉鍏�
const approverNodes = ref([
@@ -176,6 +211,7 @@
// 鎵撳紑寮规
const openDialog = (type, row) => {
+ console.log('openDialog', type, row)
operationType.value = type;
dialogFormVisible.value = true;
userListNoPageByTenantId().then((res) => {
@@ -192,6 +228,8 @@
// 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅骞惰缃儴闂↖D
form.value.approveDeptId = userStore.currentDeptId
if (operationType.value === 'edit') {
+ fileList.value = row.commonFileList
+ form.value.tempFileIds = fileList.value.map(file => file.id)
currentApproveStatus.value = row.approveStatus
approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => {
form.value = {...res.data}
@@ -233,6 +271,7 @@
const submitForm = () => {
// 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
+ form.value.approveType = props.approveType
// 瀹℃壒浜哄繀濉牎楠�
const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
if (hasEmptyApprover) {
@@ -257,6 +296,7 @@
}
// 鍏抽棴寮规
const closeDia = () => {
+ fileList.value = []
proxy.resetForm("formRef");
dialogFormVisible.value = false;
emit('close')
@@ -269,6 +309,48 @@
const day = String(today.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
+
+// 涓婁紶鍓嶆牎妫�
+function handleBeforeUpload(file) {
+ // 鏍℃鏂囦欢澶у皬
+ // if (file.size > 1024 * 1024 * 10) {
+ // proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+ // return false;
+ // }
+ proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+ return true;
+}
+// 涓婁紶澶辫触
+function handleUploadError(err) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+ proxy.$modal.closeLoading();
+}
+// 涓婁紶鎴愬姛鍥炶皟
+function handleUploadSuccess(res, file, uploadFiles) {
+ proxy.$modal.closeLoading();
+ if (res.code === 200) {
+ // 纭繚 tempFileIds 瀛樺湪涓斾负鏁扮粍
+ if (!form.value.tempFileIds) {
+ form.value.tempFileIds = [];
+ }
+ form.value.tempFileIds.push(res.data.tempId);
+ proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+ } else {
+ proxy.$modal.msgError(res.msg);
+ proxy.$refs.fileUpload.handleRemove(file);
+ }
+}
+// 绉婚櫎鏂囦欢
+function handleRemove(file) {
+ if (operationType.value === "edit") {
+ let ids = [];
+ ids.push(file.id);
+ delLedgerFile(ids).then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ });
+ }
+}
+
defineExpose({
openDialog,
});
diff --git a/src/views/collaborativeApproval/approvalProcess/fileList.vue b/src/views/collaborativeApproval/approvalProcess/fileList.vue
new file mode 100644
index 0000000..e9e3b87
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/fileList.vue
@@ -0,0 +1,43 @@
+<template>
+ <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
+ <el-table :data="tableData" border height="40vh" stripe>
+ <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
+ <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
+ <template #default="scope">
+ <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
+ <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-dialog>
+ <filePreview ref="filePreviewRef" />
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import filePreview from '@/components/filePreview/index.vue'
+
+const dialogVisible = ref(false)
+const tableData = ref([])
+const { proxy } = getCurrentInstance();
+const filePreviewRef = ref()
+const handleClose = () => {
+ dialogVisible.value = false
+}
+const open = (list) => {
+ dialogVisible.value = true
+ tableData.value = list
+}
+const downLoadFile = (row) => {
+ proxy.$download.name(row.url);
+
+}
+const lookFile = (row) => {
+ filePreviewRef.value.open(row.url)
+}
+defineExpose({
+ open
+})
+</script>
+
+<style></style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index 79f358b..c3b713e 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -42,12 +42,14 @@
:total="page.total"
></PIMTable>
</div>
- <info-form-dia ref="infoFormDia" @close="handleQuery"></info-form-dia>
+ <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="approveType"></info-form-dia>
<approval-dia ref="approvalDia" @close="handleQuery"></approval-dia>
+ <FileList ref="fileListRef" />
</div>
</template>
<script setup>
+import FileList from "./fileList.vue";
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {ElMessageBox} from "element-plus";
@@ -55,6 +57,15 @@
import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user";
+
+// 瀹氫箟缁勪欢鎺ユ敹鐨刾rops
+const props = defineProps({
+ approveType: {
+ type: [Number, String],
+ default: 0
+ }
+});
+
const userStore = useUserStore();
@@ -116,11 +127,12 @@
{
label: "鐢宠浜�",
prop: "approveUserName",
+ width: 120
},
{
label: "鐢宠鏃ユ湡",
prop: "approveTime",
- width: 120
+ width: 200
},
{
label: "缁撴潫鏃ユ湡",
@@ -137,7 +149,7 @@
label: "鎿嶄綔",
align: "center",
fixed: "right",
- width: 150,
+ width: 230,
operation: [
{
name: "缂栬緫",
@@ -162,6 +174,13 @@
openApprovalDia('view', row);
},
},
+ {
+ name: "闄勪欢",
+ type: "text",
+ clickFun: (row) => {
+ downLoadFile(row);
+ },
+ },
],
},
]);
@@ -183,6 +202,11 @@
page.current = 1;
getList();
};
+const fileListRef = ref(null)
+const downLoadFile = (row) => {
+ fileListRef.value.open(row.commonFileList)
+
+}
const pagination = (obj) => {
page.current = obj.page;
page.size = obj.limit;
@@ -190,7 +214,7 @@
};
const getList = () => {
tableLoading.value = true;
- approveProcessListPage({...page, ...searchForm.value,}).then(res => {
+ approveProcessListPage({...page, ...searchForm.value,approveType:props.approveType}).then(res => {
tableLoading.value = false;
tableData.value = res.data.records
page.total = res.data.total;
diff --git a/src/views/collaborativeApproval/approvalProcess/index1.vue b/src/views/collaborativeApproval/approvalProcess/index1.vue
new file mode 100644
index 0000000..c46c68a
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/index1.vue
@@ -0,0 +1,22 @@
+<template>
+ <div class="container">
+ <!-- 寮曞叆index.vue缁勪欢骞朵紶閫掑弬鏁� -->
+ <ApprovalProcessIndex :approveType="1" />
+ </div>
+</template>
+
+<script setup>
+import ApprovalProcessIndex from './index.vue'
+
+// 瀹氫箟缁勪欢鍚嶇О
+defineOptions({
+ name: 'ApprovalProcessIndex1'
+})
+</script>
+
+<style scoped>
+.container {
+ width: 100%;
+ height: 100%;
+}
+</style>
diff --git a/src/views/collaborativeApproval/approvalProcess/index2.vue b/src/views/collaborativeApproval/approvalProcess/index2.vue
new file mode 100644
index 0000000..7c15c3e
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/index2.vue
@@ -0,0 +1,22 @@
+<template>
+ <div class="container">
+ <!-- 寮曞叆index.vue缁勪欢骞朵紶閫掑弬鏁� -->
+ <ApprovalProcessIndex :approveType="2" />
+ </div>
+</template>
+
+<script setup>
+import ApprovalProcessIndex from './index.vue'
+
+// 瀹氫箟缁勪欢鍚嶇О
+defineOptions({
+ name: 'ApprovalProcessIndex1'
+})
+</script>
+
+<style scoped>
+.container {
+ width: 100%;
+ height: 100%;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/approvalProcess/index3.vue b/src/views/collaborativeApproval/approvalProcess/index3.vue
new file mode 100644
index 0000000..3afb6f5
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/index3.vue
@@ -0,0 +1,22 @@
+<template>
+ <div class="container">
+ <!-- 寮曞叆index.vue缁勪欢骞朵紶閫掑弬鏁� -->
+ <ApprovalProcessIndex :approveType="3" />
+ </div>
+</template>
+
+<script setup>
+import ApprovalProcessIndex from './index.vue'
+
+// 瀹氫箟缁勪欢鍚嶇О
+defineOptions({
+ name: 'ApprovalProcessIndex1'
+})
+</script>
+
+<style scoped>
+.container {
+ width: 100%;
+ height: 100%;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/approvalProcess/index4.vue b/src/views/collaborativeApproval/approvalProcess/index4.vue
new file mode 100644
index 0000000..77236af
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/index4.vue
@@ -0,0 +1,22 @@
+<template>
+ <div class="container">
+ <!-- 寮曞叆index.vue缁勪欢骞朵紶閫掑弬鏁� -->
+ <ApprovalProcessIndex :approveType="4" />
+ </div>
+</template>
+
+<script setup>
+import ApprovalProcessIndex from './index.vue'
+
+// 瀹氫箟缁勪欢鍚嶇О
+defineOptions({
+ name: 'ApprovalProcessIndex1'
+})
+</script>
+
+<style scoped>
+.container {
+ width: 100%;
+ height: 100%;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/attendanceManagement/index.vue b/src/views/collaborativeApproval/attendanceManagement/index.vue
new file mode 100644
index 0000000..8db29fc
--- /dev/null
+++ b/src/views/collaborativeApproval/attendanceManagement/index.vue
@@ -0,0 +1,714 @@
+<template>
+ <div class="app-container">
+ <el-tabs v-model="activeTab" type="border-card">
+ <!-- 鍋囨湡璁剧疆 -->
+ <el-tab-pane label="鍋囨湡璁剧疆" name="holiday">
+ <div class="tab-content">
+ <el-button type="primary" @click="openDialog('holiday', 'add')">鏂板鍋囨湡</el-button>
+
+ <el-table :data="holidayData" border style="width: 100%; margin-top: 20px;" stripe>
+ <el-table-column prop="name" label="鍋囨湡鍚嶇О" />
+ <el-table-column prop="type" label="鍋囨湡绫诲瀷">
+ <template #default="scope">
+ <el-tag :type="getTagType(scope.row.type)">{{ getTypeLabel(scope.row.type) }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="startDate" label="寮�濮嬫棩鏈�" />
+ <el-table-column prop="endDate" label="缁撴潫鏃ユ湡" />
+ <el-table-column prop="days" label="澶╂暟" align="center" />
+ <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 label="鎿嶄綔" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" size="small" @click="openDialog('holiday', 'edit', scope.row)">缂栬緫</el-button>
+ <el-button type="danger" size="small" @click="deleteItem('holiday', scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-tab-pane>
+
+ <!-- 骞村亣璁剧疆 -->
+ <el-tab-pane label="骞村亣璁剧疆" name="annual">
+ <div class="tab-content">
+ <el-button type="primary" @click="openDialog('annual', 'add')">鏂板骞村亣瑙勫垯</el-button>
+
+ <el-table :data="annualData" border style="width: 100%; margin-top: 20px;" stripe>
+ <el-table-column prop="employeeType" label="鍛樺伐绫诲瀷"/>
+ <el-table-column prop="workYears" label="宸ヤ綔骞撮檺" />
+ <el-table-column prop="annualDays" label="骞村亣澶╂暟" align="center" />
+ <el-table-column prop="maxCarryOver" label="鏈�澶х粨杞ぉ鏁�" align="center" />
+ <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 label="鎿嶄綔" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" size="small" @click="openDialog('annual', 'edit', scope.row)">缂栬緫</el-button>
+ <el-button type="danger" size="small" @click="deleteItem('annual', scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-tab-pane>
+
+ <!-- 鍔犵彮璁剧疆 -->
+ <el-tab-pane label="鍔犵彮璁剧疆" name="overtime">
+ <div class="tab-content">
+ <el-button type="primary" @click="openDialog('overtime', 'add')">鏂板鍔犵彮瑙勫垯</el-button>
+
+ <el-table :data="overtimeData" border style="width: 100%; margin-top: 20px;" stripe>
+ <el-table-column prop="name" label="瑙勫垯鍚嶇О" />
+ <el-table-column prop="type" label="鍔犵彮绫诲瀷" >
+ <template #default="scope">
+ <el-tag :type="getTagType(scope.row.type)">{{ getTypeLabel(scope.row.type) }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="startTime" label="寮�濮嬫椂闂�" />
+ <el-table-column prop="endTime" label="缁撴潫鏃堕棿" />
+ <el-table-column prop="rate" label="鍊嶇巼" align="center" />
+ <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 label="鎿嶄綔" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" size="small" @click="openDialog('overtime', 'edit', scope.row)">缂栬緫</el-button>
+ <el-button type="danger" size="small" @click="deleteItem('overtime', scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-tab-pane>
+
+ <!-- 涓婄彮鏃堕棿璁剧疆 -->
+ <el-tab-pane label="涓婄彮鏃堕棿璁剧疆" name="worktime">
+ <div class="tab-content">
+ <el-button type="primary" @click="openDialog('worktime', 'add')">鏂板鏃堕棿娈�</el-button>
+
+ <el-table :data="worktimeData" border style="width: 100%; margin-top: 20px;" stripe>
+ <el-table-column prop="name" label="鏃堕棿娈靛悕绉�" />
+ <el-table-column prop="startTime" label="涓婄彮鏃堕棿"/>
+ <el-table-column prop="endTime" label="涓嬬彮鏃堕棿" />
+ <el-table-column prop="flexibleStart" label="寮规�т笂鐝�">
+ <template #default="scope">
+ <el-tag :type="scope.row.flexibleStart ? 'success' : 'info'">
+ {{ scope.row.flexibleStart ? '鏄�' : '鍚�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="flexibleMinutes" label="寮规�ф椂闂�(鍒嗛挓)" width="120" align="center" />
+ <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 label="鎿嶄綔" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" size="small" @click="openDialog('worktime', 'edit', scope.row)">缂栬緫</el-button>
+ <el-button type="danger" size="small" @click="deleteItem('worktime', scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+
+ <!-- 閫氱敤寮圭獥 -->
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+ <el-form-item label="鍚嶇О" prop="name" v-if="currentType !== 'annual'">
+ <el-input v-model="form.name" placeholder="璇疯緭鍏ュ悕绉�" />
+ </el-form-item>
+
+ <el-form-item label="绫诲瀷" prop="type" v-if="currentType === 'holiday' || currentType === 'overtime'">
+ <el-select v-model="form.type" placeholder="璇烽�夋嫨绫诲瀷" style="width: 100%">
+ <el-option
+ v-for="option in getTypeOptions()"
+ :key="option.value"
+ :label="option.label"
+ :value="option.value"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="鍛樺伐绫诲瀷" prop="employeeType" v-if="currentType === 'annual'">
+ <el-select v-model="form.employeeType" placeholder="璇烽�夋嫨鍛樺伐绫诲瀷" style="width: 100%">
+ <el-option label="姝e紡鍛樺伐" value="regular" />
+ <el-option label="璇曠敤鏈熷憳宸�" value="probation" />
+ <el-option label="瀹炰範鐢�" value="intern" />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="宸ヤ綔骞撮檺" prop="workYears" v-if="currentType === 'annual'">
+ <el-input v-model="form.workYears" placeholder="濡傦細1-3骞淬��3-5骞寸瓑" />
+ </el-form-item>
+
+ <el-form-item label="骞村亣澶╂暟" prop="annualDays" v-if="currentType === 'annual'">
+ <el-input-number v-model="form.annualDays" :min="0" :max="365" style="width: 100%" />
+ </el-form-item>
+
+ <el-form-item label="鏈�澶х粨杞ぉ鏁�" prop="maxCarryOver" v-if="currentType === 'annual'">
+ <el-input-number v-model="form.maxCarryOver" :min="0" :max="30" style="width: 100%" />
+ </el-form-item>
+
+ <el-form-item label="鏃ユ湡鑼冨洿" prop="dateRange" v-if="currentType === 'holiday'">
+ <el-date-picker
+ v-model="form.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ style="width: 100%"
+ @change="calculateDays"
+ />
+ </el-form-item>
+
+ <el-form-item label="澶╂暟" prop="days" v-if="currentType === 'holiday'">
+ <el-input-number v-model="form.days" :min="0" style="width: 100%" />
+ </el-form-item>
+
+ <el-form-item label="寮�濮嬫椂闂�" prop="startTime" v-if="currentType === 'overtime'">
+ <el-time-picker
+ v-model="form.startTime"
+ placeholder="寮�濮嬫椂闂�"
+ format="HH:mm"
+ value-format="HH:mm"
+ style="width: 100%"
+ @change="validateTimeField('startTime')"
+ />
+ </el-form-item>
+
+ <el-form-item label="缁撴潫鏃堕棿" prop="endTime" v-if="currentType === 'overtime'">
+ <el-time-picker
+ v-model="form.endTime"
+ placeholder="缁撴潫鏃堕棿"
+ format="HH:mm"
+ value-format="HH:mm"
+ style="width: 100%"
+ @change="validateTimeField('endTime')"
+ />
+ </el-form-item>
+
+ <el-form-item label="鍊嶇巼" prop="rate" v-if="currentType === 'overtime'">
+ <el-input-number v-model="form.rate" :min="1" :max="3" :step="0.5" style="width: 100%" />
+ </el-form-item>
+
+ <el-form-item label="涓婄彮鏃堕棿" prop="workStartTime" v-if="currentType === 'worktime'">
+ <el-time-picker
+ v-model="form.workStartTime"
+ placeholder="涓婄彮鏃堕棿"
+ format="HH:mm"
+ value-format="HH:mm"
+ style="width: 100%"
+ @change="validateTimeField('workStartTime')"
+ />
+ </el-form-item>
+
+ <el-form-item label="涓嬬彮鏃堕棿" prop="workEndTime" v-if="currentType === 'worktime'">
+ <el-time-picker
+ v-model="form.workEndTime"
+ placeholder="涓嬬彮鏃堕棿"
+ format="HH:mm"
+ value-format="HH:mm"
+ style="width: 100%"
+ @change="validateTimeField('workEndTime')"
+ />
+ </el-form-item>
+
+ <el-form-item label="寮规�т笂鐝�" prop="flexibleStart" v-if="currentType === 'worktime'">
+ <el-switch v-model="form.flexibleStart" />
+ </el-form-item>
+
+ <el-form-item label="寮规�ф椂闂�(鍒嗛挓)" prop="flexibleMinutes" v-if="currentType === 'worktime' && form.flexibleStart">
+ <el-input-number v-model="form.flexibleMinutes" :min="0" :max="120" style="width: 100%" />
+ </el-form-item>
+
+ <el-form-item label="鐘舵��" prop="status">
+ <el-radio-group v-model="form.status">
+ <el-radio value="active">鍚敤</el-radio>
+ <el-radio value="inactive">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+// 褰撳墠婵�娲荤殑鏍囩椤�
+const activeTab = ref('holiday')
+
+// 寮圭獥鐩稿叧
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const currentType = ref('')
+const currentAction = ref('')
+const currentEditId = ref('')
+const formRef = ref()
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+ name: '',
+ type: '',
+ dateRange: [],
+ days: 0,
+ employeeType: '',
+ workYears: '',
+ annualDays: 0,
+ maxCarryOver: 0,
+ startTime: '', // 鍔犵彮寮�濮嬫椂闂�
+ endTime: '', // 鍔犵彮缁撴潫鏃堕棿
+ workStartTime: '', // 涓婄彮鏃堕棿
+ workEndTime: '', // 涓嬬彮鏃堕棿
+ rate: 1.5,
+ flexibleStart: false,
+ flexibleMinutes: 30,
+ status: 'active'
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const rules = {
+ name: [{ required: true, message: '璇疯緭鍏ュ悕绉�', trigger: 'blur' }],
+ type: [{ required: true, message: '璇烽�夋嫨绫诲瀷', trigger: 'change' }],
+ dateRange: [{ required: true, message: '璇烽�夋嫨鏃ユ湡鑼冨洿', trigger: 'change' }],
+ days: [{ required: true, message: '璇疯緭鍏ュぉ鏁�', trigger: 'blur' }],
+ employeeType: [{ required: true, message: '璇烽�夋嫨鍛樺伐绫诲瀷', trigger: 'change' }],
+ workYears: [{ required: true, message: '璇疯緭鍏ュ伐浣滃勾闄�', trigger: 'blur' }],
+ annualDays: [{ required: true, message: '璇疯緭鍏ュ勾鍋囧ぉ鏁�', trigger: 'blur' }],
+ maxCarryOver: [{ required: true, message: '璇疯緭鍏ユ渶澶х粨杞ぉ鏁�', trigger: 'blur' }],
+ startTime: [{
+ required: true,
+ message: '璇烽�夋嫨寮�濮嬫椂闂�',
+ trigger: 'change',
+ validator: (rule, value, callback) => {
+ if (!value) {
+ callback(new Error('璇烽�夋嫨寮�濮嬫椂闂�'))
+ } else {
+ callback()
+ }
+ }
+ }],
+ endTime: [{
+ required: true,
+ message: '璇烽�夋嫨缁撴潫鏃堕棿',
+ trigger: 'change',
+ validator: (rule, value, callback) => {
+ if (!value) {
+ callback(new Error('璇烽�夋嫨缁撴潫鏃堕棿'))
+ } else {
+ callback()
+ }
+ }
+ }],
+ workStartTime: [{
+ required: true,
+ message: '璇烽�夋嫨涓婄彮鏃堕棿',
+ trigger: 'change',
+ validator: (rule, value, callback) => {
+ if (!value) {
+ callback(new Error('璇烽�夋嫨涓婄彮鏃堕棿'))
+ } else {
+ callback()
+ }
+ }
+ }],
+ workEndTime: [{
+ required: true,
+ message: '璇烽�夋嫨涓嬬彮鏃堕棿',
+ trigger: 'change',
+ validator: (rule, value, callback) => {
+ if (!value) {
+ callback(new Error('璇烽�夋嫨涓嬬彮鏃堕棿'))
+ } else {
+ callback()
+ }
+ }
+ }],
+ rate: [{ required: true, message: '璇疯緭鍏ュ�嶇巼', trigger: 'blur' }]
+}
+
+// 妯℃嫙鏁版嵁
+const holidayData = ref([
+ { id: '1', name: '鏄ヨ妭', type: 'legal', startDate: '2024-02-10', endDate: '2024-02-17', days: 8, status: 'active' },
+ { id: '2', name: '娓呮槑鑺�', type: 'legal', startDate: '2024-04-05', endDate: '2024-04-05', days: 1, status: 'active' },
+ { id: '3', name: '鍔冲姩鑺�', type: 'legal', startDate: '2024-05-01', endDate: '2024-05-05', days: 5, status: 'active' }
+])
+
+const annualData = ref([
+ { id: '1', employeeType: 'regular', workYears: '1-3骞�', annualDays: 5, maxCarryOver: 2, status: 'active' },
+ { id: '2', employeeType: 'regular', workYears: '3-5骞�', annualDays: 10, maxCarryOver: 5, status: 'active' },
+ { id: '3', employeeType: 'regular', workYears: '5骞翠互涓�', annualDays: 15, maxCarryOver: 10, status: 'active' }
+])
+
+const overtimeData = ref([
+ { id: '1', name: '宸ヤ綔鏃ュ姞鐝�', type: 'weekday', startTime: '18:00', endTime: '22:00', rate: 1.5, status: 'active' },
+ { id: '2', name: '鍛ㄦ湯鍔犵彮', type: 'weekend', startTime: '09:00', endTime: '18:00', rate: 2.0, status: 'active' },
+ { id: '3', name: '娣卞鍔犵彮', type: 'night', startTime: '22:00', endTime: '06:00', rate: 2.5, status: 'active' }
+])
+
+const worktimeData = ref([
+ { id: '1', name: '鏍囧噯宸ヤ綔鏃堕棿', startTime: '09:00', endTime: '18:00', flexibleStart: true, flexibleMinutes: 30, status: 'active' },
+ { id: '2', name: '鏃╃彮鏃堕棿', startTime: '08:00', endTime: '17:00', flexibleStart: false, flexibleMinutes: 0, status: 'active' },
+ { id: '3', name: '鏅氱彮鏃堕棿', startTime: '14:00', endTime: '23:00', flexibleStart: false, flexibleMinutes: 0, status: 'active' }
+])
+
+// 宸ュ叿鍑芥暟
+const getTagType = (type) => {
+ const tagMap = {
+ legal: 'success', adjustment: 'warning', special: 'info', company: 'primary',
+ weekday: 'primary', weekend: 'warning', holiday: 'danger', night: 'info'
+ }
+ return tagMap[type] || 'info'
+}
+
+const getTypeLabel = (type) => {
+ const labelMap = {
+ legal: '娉曞畾鑺傚亣鏃�', adjustment: '璋冧紤鏃�', special: '鐗规畩鍋囨湡', company: '鍏徃鍋囨湡',
+ weekday: '宸ヤ綔鏃ュ姞鐝�', weekend: '鍛ㄦ湯鍔犵彮', holiday: '鑺傚亣鏃ュ姞鐝�', night: '娣卞鍔犵彮'
+ }
+ return labelMap[type] || type
+}
+
+const getTypeOptions = () => {
+ if (currentType.value === 'holiday') {
+ return [
+ { label: '娉曞畾鑺傚亣鏃�', value: 'legal' },
+ { label: '璋冧紤鏃�', value: 'adjustment' },
+ { label: '鐗规畩鍋囨湡', value: 'special' },
+ { label: '鍏徃鍋囨湡', value: 'company' }
+ ]
+ } else if (currentType.value === 'overtime') {
+ return [
+ { label: '宸ヤ綔鏃ュ姞鐝�', value: 'weekday' },
+ { label: '鍛ㄦ湯鍔犵彮', value: 'weekend' },
+ { label: '鑺傚亣鏃ュ姞鐝�', value: 'holiday' },
+ { label: '娣卞鍔犵彮', value: 'night' }
+ ]
+ }
+ return []
+}
+
+// 璁$畻鍋囨湡澶╂暟
+const calculateDays = () => {
+ try {
+ if (form.dateRange && form.dateRange.length === 2 && form.dateRange[0] && form.dateRange[1]) {
+ const start = new Date(form.dateRange[0])
+ const end = new Date(form.dateRange[1])
+
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) {
+ console.warn('鏃犳晥鐨勬棩鏈熸牸寮�')
+ return
+ }
+
+ const diffTime = Math.abs(end - start)
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1
+ form.days = diffDays
+ }
+ } catch (error) {
+ console.error('璁$畻澶╂暟澶辫触:', error)
+ }
+}
+
+// 楠岃瘉鏃堕棿鏍煎紡
+const validateTime = (time) => {
+ if (!time) return ''
+ if (typeof time === 'string') return time
+ if (time instanceof Date) {
+ return time.toTimeString().slice(0, 5)
+ }
+ return ''
+}
+
+// 楠岃瘉鏃堕棿瀛楁
+const validateTimeField = (fieldName) => {
+ try {
+ const value = form[fieldName]
+ if (value && typeof value === 'object' && value.hour !== undefined) {
+ // 濡傛灉鏄椂闂村璞★紝杞崲涓哄瓧绗︿覆鏍煎紡
+ const hours = value.hour.toString().padStart(2, '0')
+ const minutes = value.minute.toString().padStart(2, '0')
+ form[fieldName] = `${hours}:${minutes}`
+ }
+ } catch (error) {
+ console.error(`楠岃瘉鏃堕棿瀛楁 ${fieldName} 澶辫触:`, error)
+ form[fieldName] = ''
+ }
+}
+
+// 鎵撳紑寮圭獥
+const openDialog = (type, action, row = null) => {
+ try {
+ currentType.value = type
+ currentAction.value = action
+
+ if (action === 'add') {
+ dialogTitle.value = `鏂板${getTypeName(type)}`
+ currentEditId.value = ''
+ resetForm()
+ } else if (action === 'edit' && row) {
+ dialogTitle.value = `缂栬緫${getTypeName(type)}`
+ currentEditId.value = row.id
+ fillForm(row)
+ }
+
+ dialogVisible.value = true
+ } catch (error) {
+ console.error('鎵撳紑寮圭獥澶辫触:', error)
+ ElMessage.error('鎵撳紑寮圭獥澶辫触锛岃閲嶈瘯')
+ }
+}
+
+const getTypeName = (type) => {
+ const nameMap = {
+ holiday: '鍋囨湡',
+ annual: '骞村亣瑙勫垯',
+ overtime: '鍔犵彮瑙勫垯',
+ worktime: '鏃堕棿娈�'
+ }
+ return nameMap[type] || ''
+}
+
+const resetForm = () => {
+ Object.assign(form, {
+ name: '',
+ type: '',
+ dateRange: [],
+ days: 0,
+ employeeType: '',
+ workYears: '',
+ annualDays: 0,
+ maxCarryOver: 0,
+ startTime: '',
+ endTime: '',
+ workStartTime: '',
+ workEndTime: '',
+ rate: 1.5,
+ flexibleStart: false,
+ flexibleMinutes: 30,
+ status: 'active'
+ })
+}
+
+const fillForm = (row) => {
+ if (currentType.value === 'holiday') {
+ Object.assign(form, {
+ name: row.name,
+ type: row.type,
+ dateRange: [new Date(row.startDate), new Date(row.endDate)],
+ days: row.days,
+ status: row.status
+ })
+ } else if (currentType.value === 'annual') {
+ Object.assign(form, {
+ employeeType: row.employeeType,
+ workYears: row.workYears,
+ annualDays: row.annualDays,
+ maxCarryOver: row.maxCarryOver,
+ status: row.status
+ })
+ } else if (currentType.value === 'overtime') {
+ Object.assign(form, {
+ name: row.name,
+ type: row.type,
+ startTime: row.startTime || '',
+ endTime: row.endTime || '',
+ rate: row.rate,
+ status: row.status
+ })
+ } else if (currentType.value === 'worktime') {
+ Object.assign(form, {
+ name: row.name,
+ workStartTime: row.startTime || '',
+ workEndTime: row.endTime || '',
+ flexibleStart: row.flexibleStart,
+ flexibleMinutes: row.flexibleMinutes,
+ status: row.status
+ })
+ }
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+ try {
+ if (!formRef.value) {
+ ElMessage.error('琛ㄥ崟寮曠敤涓嶅瓨鍦�')
+ return
+ }
+
+ await formRef.value.validate()
+
+ if (currentAction.value === 'add') {
+ addItem()
+ } else if (currentAction.value === 'edit') {
+ editItem()
+ }
+
+ dialogVisible.value = false
+ ElMessage.success('鎿嶄綔鎴愬姛')
+ } catch (error) {
+ console.error('琛ㄥ崟楠岃瘉澶辫触:', error)
+ ElMessage.error('琛ㄥ崟楠岃瘉澶辫触锛岃妫�鏌ヨ緭鍏�')
+ }
+}
+
+const addItem = () => {
+ const newItem = { ...form, id: Date.now().toString() }
+
+ if (currentType.value === 'holiday') {
+ newItem.startDate = form.dateRange[0].toISOString().split('T')[0]
+ newItem.endDate = form.dateRange[1].toISOString().split('T')[0]
+ holidayData.value.push(newItem)
+ } else if (currentType.value === 'annual') {
+ annualData.value.push(newItem)
+ } else if (currentType.value === 'overtime') {
+ newItem.startTime = form.startTime || ''
+ newItem.endTime = form.endTime || ''
+ overtimeData.value.push(newItem)
+ } else if (currentType.value === 'worktime') {
+ newItem.startTime = form.workStartTime || ''
+ newItem.endTime = form.workEndTime || ''
+ worktimeData.value.push(newItem)
+ }
+}
+
+const editItem = () => {
+ let dataArray
+ let index
+
+ if (currentType.value === 'holiday') {
+ dataArray = holidayData.value
+ index = dataArray.findIndex(item => item.id === currentEditId.value)
+ if (index > -1) {
+ dataArray[index] = {
+ ...dataArray[index],
+ name: form.name,
+ type: form.type,
+ startDate: form.dateRange[0].toISOString().split('T')[0],
+ endDate: form.dateRange[1].toISOString().split('T')[0],
+ days: form.days,
+ status: form.status
+ }
+ }
+ } else if (currentType.value === 'annual') {
+ dataArray = annualData.value
+ index = dataArray.findIndex(item => item.id === currentEditId.value)
+ if (index > -1) {
+ dataArray[index] = {
+ ...dataArray[index],
+ employeeType: form.employeeType,
+ workYears: form.workYears,
+ annualDays: form.annualDays,
+ maxCarryOver: form.maxCarryOver,
+ status: form.status
+ }
+ }
+ } else if (currentType.value === 'overtime') {
+ dataArray = overtimeData.value
+ index = dataArray.findIndex(item => item.id === currentEditId.value)
+ if (index > -1) {
+ dataArray[index] = {
+ ...dataArray[index],
+ name: form.name,
+ type: form.type,
+ startTime: form.startTime || '',
+ endTime: form.endTime || '',
+ rate: form.rate,
+ status: form.status
+ }
+ }
+ } else if (currentType.value === 'worktime') {
+ dataArray = worktimeData.value
+ index = dataArray.findIndex(item => item.id === currentEditId.value)
+ if (index > -1) {
+ dataArray[index] = {
+ ...dataArray[index],
+ name: form.name,
+ startTime: form.workStartTime || '',
+ endTime: form.workEndTime || '',
+ flexibleStart: form.flexibleStart,
+ flexibleMinutes: form.flexibleMinutes,
+ status: form.status
+ }
+ }
+ }
+}
+
+// 鍒犻櫎椤圭洰
+const deleteItem = (type, row) => {
+ ElMessageBox.confirm('纭畾瑕佸垹闄よ繖涓」鐩悧锛�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ let dataArray
+ if (type === 'holiday') dataArray = holidayData.value
+ else if (type === 'annual') dataArray = annualData.value
+ else if (type === 'overtime') dataArray = overtimeData.value
+ else if (type === 'worktime') dataArray = worktimeData.value
+
+ const index = dataArray.findIndex(item => item.id === row.id)
+ if (index > -1) {
+ dataArray.splice(index, 1)
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ }
+ })
+}
+
+onMounted(() => {
+ console.log('鑰冨嫟绠$悊椤甸潰鍔犺浇瀹屾垚')
+})
+
+onUnmounted(() => {
+ // 娓呯悊宸ヤ綔
+ dialogVisible.value = false
+ currentType.value = ''
+ currentAction.value = ''
+ currentEditId.value = ''
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+}
+
+.tab-content {
+ padding: 20px 0;
+}
+
+.dialog-footer {
+ text-align: right;
+}
+
+:deep(.el-tabs__content) {
+ padding: 20px;
+}
+
+:deep(.el-form-item) {
+ margin-bottom: 20px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/knowledgeBase/index.vue b/src/views/collaborativeApproval/knowledgeBase/index.vue
new file mode 100644
index 0000000..f944859
--- /dev/null
+++ b/src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -0,0 +1,848 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <span class="search_title">鐭ヨ瘑鏍囬锛�</span>
+ <el-input
+ v-model="searchForm.title"
+ style="width: 240px"
+ placeholder="璇疯緭鍏ョ煡璇嗘爣棰樻悳绱�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ <span class="search_title ml10">鐭ヨ瘑绫诲瀷锛�</span>
+ <el-select v-model="searchForm.type" clearable @change="handleQuery" style="width: 240px">
+ <el-option label="鍚堝悓鐗规壒" :value="'contract'" />
+ <el-option label="瀹℃壒妗堜緥" :value="'approval'" />
+ <el-option label="瑙e喅鏂规" :value="'solution'" />
+ <el-option label="缁忛獙鎬荤粨" :value="'experience'" />
+ <el-option label="鎿嶄綔鎸囧崡" :value="'guide'" />
+ </el-select>
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
+ 鎼滅储
+ </el-button>
+ </div>
+ <div>
+ <el-button type="primary" @click="openForm('add')">鏂板鐭ヨ瘑</el-button>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total"
+ ></PIMTable>
+ </div>
+
+ <!-- 鏂板/缂栬緫鐭ヨ瘑寮圭獥 -->
+ <el-dialog
+ v-model="dialogVisible"
+ :title="dialogTitle"
+ width="800px"
+ :close-on-click-modal="false"
+ >
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鐭ヨ瘑鏍囬" prop="title">
+ <el-input v-model="form.title" placeholder="璇疯緭鍏ョ煡璇嗘爣棰�" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐭ヨ瘑绫诲瀷" prop="type">
+ <el-select v-model="form.type" placeholder="璇烽�夋嫨鐭ヨ瘑绫诲瀷" style="width: 100%">
+ <el-option label="鍚堝悓鐗规壒" value="contract" />
+ <el-option label="瀹℃壒妗堜緥" value="approval" />
+ <el-option label="瑙e喅鏂规" value="solution" />
+ <el-option label="缁忛獙鎬荤粨" value="experience" />
+ <el-option label="鎿嶄綔鎸囧崡" value="guide" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="閫傜敤鍦烘櫙" prop="scenario">
+ <el-input v-model="form.scenario" placeholder="璇疯緭鍏ラ�傜敤鍦烘櫙" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="瑙e喅鏁堢巼" prop="efficiency">
+ <el-select v-model="form.efficiency" placeholder="璇烽�夋嫨瑙e喅鏁堢巼" style="width: 100%">
+ <el-option label="鏄捐憲鎻愬崌" value="high" />
+ <el-option label="涓�鑸彁鍗�" value="medium" />
+ <el-option label="杞诲井鎻愬崌" value="low" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="闂鎻忚堪" prop="problem">
+ <el-input
+ v-model="form.problem"
+ type="textarea"
+ :rows="3"
+ placeholder="璇锋弿杩伴亣鍒扮殑闂"
+ />
+ </el-form-item>
+ <el-form-item label="瑙e喅鏂规" prop="solution">
+ <el-input
+ v-model="form.solution"
+ type="textarea"
+ :rows="4"
+ placeholder="璇疯缁嗘弿杩拌В鍐虫柟妗�"
+ />
+ </el-form-item>
+ <el-form-item label="鍏抽敭瑕佺偣" prop="keyPoints">
+ <el-input
+ v-model="form.keyPoints"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ叧閿鐐癸紝鐢ㄩ�楀彿鍒嗛殧"
+ />
+ </el-form-item>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍒涘缓浜�" prop="creator">
+ <el-input v-model="form.creator" placeholder="璇疯緭鍏ュ垱寤轰汉" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="浣跨敤娆℃暟" prop="usageCount">
+ <el-input-number v-model="form.usageCount" :min="0" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+
+ <!-- 鏌ョ湅鐭ヨ瘑璇︽儏寮圭獥 -->
+ <el-dialog
+ v-model="viewDialogVisible"
+ title="鐭ヨ瘑璇︽儏"
+ width="900px"
+ :close-on-click-modal="false"
+ >
+ <div class="knowledge-detail">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鐭ヨ瘑鏍囬" :span="2">
+ <span class="detail-title">{{ currentKnowledge.title }}</span>
+ </el-descriptions-item>
+ <el-descriptions-item label="鐭ヨ瘑绫诲瀷">
+ <el-tag :type="getTypeTagType(currentKnowledge.type)">
+ {{ getTypeLabel(currentKnowledge.type) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="閫傜敤鍦烘櫙">
+ {{ currentKnowledge.scenario }}
+ </el-descriptions-item>
+ <el-descriptions-item label="瑙e喅鏁堢巼">
+ <el-tag :type="getEfficiencyTagType(currentKnowledge.efficiency)">
+ {{ getEfficiencyLabel(currentKnowledge.efficiency) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="浣跨敤娆℃暟">
+ <el-tag type="info">{{ currentKnowledge.usageCount }} 娆�</el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓浜�">
+ {{ currentKnowledge.creator }}
+ </el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓鏃堕棿">
+ {{ currentKnowledge.createTime }}
+ </el-descriptions-item>
+ </el-descriptions>
+
+ <div class="detail-section">
+ <h4>闂鎻忚堪</h4>
+ <div class="detail-content">{{ currentKnowledge.problem }}</div>
+ </div>
+
+ <div class="detail-section">
+ <h4>瑙e喅鏂规</h4>
+ <div class="detail-content">{{ currentKnowledge.solution }}</div>
+ </div>
+
+ <div class="detail-section">
+ <h4>鍏抽敭瑕佺偣</h4>
+ <div class="key-points">
+ <el-tag
+ v-for="(point, index) in currentKnowledge.keyPoints.split(',')"
+ :key="index"
+ type="success"
+ style="margin-right: 8px; margin-bottom: 8px;"
+ >
+ {{ point.trim() }}
+ </el-tag>
+ </div>
+ </div>
+
+ <div class="detail-section">
+ <h4>浣跨敤缁熻</h4>
+ <div class="usage-stats">
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <div class="stat-item">
+ <div class="stat-number">{{ currentKnowledge.usageCount }}</div>
+ <div class="stat-label">浣跨敤娆℃暟</div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="stat-item">
+ <div class="stat-number">{{ getEfficiencyScore(currentKnowledge.efficiency) }}%</div>
+ <div class="stat-label">鏁堢巼鎻愬崌</div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="stat-item">
+ <div class="stat-number">{{ getTimeSaved(currentKnowledge.efficiency) }}</div>
+ <div class="stat-label">骞冲潎鑺傜渷鏃堕棿</div>
+ </div>
+ </el-col>
+ </el-row>
+ </div>
+ </div>
+ </div>
+
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="viewDialogVisible = false">鍏抽棴</el-button>
+ <el-button type="primary" @click="copyKnowledge">澶嶅埗鐭ヨ瘑</el-button>
+ <el-button type="success" @click="markAsFavorite">鏀惰棌</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import { onMounted, ref, reactive, toRefs } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const rules = {
+ title: [
+ { required: true, message: "璇疯緭鍏ョ煡璇嗘爣棰�", trigger: "blur" }
+ ],
+ type: [
+ { required: true, message: "璇烽�夋嫨鐭ヨ瘑绫诲瀷", trigger: "change" }
+ ],
+ problem: [
+ { required: true, message: "璇锋弿杩伴亣鍒扮殑闂", trigger: "blur" }
+ ],
+ solution: [
+ { required: true, message: "璇疯缁嗘弿杩拌В鍐虫柟妗�", trigger: "blur" }
+ ]
+};
+
+// 鍝嶅簲寮忔暟鎹�
+const data = reactive({
+ searchForm: {
+ title: "",
+ type: "",
+ },
+ tableLoading: false,
+ page: {
+ current: 1,
+ size: 100,
+ total: 0,
+ },
+ tableData: [],
+ selectedIds: [],
+ form: {
+ title: "",
+ type: "",
+ scenario: "",
+ efficiency: "medium",
+ problem: "",
+ solution: "",
+ keyPoints: "",
+ creator: "",
+ usageCount: 0
+ },
+ dialogVisible: false,
+ dialogTitle: "",
+ dialogType: "add",
+ viewDialogVisible: false,
+ currentKnowledge: {}
+});
+
+const {
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
+ selectedIds,
+ form,
+ dialogVisible,
+ dialogTitle,
+ dialogType,
+ viewDialogVisible,
+ currentKnowledge
+} = toRefs(data);
+
+// 琛ㄥ崟寮曠敤
+const formRef = ref();
+
+// 琛ㄦ牸鍒楅厤缃�
+const tableColumn = ref([
+ {
+ label: "鐭ヨ瘑鏍囬",
+ prop: "title",
+ showOverflowTooltip: true,
+ },
+ {
+ label: "鐭ヨ瘑绫诲瀷",
+ prop: "type",
+ dataType: "tag",
+ formatData: (params) => {
+ const typeMap = {
+ contract: "鍚堝悓鐗规壒",
+ approval: "瀹℃壒妗堜緥",
+ solution: "瑙e喅鏂规",
+ experience: "缁忛獙鎬荤粨",
+ guide: "鎿嶄綔鎸囧崡"
+ };
+ return typeMap[params] || params;
+ },
+ formatType: (params) => {
+ const typeMap = {
+ contract: "success",
+ approval: "warning",
+ solution: "primary",
+ experience: "info",
+ guide: "danger"
+ };
+ return typeMap[params] || "info";
+ }
+ },
+ {
+ label: "閫傜敤鍦烘櫙",
+ prop: "scenario",
+ width: 150,
+ showOverflowTooltip: true,
+ },
+ {
+ label: "瑙e喅鏁堢巼",
+ prop: "efficiency",
+ dataType: "tag",
+ formatData: (params) => {
+ const efficiencyMap = {
+ high: "鏄捐憲鎻愬崌",
+ medium: "涓�鑸彁鍗�",
+ low: "杞诲井鎻愬崌"
+ };
+ return efficiencyMap[params] || params;
+ },
+ formatType: (params) => {
+ const typeMap = {
+ high: "success",
+ medium: "warning",
+ low: "info"
+ };
+ return typeMap[params] || "info";
+ }
+ },
+ {
+ label: "浣跨敤娆℃暟",
+ prop: "usageCount",
+ width: 100,
+ align: "center"
+ },
+ {
+ label: "鍒涘缓浜�",
+ prop: "creator",
+ width: 120,
+ },
+ {
+ label: "鍒涘缓鏃堕棿",
+ prop: "createTime",
+ width: 180,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 200,
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ openForm("edit", row);
+ }
+ },
+ {
+ name: "鏌ョ湅",
+ type: "text",
+ clickFun: (row) => {
+ viewKnowledge(row);
+ }
+ }
+ ]
+ }
+]);
+
+// 妯℃嫙鏁版嵁
+let mockData = [
+ {
+ id: "1",
+ title: "鐗规畩鍚堝悓瀹℃壒娴佺▼浼樺寲鏂规",
+ type: "contract",
+ scenario: "澶ч鍚堝悓蹇�熷鎵�",
+ efficiency: "high",
+ problem: "澶ч鍚堝悓瀹℃壒娴佺▼澶嶆潅锛屽鎵规椂闂撮暱锛屽奖鍝嶄笟鍔¤繘灞�",
+ solution: "寤虹珛缁胯壊閫氶亾锛屽绗﹀悎鏉′欢鐨勫悎鍚岄噰鐢ㄧ畝鍖栧鎵规祦绋嬶紝鐢遍儴闂ㄨ礋璐d汉鐩存帴瀹℃壒锛屽钩鍧囧鎵规椂闂翠粠3澶╃缉鐭嚦1澶�",
+ keyPoints: "缁胯壊閫氶亾鏉′欢,绠�鍖栨祦绋�,瀹℃壒鏉冮檺,鏃堕棿鎺у埗",
+ creator: "寮犵粡鐞�",
+ usageCount: 15,
+ createTime: "2024-01-15 10:30:00"
+ },
+ {
+ id: "2",
+ title: "璺ㄩ儴闂ㄥ崗浣滃鎵圭粡楠屾�荤粨",
+ type: "experience",
+ scenario: "澶氶儴闂ㄥ崗浣滈」鐩�",
+ efficiency: "medium",
+ problem: "璺ㄩ儴闂ㄩ」鐩鎵规椂锛屽悇閮ㄩ棬鎰忚涓嶇粺涓�锛屽鎵硅繘搴︾紦鎱�",
+ solution: "寤虹珛椤圭洰鍗忚皟鏈哄埗锛屾寚瀹氶」鐩礋璐d汉锛屽畾鏈熷彫寮�鍗忚皟浼氳锛岀粺涓�鍚勬柟鎰忚鍚庡啀杩涜瀹℃壒",
+ keyPoints: "椤圭洰鍗忚皟,瀹氭湡浼氳,缁熶竴鎰忚,璐熻矗浜哄埗搴�",
+ creator: "鏉庝富绠�",
+ usageCount: 8,
+ createTime: "2024-01-14 15:20:00"
+ },
+ {
+ id: "3",
+ title: "绱ф�ラ噰璐鎵规搷浣滄寚鍗�",
+ type: "guide",
+ scenario: "绱ф�ラ噰璐渶姹�",
+ efficiency: "high",
+ problem: "绱ф�ラ噰璐椂瀹℃壒娴佺▼澶嶆潅锛屾棤娉曟弧瓒崇揣鎬ラ渶姹�",
+ solution: "鍒跺畾绱ф�ラ噰璐鎵规爣鍑嗭紝鏄庣‘绱ф�ョ▼搴﹀垎绾э紝涓嶅悓绾у埆閲囩敤涓嶅悓瀹℃壒娴佺▼锛岀‘淇濈揣鎬ラ渶姹傚緱鍒板強鏃跺鐞�",
+ keyPoints: "绱ф�ュ垎绾�,鏍囧噯鍒跺畾,娴佺▼绠�鍖�,鍙婃椂澶勭悊",
+ creator: "鐜嬩笓鍛�",
+ usageCount: 12,
+ createTime: "2024-01-13 09:15:00"
+ }
+];
+
+// 鐭ヨ瘑鏍囬妯℃澘
+const titleTemplates = [
+ "{type}瀹℃壒娴佺▼浼樺寲鏂规",
+ "{scenario}澶勭悊缁忛獙鎬荤粨",
+ "{type}鐗规畩鎯呭喌澶勭悊鎸囧崡",
+ "{scenario}蹇�熷鎵规柟妗�",
+ "{type}鏍囧噯鍖栨搷浣滄祦绋�",
+ "{scenario}闂瑙e喅鏂规",
+ "{type}鏈�浣冲疄璺垫�荤粨",
+ "{scenario}鏁堢巼鎻愬崌鏂规"
+];
+
+// 鐭ヨ瘑绫诲瀷閰嶇疆
+const knowledgeTypes = [
+ { type: "contract", label: "鍚堝悓鐗规壒", efficiency: "high" },
+ { type: "approval", label: "瀹℃壒妗堜緥", efficiency: "medium" },
+ { type: "solution", label: "瑙e喅鏂规", efficiency: "high" },
+ { type: "experience", label: "缁忛獙鎬荤粨", efficiency: "medium" },
+ { type: "guide", label: "鎿嶄綔鎸囧崡", efficiency: "low" }
+];
+
+// 鍦烘櫙鍒楄〃
+const scenarios = ["澶ч鍚堝悓瀹℃壒", "璺ㄩ儴闂ㄥ崗浣�", "绱ф�ラ噰璐�", "鐗规畩鐢宠", "娴佺▼浼樺寲", "闂澶勭悊", "鏍囧噯鍖栧缓璁�", "鏁堢巼鎻愬崌"];
+
+// 鑷姩鐢熸垚鏂版暟鎹�
+const generateNewData = () => {
+ const newId = (mockData.length + 1).toString();
+ const now = new Date();
+ const randomType = knowledgeTypes[Math.floor(Math.random() * knowledgeTypes.length)];
+ const randomScenario = scenarios[Math.floor(Math.random() * scenarios.length)];
+
+ // 鐢熸垚闅忔満鏍囬
+ let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)];
+ title = title
+ .replace('{type}', randomType.label)
+ .replace('{scenario}', randomScenario);
+
+ const newKnowledge = {
+ id: newId,
+ title: title,
+ type: randomType.type,
+ scenario: randomScenario,
+ efficiency: randomType.efficiency,
+ problem: `鍦�${randomScenario}杩囩▼涓亣鍒扮殑闂鎻忚堪...`,
+ solution: `閽堝${randomScenario}鐨勮В鍐虫柟妗堝拰鎿嶄綔姝ラ...`,
+ keyPoints: "鍏抽敭瑕佺偣1,鍏抽敭瑕佺偣2,鍏抽敭瑕佺偣3,鍏抽敭瑕佺偣4",
+ creator: ["寮犵粡鐞�", "鏉庝富绠�", "鐜嬩笓鍛�", "鍒樻�荤洃"][Math.floor(Math.random() * 4)],
+ usageCount: Math.floor(Math.random() * 20) + 1,
+ createTime: now.toLocaleString()
+ };
+
+ // 娣诲姞鍒版暟鎹紑澶�
+ mockData.unshift(newKnowledge);
+
+ // 淇濇寔鏁版嵁閲忓湪鍚堢悊鑼冨洿鍐咃紙鏈�澶氫繚鐣�30鏉★級
+ if (mockData.length > 30) {
+ mockData = mockData.slice(0, 30);
+ }
+
+ console.log(`[${new Date().toLocaleString()}] 鑷姩鐢熸垚鏂扮煡璇�: ${title}`);
+};
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ getList();
+ startAutoRefresh();
+});
+
+// 寮�濮嬭嚜鍔ㄥ埛鏂�
+const startAutoRefresh = () => {
+ setInterval(() => {
+ generateNewData();
+ getList();
+ }, 600000); // 10鍒嗛挓鍒锋柊涓�娆� (10 * 60 * 1000 = 600000ms)
+};
+
+// 鏌ヨ鏁版嵁
+const handleQuery = () => {
+ page.value.current = 1;
+ getList();
+};
+
+const getList = () => {
+ tableLoading.value = true;
+
+ setTimeout(() => {
+ let filteredData = [...mockData];
+
+ if (searchForm.value.title) {
+ filteredData = filteredData.filter(item =>
+ item.title.toLowerCase().includes(searchForm.value.title.toLowerCase())
+ );
+ }
+
+ if (searchForm.value.type) {
+ filteredData = filteredData.filter(item => item.type === searchForm.value.type);
+ }
+
+ tableData.value = filteredData;
+ page.value.total = filteredData.length;
+ tableLoading.value = false;
+ }, 500);
+};
+
+// 鍒嗛〉澶勭悊
+const pagination = (obj) => {
+ page.value.current = obj.page;
+ page.value.size = obj.limit;
+ handleQuery();
+};
+
+// 閫夋嫨鍙樺寲澶勭悊
+const handleSelectionChange = (selection) => {
+ selectedIds.value = selection.map(item => item.id);
+};
+
+// 鎵撳紑琛ㄥ崟
+const openForm = (type, row = null) => {
+ dialogType.value = type;
+ if (type === "add") {
+ dialogTitle.value = "鏂板鐭ヨ瘑";
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(form.value, {
+ title: "",
+ type: "",
+ scenario: "",
+ efficiency: "medium",
+ problem: "",
+ solution: "",
+ keyPoints: "",
+ creator: "",
+ usageCount: 0
+ });
+ } else if (type === "edit" && row) {
+ dialogTitle.value = "缂栬緫鐭ヨ瘑";
+ Object.assign(form.value, {
+ title: row.title,
+ type: row.type,
+ scenario: row.scenario,
+ efficiency: row.efficiency,
+ problem: row.problem,
+ solution: row.solution,
+ keyPoints: row.keyPoints,
+ creator: row.creator,
+ usageCount: row.usageCount
+ });
+ }
+ dialogVisible.value = true;
+};
+
+// 鏌ョ湅鐭ヨ瘑璇︽儏
+const viewKnowledge = (row) => {
+ currentKnowledge.value = { ...row };
+ viewDialogVisible.value = true;
+};
+
+// 鑾峰彇绫诲瀷鏍囩绫诲瀷
+const getTypeTagType = (type) => {
+ const typeMap = {
+ contract: "success",
+ approval: "warning",
+ solution: "primary",
+ experience: "info",
+ guide: "danger"
+ };
+ return typeMap[type] || "info";
+};
+
+// 鑾峰彇绫诲瀷鏍囩鏂囨湰
+const getTypeLabel = (type) => {
+ const typeMap = {
+ contract: "鍚堝悓鐗规壒",
+ approval: "瀹℃壒妗堜緥",
+ solution: "瑙e喅鏂规",
+ experience: "缁忛獙鎬荤粨",
+ guide: "鎿嶄綔鎸囧崡"
+ };
+ return typeMap[type] || type;
+};
+
+// 鑾峰彇鏁堢巼鏍囩绫诲瀷
+const getEfficiencyTagType = (efficiency) => {
+ const typeMap = {
+ high: "success",
+ medium: "warning",
+ low: "info"
+ };
+ return typeMap[efficiency] || "info";
+};
+
+// 鑾峰彇鏁堢巼鏍囩鏂囨湰
+const getEfficiencyLabel = (efficiency) => {
+ const efficiencyMap = {
+ high: "鏄捐憲鎻愬崌",
+ medium: "涓�鑸彁鍗�",
+ low: "杞诲井鎻愬崌"
+ };
+ return efficiencyMap[efficiency] || efficiency;
+};
+
+// 鑾峰彇鏁堢巼鎻愬崌鐧惧垎姣�
+const getEfficiencyScore = (efficiency) => {
+ const scoreMap = {
+ high: 40,
+ medium: 25,
+ low: 15
+ };
+ return scoreMap[efficiency] || 0;
+};
+
+// 鑾峰彇骞冲潎鑺傜渷鏃堕棿
+const getTimeSaved = (efficiency) => {
+ const timeMap = {
+ high: "2-3澶�",
+ medium: "1-2澶�",
+ low: "0.5-1澶�"
+ };
+ return timeMap[efficiency] || "鏈煡";
+};
+
+// 澶嶅埗鐭ヨ瘑
+const copyKnowledge = () => {
+ const knowledgeText = `
+鐭ヨ瘑鏍囬锛�${currentKnowledge.value.title}
+鐭ヨ瘑绫诲瀷锛�${getTypeLabel(currentKnowledge.value.type)}
+閫傜敤鍦烘櫙锛�${currentKnowledge.value.scenario}
+闂鎻忚堪锛�${currentKnowledge.value.problem}
+瑙e喅鏂规锛�${currentKnowledge.value.solution}
+鍏抽敭瑕佺偣锛�${currentKnowledge.value.keyPoints}
+鍒涘缓浜猴細${currentKnowledge.value.creator}
+ `.trim();
+
+ // 澶嶅埗鍒板壀璐存澘
+ navigator.clipboard.writeText(knowledgeText).then(() => {
+ ElMessage.success("鐭ヨ瘑鍐呭宸插鍒跺埌鍓创鏉�");
+ }).catch(() => {
+ ElMessage.error("澶嶅埗澶辫触锛岃鎵嬪姩澶嶅埗");
+ });
+};
+
+// 鏀惰棌鐭ヨ瘑
+const markAsFavorite = () => {
+ // 澧炲姞浣跨敤娆℃暟
+ const index = mockData.findIndex(item => item.id === currentKnowledge.value.id);
+ if (index !== -1) {
+ mockData[index].usageCount += 1;
+ currentKnowledge.value.usageCount += 1;
+ }
+
+ ElMessage.success("宸叉敹钘忥紝浣跨敤娆℃暟+1");
+};
+
+// 鎻愪氦鐭ヨ瘑琛ㄥ崟
+const submitForm = async () => {
+ try {
+ await formRef.value.validate();
+
+ if (dialogType.value === "add") {
+ // 鏂板鐭ヨ瘑
+ const newKnowledge = {
+ id: (mockData.length + 1).toString(),
+ title: form.value.title,
+ type: form.value.type,
+ scenario: form.value.scenario,
+ efficiency: form.value.efficiency,
+ problem: form.value.problem,
+ solution: form.value.solution,
+ keyPoints: form.value.keyPoints,
+ creator: form.value.creator,
+ usageCount: form.value.usageCount,
+ createTime: new Date().toLocaleString()
+ };
+
+ mockData.unshift(newKnowledge);
+ ElMessage.success("鐭ヨ瘑鍒涘缓鎴愬姛");
+ } else {
+ // 缂栬緫鐭ヨ瘑
+ const index = mockData.findIndex(item => item.id === selectedIds.value[0]);
+ if (index !== -1) {
+ Object.assign(mockData[index], {
+ title: form.value.title,
+ type: form.value.type,
+ scenario: form.value.scenario,
+ efficiency: form.value.efficiency,
+ problem: form.value.problem,
+ solution: form.value.solution,
+ keyPoints: form.value.keyPoints,
+ creator: form.value.creator,
+ usageCount: form.value.usageCount
+ });
+ ElMessage.success("鐭ヨ瘑鏇存柊鎴愬姛");
+ }
+ }
+
+ dialogVisible.value = false;
+ getList();
+ } catch (error) {
+ console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
+ }
+};
+
+// 鍒犻櫎鐭ヨ瘑
+const handleDelete = () => {
+ if (selectedIds.value.length === 0) {
+ ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑鐭ヨ瘑");
+ return;
+ }
+
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ // 浠巑ockData涓垹闄ら�変腑鐨勯」
+ selectedIds.value.forEach(id => {
+ const index = mockData.findIndex(item => item.id === id);
+ if (index !== -1) {
+ mockData.splice(index, 1);
+ }
+ });
+
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ selectedIds.value = [];
+ getList();
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷
+ });
+};
+</script>
+
+<style scoped>
+.auto-refresh-info {
+ margin-bottom: 15px;
+}
+
+.auto-refresh-info .el-alert {
+ border-radius: 8px;
+}
+
+.dialog-footer {
+ text-align: right;
+}
+
+.knowledge-detail {
+ padding: 20px 0;
+}
+
+.detail-title {
+ font-size: 18px;
+ font-weight: bold;
+ color: #303133;
+}
+
+.detail-section {
+ margin-top: 24px;
+}
+
+.detail-section h4 {
+ margin: 0 0 12px 0;
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ border-left: 4px solid #409eff;
+ padding-left: 12px;
+}
+
+.detail-content {
+ background: #f8f9fa;
+ padding: 16px;
+ border-radius: 6px;
+ line-height: 1.6;
+ color: #606266;
+ white-space: pre-wrap;
+}
+
+.key-points {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.usage-stats {
+ margin-top: 16px;
+}
+
+.stat-item {
+ text-align: center;
+ padding: 20px;
+ background: #f8f9fa;
+ border-radius: 8px;
+}
+
+.stat-number {
+ font-size: 24px;
+ font-weight: bold;
+ color: #409eff;
+ margin-bottom: 8px;
+}
+
+.stat-label {
+ font-size: 14px;
+ color: #909399;
+}
+</style>
diff --git a/src/views/collaborativeApproval/meetingBoard/index.vue b/src/views/collaborativeApproval/meetingBoard/index.vue
new file mode 100644
index 0000000..63c74f9
--- /dev/null
+++ b/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: '浼氳瀹',
+ 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: ['闄堟�荤洃', '鍒樹骇鍝佺粡鐞�', '瀛欏鎴风粡鐞�', '瀹㈡埛浠h〃'],
+ 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>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/noticeManagement/index.vue b/src/views/collaborativeApproval/noticeManagement/index.vue
new file mode 100644
index 0000000..daa4cd7
--- /dev/null
+++ b/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: "缁存姢瀹屾垚鍚庡皢杩涜璇曡繍琛岋紝纭繚璁惧姝e父杩愯",
+ 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>
diff --git a/src/views/collaborativeApproval/notificationManagement/index.vue b/src/views/collaborativeApproval/notificationManagement/index.vue
new file mode 100644
index 0000000..288acf1
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/index.vue
@@ -0,0 +1,1187 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <span class="search_title">閫氱煡鏍囬锛�</span>
+ <el-input
+ v-model="searchForm.title"
+ style="width: 240px"
+ placeholder="璇疯緭鍏ラ�氱煡鏍囬鎼滅储"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ <span class="search_title ml10">閫氱煡绫诲瀷锛�</span>
+ <el-select v-model="searchForm.type" clearable @change="handleQuery" style="width: 240px">
+ <el-option label="鏀惧亣閫氱煡" :value="'holiday'" />
+ <el-option label="澶勭綒閫氱煡" :value="'penalty'" />
+ <el-option label="寮�浼氶�氱煡" :value="'meeting'" />
+ <el-option label="涓存椂閫氱煡" :value="'temporary'" />
+ <el-option label="姝e紡閫氱煡" :value="'formal'" />
+ </el-select>
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
+ 鎼滅储
+ </el-button>
+ </div>
+ <div>
+ <el-button type="primary" @click="openForm('add')">鏂板閫氱煡</el-button>
+ <el-button type="success" @click="openMeetingDialog">鍦ㄧ嚎浼氳</el-button>
+ <el-button type="warning" @click="openFileShareDialog">鏂囦欢鍏变韩</el-button>
+ <!-- <el-button type="info" @click="refreshEmployees">鍒锋柊鍛樺伐</el-button> -->
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total"
+ ></PIMTable>
+ </div>
+
+ <!-- 鏂板/缂栬緫閫氱煡寮圭獥 -->
+ <el-dialog
+ v-model="dialogVisible"
+ :title="dialogTitle"
+ width="800px"
+ :close-on-click-modal="false"
+ >
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="閫氱煡鏍囬" prop="title">
+ <el-input v-model="form.title" placeholder="璇疯緭鍏ラ�氱煡鏍囬" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="閫氱煡绫诲瀷" prop="type">
+ <el-select v-model="form.type" placeholder="璇烽�夋嫨閫氱煡绫诲瀷" style="width: 100%">
+ <el-option label="鏀惧亣閫氱煡" value="holiday" />
+ <el-option label="澶勭綒閫氱煡" value="penalty" />
+ <el-option label="寮�浼氶�氱煡" value="meeting" />
+ <el-option label="涓存椂閫氱煡" value="temporary" />
+ <el-option label="姝e紡閫氱煡" value="formal" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浼樺厛绾�" prop="priority">
+ <el-select v-model="form.priority" placeholder="璇烽�夋嫨浼樺厛绾�" style="width: 100%">
+ <el-option label="鏅��" value="low" />
+ <el-option label="閲嶈" value="medium" />
+ <el-option label="绱ф��" value="high" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏈夋晥鏈熻嚦" prop="expireDate">
+ <el-date-picker
+ v-model="form.expireDate"
+ type="date"
+ placeholder="璇烽�夋嫨鏈夋晥鏈�"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="鎺ユ敹閮ㄩ棬" prop="departments">
+ <el-select
+ v-model="form.departments"
+ multiple
+ placeholder="璇烽�夋嫨鎺ユ敹閮ㄩ棬"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="dept in departments"
+ :key="dept"
+ :label="dept"
+ :value="dept"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍚屾鏂瑰紡" prop="syncMethods">
+ <el-checkbox-group v-model="form.syncMethods">
+ <el-checkbox
+ v-for="method in syncMethods"
+ :key="method.value"
+ :label="method.value"
+ >
+ {{ method.label }}
+ </el-checkbox>
+ </el-checkbox-group>
+ </el-form-item>
+ <el-form-item label="閫氱煡鍐呭" prop="content">
+ <el-input
+ v-model="form.content"
+ type="textarea"
+ :rows="4"
+ placeholder="璇疯緭鍏ラ�氱煡鍐呭"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+
+ <!-- 鍦ㄧ嚎浼氳寮圭獥 -->
+ <el-dialog
+ v-model="meetingDialogVisible"
+ title="鍒涘缓鍦ㄧ嚎浼氳"
+ width="700px"
+ :close-on-click-modal="false"
+ >
+ <el-form ref="meetingFormRef" :model="meetingForm" :rules="meetingRules" label-width="120px">
+ <el-form-item label="浼氳鏍囬" prop="title">
+ <el-input v-model="meetingForm.title" placeholder="璇疯緭鍏ヤ細璁爣棰�" />
+ </el-form-item>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="寮�濮嬫椂闂�" prop="startTime">
+ <el-date-picker
+ v-model="meetingForm.startTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="浼氳鏃堕暱" prop="duration">
+ <el-input-number
+ v-model="meetingForm.duration"
+ :min="15"
+ :max="480"
+ :step="15"
+ style="width: 100%"
+ />
+ <span style="margin-left: 10px">鍒嗛挓</span>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="浼氳骞冲彴" prop="platform">
+ <el-select v-model="meetingForm.platform" placeholder="璇烽�夋嫨浼氳骞冲彴" style="width: 100%">
+ <el-option
+ v-for="platform in meetingPlatforms"
+ :key="platform.value"
+ :label="platform.label"
+ :value="platform.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍙備細浜哄憳" prop="participants">
+ <el-select
+ v-model="meetingForm.participants"
+ multiple
+ filterable
+ remote
+ :remote-method="filterEmployees"
+ :loading="employeesLoading"
+ placeholder="璇烽�夋嫨鍙備細浜哄憳"
+ style="width: 100%"
+ >
+ <el-option-group
+ v-for="group in employeeGroups"
+ :key="group.label"
+ :label="group.label"
+ >
+ <el-option
+ v-for="employee in group.options"
+ :key="employee.value"
+ :label="`${employee.label} (${employee.dept})`"
+ :value="employee.value"
+ >
+ <div style="display: flex; justify-content: space-between; align-items: center;">
+ <div>
+ <div style="font-weight: 500;">{{ employee.label }}</div>
+ <div style="color: #909399; font-size: 12px;">{{ employee.dept }}</div>
+ </div>
+ <div style="text-align: right; font-size: 12px; color: #909399;">
+ <div v-if="employee.phone">{{ employee.phone }}</div>
+ <div v-if="employee.email">{{ employee.email }}</div>
+ </div>
+ </div>
+ </el-option>
+ </el-option-group>
+ </el-select>
+ <div style="margin-top: 8px; color: #909399; font-size: 12px;">
+ 宸查�夋嫨 {{ meetingForm.participants.length }} 浜�
+ </div>
+ <!-- 宸查�夋嫨浜哄憳璇︽儏 -->
+ <div v-if="meetingForm.participants.length > 0" style="margin-top: 10px;">
+ <el-tag
+ v-for="participantId in meetingForm.participants"
+ :key="participantId"
+ closable
+ @close="removeParticipant(participantId)"
+ style="margin-right: 8px; margin-bottom: 8px;"
+ >
+ {{ getEmployeeName(participantId) }}
+ </el-tag>
+ </div>
+ </el-form-item>
+ <el-form-item label="浼氳鎻忚堪" prop="description">
+ <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="meetingDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="createMeeting">鍒涘缓浼氳</el-button>
+ </span>
+ </template>
+ </el-dialog>
+
+ <!-- 鏂囦欢鍏变韩寮圭獥 -->
+ <el-dialog
+ v-model="fileShareDialogVisible"
+ title="鏂囦欢鍏变韩"
+ width="700px"
+ :close-on-click-modal="false"
+ >
+ <el-form ref="fileShareFormRef" :model="fileShareForm" :rules="fileShareRules" label-width="120px">
+ <el-form-item label="鍏变韩鏍囬" prop="title">
+ <el-input v-model="fileShareForm.title" placeholder="璇疯緭鍏ュ叡浜爣棰�" />
+ </el-form-item>
+ <el-form-item label="鍏变韩鎻忚堪" prop="description">
+ <el-input
+ v-model="fileShareForm.description"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ叡浜弿杩�"
+ />
+ </el-form-item>
+ <el-form-item label="鎺ユ敹閮ㄩ棬" prop="departments">
+ <el-select
+ v-model="fileShareForm.departments"
+ multiple
+ placeholder="璇烽�夋嫨鎺ユ敹閮ㄩ棬"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="dept in departments"
+ :key="dept"
+ :label="dept"
+ :value="dept"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="涓婁紶鏂囦欢" prop="files">
+ <el-upload
+ ref="uploadRef"
+ :auto-upload="false"
+ :on-change="handleFileChange"
+ :on-remove="removeFile"
+ :file-list="fileList"
+ multiple
+ :limit="10"
+ accept=".doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.txt,.jpg,.jpeg,.png,.gif"
+ >
+ <el-button type="primary">閫夋嫨鏂囦欢</el-button>
+ <template #tip>
+ <div class="el-upload__tip">
+ 鏀寔涓婁紶鏂囨。銆佸浘鐗囩瓑鏍煎紡锛屽崟涓枃浠朵笉瓒呰繃10MB锛屾渶澶�10涓枃浠�
+ </div>
+ </template>
+ </el-upload>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="fileShareDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="shareFiles">鍏变韩鏂囦欢</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import { onMounted, ref, reactive, toRefs, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import { userListNoPageByTenantId } from "@/api/system/user.js";
+import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js";
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const rules = {
+ title: [
+ { required: true, message: "璇疯緭鍏ラ�氱煡鏍囬", trigger: "blur" }
+ ],
+ type: [
+ { required: true, message: "璇烽�夋嫨閫氱煡绫诲瀷", trigger: "change" }
+ ],
+ content: [
+ { required: true, message: "璇疯緭鍏ラ�氱煡鍐呭", trigger: "blur" }
+ ]
+};
+
+const meetingRules = {
+ title: [
+ { required: true, message: "璇疯緭鍏ヤ細璁爣棰�", trigger: "blur" }
+ ],
+ startTime: [
+ { required: true, message: "璇烽�夋嫨浼氳寮�濮嬫椂闂�", trigger: "change" }
+ ],
+ participants: [
+ { required: true, message: "璇烽�夋嫨鍙備細浜哄憳", trigger: "change" }
+ ]
+};
+
+const fileShareRules = {
+ title: [
+ { required: true, message: "璇疯緭鍏ュ叡浜爣棰�", trigger: "blur" }
+ ],
+ description: [
+ { required: true, message: "璇疯緭鍏ュ叡浜弿杩�", trigger: "blur" }
+ ]
+};
+
+// 鍝嶅簲寮忔暟鎹�
+const data = reactive({
+ searchForm: {
+ title: "",
+ type: "",
+ status: "",
+ },
+ tableLoading: false,
+ page: {
+ current: 1,
+ size: 100,
+ total: 0,
+ },
+ tableData: [],
+ selectedIds: [],
+ // 鏂板閫氱煡鐩稿叧
+ form: {
+ title: "",
+ type: "",
+ priority: "medium",
+ content: "",
+ departments: [],
+ expireDate: "",
+ syncMethods: []
+ },
+ dialogVisible: false,
+ dialogTitle: "",
+ dialogType: "add",
+ // 鍦ㄧ嚎浼氳鐩稿叧
+ meetingDialogVisible: false,
+ meetingForm: {
+ title: "",
+ startTime: "",
+ duration: 60,
+ participants: [],
+ description: "",
+ platform: "wechat"
+ },
+ // 鏂囦欢鍏变韩鐩稿叧
+ fileShareDialogVisible: false,
+ fileShareForm: {
+ title: "",
+ description: "",
+ departments: [],
+ files: []
+ },
+ fileList: []
+});
+
+const {
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
+ selectedIds,
+ form,
+ dialogVisible,
+ dialogTitle,
+ dialogType,
+ meetingDialogVisible,
+ meetingForm,
+ fileShareDialogVisible,
+ fileShareForm,
+ fileList
+} = toRefs(data);
+
+// 琛ㄥ崟寮曠敤
+const formRef = ref();
+const meetingFormRef = ref();
+const fileShareFormRef = ref();
+
+// 琛ㄦ牸鍒楅厤缃�
+const tableColumn = ref([
+ {
+ label: "閫氱煡鏍囬",
+ prop: "title",
+ showOverflowTooltip: true,
+ },
+ {
+ label: "閫氱煡绫诲瀷",
+ prop: "type",
+ dataType: "tag",
+ formatData: (params) => {
+ const typeMap = {
+ holiday: "鏀惧亣閫氱煡",
+ penalty: "澶勭綒閫氱煡",
+ meeting: "寮�浼氶�氱煡",
+ temporary: "涓存椂閫氱煡",
+ formal: "姝e紡閫氱煡"
+ };
+ return typeMap[params] || params;
+ },
+ formatType: (params) => {
+ const typeMap = {
+ holiday: "success",
+ penalty: "danger",
+ meeting: "warning",
+ temporary: "info",
+ formal: "primary"
+ };
+ return typeMap[params] || "info";
+ }
+ },
+ {
+ label: "浼樺厛绾�",
+ prop: "priority",
+ dataType: "tag",
+ formatData: (params) => {
+ const priorityMap = {
+ low: "鏅��",
+ medium: "閲嶈",
+ high: "绱ф��"
+ };
+ return priorityMap[params] || params;
+ },
+ formatType: (params) => {
+ const typeMap = {
+ low: "info",
+ medium: "warning",
+ high: "danger"
+ };
+ return typeMap[params] || "info";
+ }
+ },
+ {
+ label: "鐘舵��",
+ prop: "status",
+ dataType: "tag",
+ formatData: (params) => {
+ const statusMap = {
+ draft: "鑽夌",
+ published: "宸插彂甯�",
+ expired: "宸茶繃鏈�"
+ };
+ return statusMap[params] || params;
+ },
+ formatType: (params) => {
+ const typeMap = {
+ draft: "info",
+ published: "success",
+ expired: "danger"
+ };
+ return typeMap[params] || "info";
+ }
+ },
+ {
+ label: "鎺ユ敹閮ㄩ棬",
+ prop: "departments",
+ width: 150,
+ showOverflowTooltip: true,
+ formatData: (params) => {
+ if (!params || params.length === 0) return "鍏ㄩ儴閮ㄩ棬";
+ return params.join(", ");
+ }
+ },
+ {
+ label: "鏈夋晥鏈熻嚦",
+ prop: "expireDate",
+ width: 150,
+ formatData: (params) => {
+ if (!params) return "姘镐箙鏈夋晥";
+ return params;
+ }
+ },
+ {
+ label: "鍒涘缓鏃堕棿",
+ prop: "createTime",
+ width: 180,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 280,
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ openForm("edit", row);
+ }
+ },
+ {
+ name: "鍙戝竷",
+ type: "text",
+ clickFun: (row) => {
+ publishNotification(row);
+ },
+ // disabled: (row) => row.status === "published"
+ },
+ {
+ name: "鎾ゅ洖",
+ type: "text",
+ clickFun: (row) => {
+ revokeNotification(row);
+ },
+ // disabled: (row) => row.status !== "published"
+ }
+ ]
+ }
+]);
+
+// 妯℃嫙鏁版嵁
+let mockData = [
+ {
+ id: "1",
+ title: "2024骞存槬鑺傛斁鍋囬�氱煡",
+ type: "holiday",
+ priority: "high",
+ status: "published",
+ content: "鏍规嵁鍥藉瑙勫畾锛岀粨鍚堝叕鍙稿疄闄呮儏鍐碉紝鐜板皢2024骞存槬鑺傛斁鍋囧畨鎺掗�氱煡濡備笅...",
+ departments: ["鎶�鏈儴", "閿�鍞儴", "浜轰簨閮�", "璐㈠姟閮�", "杩愯惀閮�", "甯傚満閮�", "瀹㈡湇閮�"],
+ expireDate: "2024-02-15",
+ syncMethods: ["wechat", "dingtalk", "email"],
+ createTime: "2024-01-15 10:30:00"
+ },
+ {
+ id: "2",
+ title: "鎶�鏈儴鍛ㄤ緥浼氶�氱煡",
+ type: "meeting",
+ priority: "medium",
+ status: "published",
+ content: "鎶�鏈儴瀹氫簬姣忓懆浜斾笅鍗�2鐐瑰彫寮�鍛ㄤ緥浼氾紝璇峰悇浣嶅悓浜嬪噯鏃跺弬鍔�...",
+ departments: ["鎶�鏈儴"],
+ expireDate: "2024-01-20",
+ syncMethods: ["wechat", "dingtalk"],
+ createTime: "2024-01-14 15:20:00"
+ },
+ {
+ id: "3",
+ title: "鍛樺伐琛屼负瑙勮寖澶勭綒閫氱煡",
+ type: "penalty",
+ priority: "high",
+ status: "draft",
+ content: "涓虹淮鎶ゅ叕鍙告甯哥З搴忥紝瑙勮寖鍛樺伐琛屼负锛岀幇瀵硅繚鍙嶅叕鍙歌瀹氱殑琛屼负杩涜澶勭綒...",
+ departments: ["浜轰簨閮�", "鎶�鏈儴", "閿�鍞儴"],
+ expireDate: "2024-02-13",
+ syncMethods: ["wechat", "email"],
+ createTime: "2024-01-13 09:15:00"
+ }
+];
+
+// 閫氱煡鏍囬妯℃澘
+const titleTemplates = [
+ "鍏充簬{year}骞磠holiday}鏀惧亣瀹夋帓鐨勯�氱煡",
+ "{dept}閮ㄩ棬{meeting}浼氳閫氱煡",
+ "鍛樺伐{behavior}琛屼负瑙勮寖鎻愰啋",
+ "{company}閲嶈浜嬮」閫氱煡",
+ "{dept}閮ㄩ棬宸ヤ綔瀹夋帓閫氱煡",
+ "鍏充簬{project}椤圭洰杩涘害鐨勯�氱煡",
+ "{dept}閮ㄩ棬浜哄憳璋冩暣閫氱煡",
+ "鍏徃{policy}鏀跨瓥鏇存柊閫氱煡"
+];
+
+// 閫氱煡绫诲瀷閰嶇疆
+const notificationTypes = [
+ { type: "holiday", label: "鏀惧亣閫氱煡", priority: "high" },
+ { type: "meeting", label: "寮�浼氶�氱煡", priority: "medium" },
+ { type: "penalty", label: "澶勭綒閫氱煡", priority: "high" },
+ { type: "temporary", label: "涓存椂閫氱煡", priority: "low" },
+ { type: "formal", label: "姝e紡閫氱煡", priority: "medium" }
+];
+
+// 閮ㄩ棬鍒楄〃
+const departments = ["鎶�鏈儴", "閿�鍞儴", "浜轰簨閮�", "璐㈠姟閮�", "杩愯惀閮�", "甯傚満閮�", "瀹㈡湇閮�"];
+
+// 浜哄憳鍒楄〃
+const employees = ref([]);
+const employeesLoading = ref(false);
+
+// 鑾峰彇鍦ㄨ亴鍛樺伐鍒楄〃
+const getEmployeesList = async () => {
+ try {
+ employeesLoading.value = true;
+ // 浼樺厛浣跨敤绯荤粺鐢ㄦ埛鎺ュ彛锛堟寜绉熸埛鑾峰彇锛�
+ const userResponse = await userListNoPageByTenantId();
+
+ if (userResponse.data) {
+ employees.value = userResponse.data.map(user => ({
+ label: user.nickName || user.userName || '鏈煡濮撳悕',
+ value: user.userId || user.id,
+ dept: user.dept?.deptName || '鏈煡閮ㄩ棬',
+ phone: user.phonenumber || '',
+ email: user.email || '',
+ status: user.status || '0'
+ })).filter(user => user.status === '0'); // 鍙樉绀烘甯哥姸鎬佺殑鐢ㄦ埛
+ } else {
+ // 濡傛灉绯荤粺鐢ㄦ埛鎺ュ彛澶辫触锛屼娇鐢ㄥ憳宸ュ彴璐︽帴鍙�
+ const response = await staffOnJobListPage({
+ pageNum: 1,
+ pageSize: 1000,
+ staffState: 1 // 鍦ㄨ亴鐘舵��
+ });
+
+ if (response.data && response.data.records) {
+ employees.value = response.data.records.map(employee => ({
+ label: employee.staffName || employee.name || '鏈煡濮撳悕',
+ value: employee.staffNo || employee.id || employee.staffId,
+ dept: employee.deptName || employee.department || '鏈煡閮ㄩ棬',
+ phone: employee.phone || employee.mobile || '',
+ email: employee.email || '',
+ status: '0'
+ }));
+ }
+ }
+ } catch (error) {
+ console.error('鑾峰彇鍛樺伐鍒楄〃澶辫触:', error);
+ // 濡傛灉鎺ュ彛閮藉け璐ワ紝浣跨敤榛樿鏁版嵁
+ employees.value = [
+ { label: "寮犱笁", value: "001", dept: "鎶�鏈儴", phone: "13800138001", email: "zhangsan@company.com", status: "0" },
+ { label: "鏉庡洓", value: "002", dept: "閿�鍞儴", phone: "13800138002", email: "lisi@company.com", status: "0" },
+ { label: "鐜嬩簲", value: "003", dept: "浜轰簨閮�", phone: "13800138003", email: "wangwu@company.com", status: "0" }
+ ];
+ } finally {
+ employeesLoading.value = false;
+ }
+};
+
+// 鍛樺伐鍒嗙粍
+const employeeGroups = computed(() => {
+ const groups = {};
+ employees.value.forEach(employee => {
+ const dept = employee.dept || '鍏朵粬閮ㄩ棬';
+ if (!groups[dept]) {
+ groups[dept] = [];
+ }
+ groups[dept].push(employee);
+ });
+
+ // 鎸夐儴闂ㄥ悕绉版帓搴忥紝纭繚鏄剧ず椤哄簭涓�鑷�
+ return Object.keys(groups)
+ .sort()
+ .map(dept => ({
+ label: dept,
+ options: groups[dept].sort((a, b) => a.label.localeCompare(b.label, 'zh-CN'))
+ }));
+});
+
+// 杩囨护鍛樺伐锛堣繙绋嬫悳绱級
+const filterEmployees = (query) => {
+ if (query !== '') {
+ const lowerQuery = query.toLowerCase();
+ return employees.value.filter(employee =>
+ employee.label.toLowerCase().includes(lowerQuery) ||
+ employee.dept.toLowerCase().includes(lowerQuery) ||
+ (employee.phone && employee.phone.includes(query)) ||
+ (employee.email && employee.email.toLowerCase().includes(lowerQuery))
+ );
+ } else {
+ return employees.value;
+ }
+};
+
+// 鍒锋柊鍛樺伐鍒楄〃
+const refreshEmployees = async () => {
+ ElMessage.info("姝e湪鍒锋柊鍛樺伐鍒楄〃...");
+ await getEmployeesList();
+
+ // 缁熻鍚勯儴闂ㄤ汉鏁�
+ const deptStats = {};
+ employees.value.forEach(emp => {
+ const dept = emp.dept || '鍏朵粬閮ㄩ棬';
+ deptStats[dept] = (deptStats[dept] || 0) + 1;
+ });
+
+ const deptInfo = Object.entries(deptStats)
+ .map(([dept, count]) => `${dept}: ${count}浜篳)
+ .join(', ');
+
+ ElMessage.success(`鍛樺伐鍒楄〃鍒锋柊瀹屾垚锛屽叡 ${employees.value.length} 浜� (${deptInfo})`);
+};
+
+// 鑾峰彇鍛樺伐濮撳悕
+const getEmployeeName = (employeeId) => {
+ const employee = employees.value.find(emp => emp.value === employeeId);
+ return employee ? employee.label : '鏈煡浜哄憳';
+};
+
+// 鑾峰彇鍛樺伐璇︾粏淇℃伅
+const getEmployeeInfo = (employeeId) => {
+ const employee = employees.value.find(emp => emp.value === employeeId);
+ if (!employee) return null;
+
+ return {
+ name: employee.label,
+ dept: employee.dept,
+ phone: employee.phone,
+ email: employee.email
+ };
+};
+
+// 绉婚櫎鍙備細浜哄憳
+const removeParticipant = (participantId) => {
+ const index = meetingForm.value.participants.indexOf(participantId);
+ if (index > -1) {
+ meetingForm.value.participants.splice(index, 1);
+ }
+};
+
+// 鍚屾鏂瑰紡閫夐」
+const syncMethods = [
+ { label: "浼佷笟寰俊", value: "wechat" },
+ { label: "閽夐拤", value: "dingtalk" },
+ { label: "閭欢", value: "email" },
+ { label: "鐭俊", value: "sms" }
+];
+
+// 浼氳骞冲彴閫夐」
+const meetingPlatforms = [
+ { label: "浼佷笟寰俊浼氳", value: "wechat" },
+ { label: "閽夐拤浼氳", value: "dingtalk" },
+ { label: "鑵捐浼氳", value: "tencent" },
+ { label: "Zoom", value: "zoom" }
+];
+
+// 鑷姩鐢熸垚鏂版暟鎹�
+const generateNewData = () => {
+ const newId = (mockData.length + 1).toString();
+ const now = new Date();
+ const randomType = notificationTypes[Math.floor(Math.random() * notificationTypes.length)];
+ const randomDept = departments[Math.floor(Math.random() * departments.length)];
+
+ // 鐢熸垚闅忔満鏍囬
+ let title = titleTemplates[Math.floor(Math.random() * titleTemplates.length)];
+ title = title
+ .replace('{year}', now.getFullYear())
+ .replace('{holiday}', ['鏄ヨ妭', '鍥藉簡', '涓', '鍏冩棪'][Math.floor(Math.random() * 4)])
+ .replace('{dept}', randomDept)
+ .replace('{meeting}', ['鍛ㄤ緥浼�', '鏈堝害鎬荤粨', '椤圭洰璇勫', '鍩硅浼氳'][Math.floor(Math.random() * 4)])
+ .replace('{behavior}', ['鑰冨嫟', '鐫�瑁�', '宸ヤ綔鎬佸害', '鍥㈤槦鍗忎綔'][Math.floor(Math.random() * 4)])
+ .replace('{company}', ['鍏徃', '闆嗗洟', '鎬婚儴'][Math.floor(Math.random() * 4)])
+ .replace('{project}', ['鏁板瓧鍖栬浆鍨�', '浜у搧鍗囩骇', '甯傚満鎷撳睍', '浜烘墠鍩瑰吇'][Math.floor(Math.random() * 4)])
+ .replace('{policy}', ['鑰冨嫟', '钖叕', '绂忓埄', '鏅嬪崌'][Math.floor(Math.random() * 4)]);
+
+ // 闅忔満鐘舵��
+ const statuses = ['draft', 'published'];
+ const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
+
+ // 闅忔満浼樺厛绾�
+ const priorities = ['low', 'medium', 'high'];
+ const randomPriority = priorities[Math.floor(Math.random() * priorities.length)];
+
+ const newNotification = {
+ id: newId,
+ title: title,
+ type: randomType.type,
+ priority: randomPriority,
+ status: randomStatus,
+ content: `杩欐槸${title}鐨勮缁嗗唴瀹癸紝璇风浉鍏充汉鍛樻敞鎰忔煡鐪�...`,
+ departments: [randomDept],
+ expireDate: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], // 30澶╁悗杩囨湡
+ syncMethods: ["wechat", "dingtalk"],
+ createTime: now.toLocaleString()
+ };
+
+ // 娣诲姞鍒版暟鎹紑澶�
+ mockData.unshift(newNotification);
+
+ // 淇濇寔鏁版嵁閲忓湪鍚堢悊鑼冨洿鍐咃紙鏈�澶氫繚鐣�20鏉★級
+ if (mockData.length > 20) {
+ mockData = mockData.slice(0, 20);
+ }
+
+ console.log(`[${new Date().toLocaleString()}] 鑷姩鐢熸垚鏂伴�氱煡: ${title}`);
+};
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ getList();
+ getEmployeesList(); // 鑾峰彇鍛樺伐鍒楄〃
+ startAutoRefresh();
+});
+
+// 寮�濮嬭嚜鍔ㄥ埛鏂�
+const startAutoRefresh = () => {
+ setInterval(() => {
+ generateNewData();
+ getList();
+ }, 600000); // 10鍒嗛挓鍒锋柊涓�娆� (10 * 60 * 1000 = 600000ms)
+};
+
+// 鏌ヨ鏁版嵁
+const handleQuery = () => {
+ page.value.current = 1;
+ getList();
+};
+
+const getList = () => {
+ tableLoading.value = true;
+
+ setTimeout(() => {
+ let filteredData = [...mockData];
+
+ if (searchForm.value.title) {
+ filteredData = filteredData.filter(item =>
+ item.title.toLowerCase().includes(searchForm.value.title.toLowerCase())
+ );
+ }
+
+ if (searchForm.value.type) {
+ filteredData = filteredData.filter(item => item.type === searchForm.value.type);
+ }
+
+ tableData.value = filteredData;
+ page.value.total = filteredData.length;
+ tableLoading.value = false;
+ }, 500);
+};
+
+// 鍒嗛〉澶勭悊
+const pagination = (obj) => {
+ page.value.current = obj.page;
+ page.value.size = obj.limit;
+ handleQuery();
+};
+
+// 閫夋嫨鍙樺寲澶勭悊
+const handleSelectionChange = (selection) => {
+ selectedIds.value = selection.map(item => item.id);
+};
+
+// 鎵撳紑琛ㄥ崟
+const openForm = (type, row = null) => {
+ dialogType.value = type;
+ if (type === "add") {
+ dialogTitle.value = "鏂板閫氱煡";
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(form.value, {
+ title: "",
+ type: "",
+ priority: "medium",
+ content: "",
+ departments: [],
+ expireDate: "",
+ syncMethods: []
+ });
+ } else if (type === "edit" && row) {
+ dialogTitle.value = "缂栬緫閫氱煡";
+ Object.assign(form.value, {
+ title: row.title,
+ type: row.type,
+ priority: row.priority,
+ content: row.content || "",
+ departments: row.departments || [],
+ expireDate: row.expireDate || "",
+ syncMethods: row.syncMethods || []
+ });
+ }
+ dialogVisible.value = true;
+};
+
+// 鎵撳紑鍦ㄧ嚎浼氳寮圭獥
+const openMeetingDialog = () => {
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(meetingForm.value, {
+ title: "",
+ startTime: "",
+ duration: 60,
+ participants: [],
+ description: "",
+ platform: "wechat"
+ });
+ meetingDialogVisible.value = true;
+};
+
+// 鎵撳紑鏂囦欢鍏变韩寮圭獥
+const openFileShareDialog = () => {
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(fileShareForm.value, {
+ title: "",
+ description: "",
+ departments: [],
+ files: []
+ });
+ fileList.value = [];
+ fileShareDialogVisible.value = true;
+};
+
+// 鎵嬪姩鍒锋柊鏁版嵁
+const manualRefresh = () => {
+ generateNewData();
+ getList();
+ ElMessage.success("鎵嬪姩鍒锋柊瀹屾垚锛屽凡鐢熸垚鏂伴�氱煡");
+};
+
+// 鎻愪氦閫氱煡琛ㄥ崟
+const submitForm = async () => {
+ try {
+ await formRef.value.validate();
+
+ if (dialogType.value === "add") {
+ // 鏂板閫氱煡
+ const newNotification = {
+ id: (mockData.length + 1).toString(),
+ title: form.value.title,
+ type: form.value.type,
+ priority: form.value.priority,
+ status: "draft",
+ content: form.value.content,
+ departments: form.value.departments,
+ expireDate: form.value.expireDate,
+ syncMethods: form.value.syncMethods,
+ createTime: new Date().toLocaleString()
+ };
+
+ mockData.unshift(newNotification);
+ ElMessage.success("閫氱煡鍒涘缓鎴愬姛");
+ } else {
+ // 缂栬緫閫氱煡
+ const index = mockData.findIndex(item => item.id === selectedIds.value[0]);
+ if (index !== -1) {
+ Object.assign(mockData[index], {
+ title: form.value.title,
+ type: form.value.type,
+ priority: form.value.priority,
+ content: form.value.content,
+ departments: form.value.departments,
+ expireDate: form.value.expireDate,
+ syncMethods: form.value.syncMethods
+ });
+ ElMessage.success("閫氱煡鏇存柊鎴愬姛");
+ }
+ }
+
+ dialogVisible.value = false;
+ getList();
+ } catch (error) {
+ console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
+ }
+};
+
+// 鍒涘缓浼氳
+const createMeeting = async () => {
+ try {
+ await meetingFormRef.value.validate();
+
+ // 妯℃嫙鍒涘缓浼氳
+ const meetingInfo = {
+ title: meetingForm.value.title,
+ startTime: meetingForm.value.startTime,
+ duration: meetingForm.value.duration,
+ participants: meetingForm.value.participants,
+ description: meetingForm.value.description,
+ platform: meetingForm.value.platform,
+ meetingId: `MTG${Date.now()}`
+ };
+
+ // 妯℃嫙鍙戦�佸埌浼佷笟寰俊/閽夐拤
+ const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "鏈煡骞冲彴";
+
+ ElMessage.success(`浼氳鍒涘缓鎴愬姛锛佷細璁甀D: ${meetingInfo.meetingId}锛屽皢閫氳繃${platformName}鍙戦�侀�氱煡`);
+ meetingDialogVisible.value = false;
+
+ // 鑾峰彇鍙備細浜哄憳淇℃伅
+ const participantNames = meetingForm.value.participants.map(participantId => {
+ const employee = employees.value.find(emp => emp.value === participantId);
+ return employee ? employee.label : '鏈煡浜哄憳';
+ }).join('銆�');
+
+ // 鑾峰彇鍙備細浜哄憳璇︾粏淇℃伅
+ const participantDetails = meetingForm.value.participants.map(participantId => {
+ const employee = employees.value.find(emp => emp.value === participantId);
+ return employee ? {
+ name: employee.label,
+ dept: employee.dept,
+ phone: employee.phone,
+ email: employee.email
+ } : null;
+ }).filter(Boolean);
+
+ // 灏嗕細璁俊鎭坊鍔犲埌閫氱煡鍒楄〃
+ const meetingNotification = {
+ id: (mockData.length + 1).toString(),
+ title: `[浼氳閫氱煡] ${meetingInfo.title}`,
+ type: "meeting",
+ priority: "high",
+ status: "published",
+ content: `浼氳鏃堕棿: ${meetingInfo.startTime}锛屾椂闀�: ${meetingInfo.duration}鍒嗛挓锛屽钩鍙�: ${meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "鏈煡骞冲彴"}锛屽弬浼氫汉鍛�: ${participantNames}锛屽叡${participantDetails.length}浜篳,
+ departments: [],
+ expireDate: "",
+ syncMethods: [meetingForm.value.platform],
+ createTime: new Date().toLocaleString()
+ };
+
+ mockData.unshift(meetingNotification);
+ getList();
+ } catch (error) {
+ console.error("浼氳琛ㄥ崟楠岃瘉澶辫触:", error);
+ }
+};
+
+// 鏂囦欢涓婁紶澶勭悊
+const handleFileChange = (file) => {
+ const isLt10M = file.size / 1024 / 1024 < 10;
+ if (!isLt10M) {
+ ElMessage.error("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!");
+ return false;
+ }
+
+ const fileInfo = {
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ uid: file.uid
+ };
+
+ fileList.value.push(fileInfo);
+ fileShareForm.value.files.push(fileInfo);
+ return false; // 闃绘鑷姩涓婁紶
+};
+
+// 绉婚櫎鏂囦欢
+const removeFile = (file) => {
+ const index = fileList.value.findIndex(item => item.uid === file.uid);
+ if (index !== -1) {
+ const index2 = fileShareForm.value.files.findIndex(item => item.uid === file.uid);
+ if (index2 !== -1) {
+ fileShareForm.value.files.splice(index2, 1);
+ }
+ fileList.value.splice(index, 1);
+ }
+};
+
+// 鍏变韩鏂囦欢
+const shareFiles = async () => {
+ try {
+ await fileShareFormRef.value.validate();
+
+ if (fileShareForm.value.files.length === 0) {
+ ElMessage.warning("璇疯嚦灏戦�夋嫨涓�涓枃浠�");
+ return;
+ }
+
+ // 妯℃嫙鏂囦欢鍏变韩
+ const shareInfo = {
+ title: fileShareForm.value.title,
+ description: fileShareForm.value.description,
+ departments: fileShareForm.value.departments,
+ files: fileShareForm.value.files,
+ shareId: `FILE${Date.now()}`
+ };
+
+ ElMessage.success(`鏂囦欢鍏变韩鎴愬姛锛佸叡浜獻D: ${shareInfo.shareId}锛屽凡閫氱煡鐩稿叧閮ㄩ棬`);
+ fileShareDialogVisible.value = false;
+
+ // 灏嗘枃浠跺叡浜俊鎭坊鍔犲埌閫氱煡鍒楄〃
+ const fileShareNotification = {
+ id: (mockData.length + 1).toString(),
+ title: `[鏂囦欢鍏变韩] ${shareInfo.title}`,
+ type: "temporary",
+ priority: "medium",
+ status: "published",
+ content: `鍏变韩鎻忚堪: ${shareInfo.description}锛屾枃浠舵暟閲�: ${shareInfo.files.length}涓猔,
+ departments: shareInfo.departments,
+ expireDate: "",
+ syncMethods: ["wechat", "dingtalk"],
+ createTime: new Date().toLocaleString()
+ };
+
+ mockData.unshift(fileShareNotification);
+ getList();
+ } catch (error) {
+ console.error("鏂囦欢鍏变韩琛ㄥ崟楠岃瘉澶辫触:", error);
+ }
+};
+
+// 鍙戝竷閫氱煡
+const publishNotification = (row) => {
+ row.status = "published";
+ ElMessage.success("閫氱煡鍙戝竷鎴愬姛");
+ getList();
+};
+
+// 鎾ゅ洖閫氱煡
+const revokeNotification = (row) => {
+ row.status = "draft";
+ ElMessage.success("閫氱煡宸叉挙鍥�");
+ getList();
+};
+
+// 鍒犻櫎閫氱煡
+const handleDelete = () => {
+ if (selectedIds.value.length === 0) {
+ ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑閫氱煡");
+ return;
+ }
+
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ selectedIds.value = [];
+ getList();
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷
+ });
+};
+</script>
+
+<style scoped>
+.auto-refresh-info {
+ margin-bottom: 15px;
+}
+
+.auto-refresh-info .el-alert {
+ border-radius: 8px;
+}
+
+.dialog-footer {
+ text-align: right;
+}
+
+.el-upload__tip {
+ color: #909399;
+ font-size: 12px;
+ margin-top: 8px;
+}
+
+.el-checkbox-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+
+.el-checkbox {
+ margin-right: 0;
+}
+</style>
diff --git a/src/views/collaborativeApproval/rpaManagement/index.vue b/src/views/collaborativeApproval/rpaManagement/index.vue
new file mode 100644
index 0000000..51cef73
--- /dev/null
+++ b/src/views/collaborativeApproval/rpaManagement/index.vue
@@ -0,0 +1,400 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <span class="search_title">绋嬪簭鍚嶏細</span>
+ <el-input
+ v-model="searchForm.programName"
+ style="width: 240px"
+ placeholder="璇疯緭鍏ョ▼搴忓悕鎼滅储"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ <span class="search_title ml10">鎵ц鐘舵�侊細</span>
+ <el-select v-model="searchForm.status" clearable @change="handleQuery" style="width: 240px">
+ <el-option label="杩愯涓�" :value="'running'" />
+ <el-option label="宸插仠姝�" :value="'stopped'" />
+ <el-option label="寮傚父" :value="'error'" />
+ </el-select>
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
+ 鎼滅储
+ </el-button>
+ </div>
+ <div>
+ <el-button type="primary" @click="openForm('add')">鏂板</el-button>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ :total="page.total"
+ ></PIMTable>
+ </div>
+
+ <!-- RPA琛ㄥ崟寮圭獥 -->
+ <el-dialog
+ v-model="dialogVisible"
+ :title="dialogTitle"
+ width="500px"
+ :close-on-click-modal="false"
+ >
+ <el-form
+ ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="100px"
+ >
+ <el-form-item label="绋嬪簭鍚�" prop="programName">
+ <el-input
+ v-model="form.programName"
+ placeholder="璇疯緭鍏ョ▼搴忓悕"
+ clearable
+ />
+ </el-form-item>
+ <el-form-item label="鎵ц鐘舵��" prop="status">
+ <el-select v-model="form.status" placeholder="璇烽�夋嫨鎵ц鐘舵��" style="width: 100%">
+ <el-option label="杩愯涓�" value="running" />
+ <el-option label="宸插仠姝�" value="stopped" />
+ <el-option label="寮傚父" value="error" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鎻忚堪" prop="description">
+ <el-input
+ v-model="form.description"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏PA绋嬪簭鎻忚堪"
+ clearable
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitForm">纭畾</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import { onMounted, ref, reactive, toRefs } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+
+// 鍝嶅簲寮忔暟鎹�
+const data = reactive({
+ searchForm: {
+ programName: "",
+ status: "",
+ },
+ form: {
+ id: "",
+ programName: "",
+ status: "stopped",
+ description: "",
+ createTime: "",
+ },
+ dialogVisible: false,
+ dialogTitle: "",
+ dialogType: "add",
+ selectedIds: [],
+ tableLoading: false,
+ page: {
+ current: 1,
+ size: 100,
+ total: 0,
+ },
+ tableData: [],
+});
+
+const { searchForm, form, dialogVisible, dialogTitle, dialogType, selectedIds, tableLoading, page, tableData } = toRefs(data);
+
+// 琛ㄥ崟寮曠敤
+const formRef = ref();
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const rules = {
+ programName: [
+ { required: true, message: "璇疯緭鍏ョ▼搴忓悕", trigger: "blur" }
+ ],
+ status: [
+ { required: true, message: "璇烽�夋嫨鎵ц鐘舵��", trigger: "change" }
+ ]
+};
+
+// 琛ㄦ牸鍒楅厤缃�
+const tableColumn = ref([
+ {
+ label: "绋嬪簭鍚�",
+ prop: "programName",
+ // width: 200,
+ },
+ {
+ label: "鎵ц鐘舵��",
+ prop: "status",
+ dataType: "tag",
+ // width: 120,
+ formatData: (params) => {
+ const statusMap = {
+ running: "杩愯涓�",
+ stopped: "宸插仠姝�",
+ error: "寮傚父"
+ };
+ return statusMap[params] || params;
+ },
+ formatType: (params) => {
+ const typeMap = {
+ running: "success",
+ stopped: "info",
+ error: "danger"
+ };
+ return typeMap[params] || "info";
+ }
+ },
+ {
+ label: "鎻忚堪",
+ prop: "description",
+ // width: 300,
+ showOverflowTooltip: true,
+ },
+ {
+ label: "鍒涘缓鏃堕棿",
+ prop: "createTime",
+ // width: 180,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 230,
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ openForm("edit", row);
+ }
+ },
+ {
+ name: "寮�濮�",
+ type: "text",
+ clickFun: (row) => {
+ handleStart(row);
+ },
+ disabled: (row) => row.status !== 'stopped'
+ },
+ {
+ name: "鍋滄",
+ type: "text",
+ clickFun: (row) => {
+ handleStop(row);
+ },
+ disabled: (row) => row.status === 'stopped'
+ }
+ ]
+ }
+]);
+
+// 妯℃嫙鏁版嵁
+const mockData = [
+ {
+ id: "1",
+ programName: "璁㈠崟澶勭悊RPA",
+ status: "running",
+ description: "鑷姩澶勭悊瀹㈡埛璁㈠崟锛屽寘鎷獙璇併�佸垎閰嶅拰纭",
+ createTime: "2024-01-15 10:30:00"
+ },
+ {
+ id: "2",
+ programName: "搴撳瓨鍚屾RPA",
+ status: "stopped",
+ description: "鍚屾澶氫釜浠撳簱鐨勫簱瀛樻暟鎹紝纭繚鏁版嵁涓�鑷存��",
+ createTime: "2024-01-14 15:20:00"
+ },
+ {
+ id: "3",
+ programName: "鎶ヨ〃鐢熸垚RPA",
+ status: "error",
+ description: "鑷姩鐢熸垚姣忔棩閿�鍞姤琛ㄥ拰搴撳瓨鎶ヨ〃",
+ createTime: "2024-01-13 09:15:00"
+ }
+];
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ getList();
+});
+
+// 鏌ヨ鏁版嵁
+const handleQuery = () => {
+ page.value.current = 1;
+ getList();
+};
+
+const getList = () => {
+ tableLoading.value = true;
+
+ // 妯℃嫙API璋冪敤寤惰繜
+ setTimeout(() => {
+ let filteredData = [...mockData];
+
+ // 鏍规嵁鎼滅储鏉′欢杩囨护鏁版嵁
+ if (searchForm.value.programName) {
+ filteredData = filteredData.filter(item =>
+ item.programName.toLowerCase().includes(searchForm.value.programName.toLowerCase())
+ );
+ }
+
+ if (searchForm.value.status) {
+ filteredData = filteredData.filter(item => item.status === searchForm.value.status);
+ }
+
+ tableData.value = filteredData;
+ page.value.total = filteredData.length;
+ tableLoading.value = false;
+ }, 500);
+};
+
+// 鍒嗛〉澶勭悊
+const pagination = (obj) => {
+ page.value.current = obj.page;
+ page.value.size = obj.limit;
+ handleQuery();
+};
+
+// 閫夋嫨鍙樺寲澶勭悊
+const handleSelectionChange = (selection) => {
+ selectedIds.value = selection.map(item => item.id);
+};
+
+// 鎵撳紑琛ㄥ崟
+const openForm = (type, row) => {
+ dialogType.value = type;
+ dialogVisible.value = true;
+
+ if (type === "add") {
+ dialogTitle.value = "娣诲姞RPA";
+ form.value = {
+ id: "",
+ programName: "",
+ status: "stopped",
+ description: "",
+ createTime: "",
+ };
+ } else {
+ dialogTitle.value = "缂栬緫RPA";
+ form.value = { ...row };
+ }
+};
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+ if (!formRef.value) return;
+
+ try {
+ await formRef.value.validate();
+
+ if (dialogType.value === "add") {
+ // 娣诲姞鏂癛PA
+ const newRPA = {
+ id: Date.now().toString(),
+ programName: form.value.programName,
+ status: form.value.status,
+ description: form.value.description,
+ createTime: new Date().toLocaleString(),
+ };
+
+ mockData.unshift(newRPA);
+ ElMessage.success("RPA娣诲姞鎴愬姛");
+ } else {
+ // 缂栬緫RPA
+ const index = mockData.findIndex(item => item.id === form.value.id);
+ if (index !== -1) {
+ mockData[index] = { ...form.value };
+ ElMessage.success("RPA鏇存柊鎴愬姛");
+ }
+ }
+
+ dialogVisible.value = false;
+ getList();
+ } catch (error) {
+ console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
+ }
+};
+
+// 寮�濮婻PA
+const handleStart = (row) => {
+ ElMessageBox.confirm(`纭畾瑕佸惎鍔≧PA绋嬪簭"${row.programName}"鍚楋紵`, "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ row.status = "running";
+ ElMessage.success("RPA鍚姩鎴愬姛");
+ getList();
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷
+ });
+};
+
+// 鍋滄RPA
+const handleStop = (row) => {
+ ElMessageBox.confirm(`纭畾瑕佸仠姝PA绋嬪簭"${row.programName}"鍚楋紵`, "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ row.status = "stopped";
+ ElMessage.success("RPA鍋滄鎴愬姛");
+ getList();
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷
+ });
+};
+
+// 鍒犻櫎RPA
+const handleDelete = () => {
+ let ids = [];
+ if (selectedIds.value.length > 0) {
+ ids = selectedIds.value.map((item) => item.id);
+ } else {
+ ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑RPA");
+ return;
+ }
+
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ // 浠庢ā鎷熸暟鎹腑鍒犻櫎閫変腑鐨勯」
+ ids.forEach(id => {
+ const index = mockData.findIndex(item => item.id === id);
+ if (index !== -1) {
+ mockData.splice(index, 1);
+ }
+ });
+
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ selectedIds.value = [];
+ getList();
+ }).catch(() => {
+ // 鐢ㄦ埛鍙栨秷
+ });
+};
+</script>
+
+<style scoped></style>
diff --git a/src/views/collaborativeApproval/warningSystem/index.vue b/src/views/collaborativeApproval/warningSystem/index.vue
new file mode 100644
index 0000000..b04c583
--- /dev/null
+++ b/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>璐d换浜�</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: '鐢熶骇绾胯澶嘋宸茶繍琛�8000灏忔椂锛屾帴杩戠淮鎶ゅ懆鏈熴��',
+ impact: '鍙兘褰卞搷鐢熶骇鏁堢巼鍜屼骇鍝佽川閲�',
+ suggestions: '瀹夋帓缁存姢鏃堕棿锛屽噯澶囧浠讹紝鍒跺畾缁存姢璁″垝'
+ },
+ {
+ id: 'W004',
+ title: '浜哄憳閰嶇疆棰勮',
+ type: '杩愯惀棰勮',
+ level: 'green',
+ levelText: '缁胯壊棰勮',
+ status: 'resolved',
+ statusText: '宸茶В鍐�',
+ responsible: '璧礖R',
+ description: '鎶�鏈儴闂ㄤ汉鍛橀厤缃厖瓒筹紝椤圭洰杩涘害姝e父銆�',
+ 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>
diff --git a/src/views/demo/fakePage/index.vue b/src/views/demo/fakePage/index.vue
new file mode 100644
index 0000000..7ef8b89
--- /dev/null
+++ b/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" stripe>
+ <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>
+
+
+
diff --git a/src/views/energyManagement/dynamicEnergySaving/index.vue b/src/views/energyManagement/dynamicEnergySaving/index.vue
new file mode 100644
index 0000000..2666e0f
--- /dev/null
+++ b/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%" stripe>
+ <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%" stripe>
+ <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>
diff --git a/src/views/energyManagement/energyArea/index.vue b/src/views/energyManagement/energyArea/index.vue
new file mode 100644
index 0000000..63feff8
--- /dev/null
+++ b/src/views/energyManagement/energyArea/index.vue
@@ -0,0 +1,511 @@
+<template>
+ <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('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="
+ 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'" />
+ </el-icon>
+ {{ data.label }}
+ </span>
+ <div>
+ <el-button
+ type="primary"
+ link
+ @click="openProDia('edit', data)"
+ >
+ 缂栬緫
+ </el-button>
+ <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>
+ </div>
+ </div>
+ </template>
+ </el-tree>
+ </div>
+ </div>
+ <div class="right">
+ <div style="margin-bottom: 10px" v-if="isShowButton">
+ <el-button type="primary" @click="openModelDia('add')">
+ 鏂板瀛愬尯鍩�
+ </el-button>
+ <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>
+ </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 :gutter="30">
+ <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-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitForm">纭</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="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">
+ <el-button type="primary" @click="submitModelForm">纭</el-button>
+ <el-button @click="closeModelDia">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { ElMessageBox } from "element-plus";
+import {
+ areaAdd,
+ areaDelete,
+ areaListPage,
+ areaListTree,
+} from "@/api/energyManagement/index.js";
+
+const { proxy } = getCurrentInstance();
+const tree = ref(null);
+const containerRef = ref(null);
+
+const productDia = ref(false);
+const modelDia = ref(false);
+const modelOperationType = ref("");
+const search = ref("");
+const currentId = ref("");
+const currentParentId = ref("");
+const operationType = ref("");
+const treeLoad = ref(false);
+const list = ref([]);
+const expandedKeys = ref([]);
+const {area_type} = proxy.useDict("area_type")
+const tableColumn = ref([
+ {
+ label: "鍖哄煙鍚嶇О",
+ prop: "areaName",
+ },
+ {
+ label: "鍖哄煙绫诲瀷",
+ prop: "areaType",
+ dataType: "tag",
+ formatData: (row) => {
+ return area_type.value.find(item => item.value == row)?.label;
+ }
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ openModelDia("edit", row);
+ },
+ },
+ ],
+ },
+]);
+const tableData = ref([]);
+const tableLoading = ref(false);
+const isShowButton = ref(false);
+const selectedRows = ref([]);
+const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
+});
+const data = reactive({
+ form: {
+ areaName: "",
+ },
+ rules: {
+ areaName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ },
+ modelForm: {
+ areaName: "",
+ fuId: "",
+ },
+ modelRules: {
+ areaName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ fuId: [{ required: true, message: "璇疯緭鍏�", trigger: "change" }],
+ },
+});
+const { form, rules, modelForm, modelRules } = toRefs(data);
+
+// 鏌ヨ浜у搧鏍�
+const getProductTreeList = () => {
+ treeLoad.value = true;
+ areaListTree()
+ .then((res) => {
+ list.value = res;
+ list.value.forEach((a) => {
+ expandedKeys.value.push(a.label);
+ });
+ treeLoad.value = false;
+ })
+ .catch((err) => {
+ treeLoad.value = false;
+ });
+};
+// 杩囨护浜у搧鏍�
+const searchFilter = () => {
+ proxy.$refs.tree.filter(search.value);
+};
+// 鎵撳紑浜у搧寮规
+const openProDia = (type, data) => {
+ operationType.value = type;
+ productDia.value = true;
+ form.value.areaName = "";
+ if (type === "edit") {
+ form.value.areaName = data.areaName;
+ }
+};
+// 鎵撳紑瑙勬牸鍨嬪彿寮规
+const openModelDia = (type, data,fatherId) => {
+ modelOperationType.value = type;
+ modelDia.value = true;
+ modelForm.value.fuId = "";
+ modelForm.value.areaType = "";
+ modelForm.value.areaName = "";
+ modelForm.value.id = "";
+ modelForm.value.fuId = fatherId;
+ if (type === "edit") {
+ modelForm.value = { ...data };
+ }
+};
+// 鎻愪氦浜у搧鍚嶇О淇敼
+const submitForm = () => {
+ proxy.$refs.formRef.validate((valid) => {
+ if (valid) {
+ if (operationType.value === "add") {
+ 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;
+ form.value.parentId = "";
+ }
+ areaAdd(form.value).then((res) => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeProDia();
+ getProductTreeList();
+ });
+ }
+ });
+};
+// 鍏抽棴浜у搧寮规
+const closeProDia = () => {
+ proxy.$refs.formRef.resetFields();
+ productDia.value = false;
+};
+
+// 鍒犻櫎浜у搧
+const remove = (node, data) => {
+ let ids = [];
+ ids.push(data.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ tableLoading.value = true;
+ areaDelete(ids)
+ .then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getProductTreeList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+// 閫夋嫨浜у搧
+const handleNodeClick = (val, node, el) => {
+ // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
+ isShowButton.value = !(val.children && val.children.length > 0);
+ // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
+ currentId.value = val.id;
+ currentParentId.value = val.parentId;
+ getModelList(true);
+};
+
+// 鎻愪氦瑙勬牸鍨嬪彿淇敼
+const submitModelForm = () => {
+ proxy.$refs.modelFormRef.validate((valid) => {
+ if (valid) {
+ modelForm.value.fuId = currentId.value;
+ areaAdd(modelForm.value).then((res) => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeModelDia();
+ getModelList();
+ getProductTreeList();
+ });
+ }
+ });
+};
+// 鍏抽棴鍨嬪彿寮规
+const closeModelDia = () => {
+ proxy.$refs.modelFormRef.resetFields();
+ modelDia.value = false;
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+};
+
+// 鏌ヨ瑙勬牸鍨嬪彿
+const pagination = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getModelList();
+};
+const getModelList = (val = false) => {
+ tableLoading.value = true;
+ let obj = {
+ id: currentId.value,
+ fuId:currentId.value,
+ current: page.current,
+ size: page.size
+ }
+ if(val){
+ delete obj.id;
+ }else{
+ delete obj.fuId
+ }
+ areaListPage(obj).then((res) => {
+ console.log("res", res);
+ tableData.value = res.data.records;
+ page.total = res.data.total;
+ tableLoading.value = false;
+ });
+};
+// 鍒犻櫎瑙勬牸鍨嬪彿
+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(() => {
+ tableLoading.value = true;
+ areaDelete(ids)
+ .then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getModelList();
+ getProductTreeList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+// 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
+const filterNode = (value, data, node) => {
+ if (!value) {
+ //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
+ return true;
+ }
+ // 鏌ヨ鍒楄〃鏄惁鏈夊尮閰嶆暟鎹紝灏嗗�煎皬鍐欙紝鍖归厤鑻辨枃鏁版嵁
+ let val = value.toLowerCase();
+ return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
+};
+// 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
+const chooseNode = (value, data, node) => {
+ if (data.label.indexOf(value) !== -1) {
+ return true;
+ }
+ const level = node.level;
+ // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
+ if (level === 1) {
+ return false;
+ }
+ // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
+ let parentData = node.parent;
+ // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
+ let index = 0;
+ while (index < level - 1) {
+ // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紝姝ゅname鍊兼槸涓枃瀛楃锛宔nName鏄嫳鏂囧瓧绗︺�傚垽鏂尮閰嶄腑鑻辨枃杩囨护
+ if (parentData.data.label.indexOf(value) !== -1) {
+ return true;
+ }
+ // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
+ parentData = parentData.parent;
+ index++;
+ }
+ // 娌″尮閰嶅埌杩斿洖false
+ return false;
+};
+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;
+ align-items: center;
+ justify-content: space-between;
+ font-size: 14px;
+ padding-right: 8px;
+}
+.tree-node-content {
+ display: flex;
+ align-items: center; /* 鍨傜洿灞呬腑 */
+ height: 100%;
+}
+.orange-icon {
+ color: orange;
+ font-size: 18px;
+ margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
+}
+</style>
diff --git a/src/views/energyManagement/energyCockpit/index.vue b/src/views/energyManagement/energyCockpit/index.vue
new file mode 100644
index 0000000..9281e37
--- /dev/null
+++ b/src/views/energyManagement/energyCockpit/index.vue
@@ -0,0 +1,1380 @@
+<template>
+ <div class="app-container">
+ <!-- 椤甸潰鏍囬 -->
+ <div class="page-header">
+ <h2>鑳芥簮椹鹃┒鑸�</h2>
+ <div class="header-info">
+ <span class="update-time">鏈�鍚庢洿鏂帮細{{ lastUpdateTime }}</span>
+ <el-button type="primary" size="small" @click="refreshData">
+ <el-icon><Refresh /></el-icon>
+ 鍒锋柊鏁版嵁
+ </el-button>
+ </div>
+ </div>
+
+ <!-- 瀹炴椂鑳借�楃洃鎺� -->
+ <div class="real-time-monitor">
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <el-card class="monitor-card">
+ <template #header>
+ <div class="card-header">
+ <span>鐢靛姏娑堣��</span>
+ <el-tag type="success" size="small">瀹炴椂</el-tag>
+ </div>
+ </template>
+ <div class="monitor-content">
+ <div class="monitor-value">
+ <span class="value">{{ electricityConsumption }}</span>
+ <span class="unit">kW路h</span>
+ </div>
+ <div class="monitor-trend">
+ <span class="trend-label">瓒嬪娍锛�</span>
+ <el-tag :type="getTrendType(electricityTrend)" size="small">
+ {{ electricityTrend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(electricityTrend) }}%
+ </el-tag>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="8">
+ <el-card class="monitor-card">
+ <template #header>
+ <div class="card-header">
+ <span>姘存秷鑰�</span>
+ <el-tag type="primary" size="small">瀹炴椂</el-tag>
+ </div>
+ </template>
+ <div class="monitor-content">
+ <div class="monitor-value">
+ <span class="value">{{ waterConsumption }}</span>
+ <span class="unit">m鲁</span>
+ </div>
+ <div class="monitor-trend">
+ <span class="trend-label">瓒嬪娍锛�</span>
+ <el-tag :type="getTrendType(waterTrend)" size="small">
+ {{ waterTrend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(waterTrend) }}%
+ </el-tag>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="8">
+ <el-card class="monitor-card">
+ <template #header>
+ <div class="card-header">
+ <span>姘斾綋娑堣��</span>
+ <el-tag type="warning" size="small">瀹炴椂</el-tag>
+ </div>
+ </template>
+ <div class="monitor-content">
+ <div class="monitor-value">
+ <span class="value">{{ gasConsumption }}</span>
+ <span class="unit">m鲁</span>
+ </div>
+ <div class="monitor-trend">
+ <span class="trend-label">瓒嬪娍锛�</span>
+ <el-tag :type="getTrendType(gasTrend)" size="small">
+ {{ gasTrend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(gasTrend) }}%
+ </el-tag>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 鑳借�楄秼鍔垮垎鏋� -->
+ <div class="trend-analysis">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>鑳借�楄秼鍔垮垎鏋�</span>
+ <div class="time-selector">
+ <el-radio-group v-model="trendTimeUnit" @change="handleTrendTimeChange">
+ <el-radio value="hour">灏忔椂</el-radio>
+ <el-radio value="day">鏃�</el-radio>
+ <el-radio value="week">鍛�</el-radio>
+ <el-radio value="month">鏈�</el-radio>
+ <el-radio value="year">骞�</el-radio>
+ </el-radio-group>
+ </div>
+ </div>
+ </template>
+ <div class="chart-container">
+ <div ref="trendChart" style="width: 100%; height: 400px;"></div>
+ </div>
+ </el-card>
+ </div>
+
+ <!-- 鑳借�楃粺璁′笌鎺掑悕 -->
+ <div class="statistics-ranking">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-card class="statistics-card">
+ <template #header>
+ <div class="card-header">
+ <span class="card-title">鑳借�楃粺璁℃姤琛�</span>
+ <div class="header-actions">
+ <el-select v-model="statisticsPeriod" @change="handleStatisticsChange" size="small" style="width: 100px;">
+ <el-option label="鏃ョ粺璁�" value="day" />
+ <el-option label="鍛ㄧ粺璁�" value="week" />
+ <el-option label="鏈堢粺璁�" value="month" />
+ <el-option label="骞寸粺璁�" value="year" />
+ </el-select>
+ </div>
+ </div>
+ </template>
+ <div class="statistics-content">
+ <div class="statistics-item">
+ <span class="label">鎬昏兘鑰楋細</span>
+ <span class="value">{{ totalEnergyConsumption }} kW路h</span>
+ </div>
+ <div class="statistics-item">
+ <span class="label">鍚屾瘮锛�</span>
+ <span class="value" :class="getComparisonClass(yearOverYear)">
+ {{ yearOverYear > 0 ? '+' : '' }}{{ yearOverYear }}%
+ </span>
+ </div>
+ <div class="statistics-item">
+ <span class="label">鐜瘮锛�</span>
+ <span class="value" :class="getComparisonClass(monthOverMonth)">
+ {{ monthOverMonth > 0 ? '+' : '' }}{{ monthOverMonth }}%
+ </span>
+ </div>
+ <div class="statistics-item">
+ <span class="label">鑺傝兘鐜囷細</span>
+ <span class="value success">{{ energySavingRate }}%</span>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="12">
+ <el-card class="ranking-card">
+ <template #header>
+ <div class="card-header">
+ <span class="card-title">鑳借�楁帓鍚�</span>
+ <el-select v-model="rankingType" @change="handleRankingChange" size="small" style="width: 120px;">
+ <el-option label="閮ㄩ棬鎺掑悕" value="department" />
+ <el-option label="杞﹂棿鎺掑悕" value="workshop" />
+ <el-option label="璁惧鎺掑悕" value="equipment" />
+ </el-select>
+ </div>
+ </template>
+ <div class="ranking-list">
+ <div v-for="(item, index) in rankingList" :key="index" class="ranking-item">
+ <div class="ranking-number" :class="getRankingClass(index + 1)">{{ index + 1 }}</div>
+ <div class="ranking-info">
+ <div class="ranking-name">{{ item.name }}</div>
+ <div class="ranking-value">{{ item.value }} kW路h</div>
+ </div>
+ <div class="ranking-trend">
+ <el-tag :type="getTrendType(item.trend)" size="small">
+ {{ item.trend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(item.trend) }}%
+ </el-tag>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 寮傚父鍒嗘瀽涓庢櫤鑳芥帶鍒� -->
+ <div class="analysis-control">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-card class="abnormal-card">
+ <template #header>
+ <div class="card-header">
+ <span class="card-title">寮傚父鍒嗘瀽</span>
+ <el-tag type="danger" size="small">{{ abnormalCount }}涓紓甯�</el-tag>
+ </div>
+ </template>
+ <div class="abnormal-list">
+ <div v-for="(item, index) in abnormalList" :key="index" class="abnormal-item">
+ <div class="abnormal-icon">
+ <el-icon :color="getAbnormalColor(item.level)">
+ <Warning v-if="item.level === 'warning'" />
+ <CircleClose v-else />
+ </el-icon>
+ </div>
+ <div class="abnormal-content">
+ <div class="abnormal-title">{{ item.title }}</div>
+ <div class="abnormal-desc">{{ item.description }}</div>
+ <div class="abnormal-time">{{ item.time }}</div>
+ </div>
+ <div class="abnormal-action">
+ <el-button link size="small" @click="handleAbnormal(item)">澶勭悊</el-button>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="12">
+ <el-card class="control-card">
+ <template #header>
+ <div class="card-header">
+ <span class="card-title">鏅鸿兘鎺у埗绯荤粺</span>
+ <el-switch v-model="autoControlEnabled" @change="handleAutoControlChange" />
+ </div>
+ </template>
+ <div class="control-content">
+ <div class="control-item">
+ <span class="label">宄拌胺骞崇數浠风鐞嗭細</span>
+ <el-tag :type="getPriceType(currentPriceType)" size="small">
+ {{ getPriceTypeText(currentPriceType) }}
+ </el-tag>
+ </div>
+ <div class="control-item">
+ <span class="label">璐熻嵎棰勬祴锛�</span>
+ <span class="value">{{ loadForecast }} kW</span>
+ </div>
+ <div class="control-item">
+ <span class="label">鑷姩鍚仠锛�</span>
+ <el-tag :type="autoStartStop ? 'success' : 'info'" size="small">
+ {{ autoStartStop ? '宸插惎鐢�' : '宸茬鐢�' }}
+ </el-tag>
+ </div>
+ <div class="control-item">
+ <span class="label">鏅鸿兘璋冭妭锛�</span>
+ <el-progress :percentage="intelligentAdjustment" :color="getProgressColor" />
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 鐜繚鎸囨爣 -->
+ <div class="environmental-indicators">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>鐜繚鎸囨爣鐩戞帶</span>
+ </div>
+ </template>
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <div class="indicator-item">
+ <div class="indicator-title">纰虫帓鏀鹃噺</div>
+ <div class="indicator-value">{{ carbonEmission }} kg</div>
+ <div class="indicator-trend">
+ <span>鍚屾瘮锛�</span>
+ <span :class="getComparisonClass(carbonEmissionTrend)">
+ {{ carbonEmissionTrend > 0 ? '+' : '' }}{{ carbonEmissionTrend }}%
+ </span>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="indicator-item">
+ <div class="indicator-title">鐜繚杈炬爣鐜�</div>
+ <div class="indicator-value">{{ environmentalCompliance }}%</div>
+ <div class="indicator-trend">
+ <span>鐩爣锛�</span>
+ <span class="success">95%</span>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="indicator-item">
+ <div class="indicator-title">缁胯壊鑳芥簮鍗犳瘮</div>
+ <div class="indicator-value">{{ greenEnergyRatio }}%</div>
+ <div class="indicator-trend">
+ <span>鐩爣锛�</span>
+ <span class="success">30%</span>
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+ </el-card>
+ </div>
+
+ <!-- 澶氱淮搴︽姤琛� -->
+ <div class="multi-dimensional-reports">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>澶氱淮搴︽姤琛�</span>
+ </div>
+ </template>
+ <div class="report-filters">
+ <el-row :gutter="20">
+ <el-col :span="6">
+ <el-form-item label="鏃堕棿缁村害">
+ <el-select v-model="reportTimeDimension" placeholder="閫夋嫨鏃堕棿缁村害">
+ <el-option label="灏忔椂" value="hour" />
+ <el-option label="鏃�" value="day" />
+ <el-option label="鍛�" value="week" />
+ <el-option label="鏈�" value="month" />
+ <el-option label="骞�" value="year" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="6">
+ <el-form-item label="閮ㄩ棬缁村害">
+ <el-select v-model="reportDepartmentDimension" placeholder="閫夋嫨閮ㄩ棬">
+ <el-option label="鍏ㄩ儴閮ㄩ棬" value="all" />
+ <el-option label="鐢熶骇閮�" value="production" />
+ <el-option label="鎶�鏈儴" value="technology" />
+ <el-option label="琛屾斂閮�" value="administration" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="6">
+ <el-form-item label="璁惧缁村害">
+ <el-select v-model="reportEquipmentDimension" placeholder="閫夋嫨璁惧绫诲瀷">
+ <el-option label="鍏ㄩ儴璁惧" value="all" />
+ <el-option label="鐢靛姏璁惧" value="electricity" />
+ <el-option label="姘村鐞嗚澶�" value="water" />
+ <el-option label="姘斾綋璁惧" value="gas" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="6">
+ <el-form-item>
+ <el-button type="primary" @click="generateReport">鐢熸垚鎶ヨ〃</el-button>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </div>
+ <div class="report-preview">
+ <div class="report-data">
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <div class="data-card">
+ <div class="data-title">鐢靛姏娑堣��</div>
+ <div class="data-value">{{ reportData.electricity }} kW路h</div>
+ <div class="data-trend">
+ <span :class="getTrendClass(reportData.electricityTrend)">
+ {{ reportData.electricityTrend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(reportData.electricityTrend) }}%
+ </span>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="data-card">
+ <div class="data-title">姘存秷鑰�</div>
+ <div class="data-value">{{ reportData.water }} m鲁</div>
+ <div class="data-trend">
+ <span :class="getTrendClass(reportData.waterTrend)">
+ {{ reportData.waterTrend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(reportData.waterTrend) }}%
+ </span>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="data-card">
+ <div class="data-title">姘斾綋娑堣��</div>
+ <div class="data-value">{{ reportData.gas }} m鲁</div>
+ <div class="data-trend">
+ <span :class="getTrendClass(reportData.gasTrend)">
+ {{ reportData.gasTrend > 0 ? '鈫�' : '鈫�' }} {{ Math.abs(reportData.gasTrend) }}%
+ </span>
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <div class="report-chart">
+ <div class="chart-title">鑳借�楄秼鍔垮浘</div>
+ <div class="chart-bars">
+ <div v-for="(item, index) in reportData.chartData" :key="index" class="chart-bar">
+ <div class="bar-label">{{ item.label }}</div>
+ <div class="bar-container">
+ <div class="bar-fill" :style="{ height: item.percentage + '%', backgroundColor: item.color }"></div>
+ </div>
+ <div class="bar-value">{{ item.value }}</div>
+ </div>
+ </div>
+ </div>
+
+ <div class="report-summary">
+ <div class="summary-item">
+ <span class="summary-label">鎬昏兘鑰楋細</span>
+ <span class="summary-value">{{ reportData.totalEnergy }} kW路h</span>
+ </div>
+ <div class="summary-item">
+ <span class="summary-label">骞冲潎鑳借�楋細</span>
+ <span class="summary-value">{{ reportData.averageEnergy }} kW路h</span>
+ </div>
+ <div class="summary-item">
+ <span class="summary-label">鑳借�楁晥鐜囷細</span>
+ <span class="summary-value">{{ reportData.efficiency }}%</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import * as echarts from 'echarts'
+import {
+ Refresh,
+ Download,
+ Warning,
+ CircleClose,
+ Document,
+ Edit,
+ Bell
+} from '@element-plus/icons-vue'
+
+// 鍝嶅簲寮忔暟鎹�
+const lastUpdateTime = ref('')
+const electricityConsumption = ref(0)
+const waterConsumption = ref(0)
+const gasConsumption = ref(0)
+const electricityTrend = ref(0)
+const waterTrend = ref(0)
+const gasTrend = ref(0)
+
+// 瓒嬪娍鍒嗘瀽
+const trendTimeUnit = ref('day')
+const trendChart = ref(null)
+let chartInstance = null
+
+// 缁熻鎶ヨ〃
+const statisticsPeriod = ref('month')
+const totalEnergyConsumption = ref(0)
+const yearOverYear = ref(0)
+const monthOverMonth = ref(0)
+const energySavingRate = ref(0)
+
+// 鑳借�楁帓鍚�
+const rankingType = ref('department')
+const rankingList = ref([])
+
+// 寮傚父鍒嗘瀽
+const abnormalCount = ref(0)
+const abnormalList = ref([])
+
+// 鏅鸿兘鎺у埗
+const autoControlEnabled = ref(true)
+const currentPriceType = ref('peak')
+const loadForecast = ref(0)
+const autoStartStop = ref(true)
+const intelligentAdjustment = ref(0)
+
+// 鐜繚鎸囨爣
+const carbonEmission = ref(0)
+const carbonEmissionTrend = ref(0)
+const environmentalCompliance = ref(0)
+const greenEnergyRatio = ref(0)
+
+// 澶氱淮搴︽姤琛�
+const reportTimeDimension = ref('month')
+const reportDepartmentDimension = ref('all')
+const reportEquipmentDimension = ref('all')
+const reportData = ref({
+ electricity: 0,
+ water: 0,
+ gas: 0,
+ electricityTrend: 0,
+ waterTrend: 0,
+ gasTrend: 0,
+ totalEnergy: 0,
+ averageEnergy: 0,
+ efficiency: 0,
+ chartData: []
+})
+
+// 瀹氭椂鍣�
+let updateTimer = null
+
+// 鑾峰彇瓒嬪娍绫诲瀷鏍峰紡
+const getTrendType = (trend) => {
+ if (trend > 0) return 'danger'
+ if (trend < 0) return 'success'
+ return 'info'
+}
+
+// 鑾峰彇瀵规瘮绫诲瀷鏍峰紡
+const getComparisonClass = (value) => {
+ if (value > 0) return 'danger'
+ if (value < 0) return 'success'
+ return 'info'
+}
+
+// 鑾峰彇鎺掑悕鏍峰紡
+const getRankingClass = (rank) => {
+ if (rank === 1) return 'ranking-first'
+ if (rank === 2) return 'ranking-second'
+ if (rank === 3) return 'ranking-third'
+ return 'ranking-normal'
+}
+
+// 鑾峰彇寮傚父棰滆壊
+const getAbnormalColor = (level) => {
+ return level === 'warning' ? '#E6A23C' : '#F56C6C'
+}
+
+// 鑾峰彇鐢典环绫诲瀷鏍峰紡
+const getPriceType = (type) => {
+ const typeMap = {
+ peak: 'danger',
+ normal: 'warning',
+ valley: 'success'
+ }
+ return typeMap[type] || 'info'
+}
+
+// 鑾峰彇鐢典环绫诲瀷鏂囨湰
+const getPriceTypeText = (type) => {
+ const typeMap = {
+ peak: '宄版椂',
+ normal: '骞虫椂',
+ valley: '璋锋椂'
+ }
+ return typeMap[type] || '鏈煡'
+}
+
+// 鑾峰彇杩涘害鏉¢鑹�
+const getProgressColor = (percentage) => {
+ if (percentage < 50) return '#67C23A'
+ if (percentage < 80) return '#E6A23C'
+ return '#F56C6C'
+}
+
+// 鑾峰彇瓒嬪娍鏍峰紡
+const getTrendClass = (trend) => {
+ if (trend > 0) return 'trend-up'
+ if (trend < 0) return 'trend-down'
+ return 'trend-stable'
+}
+
+// 妯℃嫙鏁版嵁鐢熸垚
+const generateMockData = () => {
+ // 瀹炴椂鑳借�楁暟鎹�
+ electricityConsumption.value = Math.floor(Math.random() * 1000) + 2000
+ waterConsumption.value = Math.floor(Math.random() * 100) + 150
+ gasConsumption.value = Math.floor(Math.random() * 50) + 80
+
+ // 瓒嬪娍鏁版嵁
+ electricityTrend.value = (Math.random() * 20 - 10).toFixed(1)
+ waterTrend.value = (Math.random() * 15 - 7.5).toFixed(1)
+ gasTrend.value = (Math.random() * 12 - 6).toFixed(1)
+
+ // 缁熻鏁版嵁
+ totalEnergyConsumption.value = Math.floor(Math.random() * 50000) + 100000
+ yearOverYear.value = (Math.random() * 20 - 10).toFixed(1)
+ monthOverMonth.value = (Math.random() * 15 - 7.5).toFixed(1)
+ energySavingRate.value = (Math.random() * 10 + 5).toFixed(1)
+
+ // 鎺掑悕鏁版嵁
+ rankingList.value = [
+ { name: '鐢熶骇杞﹂棿A', value: Math.floor(Math.random() * 5000) + 10000, trend: (Math.random() * 20 - 10).toFixed(1) },
+ { name: '鐢熶骇杞﹂棿B', value: Math.floor(Math.random() * 4000) + 8000, trend: (Math.random() * 20 - 10).toFixed(1) },
+ { name: '鎶�鏈爺鍙戦儴', value: Math.floor(Math.random() * 3000) + 6000, trend: (Math.random() * 20 - 10).toFixed(1) },
+ { name: '琛屾斂鍔炲叕鍖�', value: Math.floor(Math.random() * 2000) + 4000, trend: (Math.random() * 20 - 10).toFixed(1) },
+ { name: '鍚庡嫟淇濋殰鍖�', value: Math.floor(Math.random() * 1500) + 3000, trend: (Math.random() * 20 - 10).toFixed(1) }
+ ].sort((a, b) => b.value - a.value)
+
+ // 寮傚父鏁版嵁
+ abnormalCount.value = Math.floor(Math.random() * 5) + 1
+ abnormalList.value = [
+ { level: 'warning', title: '鐢靛姏璐熻嵎杩囬珮', description: '鐢熶骇杞﹂棿A鐢靛姏璐熻嵎杈惧埌85%锛屽缓璁鏌ヨ澶囪繍琛岀姸鎬�', time: '2鍒嗛挓鍓�' },
+ { level: 'error', title: '姘村帇寮傚父', description: '姘村鐞嗚澶囧帇鍔涘紓甯革紝褰撳墠鍘嬪姏0.3MPa锛屼綆浜庢甯歌寖鍥�', time: '5鍒嗛挓鍓�' }
+ ]
+
+ // 鏅鸿兘鎺у埗鏁版嵁
+ loadForecast.value = Math.floor(Math.random() * 500) + 1500
+ intelligentAdjustment.value = Math.floor(Math.random() * 30) + 60
+
+ // 鐜繚鎸囨爣
+ carbonEmission.value = Math.floor(Math.random() * 1000) + 5000
+ carbonEmissionTrend.value = (Math.random() * 15 - 7.5).toFixed(1)
+ environmentalCompliance.value = (Math.random() * 5 + 95).toFixed(1)
+ greenEnergyRatio.value = (Math.random() * 10 + 25).toFixed(1)
+
+ // 鏇存柊鏈�鍚庢洿鏂版椂闂�
+ lastUpdateTime.value = new Date().toLocaleString()
+
+ // 鍚屾椂鏇存柊鎶ヨ〃鏁版嵁
+ generateReportData()
+}
+
+// 鍒濆鍖栬秼鍔垮浘琛�
+const initTrendChart = () => {
+ if (chartInstance) {
+ chartInstance.dispose()
+ }
+
+ chartInstance = echarts.init(trendChart.value)
+
+ const option = {
+ title: {
+ text: '鑳借�楄秼鍔垮垎鏋�',
+ left: 'center'
+ },
+ tooltip: {
+ trigger: 'axis'
+ },
+ legend: {
+ data: ['鐢靛姏', '姘�', '姘斾綋'],
+ bottom: 10
+ },
+ xAxis: {
+ type: 'category',
+ data: generateTimeData()
+ },
+ yAxis: {
+ type: 'value',
+ name: '娑堣�楅噺'
+ },
+ series: [
+ {
+ name: '鐢靛姏',
+ type: 'line',
+ data: generateSeriesData(),
+ smooth: true
+ },
+ {
+ name: '姘�',
+ type: 'line',
+ data: generateSeriesData(),
+ smooth: true
+ },
+ {
+ name: '姘斾綋',
+ type: 'line',
+ data: generateSeriesData(),
+ smooth: true
+ }
+ ]
+ }
+
+ chartInstance.setOption(option)
+}
+
+// 鐢熸垚鏃堕棿鏁版嵁
+const generateTimeData = () => {
+ const data = []
+ const now = new Date()
+
+ switch (trendTimeUnit.value) {
+ case 'hour':
+ for (let i = 23; i >= 0; i--) {
+ const time = new Date(now.getTime() - i * 60 * 60 * 1000)
+ data.unshift(time.getHours() + ':00')
+ }
+ break
+ case 'day':
+ for (let i = 29; i >= 0; i--) {
+ const time = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
+ data.unshift(time.getDate() + '鏃�')
+ }
+ break
+ case 'week':
+ for (let i = 11; i >= 0; i--) {
+ data.unshift(`绗�${12 - i}鍛╜)
+ }
+ break
+ case 'month':
+ for (let i = 11; i >= 0; i--) {
+ const month = (12 - i) % 12 || 12
+ data.unshift(`${month}鏈坄)
+ }
+ break
+ case 'year':
+ for (let i = 4; i >= 0; i--) {
+ const year = new Date().getFullYear() - i
+ data.unshift(`${year}骞碻)
+ }
+ break
+ }
+
+ return data
+}
+
+// 鐢熸垚绯诲垪鏁版嵁
+const generateSeriesData = () => {
+ const data = []
+ const count = trendTimeUnit.value === 'hour' ? 24 :
+ trendTimeUnit.value === 'day' ? 30 :
+ trendTimeUnit.value === 'week' ? 12 :
+ trendTimeUnit.value === 'month' ? 12 : 5
+
+ for (let i = 0; i < count; i++) {
+ data.push(Math.floor(Math.random() * 1000) + 500)
+ }
+
+ return data
+}
+
+// 澶勭悊瓒嬪娍鏃堕棿鍙樺寲
+const handleTrendTimeChange = () => {
+ nextTick(() => {
+ initTrendChart()
+ })
+}
+
+// 澶勭悊缁熻鍛ㄦ湡鍙樺寲
+const handleStatisticsChange = () => {
+ generateMockData()
+}
+
+// 澶勭悊鎺掑悕绫诲瀷鍙樺寲
+const handleRankingChange = () => {
+ // 鏍规嵁绫诲瀷閲嶆柊鐢熸垚鎺掑悕鏁版嵁
+ generateMockData()
+}
+
+// 澶勭悊鑷姩鎺у埗鍙樺寲
+const handleAutoControlChange = (value) => {
+ ElMessage.success(`鏅鸿兘鎺у埗绯荤粺宸�${value ? '鍚敤' : '绂佺敤'}`)
+}
+
+// 澶勭悊寮傚父
+const handleAbnormal = (item) => {
+ ElMessage.info(`姝e湪澶勭悊寮傚父锛�${item.title}`)
+}
+
+// 鍒锋柊鏁版嵁
+const refreshData = () => {
+ generateMockData()
+ if (chartInstance) {
+ initTrendChart()
+ }
+ ElMessage.success('鏁版嵁宸插埛鏂�')
+}
+
+// 瀵煎嚭缁熻
+const exportStatistics = () => {
+ ElMessage.success('缁熻鏁版嵁瀵煎嚭鎴愬姛')
+}
+
+// 瀵煎嚭鐜繚鎶ュ憡
+const exportEnvironmentalReport = () => {
+ ElMessage.success('鐜繚鎶ュ憡瀵煎嚭鎴愬姛')
+}
+
+// 鐢熸垚鑷畾涔夋姤琛�
+const generateCustomReport = () => {
+ ElMessage.info('鑷畾涔夋姤琛ㄥ姛鑳藉紑鍙戜腑...')
+}
+
+// 璁㈤槄鎶ヨ〃
+const subscribeReport = () => {
+ ElMessage.info('鎶ヨ〃璁㈤槄鍔熻兘寮�鍙戜腑...')
+}
+
+// 鐢熸垚鎶ヨ〃鏁版嵁
+const generateReportData = () => {
+ // 鐢熸垚鍩虹鏁版嵁
+ reportData.value.electricity = Math.floor(Math.random() * 5000) + 8000
+ reportData.value.water = Math.floor(Math.random() * 200) + 300
+ reportData.value.gas = Math.floor(Math.random() * 100) + 150
+
+ // 鐢熸垚瓒嬪娍鏁版嵁
+ reportData.value.electricityTrend = (Math.random() * 20 - 10).toFixed(1)
+ reportData.value.waterTrend = (Math.random() * 15 - 7.5).toFixed(1)
+ reportData.value.gasTrend = (Math.random() * 12 - 6).toFixed(1)
+
+ // 璁$畻鎬昏兘鑰楀拰骞冲潎鑳借��
+ reportData.value.totalEnergy = reportData.value.electricity + reportData.value.water * 0.1 + reportData.value.gas * 0.05
+ reportData.value.averageEnergy = Math.floor(reportData.value.totalEnergy / 3)
+ reportData.value.efficiency = (Math.random() * 20 + 80).toFixed(1)
+
+ // 鐢熸垚鍥捐〃鏁版嵁
+ const labels = ['鍛ㄤ竴', '鍛ㄤ簩', '鍛ㄤ笁', '鍛ㄥ洓', '鍛ㄤ簲', '鍛ㄥ叚', '鍛ㄦ棩']
+ const colors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c', '#909399', '#9c27b0', '#ff9800']
+
+ reportData.value.chartData = labels.map((label, index) => ({
+ label,
+ value: Math.floor(Math.random() * 1000) + 500,
+ percentage: Math.floor(Math.random() * 40) + 30,
+ color: colors[index]
+ }))
+}
+
+// 鐢熸垚鎶ヨ〃
+const generateReport = () => {
+ generateReportData()
+ ElMessage.success('鎶ヨ〃鐢熸垚鎴愬姛')
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// 鍚姩瀹氭椂鏇存柊
+const startAutoUpdate = () => {
+ updateTimer = setInterval(() => {
+ generateMockData()
+ if (chartInstance) {
+ initTrendChart()
+ }
+ }, 60000) // 姣忓垎閽熸洿鏂颁竴娆�
+}
+
+// 鍋滄瀹氭椂鏇存柊
+const stopAutoUpdate = () => {
+ if (updateTimer) {
+ clearInterval(updateTimer)
+ updateTimer = null
+ }
+}
+
+// 缁勪欢鎸傝浇
+onMounted(() => {
+ generateMockData()
+ nextTick(() => {
+ initTrendChart()
+ })
+ startAutoUpdate()
+})
+
+// 缁勪欢鍗歌浇
+onUnmounted(() => {
+ stopAutoUpdate()
+ if (chartInstance) {
+ chartInstance.dispose()
+ }
+})
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+ padding: 12px;
+ background: #f5f5f5;
+ min-height: 100vh;
+}
+
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ padding: 16px;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+ h2 {
+ margin: 0;
+ color: #303133;
+ font-size: 22px;
+ }
+
+ .header-info {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+
+ .update-time {
+ color: #909399;
+ font-size: 14px;
+ }
+ }
+}
+
+.real-time-monitor {
+ margin-bottom: 12px;
+
+ .monitor-card {
+ .monitor-content {
+ text-align: center;
+ padding: 16px 0;
+
+ .monitor-value {
+ margin-bottom: 12px;
+
+ .value {
+ font-size: 28px;
+ font-weight: bold;
+ color: #409eff;
+ }
+
+ .unit {
+ font-size: 14px;
+ color: #909399;
+ margin-left: 4px;
+ }
+ }
+
+ .monitor-trend {
+ .trend-label {
+ font-size: 14px;
+ color: #606266;
+ margin-right: 6px;
+ }
+ }
+ }
+ }
+}
+
+.trend-analysis {
+ margin-bottom: 12px;
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .time-selector {
+ .el-radio-group {
+ .el-radio {
+ margin-right: 4px;
+ }
+ }
+ }
+ }
+
+ .chart-container {
+ padding: 16px 0;
+ }
+}
+
+.statistics-ranking {
+ margin-bottom: 12px;
+
+ .statistics-card, .ranking-card {
+ height: 100%;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ }
+ }
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 16px;
+ border-bottom: 1px solid #f0f0f0;
+ background: #fafafa;
+
+ .card-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .header-actions {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ }
+ }
+
+ .statistics-content {
+ padding: 16px;
+
+ .statistics-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ padding: 10px 12px;
+ background: #f8f9fa;
+ border-radius: 6px;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background: #e9ecef;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ color: #606266;
+ font-size: 14px;
+ font-weight: 500;
+ }
+
+ .value {
+ font-weight: bold;
+ font-size: 15px;
+
+ &.success {
+ color: #67c23a;
+ }
+ }
+ }
+ }
+
+ .ranking-list {
+ padding: 16px;
+
+ .ranking-item {
+ display: flex;
+ align-items: center;
+ padding: 12px;
+ margin-bottom: 6px;
+ background: #f8f9fa;
+ border-radius: 6px;
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: #e9ecef;
+ transform: translateX(4px);
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .ranking-number {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+ font-size: 14px;
+ margin-right: 12px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+ &.ranking-first {
+ background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
+ color: #fff;
+ }
+
+ &.ranking-second {
+ background: linear-gradient(135deg, #c0c0c0 0%, #d4d4d4 100%);
+ color: #fff;
+ }
+
+ &.ranking-third {
+ background: linear-gradient(135deg, #cd7f32 0%, #daa520 100%);
+ color: #fff;
+ }
+
+ &.ranking-normal {
+ background: linear-gradient(135deg, #f5f5f5 0%, #e9ecef 100%);
+ color: #909399;
+ }
+ }
+
+ .ranking-info {
+ flex: 1;
+
+ .ranking-name {
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 4px;
+ font-size: 14px;
+ }
+
+ .ranking-value {
+ color: #606266;
+ font-size: 13px;
+ font-weight: 500;
+ }
+ }
+
+ .ranking-trend {
+ margin-left: 12px;
+ }
+ }
+ }
+}
+
+.analysis-control {
+ margin-bottom: 20px;
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .abnormal-list {
+ .abnormal-item {
+ display: flex;
+ align-items: flex-start;
+ padding: 15px 0;
+ border-bottom: 1px solid #f0f0f0;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .abnormal-icon {
+ margin-right: 15px;
+ margin-top: 2px;
+ }
+
+ .abnormal-content {
+ flex: 1;
+
+ .abnormal-title {
+ font-weight: bold;
+ color: #303133;
+ margin-bottom: 5px;
+ }
+
+ .abnormal-desc {
+ color: #606266;
+ font-size: 14px;
+ margin-bottom: 5px;
+ }
+
+ .abnormal-time {
+ color: #909399;
+ font-size: 12px;
+ }
+ }
+
+ .abnormal-action {
+ margin-left: 15px;
+ }
+ }
+ }
+
+ .control-content {
+ .control-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .label {
+ color: #606266;
+ font-size: 14px;
+ }
+
+ .value {
+ font-weight: bold;
+ color: #303133;
+ }
+ }
+ }
+}
+
+.environmental-indicators {
+ margin-bottom: 20px;
+
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+ }
+
+ .indicator-item {
+ text-align: center;
+ padding: 20px 0;
+
+ .indicator-title {
+ color: #606266;
+ font-size: 14px;
+ margin-bottom: 10px;
+ }
+
+ .indicator-value {
+ font-size: 24px;
+ font-weight: bold;
+ color: #409eff;
+ margin-bottom: 10px;
+ }
+
+ .indicator-trend {
+ font-size: 12px;
+ color: #909399;
+
+ .success {
+ color: #67c23a;
+ }
+ }
+ }
+}
+
+.multi-dimensional-reports {
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+ }
+
+ .report-filters {
+ padding: 20px 0;
+ border-bottom: 1px solid #f0f0f0;
+ margin-bottom: 20px;
+ }
+
+ .report-preview {
+ .report-data {
+ padding: 20px 0;
+
+ .data-card {
+ text-align: center;
+ padding: 16px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ margin-bottom: 16px;
+
+ .data-title {
+ color: #606266;
+ font-size: 14px;
+ margin-bottom: 8px;
+ }
+
+ .data-value {
+ font-size: 20px;
+ font-weight: bold;
+ color: #303133;
+ margin-bottom: 8px;
+ }
+
+ .data-trend {
+ font-size: 12px;
+
+ .trend-up {
+ color: #f56c6c;
+ }
+
+ .trend-down {
+ color: #67c23a;
+ }
+
+ .trend-stable {
+ color: #909399;
+ }
+ }
+ }
+
+ .report-chart {
+ margin: 20px 0;
+ padding: 20px;
+ background: #f8f9fa;
+ border-radius: 8px;
+
+ .chart-title {
+ text-align: center;
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ margin-bottom: 16px;
+ }
+
+ .chart-bars {
+ display: flex;
+ justify-content: space-around;
+ align-items: flex-end;
+ height: 120px;
+
+ .chart-bar {
+ text-align: center;
+ flex: 1;
+ margin: 0 8px;
+
+ .bar-label {
+ font-size: 12px;
+ color: #606266;
+ margin-bottom: 8px;
+ }
+
+ .bar-container {
+ height: 80px;
+ background: #e9ecef;
+ border-radius: 4px;
+ position: relative;
+ margin-bottom: 8px;
+ }
+
+ .bar-fill {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ border-radius: 4px;
+ transition: height 0.3s ease;
+ }
+
+ .bar-value {
+ font-size: 12px;
+ color: #303133;
+ font-weight: 500;
+ }
+ }
+ }
+ }
+
+ .report-summary {
+ display: flex;
+ justify-content: space-around;
+ padding: 20px;
+ background: #f8f9fa;
+ border-radius: 8px;
+
+ .summary-item {
+ text-align: center;
+
+ .summary-label {
+ display: block;
+ color: #606266;
+ font-size: 14px;
+ margin-bottom: 8px;
+ }
+
+ .summary-value {
+ font-size: 18px;
+ font-weight: bold;
+ color: #303133;
+ }
+ }
+ }
+ }
+ }
+}
+
+// 閫氱敤鏍峰紡
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.success {
+ color: #67c23a;
+}
+
+.danger {
+ color: #f56c6c;
+}
+
+.warning {
+ color: #e6a23c;
+}
+
+.info {
+ color: #909399;
+}
+</style>
diff --git a/src/views/energyManagement/energyPeriodTime/index.vue b/src/views/energyManagement/energyPeriodTime/index.vue
new file mode 100644
index 0000000..0c94322
--- /dev/null
+++ b/src/views/energyManagement/energyPeriodTime/index.vue
@@ -0,0 +1,444 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <span class="search_title">鏃ユ湡锛�</span>
+ <!-- <el-time-picker
+ style="width: 240px;margin-right: 10px"
+ v-model="searchForm.startTime"
+ value-format="HH:mm:ss"
+ format="HH:mm:ss"
+ type="time"
+ placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
+ clearable
+ /> -->
+ <el-date-picker
+ v-model="searchForm.date"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ :size="size"
+ />
+ <!-- <el-time-picker
+ v-model="searchForm.timeRange"
+ is-range
+ arrow-control
+ range-separator="To"
+ start-placeholder="閫夋嫨缁撴潫鏃堕棿"
+ end-placeholder="閫夋嫨缁撴潫鏃堕棿"
+ /> -->
+ <span class="search_title">鐢典环锛堝厓/搴︼級锛�</span>
+ <el-input
+ v-model="searchForm.price"
+ style="width: 240px"
+ placeholder="璇疯緭鍏ョ數浠�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
+ >鎼滅储</el-button>
+ <el-button @click="resetFilters">閲嶇疆</el-button>
+ </div>
+ <div>
+ <el-button type="primary" @click="openForm('add')">鏂板</el-button>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <div class="table_list">
+ <PIMTable
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination"
+ ></PIMTable>
+ </div>
+ <el-dialog
+ v-model="dialogFormVisible"
+ title="鐢ㄧ數鏃舵绠$悊"
+ width="70%"
+ @close="closeDia"
+ >
+ <el-form
+ :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef"
+ >
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="鏃ユ湡锛�" prop="date">
+ <el-date-picker
+ style="width: 100%"
+ v-model="form.date"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐢典环锛堝厓/搴︼級锛�" prop="price">
+ <el-input
+ v-model="form.price"
+ placeholder="璇疯緭鍏ョ數浠�"
+ clearable
+ type="number"
+ step="0.01"
+ min="0"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="宄版锛�" prop="peak">
+ <el-input
+ v-model="form.peak"
+ placeholder="璇疯緭鍏ュ嘲娈�"
+ clearable
+ type="number"
+ step="0.01"
+ min="0"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璋锋锛�" prop="valley">
+ <el-input
+ v-model="form.valley"
+ placeholder="璇疯緭鍏ヨ胺娈�"
+ clearable
+ type="number"
+ step="0.01"
+ min="0"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="骞虫锛�" prop="flat">
+ <el-input
+ v-model="form.flat"
+ placeholder="璇疯緭鍏ュ钩娈�"
+ clearable
+ type="number"
+ step="0.01"
+ min="0"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="灏栨锛�" prop="sharp">
+ <el-input
+ v-model="form.sharp"
+ placeholder="璇疯緭鍏ュ皷娈�"
+ clearable
+ type="number"
+ step="0.01"
+ min="0"
+ />
+ </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="closeDia">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+<script setup>
+import {Search} from "@element-plus/icons-vue";
+import {onMounted, ref} from "vue";
+import {ElMessageBox} from "element-plus";
+import {getToken} from "@/utils/auth.js";
+import {periodListPage,periodDelete,periodAdd,periodUpdate} from "@/api/energyManagement/index.js";
+const { proxy } = getCurrentInstance();
+
+const data = reactive({
+ searchForm: {
+ date: "",
+ price: ""
+ },
+ form: {
+ date: "",
+ price: "",
+ peak: "",
+ valley: "",
+ flat: "",
+ sharp: ""
+ }
+});
+const { searchForm,form } = toRefs(data);
+const page = ref({
+ current: 1,
+ size: 10,
+ total: 0
+});
+const dialogFormVisible = ref(false);
+const selectedRows = ref([]);
+const operationType = ref('');
+const tableData = ref([]);
+const emit = defineEmits(['close'])
+const tableLoading = ref(false);
+const tableColumn = ref([
+ // {
+ // label: "鏃舵鍚嶇О",
+ // prop: "timeName",
+ // width: 200,
+ // },
+ {
+ label: "鏃ユ湡",
+ prop: "date",
+ width: 200,
+ },
+ {
+ label: "鐢典环锛堝厓/搴︼級",
+ prop: "price",
+ width: 200,
+ },
+ {
+ label: "宄版",
+ prop: "peak",
+ },
+ {
+ label: "璋锋",
+ prop: "valley",
+ },
+ {
+ label: "骞虫",
+ prop: "flat",
+ },
+ {
+ label: "灏栨",
+ prop: "sharp",
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: 'right',
+ operation: [
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: (row) => {
+ openForm("edit", row);
+ },
+ },
+ ],
+ },
+]);
+
+
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+};
+const formDia = ref()
+const upload = reactive({
+ // 鏄惁鏄剧ず寮瑰嚭灞傦紙瀹㈡埛瀵煎叆锛�
+ open: false,
+ // 寮瑰嚭灞傛爣棰橈紙瀹㈡埛瀵煎叆锛�
+ title: "",
+ // 鏄惁绂佺敤涓婁紶
+ isUploading: false,
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/equipmentEnergyConsumption/importData",
+ // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
+ beforeUpload: (file) => {
+ console.log('鏂囦欢鍗冲皢涓婁紶', file);
+ // 鍙互鍦ㄦ澶勫仛鏂囦欢绫诲瀷鎴栧ぇ灏忔牎楠�
+ const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
+ if (!isValid) {
+ proxy.$modal.msgError("鍙兘涓婁紶 Excel 鏂囦欢");
+ }
+ return isValid;
+ },
+ // 鏂囦欢鐘舵�佹敼鍙樻椂鐨勫洖璋�
+ onChange: (file, fileList) => {
+ console.log('鏂囦欢鐘舵�佹敼鍙�', file, fileList);
+ },
+ // 鏂囦欢涓婁紶鎴愬姛鏃剁殑鍥炶皟
+ onSuccess: (response, file, fileList) => {
+ console.log('涓婁紶鎴愬姛', response, file, fileList);
+ if(response.code === 200){
+ proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+ }else if(response.code === 500){
+ ElMessageBox.error(response.msg);
+ }else{
+ ElMessageBox.warning(response.msg);
+ }
+ },
+ // 鏂囦欢涓婁紶澶辫触鏃剁殑鍥炶皟
+ onError: (error, file, fileList) => {
+ console.error('涓婁紶澶辫触', error, file, fileList);
+ ElMessageBox.error("鏂囦欢涓婁紶澶辫触");
+ },
+ // 鏂囦欢涓婁紶杩涘害鍥炶皟
+ onProgress: (event, file, fileList) => {
+ console.log('涓婁紶涓�...', event.percent);
+ }
+});
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+//閲嶇疆
+const resetFilters = () => {
+ searchForm.value = {
+ date: "",
+ price: ""
+ };
+ getList();
+
+};
+const pagination = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+const getList = () => {
+ tableLoading.value = true;
+ periodListPage({ ...searchForm, ...page.value }).then((res) => {
+ tableLoading.value = false;
+ if (res && res.data) {
+ tableData.value = res.data.records || [];
+ page.total = res.data.total || 0;
+ } else {
+ tableData.value = [];
+ page.total = 0;
+ ElMessageBox.warning('鏈幏鍙栧埌鏁版嵁');
+ }
+ })
+ .catch((err) => {
+ tableLoading.value = false;
+ console.error('鏁版嵁鍔犺浇澶辫触:', err);
+ ElMessageBox.error('鏁版嵁鍔犺浇澶辫触锛岃閲嶈瘯');
+ });
+};
+// 鎵撳紑寮规
+const openDialog = (type, row) => {
+ operationType.value = type;
+ dialogFormVisible.value = true;
+ // form.value.maintainer = userStore.nickName;
+ // form.value.maintenanceTime = getCurrentDate();
+ form.value = {}
+ proxy.resetForm("formRef");
+ periodListPage().then((res) => {
+ codeList.value = res.data;
+ });
+ if (type === "edit") {
+ form.value = {...row}
+ }
+}
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+ openDialog(type, row)
+};
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ if (operationType.value === "add") {
+ periodAdd(form.value).then(response => {
+ proxy.$modal.msgSuccess("鏂板鎴愬姛")
+ closeDia()
+ getList()
+ })
+ } else {
+ periodUpdate(form.value).then(response => {
+ proxy.$modal.msgSuccess("淇敼鎴愬姛")
+ closeDia()
+ getList()
+ })
+ }
+ }
+ })
+}
+// 鍏抽棴寮规
+const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+ emit('close')
+};
+/** 瀵煎叆鎸夐挳鎿嶄綔 */
+function handleImport() {
+ upload.title = "璁惧鑳借��";
+ upload.open = true;
+ // 娓呯┖涓婃涓婁紶鐨勬枃浠跺垪琛�
+ nextTick(() => {
+ proxy.$refs["uploadRef"]?.clearFiles();
+ });
+}
+function importTemplate() {
+ proxy.download(
+ "/equipmentEnergyConsumption/export",
+ {},
+ '璁惧鑳借�楀鍏ユā鐗�.xlsx'
+ );
+}
+/** 鎻愪氦涓婁紶鏂囦欢 */
+function submitFileForm() {
+ proxy.$refs["uploadRef"].submit();
+}
+
+/** 寮规鍏抽棴鏃舵竻绌烘枃浠跺垪琛� */
+function handleDialogClose() {
+ nextTick(() => {
+ proxy.$refs["uploadRef"]?.clearFiles();
+ });
+}
+
+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(() => {
+ tableLoading.value = true;
+ periodDelete(ids)
+ .then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/energyManagement/energyPower/components/formDia.vue b/src/views/energyManagement/energyPower/components/formDia.vue
index 30f1c36..518a254 100644
--- a/src/views/energyManagement/energyPower/components/formDia.vue
+++ b/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}
+ // 缂栬緫鏃讹紝灏嗗崟涓狪D杞崲涓烘暟缁勬牸寮忕敤浜庡洖鏄�
+ 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()
})
diff --git a/src/views/energyManagement/gasManagement/index.vue b/src/views/energyManagement/gasManagement/index.vue
new file mode 100644
index 0000000..59ca9f7
--- /dev/null
+++ b/src/views/energyManagement/gasManagement/index.vue
@@ -0,0 +1,624 @@
+<template>
+ <div class="app-container">
+ <!-- 椤甸潰鏍囬 -->
+ <div class="page-header">
+ <h2>鐢ㄦ皵绠$悊绯荤粺</h2>
+ <div class="header-info">
+ <span class="update-time">鏈�鍚庢洿鏂帮細{{ lastUpdateTime }}</span>
+ <el-button type="primary" size="small" @click="refreshData">
+ <el-icon><Refresh /></el-icon>
+ 鍒锋柊鏁版嵁
+ </el-button>
+ </div>
+ </div>
+
+ <!-- 缁熻鍗$墖鍖哄煙 -->
+ <div class="stats-cards">
+ <el-row :gutter="20">
+ <el-col :span="6">
+ <el-card class="stat-card">
+ <div class="stat-content">
+ <div class="stat-icon gas-device">
+ <el-icon size="32"><Box /></el-icon>
+ </div>
+ <div class="stat-info">
+ <div class="stat-value">{{ totalDevices }}</div>
+ <div class="stat-label">鍦ㄧ敤璁惧</div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="stat-card">
+ <div class="stat-content">
+ <div class="stat-icon daily-consumption">
+ <el-icon size="32"><TrendCharts /></el-icon>
+ </div>
+ <div class="stat-info">
+ <div class="stat-value">{{ dailyConsumption }} m鲁</div>
+ <div class="stat-label">鏃ヨ�楅噺</div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="stat-card">
+ <div class="stat-content">
+ <div class="stat-icon monthly-consumption">
+ <el-icon size="32"><DataLine /></el-icon>
+ </div>
+ <div class="stat-info">
+ <div class="stat-value">{{ monthlyConsumption }} m鲁</div>
+ <div class="stat-label">鏈堣�楅噺</div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="stat-card">
+ <div class="stat-content">
+ <div class="stat-icon gas-price">
+ <el-icon size="32"><Money /></el-icon>
+ </div>
+ <div class="stat-info">
+ <div class="stat-value">楼{{ gasUnitPrice }}</div>
+ <div class="stat-label">姘斾綋鍗曚环</div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 璐圭敤缁熻鍖哄煙 -->
+ <div class="cost-stats">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-card class="cost-card">
+ <template #header>
+ <div class="card-header">
+ <span>鏃ヨ垂鐢ㄧ粺璁�</span>
+ <el-tag type="success" size="small">浠婃棩</el-tag>
+ </div>
+ </template>
+ <div class="cost-content">
+ <div class="cost-main">
+ <span class="cost-amount">楼{{ dailyTotalCost.toFixed(2) }}</span>
+ <span class="cost-unit">鍏�</span>
+ </div>
+ <div class="cost-details">
+ <div class="cost-item">
+ <span>娑堣�楅噺锛�</span>
+ <span>{{ dailyConsumption }} m鲁</span>
+ </div>
+ <div class="cost-item">
+ <span>鍗曚环锛�</span>
+ <span>楼{{ gasUnitPrice }}/m鲁</span>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="12">
+ <el-card class="cost-card">
+ <template #header>
+ <div class="card-header">
+ <span>鏈堣垂鐢ㄧ粺璁�</span>
+ <el-tag type="primary" size="small">鏈湀</el-tag>
+ </div>
+ </template>
+ <div class="cost-content">
+ <div class="cost-main">
+ <span class="cost-amount">楼{{ monthlyTotalCost.toFixed(2) }}</span>
+ <span class="cost-unit">鍏�</span>
+ </div>
+ <div class="cost-details">
+ <div class="cost-item">
+ <span>娑堣�楅噺锛�</span>
+ <span>{{ monthlyConsumption }} m鲁</span>
+ </div>
+ <div class="cost-item">
+ <span>骞冲潎鍗曚环锛�</span>
+ <span>楼{{ gasUnitPrice }}/m鲁</span>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 璁惧鍒楄〃鍖哄煙 -->
+ <div class="device-section">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>璁惧鐩戞帶</span>
+ <div class="header-actions">
+ <el-button type="primary" size="small" @click="addDevice">
+ <el-icon><Plus /></el-icon>
+ 娣诲姞璁惧
+ </el-button>
+ </div>
+ </div>
+ </template>
+
+ <el-table :data="deviceList" border style="width: 100%" v-loading="tableLoading" stripe>
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column label="璁惧缂栧彿" prop="deviceCode" width="120" show-overflow-tooltip />
+ <el-table-column label="璁惧鍚嶇О" prop="deviceName" width="150" show-overflow-tooltip />
+ <el-table-column label="璁惧绫诲瀷" prop="deviceType" width="120" show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="specification" width="150" show-overflow-tooltip />
+ <el-table-column label="褰撳墠鍘嬪姏(MPa)" prop="currentPressure" width="130" show-overflow-tooltip>
+ <template #default="scope">
+ <span :class="getPressureClass(scope.row.currentPressure)">
+ {{ scope.row.currentPressure }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column label="褰撳墠娓╁害(鈩�)" prop="currentTemperature" width="130" show-overflow-tooltip>
+ <template #default="scope">
+ <span :class="getTemperatureClass(scope.row.currentTemperature)">
+ {{ scope.row.currentTemperature }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column label="姘斾綋娴撳害(ppm)" prop="gasConcentration" width="140" show-overflow-tooltip>
+ <template #default="scope">
+ <span :class="getConcentrationClass(scope.row.gasConcentration)">
+ {{ scope.row.gasConcentration }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column label="杩愯鐘舵��" prop="status" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)" size="small">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏈�鍚庢洿鏂�" prop="lastUpdate" width="160" show-overflow-tooltip />
+ <el-table-column label="鎿嶄綔" align="center" width="100" fixed="right">
+ <template #default="scope">
+ <el-button link size="small" @click="editDevice(scope.row)">
+ 缂栬緫
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-card>
+ </div>
+
+ <!-- 娣诲姞/缂栬緫璁惧寮圭獥 -->
+ <el-dialog v-model="deviceDialogVisible" :title="dialogTitle" width="600px">
+ <el-form :model="deviceForm" :rules="deviceRules" ref="deviceFormRef" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="璁惧缂栧彿" prop="deviceCode">
+ <el-input v-model="deviceForm.deviceCode" placeholder="璇疯緭鍏ヨ澶囩紪鍙�" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璁惧鍚嶇О" prop="deviceName">
+ <el-input v-model="deviceForm.deviceName" placeholder="璇疯緭鍏ヨ澶囧悕绉�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="璁惧绫诲瀷" prop="deviceType">
+ <el-select v-model="deviceForm.deviceType" placeholder="璇烽�夋嫨璁惧绫诲瀷" style="width: 100%">
+ <el-option label="娑插寲姘斿偍缃�" value="娑插寲姘斿偍缃�" />
+ <el-option label="鍘嬬缉姘斿偍缃�" value="鍘嬬缉姘斿偍缃�" />
+ <el-option label="澶╃劧姘斿偍缃�" value="澶╃劧姘斿偍缃�" />
+ <el-option label="姘ф皵鍌ㄧ綈" value="姘ф皵鍌ㄧ綈" />
+ <el-option label="鍏朵粬" value="鍏朵粬" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="瑙勬牸鍨嬪彿" prop="specification">
+ <el-input v-model="deviceForm.specification" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="璁捐鍘嬪姏(MPa)" prop="designPressure">
+ <el-input-number v-model="deviceForm.designPressure" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="瀹圭Н(m鲁)" prop="volume">
+ <el-input-number v-model="deviceForm.volume" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <el-button @click="deviceDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="saveDevice">淇濆瓨</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+ Refresh,
+ Box,
+ TrendCharts,
+ DataLine,
+ Money,
+ Plus
+} from '@element-plus/icons-vue'
+
+// 鍝嶅簲寮忔暟鎹�
+const lastUpdateTime = ref('')
+const totalDevices = ref(0)
+const dailyConsumption = ref(0)
+const monthlyConsumption = ref(0)
+const gasUnitPrice = ref(0)
+const dailyTotalCost = ref(0)
+const monthlyTotalCost = ref(0)
+const deviceList = ref([])
+const tableLoading = ref(false)
+const deviceDialogVisible = ref(false)
+const dialogTitle = ref('')
+const deviceFormRef = ref()
+
+// 璁惧琛ㄥ崟鏁版嵁
+const deviceForm = reactive({
+ deviceCode: '',
+ deviceName: '',
+ deviceType: '',
+ specification: '',
+ designPressure: 0,
+ volume: 0
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const deviceRules = {
+ deviceCode: [{ required: true, message: '璇疯緭鍏ヨ澶囩紪鍙�', trigger: 'blur' }],
+ deviceName: [{ required: true, message: '璇疯緭鍏ヨ澶囧悕绉�', trigger: 'blur' }],
+ deviceType: [{ required: true, message: '璇烽�夋嫨璁惧绫诲瀷', trigger: 'change' }],
+ specification: [{ required: true, message: '璇疯緭鍏ヨ鏍煎瀷鍙�', trigger: 'blur' }],
+ designPressure: [{ required: true, message: '璇疯緭鍏ヨ璁″帇鍔�', trigger: 'blur' }],
+ volume: [{ required: true, message: '璇疯緭鍏ュ绉�', trigger: 'blur' }]
+}
+
+// 瀹氭椂鍣�
+let updateTimer = null
+
+// 妯℃嫙鏁版嵁鐢熸垚
+const generateMockData = () => {
+ // 鏇存柊缁熻鏁版嵁
+ totalDevices.value = Math.floor(Math.random() * 10) + 15 // 15-25鍙拌澶�
+ dailyConsumption.value = Math.floor(Math.random() * 100) + 200 // 200-300 m鲁
+ monthlyConsumption.value = Math.floor(Math.random() * 2000) + 5000 // 5000-7000 m鲁
+ gasUnitPrice.value = (Math.random() * 2 + 3).toFixed(2) // 3-5鍏�/m鲁
+
+ // 璁$畻璐圭敤
+ dailyTotalCost.value = dailyConsumption.value * gasUnitPrice.value
+ monthlyTotalCost.value = monthlyConsumption.value * gasUnitPrice.value
+
+ // 鏇存柊璁惧鍒楄〃鏁版嵁
+ deviceList.value = Array.from({ length: totalDevices.value }, (_, index) => ({
+ id: index + 1,
+ deviceCode: `GT${String(index + 1).padStart(3, '0')}`,
+ deviceName: `鍌ㄦ皵缃�${index + 1}`,
+ deviceType: ['娑插寲姘斿偍缃�', '鍘嬬缉姘斿偍缃�', '澶╃劧姘斿偍缃�', '姘ф皵鍌ㄧ綈'][Math.floor(Math.random() * 4)],
+ specification: `${Math.floor(Math.random() * 50) + 50}m鲁`,
+ currentPressure: (Math.random() * 2 + 0.5).toFixed(2),
+ currentTemperature: (Math.random() * 20 + 15).toFixed(1),
+ gasConcentration: (Math.random() * 10).toFixed(2),
+ status: ['running', 'stopped', 'warning', 'error'][Math.floor(Math.random() * 4)],
+ lastUpdate: new Date().toLocaleString()
+ }))
+
+ // 鏇存柊鏈�鍚庢洿鏂版椂闂�
+ lastUpdateTime.value = new Date().toLocaleString()
+}
+
+// 鑾峰彇鍘嬪姏鐘舵�佹牱寮�
+const getPressureClass = (pressure) => {
+ const p = parseFloat(pressure)
+ if (p < 0.8) return 'pressure-low'
+ if (p > 1.5) return 'pressure-high'
+ return 'pressure-normal'
+}
+
+// 鑾峰彇娓╁害鐘舵�佹牱寮�
+const getTemperatureClass = (temperature) => {
+ const t = parseFloat(temperature)
+ if (t < 10 || t > 35) return 'temperature-warning'
+ return 'temperature-normal'
+}
+
+// 鑾峰彇娴撳害鐘舵�佹牱寮�
+const getConcentrationClass = (concentration) => {
+ const c = parseFloat(concentration)
+ if (c > 5) return 'concentration-warning'
+ return 'concentration-normal'
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const statusMap = {
+ running: 'success',
+ stopped: 'info',
+ warning: 'warning',
+ error: 'danger'
+ }
+ return statusMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const statusMap = {
+ running: '杩愯涓�',
+ stopped: '宸插仠姝�',
+ warning: '璀﹀憡',
+ error: '鏁呴殰'
+ }
+ return statusMap[status] || '鏈煡'
+}
+
+// 鍒锋柊鏁版嵁
+const refreshData = () => {
+ generateMockData()
+ ElMessage.success('鏁版嵁宸插埛鏂�')
+}
+
+// 娣诲姞璁惧
+const addDevice = () => {
+ dialogTitle.value = '娣诲姞璁惧'
+ Object.keys(deviceForm).forEach(key => {
+ deviceForm[key] = key === 'designPressure' || key === 'volume' ? 0 : ''
+ })
+ deviceDialogVisible.value = true
+}
+
+// 缂栬緫璁惧
+const editDevice = (row) => {
+ dialogTitle.value = '缂栬緫璁惧'
+ Object.keys(deviceForm).forEach(key => {
+ if (row[key] !== undefined) {
+ deviceForm[key] = row[key]
+ }
+ })
+ deviceDialogVisible.value = true
+}
+
+
+
+// 淇濆瓨璁惧
+const saveDevice = () => {
+ deviceFormRef.value.validate((valid) => {
+ if (valid) {
+ ElMessage.success('淇濆瓨鎴愬姛')
+ deviceDialogVisible.value = false
+ refreshData()
+ }
+ })
+}
+
+
+
+// 鍚姩瀹氭椂鏇存柊
+const startAutoUpdate = () => {
+ updateTimer = setInterval(() => {
+ generateMockData()
+ }, 60000) // 姣忓垎閽熸洿鏂颁竴娆�
+}
+
+// 鍋滄瀹氭椂鏇存柊
+const stopAutoUpdate = () => {
+ if (updateTimer) {
+ clearInterval(updateTimer)
+ updateTimer = null
+ }
+}
+
+// 缁勪欢鎸傝浇
+onMounted(() => {
+ generateMockData()
+ startAutoUpdate()
+})
+
+// 缁勪欢鍗歌浇
+onUnmounted(() => {
+ stopAutoUpdate()
+})
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+ padding: 20px;
+ background: #f5f5f5;
+ min-height: 100vh;
+}
+
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ padding: 20px;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+
+ h2 {
+ margin: 0;
+ color: #303133;
+ font-size: 24px;
+ }
+
+ .header-info {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+
+ .update-time {
+ color: #909399;
+ font-size: 14px;
+ }
+ }
+}
+
+.stats-cards {
+ margin-bottom: 20px;
+
+ .stat-card {
+ .stat-content {
+ display: flex;
+ align-items: center;
+ padding: 10px;
+
+ .stat-icon {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 15px;
+
+ &.gas-device {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ }
+
+ &.daily-consumption {
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+ color: white;
+ }
+
+ &.monthly-consumption {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+ color: white;
+ }
+
+ &.gas-price {
+ background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
+ color: white;
+ }
+ }
+
+ .stat-info {
+ .stat-value {
+ font-size: 24px;
+ font-weight: bold;
+ color: #303133;
+ line-height: 1;
+ }
+
+ .stat-label {
+ font-size: 14px;
+ color: #909399;
+ margin-top: 5px;
+ }
+ }
+ }
+ }
+}
+
+.cost-stats {
+ margin-bottom: 20px;
+
+ .cost-card {
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .cost-content {
+ text-align: center;
+ padding: 20px 0;
+
+ .cost-main {
+ margin-bottom: 15px;
+
+ .cost-amount {
+ font-size: 36px;
+ font-weight: bold;
+ color: #409eff;
+ }
+
+ .cost-unit {
+ font-size: 16px;
+ color: #909399;
+ margin-left: 5px;
+ }
+ }
+
+ .cost-details {
+ .cost-item {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ font-size: 14px;
+ color: #606266;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+.device-section {
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+ }
+}
+
+// 鐘舵�佹牱寮�
+.pressure-low {
+ color: #e6a23c;
+ font-weight: bold;
+}
+
+.pressure-normal {
+ color: #67c23a;
+ font-weight: bold;
+}
+
+.pressure-high {
+ color: #f56c6c;
+ font-weight: bold;
+}
+
+.temperature-normal {
+ color: #67c23a;
+ font-weight: bold;
+}
+
+.temperature-warning {
+ color: #e6a23c;
+ font-weight: bold;
+}
+
+.concentration-normal {
+ color: #67c23a;
+ font-weight: bold;
+}
+
+.concentration-warning {
+ color: #f56c6c;
+ font-weight: bold;
+}
+</style>
diff --git a/src/views/energyManagement/meterCollection/index.vue b/src/views/energyManagement/meterCollection/index.vue
new file mode 100644
index 0000000..8b2636b
--- /dev/null
+++ b/src/views/energyManagement/meterCollection/index.vue
@@ -0,0 +1,556 @@
+<template>
+ <div class="app-container">
+ <el-card class="box-card">
+ <div slot="header" class="clearfix">
+ <span>鐢佃〃閲囬泦绠$悊</span>
+ <el-button style="float: right; padding: 3px 0" link @click="refreshData">
+ <i class="el-icon-refresh"></i> 鍒锋柊
+ </el-button>
+ </div>
+
+ <!-- 娴嬭瘯鎸夐挳 -->
+ <el-row :gutter="20" style="margin-bottom: 15px;">
+ <el-col :span="24">
+ <el-button @click="addTestData" type="primary" size="small">娣诲姞娴嬭瘯鏁版嵁</el-button>
+ <el-button @click="clearData" type="danger" size="small">娓呯┖鏁版嵁</el-button>
+ <el-button @click="testChart" type="success" size="small">娴嬭瘯鍥捐〃</el-button>
+ </el-col>
+ </el-row>
+
+ <!-- 鎼滅储鍖哄煙 -->
+ <el-row :gutter="20" class="search-row">
+ <el-col :span="6">
+ <el-input
+ v-model="searchForm.meterNo"
+ placeholder="璇疯緭鍏ョ數琛ㄧ紪鍙�"
+ clearable
+ @keyup.enter.native="handleSearch"
+ >
+ <i slot="prefix" class="el-input__icon el-icon-search"></i>
+ </el-input>
+ </el-col>
+ <el-col :span="6">
+ <el-select v-model="searchForm.location" placeholder="璇烽�夋嫨浣嶇疆" clearable>
+ <el-option label="鐢熶骇杞﹂棿A" value="杞﹂棿A"></el-option>
+ <el-option label="鐢熶骇杞﹂棿B" value="杞﹂棿B"></el-option>
+ <el-option label="鍔炲叕鍖哄煙" value="鍔炲叕鍖�"></el-option>
+ <el-option label="閰嶇數瀹�" value="閰嶇數瀹�"></el-option>
+ </el-select>
+ </el-col>
+ <el-col :span="6">
+ <el-date-picker
+ v-model="searchForm.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ format="yyyy-MM-dd"
+ value-format="yyyy-MM-dd"
+ />
+ </el-col>
+ <el-col :span="6">
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-col>
+ </el-row>
+
+ <!-- 鐢佃〃鍒楄〃 -->
+ <el-table
+ :data="meterList"
+ style="width: 100%"
+ v-loading="loading"
+ border
+ stripe
+ height="calc(100vh - 22em)"
+ >
+ <el-table-column prop="meterNo" label="鐢佃〃缂栧彿" width="120" />
+ <el-table-column prop="location" label="瀹夎浣嶇疆" width="120" />
+ <el-table-column prop="meterType" label="鐢佃〃绫诲瀷" width="120" />
+ <el-table-column prop="voltage" label="鐢靛帇绛夌骇" width="100" />
+ <el-table-column prop="currentReading" label="褰撳墠璇绘暟(kWh)" width="140" />
+ <el-table-column prop="lastReading" label="涓婃璇绘暟(kWh)" width="140" />
+ <el-table-column prop="consumption" label="鐢ㄧ數閲�(kWh)" width="120" />
+ <el-table-column prop="power" label="鍔熺巼(kW)" width="100" />
+ <el-table-column prop="powerFactor" label="鍔熺巼鍥犳暟" width="100" />
+ <el-table-column prop="status" label="鐘舵��" width="80">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === '姝e父' ? 'success' : 'danger'">
+ {{ scope.row.status }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="lastUpdateTime" label="鏈�鍚庢洿鏂版椂闂�" width="160" />
+ <el-table-column label="鎿嶄綔" width="180" fixed="right" align="center">
+ <template #default="scope">
+ <el-button link @click="viewDetails(scope.row)">
+ 鏌ョ湅璇︽儏
+ </el-button>
+ <el-button link @click="manualCollection(scope.row)">
+ 鎵嬪姩閲囬泦
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <!-- 鍒嗛〉 -->
+ <pagination
+ :total="pagination.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="pagination.currentPage"
+ :limit="pagination.pageSize"
+ @pagination="handleCurrentChange"
+ />
+ </el-card>
+
+ <!-- 璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ title="鐢佃〃璇︽儏"
+ v-model="detailDialogVisible"
+ width="60%"
+ @opened="onDialogOpened"
+ >
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鐢佃〃缂栧彿:</label>
+ <span>{{ currentMeter.meterNo }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>瀹夎浣嶇疆:</label>
+ <span>{{ currentMeter.location }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鐢佃〃绫诲瀷:</label>
+ <span>{{ currentMeter.meterType }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鐢靛帇绛夌骇:</label>
+ <span>{{ currentMeter.voltage }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>褰撳墠璇绘暟:</label>
+ <span>{{ currentMeter.currentReading }} kWh</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>涓婃璇绘暟:</label>
+ <span>{{ currentMeter.lastReading }} kWh</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鐢ㄧ數閲�:</label>
+ <span>{{ currentMeter.consumption }} kWh</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鍔熺巼:</label>
+ <span>{{ currentMeter.power }} kW</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鍔熺巼鍥犳暟:</label>
+ <span>{{ currentMeter.powerFactor }}</span>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鐘舵��:</label>
+ <el-tag :type="currentMeter.status === '姝e父' ? 'success' : 'danger'">
+ {{ currentMeter.status }}
+ </el-tag>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="detail-item">
+ <label>鏈�鍚庢洿鏂版椂闂�:</label>
+ <span>{{ currentMeter.lastUpdateTime }}</span>
+ </div>
+ </el-col>
+ </el-row>
+
+ <!-- 鐢ㄧ數瓒嬪娍鍥� -->
+ <div style="margin-top: 20px;">
+ <h4>24灏忔椂鐢ㄧ數瓒嬪娍</h4>
+ <div ref="chartContainer" style="height: 300px;"></div>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+
+export default {
+ name: 'MeterCollection',
+ data() {
+ return {
+ loading: false,
+ searchForm: {
+ meterNo: '',
+ location: '',
+ dateRange: []
+ },
+ meterList: [],
+ pagination: {
+ currentPage: 1,
+ pageSize: 10,
+ total: 0
+ },
+ detailDialogVisible: false,
+ currentMeter: {},
+ chart: null
+ }
+ },
+ created() {
+ // 绔嬪嵆鐢熸垚涓�浜涙祴璇曟暟鎹�
+ this.meterList = [
+ {
+ id: 1,
+ meterNo: 'M001',
+ location: '杞﹂棿A',
+ meterType: '鏅鸿兘鐢佃〃',
+ voltage: '380V',
+ currentReading: 8500,
+ lastReading: 8400,
+ consumption: 100,
+ power: '75.5',
+ powerFactor: '0.85',
+ status: '姝e父',
+ lastUpdateTime: '2024-01-15 10:30:00'
+ },
+ {
+ id: 2,
+ meterNo: 'M002',
+ location: '杞﹂棿B',
+ meterType: '澶氬姛鑳界數琛�',
+ voltage: '220V',
+ currentReading: 6200,
+ lastReading: 6100,
+ consumption: 100,
+ power: '45.2',
+ powerFactor: '0.92',
+ status: '姝e父',
+ lastUpdateTime: '2024-01-15 10:25:00'
+ }
+ ]
+ this.pagination.total = this.meterList.length
+ },
+ mounted() {
+ // 寤惰繜涓�鐐规椂闂村啀璋冪敤锛岀‘淇滵OM宸茬粡娓叉煋
+ this.$nextTick(() => {
+ this.getMeterList()
+ })
+ },
+ watch: {
+ meterList: {
+ handler(newVal) {
+ console.log('meterList鏁版嵁鍙樺寲:', newVal)
+ },
+ deep: true,
+ immediate: true
+ }
+ },
+ methods: {
+ // 鑾峰彇鐢佃〃鍒楄〃
+ getMeterList() {
+ this.loading = true
+ // 妯℃嫙API璋冪敤
+ setTimeout(() => {
+ const mockData = this.generateMockData()
+ this.meterList = mockData
+ this.pagination.total = this.meterList.length
+ this.loading = false
+ }, 500)
+ },
+
+ // 鐢熸垚妯℃嫙鏁版嵁
+ generateMockData() {
+ const locations = ['杞﹂棿A', '杞﹂棿B', '鍔炲叕鍖�', '閰嶇數瀹�']
+ const meterTypes = ['鏅鸿兘鐢佃〃', '澶氬姛鑳界數琛�', '鏅�氱數琛�']
+ const voltages = ['220V', '380V', '10kV']
+ const statuses = ['姝e父', '寮傚父']
+
+ const data = []
+ for (let i = 1; i <= 25; i++) {
+ const currentReading = Math.floor(Math.random() * 10000) + 5000
+ const lastReading = currentReading - Math.floor(Math.random() * 100) - 10
+ const consumption = currentReading - lastReading
+ const power = Math.random() * 100 + 20
+ const powerFactor = (Math.random() * 0.3 + 0.7).toFixed(2)
+
+ data.push({
+ id: i,
+ meterNo: `M${String(i).padStart(3, '0')}`,
+ location: locations[Math.floor(Math.random() * locations.length)],
+ meterType: meterTypes[Math.floor(Math.random() * meterTypes.length)],
+ voltage: voltages[Math.floor(Math.random() * voltages.length)],
+ currentReading: currentReading,
+ lastReading: lastReading,
+ consumption: consumption,
+ power: power.toFixed(2),
+ powerFactor: powerFactor,
+ status: statuses[Math.floor(Math.random() * statuses.length)],
+ lastUpdateTime: this.formatDate(new Date(Date.now() - Math.random() * 86400000))
+ })
+ }
+ return data
+ },
+
+ // 鏍煎紡鍖栨棩鏈�
+ formatDate(date) {
+ const year = date.getFullYear()
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hours = String(date.getHours()).padStart(2, '0')
+ const minutes = String(date.getMinutes()).padStart(2, '0')
+ return `${year}-${month}-${day} ${hours}:${minutes}`
+ },
+
+ // 鎼滅储
+ handleSearch() {
+ this.pagination.currentPage = 1
+ this.getMeterList()
+ },
+
+ // 閲嶇疆鎼滅储
+ resetSearch() {
+ this.searchForm = {
+ meterNo: '',
+ location: '',
+ dateRange: []
+ }
+ this.handleSearch()
+ },
+
+ // 鏌ョ湅璇︽儏
+ viewDetails(row) {
+ this.currentMeter = row
+ this.detailDialogVisible = true
+ },
+
+ // 瀵硅瘽妗嗘墦寮�鍚庡垵濮嬪寲鍥捐〃
+ onDialogOpened() {
+ this.$nextTick(() => {
+ setTimeout(() => {
+ this.initChart()
+ }, 100)
+ })
+ },
+
+ // 鎵嬪姩閲囬泦
+ manualCollection(row) {
+ this.$message.success(`姝e湪閲囬泦鐢佃〃 ${row.meterNo} 鐨勬暟鎹�...`)
+ // 妯℃嫙閲囬泦杩囩▼
+ setTimeout(() => {
+ row.currentReading = Math.floor(Math.random() * 100) + row.currentReading
+ row.lastUpdateTime = this.formatDate(new Date())
+ this.$message.success('鏁版嵁閲囬泦瀹屾垚')
+ }, 1000)
+ },
+
+ // 鍒锋柊鏁版嵁
+ refreshData() {
+ this.getMeterList()
+ this.$message.success('鏁版嵁宸插埛鏂�')
+ },
+
+ // 娣诲姞娴嬭瘯鏁版嵁
+ addTestData() {
+ const testData = {
+ id: Date.now(),
+ meterNo: `M${String(this.meterList.length + 1).padStart(3, '0')}`,
+ location: '娴嬭瘯浣嶇疆',
+ meterType: '娴嬭瘯鐢佃〃',
+ voltage: '220V',
+ currentReading: Math.floor(Math.random() * 10000) + 1000,
+ lastReading: Math.floor(Math.random() * 5000) + 500,
+ consumption: Math.floor(Math.random() * 100) + 10,
+ power: (Math.random() * 100 + 10).toFixed(2),
+ powerFactor: (Math.random() * 0.3 + 0.7).toFixed(2),
+ status: '姝e父',
+ lastUpdateTime: this.formatDate(new Date())
+ }
+ this.meterList.push(testData)
+ this.pagination.total = this.meterList.length
+ this.$message.success('娴嬭瘯鏁版嵁宸叉坊鍔�')
+ },
+
+ // 娓呯┖鏁版嵁
+ clearData() {
+ this.meterList = []
+ this.pagination.total = 0
+ this.$message.success('鏁版嵁宸叉竻绌�')
+ },
+
+ // 娴嬭瘯鍥捐〃
+ testChart() {
+ this.$message.info('鍥捐〃娴嬭瘯鍔熻兘')
+ // 鍒涘缓涓�涓祴璇曞璇濇鏉ユ祴璇曞浘琛�
+ this.currentMeter = {
+ meterNo: 'TEST001',
+ location: '娴嬭瘯浣嶇疆',
+ meterType: '娴嬭瘯鐢佃〃',
+ voltage: '220V',
+ currentReading: 1000,
+ lastReading: 900,
+ consumption: 100,
+ power: '50.0',
+ powerFactor: '0.85',
+ status: '姝e父',
+ lastUpdateTime: '2024-01-15 12:00:00'
+ }
+ this.detailDialogVisible = true
+ },
+
+ // 鍒嗛〉澶у皬鏀瑰彉
+ handleSizeChange(val) {
+ this.pagination.pageSize = val
+ this.getMeterList()
+ },
+
+ // 褰撳墠椤垫敼鍙�
+ handleCurrentChange(val) {
+ this.pagination.pageSize = val.limit
+ this.pagination.currentPage = val.page
+ this.getMeterList()
+ },
+
+ // 鍒濆鍖栧浘琛�
+ initChart() {
+ try {
+ if (this.chart) {
+ this.chart.dispose()
+ this.chart = null
+ }
+
+ // 纭繚DOM鍏冪礌瀛樺湪
+ if (!this.$refs.chartContainer) {
+ console.error('鍥捐〃瀹瑰櫒涓嶅瓨鍦紝绛夊緟DOM鏇存柊...')
+ // 濡傛灉瀹瑰櫒涓嶅瓨鍦紝绛夊緟涓�涓嬪啀璇�
+ setTimeout(() => {
+ this.initChart()
+ }, 100)
+ return
+ }
+
+ // 妫�鏌ュ鍣ㄥ昂瀵�
+ const container = this.$refs.chartContainer
+ if (container.offsetWidth === 0 || container.offsetHeight === 0) {
+ setTimeout(() => {
+ this.initChart()
+ }, 100)
+ return
+ }
+ this.chart = echarts.init(container)
+
+ // 鐢熸垚24灏忔椂妯℃嫙鏁版嵁
+ const hours = []
+ const consumption = []
+ for (let i = 0; i < 24; i++) {
+ hours.push(`${i}:00`)
+ consumption.push(Math.floor(Math.random() * 50) + 20)
+ }
+
+ const option = {
+ title: {
+ text: '24灏忔椂鐢ㄧ數閲忚秼鍔�',
+ left: 'center'
+ },
+ tooltip: {
+ trigger: 'axis',
+ formatter: '{b}<br/>鐢ㄧ數閲�: {c} kWh'
+ },
+ xAxis: {
+ type: 'category',
+ data: hours,
+ axisLabel: {
+ rotate: 45
+ }
+ },
+ yAxis: {
+ type: 'value',
+ name: '鐢ㄧ數閲� (kWh)'
+ },
+ series: [{
+ data: consumption,
+ type: 'line',
+ smooth: true,
+ areaStyle: {
+ opacity: 0.3
+ },
+ itemStyle: {
+ color: '#409EFF'
+ }
+ }]
+ }
+
+ this.chart.setOption(option)
+ } catch (error) {
+ console.error('鍥捐〃鍒濆鍖栧け璐�:', error)
+ this.$message.error('鍥捐〃鍒濆鍖栧け璐�: ' + error.message)
+ }
+ }
+ },
+
+ beforeUnmount() {
+ if (this.chart) {
+ try {
+ this.chart.dispose()
+ this.chart = null
+ } catch (error) {
+ console.error('娓呯悊鍥捐〃澶辫触:', error)
+ }
+ }
+ }
+}
+</script>
+
+<style scoped>
+.search-row {
+ margin-bottom: 20px;
+}
+
+.pagination {
+ margin-top: 20px;
+ text-align: right;
+}
+
+.el-table {
+ margin-top: 20px;
+}
+
+.detail-item {
+ margin-bottom: 15px;
+ padding: 10px;
+ border: 1px solid #ebeef5;
+ border-radius: 4px;
+ background-color: #fafafa;
+}
+
+.detail-item label {
+ font-weight: bold;
+ color: #606266;
+ margin-right: 10px;
+ min-width: 100px;
+ display: inline-block;
+}
+
+.detail-item span {
+ color: #303133;
+}
+
+.detail-item .el-tag {
+ margin-left: 0;
+}
+</style>
diff --git a/src/views/energyManagement/waterManagement/components/formDia.vue b/src/views/energyManagement/waterManagement/components/formDia.vue
index a692b95..2e58ea0 100644
--- a/src/views/energyManagement/waterManagement/components/formDia.vue
+++ b/src/views/energyManagement/waterManagement/components/formDia.vue
@@ -15,9 +15,9 @@
>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="璁惧锛�" prop="code">
+ <el-form-item label="璁惧锛�" prop="deviceModel">
<el-select
- v-model="form.code"
+ v-model="form.deviceModel"
placeholder="璇烽�夋嫨"
clearable
@change="setName"
@@ -35,9 +35,9 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="姣忔棩闄愬埗姘撮噺锛�" prop="everyNum">
+ <el-form-item label="姣忔棩闄愬埗姘撮噺锛�" prop="waterDayLimit">
<el-input
- v-model="form.everyNum"
+ v-model="form.waterDayLimit"
placeholder="璇疯緭鍏�"
clearable
/>
@@ -46,18 +46,18 @@
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="棰濆畾娴侀噺锛�" prop="flowRating">
+ <el-form-item label="棰濆畾娴侀噺锛�" prop="ratedRate">
<el-input
- v-model="form.flowRating"
+ v-model="form.ratedRate"
placeholder="璇疯緭鍏�"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="瀹為檯娴侀噺锛�" prop="flowActual">
+ <el-form-item label="瀹為檯娴侀噺锛�" prop="actualTraffic">
<el-input
- v-model="form.flowActual"
+ v-model="form.actualTraffic"
placeholder="璇疯緭鍏�"
clearable
/>
@@ -66,10 +66,10 @@
</el-row>
<el-row :gutter="30">
<el-col :span="12">
- <el-form-item label="杩愯鏃堕棿锛�" prop="runDate">
+ <el-form-item label="杩愯鏃堕棿锛�" prop="runTime">
<el-date-picker
style="width: 100%"
- v-model="form.runDate"
+ v-model="form.runTime"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
@@ -79,9 +79,9 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="褰撴棩鐢ㄦ按閲忥細" prop="dayNum">
+ <el-form-item label="褰撴棩鐢ㄦ按閲忥細" prop="waterDay">
<el-input
- v-model="form.dayNum"
+ v-model="form.waterDay"
placeholder="璇疯緭鍏�"
clearable
/>
@@ -99,9 +99,9 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="鐢ㄦ按绫诲瀷锛�" prop="waterType">
+ <el-form-item label="鐢ㄦ按绫诲瀷锛�" prop="type">
<el-select
- v-model="form.waterType"
+ v-model="form.type"
placeholder="璇烽�夋嫨"
clearable
>
@@ -136,25 +136,25 @@
const data = reactive({
form: {
- name: "",
- code: "",
- everyNum: "",
- flowRating: "",
- flowActual: "",
- runDate: "",
- dayNum: "",
+ deviceName: "",
+ deviceModel: "",
+ waterDayLimit: "",
+ ratedRate: "",
+ actualTraffic: "",
+ runTime: "",
+ waterDay: "",
waterPrice: "",
- waterType: "",
+ type: "",
},
rules: {
- code: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- runDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
- everyNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- flowRating: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- flowActual: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- dayNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ deviceModel: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ runTime: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ waterDayLimit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ ratedRate: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ actualTraffic: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ waterDay: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
waterPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- waterType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ type: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
},
})
const { form, rules } = toRefs(data);
@@ -166,8 +166,8 @@
dialogFormVisible.value = true;
form.value = {}
proxy.resetForm("formRef");
- waterDeviceList().then((res) => {
- codeList.value = res.data;
+ waterDeviceList({size: -1}).then((res) => {
+ codeList.value = res.data.records;
});
if (type === "edit") {
form.value = {...row}
diff --git a/src/views/energyManagement/waterManagement/index.vue b/src/views/energyManagement/waterManagement/index.vue
index 848a945..181ae88 100644
--- a/src/views/energyManagement/waterManagement/index.vue
+++ b/src/views/energyManagement/waterManagement/index.vue
@@ -4,7 +4,7 @@
<div>
<span class="search_title">璁惧鍚嶇О锛�</span>
<el-input
- v-model="searchForm.name"
+ v-model="searchForm.deviceName"
style="width: 240px"
placeholder="璇疯緭鍏�"
@change="handleQuery"
@@ -101,35 +101,35 @@
const tableColumn = ref([
{
label: "璁惧鍚嶇О",
- prop: "name",
+ prop: "deviceName",
width: 200,
},
{
label: "瑙勬牸鍨嬪彿",
- prop: "code",
+ prop: "deviceModel",
width: 200,
},
{
label: "棰濆畾娴侀噺",
- prop: "flowRating",
+ prop: "ratedRate",
},
{
label: "瀹為檯娴侀噺",
- prop: "flowActual",
+ prop: "actualTraffic",
},
{
label: "杩愯鏃堕棿",
- prop: "runDate",
+ prop: "runTime",
width:150
},
{
label: "褰撴棩鐢ㄦ按閲�",
- prop: "dayNum",
+ prop: "waterDay",
width: 150,
},
{
label: "姣忔棩闄愬埗姘撮噺",
- prop: "everyNum",
+ prop: "waterDayLimit",
width:220
},
{
@@ -175,7 +175,7 @@
// 璁剧疆涓婁紶鐨勮姹傚ご閮�
headers: { Authorization: "Bearer " + getToken() },
// 涓婁紶鐨勫湴鍧�
- url: import.meta.env.VITE_APP_BASE_API + "/waterEquipmentConsumption/importData",
+ url: import.meta.env.VITE_APP_BASE_API + "/waterRecord/importData",
// 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
beforeUpload: (file) => {
console.log('鏂囦欢鍗冲皢涓婁紶', file);
@@ -257,7 +257,7 @@
}
function importTemplate() {
proxy.download(
- "/waterEquipmentConsumption/export",
+ "/waterRecord/export",
{},
'鐢ㄦ按璁惧瀵煎叆妯$増.xlsx'
);
diff --git a/src/views/equipmentManagement/iotMonitor/index.vue b/src/views/equipmentManagement/iotMonitor/index.vue
new file mode 100644
index 0000000..b0cd818
--- /dev/null
+++ b/src/views/equipmentManagement/iotMonitor/index.vue
@@ -0,0 +1,317 @@
+<template>
+ <div class="app-container iot-monitor">
+ <div class="header">
+ <div class="title">瀹炴椂宸ュ喌鐩戞帶锛圛oT锛�</div>
+ <div class="actions">
+ <el-button type="primary" @click="toggleCollecting">{{ collecting ? '鏆傚仠閲囬泦' : '鍚姩閲囬泦' }}</el-button>
+ <el-button @click="resetAll">閲嶇疆</el-button>
+ <span class="ts">涓婃鏇存柊鏃堕棿锛歿{ lastUpdatedDisplay }}</span>
+ </div>
+ </div>
+
+<!-- <el-alert-->
+<!-- title="杈圭紭棰勮瑙勫垯锛氳酱鎵跨(鎹�-鎸姩鍊煎亸绂诲熀绾柯�5%瑙﹀彂鍛婅锛涙俯搴�/鍘嬪姏瓒婄晫瑙﹀彂鎻愰啋"-->
+<!-- type="info"-->
+<!-- :closable="false"-->
+<!-- show-icon-->
+<!-- class="rule-alert"-->
+<!-- />-->
+
+ <el-row :gutter="16">
+ <el-col v-for="dev in devices" :key="dev.id" :span="12">
+ <el-card :class="['device-card', dev.hasAlert ? 'is-alert' : '']">
+ <template #header>
+ <div class="card-header">
+ <div class="card-title">
+ <span class="device-name">{{ dev.name }}</span>
+ <el-tag :type="dev.hasAlert ? 'danger' : 'success'" size="small">{{ dev.hasAlert ? '鍛婅' : '姝e父' }}</el-tag>
+ </div>
+ <div class="meta">绫诲瀷锛歿{ dev.type }}锝滃熀绾挎尟鍔細{{ dev.baseline.vibration.toFixed(2) }} mm/s</div>
+ </div>
+ </template>
+
+ <div class="metrics">
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.vibration }">
+ <div class="metric-head">
+ <span>鎸姩(mm/s)</span>
+ <el-tag :type="dev.alerts.vibration ? 'danger' : 'info'" size="small">{{ dev.alerts.vibration ? '卤5%瓒婄晫' : '鍩虹嚎卤5%' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.vibration).toFixed(2) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: 'mm/s' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.vibration }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#409EFF']"
+ />
+ </div>
+
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.temperature }">
+ <div class="metric-head">
+ <span>娓╁害(掳C)</span>
+ <el-tag :type="dev.alerts.temperature ? 'warning' : 'info'" size="small">{{ dev.alerts.temperature ? '瓒婄晫' : '20~80' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.temperature).toFixed(1) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: '掳C' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.temperature }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#E6A23C']"
+ />
+ </div>
+
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.pressure }">
+ <div class="metric-head">
+ <span>鍘嬪姏(MPa)</span>
+ <el-tag :type="dev.alerts.pressure ? 'warning' : 'info'" size="small">{{ dev.alerts.pressure ? '瓒婄晫' : '0.2~1.5' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.pressure).toFixed(2) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: 'MPa' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.pressure }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#67C23A']"
+ />
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ElNotification } from 'element-plus'
+import Echarts from '@/components/Echarts/echarts.vue'
+
+defineOptions({ name: 'IoTMonitor' })
+
+const windowSize = 30
+const collecting = ref(true)
+const lastUpdated = ref(Date.now())
+const lastUpdatedDisplay = computed(() => new Date(lastUpdated.value).toLocaleTimeString())
+
+const xAxisLabels = ref(Array.from({ length: windowSize }, (_, i) => i - (windowSize - 1)).map(n => `${n}s`))
+
+function makeSeries(fill, decimals = 2) {
+ return Array.from({ length: windowSize }, () => Number(fill.toFixed(decimals)))
+}
+
+const devices = reactive([
+ {
+ id: 'water-pump',
+ name: '娉ㄦ按娉�1',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 9 },
+ initial: { temperature: 40, pressure: 0.70 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(9),
+ temperature: makeSeries(40, 1),
+ pressure: makeSeries(0.7, 2),
+ },
+ },
+ {
+ id: 'fluid-supply-truck',
+ name: '娉ㄦ按娉�2',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 7 },
+ initial: { temperature: 30, pressure: 0.60 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(7),
+ temperature: makeSeries(30, 1),
+ pressure: makeSeries(0.6, 2),
+ },
+ },
+ {
+ id: 'fracturing-truck',
+ name: '娉ㄦ按娉�3',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 12 },
+ initial: { temperature: 65, pressure: 1.40 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(12),
+ temperature: makeSeries(65, 1),
+ pressure: makeSeries(1.4, 2),
+ },
+ },
+ {
+ id: 'oil-tank-truck',
+ name: '娉ㄦ按娉�4',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 6 },
+ initial: { temperature: 28, pressure: 0.50 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(6),
+ temperature: makeSeries(28, 1),
+ pressure: makeSeries(0.5, 2),
+ },
+ },
+])
+
+function currentValue(arr) {
+ return arr[arr.length - 1] ?? 0
+}
+
+function pushWindow(arr, val) {
+ if (arr.length >= windowSize) arr.shift()
+ arr.push(val)
+}
+
+function clamp(val, min, max) { return Math.max(min, Math.min(max, val)) }
+
+function tickDevice(dev) {
+ const vibBase = dev.baseline.vibration
+ // 鎸姩锛氬熀绾柯�2%闅忔満娉㈠姩锛�5%姒傜巼瑙﹀彂8%~12%灏栧嘲妯℃嫙鍛婅
+ const spike = Math.random() < 0.05
+ const vibNoise = vibBase * (spike ? (1 + (Math.random() * 0.08 + 0.04) * (Math.random() < 0.5 ? -1 : 1)) : (1 + (Math.random() - 0.5) * 0.04))
+ const vibVal = Number(vibNoise.toFixed(2))
+ pushWindow(dev.series.vibration, vibVal)
+
+ // 娓╁害锛氱紦鎱㈤殢鏈烘父璧帮紝骞舵坊鍔犲伓鍙戦珮娓╁亸绉�
+ const tPrev = currentValue(dev.series.temperature)
+ const tDrift = tPrev + (Math.random() - 0.5) * 0.8 + (Math.random() < 0.02 ? 6 : 0)
+ const tVal = Number(clamp(tDrift, 15, 95).toFixed(1))
+ pushWindow(dev.series.temperature, tVal)
+
+ // 鍘嬪姏锛氬皬骞呮尝鍔紝鍋跺彂浣庡帇/楂樺帇
+ const pPrev = currentValue(dev.series.pressure)
+ const pDrift = pPrev + (Math.random() - 0.5) * 0.05 + (Math.random() < 0.02 ? (Math.random() < 0.5 ? -0.3 : 0.3) : 0)
+ const pVal = Number(clamp(pDrift, 0.05, 2.0).toFixed(2))
+ pushWindow(dev.series.pressure, pVal)
+
+ // 杈圭紭璁$畻闃堝�煎垽鏂�
+ const vibDelta = Math.abs(vibVal - vibBase) / vibBase
+ const vibAlert = vibDelta > 0.05
+ const tAlert = tVal < 20 || tVal > 80
+ const pAlert = pVal < 0.2 || pVal > 1.5
+
+ const prevHasAlert = dev.hasAlert
+ dev.alerts.vibration = vibAlert
+ dev.alerts.temperature = tAlert
+ dev.alerts.pressure = pAlert
+ dev.hasAlert = vibAlert || tAlert || pAlert
+
+ if (dev.hasAlert && !prevHasAlert) {
+ const reasons = []
+ if (vibAlert) reasons.push(`鎸姩鍋忕卤5% (褰撳墠 ${vibVal} / 鍩虹嚎 ${vibBase})`)
+ if (tAlert) reasons.push(`娓╁害瓒婄晫 (褰撳墠 ${tVal}掳C, 鏈熸湜 20~80掳C) `)
+ if (pAlert) reasons.push(`鍘嬪姏瓒婄晫 (褰撳墠 ${pVal}MPa, 鏈熸湜 0.2~1.5MPa) `)
+ ElNotification({
+ title: `${dev.name} 鍛婅`,
+ message: reasons.join('锛�'),
+ type: vibAlert ? 'error' : 'warning',
+ duration: 5000,
+ })
+ }
+}
+
+let timer = null
+function start() {
+ if (timer) return
+ timer = setInterval(() => {
+ if (!collecting.value) return
+ devices.forEach(tickDevice)
+ lastUpdated.value = Date.now()
+ }, 10000)
+}
+
+function stop() {
+ if (timer) {
+ clearInterval(timer)
+ timer = null
+ }
+}
+
+function toggleCollecting() { collecting.value = !collecting.value }
+
+function resetAll() {
+ devices.forEach(dev => {
+ dev.series.vibration = makeSeries(dev.baseline.vibration)
+ const t0 = dev.initial?.temperature ?? 45
+ const p0 = dev.initial?.pressure ?? 0.8
+ dev.series.temperature = makeSeries(t0, 1)
+ dev.series.pressure = makeSeries(p0, 2)
+ dev.alerts.vibration = false
+ dev.alerts.temperature = false
+ dev.alerts.pressure = false
+ dev.hasAlert = false
+ })
+ lastUpdated.value = Date.now()
+}
+
+onMounted(() => {
+ start()
+})
+
+onBeforeUnmount(() => {
+ stop()
+})
+</script>
+
+<style lang="scss" scoped>
+.iot-monitor {
+ .header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ .title { font-size: 18px; font-weight: 600; }
+ .actions { display: flex; align-items: center; gap: 8px; }
+ .ts { color: #909399; font-size: 12px; }
+ }
+ .rule-alert { margin-bottom: 12px; }
+}
+
+.device-card {
+ margin-bottom: 16px;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+ &.is-alert { border-color: #F56C6C; box-shadow: 0 0 0 2px rgba(245,108,108,0.2) inset; }
+ .card-header {
+ display: flex; flex-direction: column; gap: 4px;
+ .card-title { display: flex; align-items: center; gap: 8px; font-weight: 600; }
+ .meta { color: #909399; font-size: 12px; }
+ }
+ .metrics {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 12px;
+ }
+}
+
+.metric {
+ border: 1px solid #ebeef5;
+ border-radius: 6px;
+ padding: 8px 8px 0 8px;
+ &-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; font-size: 13px; color: #606266; }
+ &-value { font-size: 20px; font-weight: 600; margin: 2px 0 6px 0; }
+}
+
+.metric-alert {
+ border-color: #F56C6C;
+ background: #FFF6F6;
+}
+
+@media (min-width: 1200px) {
+ .device-card .metrics { grid-template-columns: 1fr 1fr 1fr; }
+}
+</style>
+
+
diff --git a/src/views/equipmentManagement/iotMonitor/indexWD.vue b/src/views/equipmentManagement/iotMonitor/indexWD.vue
new file mode 100644
index 0000000..40a2eb0
--- /dev/null
+++ b/src/views/equipmentManagement/iotMonitor/indexWD.vue
@@ -0,0 +1,317 @@
+<template>
+ <div class="app-container iot-monitor">
+ <div class="header">
+ <div class="title">瀹炴椂宸ュ喌鐩戞帶锛圛oT锛�</div>
+ <div class="actions">
+ <el-button type="primary" @click="toggleCollecting">{{ collecting ? '鏆傚仠閲囬泦' : '鍚姩閲囬泦' }}</el-button>
+ <el-button @click="resetAll">閲嶇疆</el-button>
+ <span class="ts">涓婃鏇存柊鏃堕棿锛歿{ lastUpdatedDisplay }}</span>
+ </div>
+ </div>
+
+<!-- <el-alert-->
+<!-- title="杈圭紭棰勮瑙勫垯锛氳酱鎵跨(鎹�-鎸姩鍊煎亸绂诲熀绾柯�5%瑙﹀彂鍛婅锛涙俯搴�/鍘嬪姏瓒婄晫瑙﹀彂鎻愰啋"-->
+<!-- type="info"-->
+<!-- :closable="false"-->
+<!-- show-icon-->
+<!-- class="rule-alert"-->
+<!-- />-->
+
+ <el-row :gutter="16">
+ <el-col v-for="dev in devices" :key="dev.id" :span="12">
+ <el-card :class="['device-card', dev.hasAlert ? 'is-alert' : '']">
+ <template #header>
+ <div class="card-header">
+ <div class="card-title">
+ <span class="device-name">{{ dev.name }}</span>
+ <el-tag :type="dev.hasAlert ? 'danger' : 'success'" size="small">{{ dev.hasAlert ? '鍛婅' : '姝e父' }}</el-tag>
+ </div>
+ <div class="meta">绫诲瀷锛歿{ dev.type }}锝滃熀绾挎尟鍔細{{ dev.baseline.vibration.toFixed(2) }} mm/s</div>
+ </div>
+ </template>
+
+ <div class="metrics">
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.vibration }">
+ <div class="metric-head">
+ <span>鎸姩(mm/s)</span>
+ <el-tag :type="dev.alerts.vibration ? 'danger' : 'info'" size="small">{{ dev.alerts.vibration ? '卤5%瓒婄晫' : '鍩虹嚎卤5%' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.vibration).toFixed(2) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: 'mm/s' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.vibration }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#409EFF']"
+ />
+ </div>
+
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.temperature }">
+ <div class="metric-head">
+ <span>娓╁害(掳C)</span>
+ <el-tag :type="dev.alerts.temperature ? 'warning' : 'info'" size="small">{{ dev.alerts.temperature ? '瓒婄晫' : '20~80' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.temperature).toFixed(1) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: '掳C' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.temperature }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#E6A23C']"
+ />
+ </div>
+
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.pressure }">
+ <div class="metric-head">
+ <span>鍘嬪姏(MPa)</span>
+ <el-tag :type="dev.alerts.pressure ? 'warning' : 'info'" size="small">{{ dev.alerts.pressure ? '瓒婄晫' : '0.2~1.5' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.pressure).toFixed(2) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: 'MPa' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.pressure }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#67C23A']"
+ />
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ElNotification } from 'element-plus'
+import Echarts from '@/components/Echarts/echarts.vue'
+
+defineOptions({ name: 'IoTMonitor' })
+
+const windowSize = 30
+const collecting = ref(true)
+const lastUpdated = ref(Date.now())
+const lastUpdatedDisplay = computed(() => new Date(lastUpdated.value).toLocaleTimeString())
+
+const xAxisLabels = ref(Array.from({ length: windowSize }, (_, i) => i - (windowSize - 1)).map(n => `${n}s`))
+
+function makeSeries(fill, decimals = 2) {
+ return Array.from({ length: windowSize }, () => Number(fill.toFixed(decimals)))
+}
+
+const devices = reactive([
+ {
+ id: 'hydrocyclone-desander',
+ name: '鏃嬫祦闄ょ爞鍣�',
+ type: '鍒嗙璁惧',
+ baseline: { vibration: 8 },
+ initial: { temperature: 35, pressure: 0.85 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(8),
+ temperature: makeSeries(35, 1),
+ pressure: makeSeries(0.85, 2),
+ },
+ },
+ {
+ id: 'high-pressure-separator',
+ name: '楂樺帇鍒嗙鍣ㄦ挰',
+ type: '鍒嗙璁惧',
+ baseline: { vibration: 6 },
+ initial: { temperature: 45, pressure: 1.20 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(6),
+ temperature: makeSeries(45, 1),
+ pressure: makeSeries(1.2, 2),
+ },
+ },
+ {
+ id: 'heating-throttle-pressure',
+ name: '缁勫悎寮忓姞鐑妭娴佽皟鍘�',
+ type: '璋冨帇璁惧',
+ baseline: { vibration: 10 },
+ initial: { temperature: 75, pressure: 1.80 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(10),
+ temperature: makeSeries(75, 1),
+ pressure: makeSeries(1.8, 2),
+ },
+ },
+ {
+ id: 'three-phase-separator',
+ name: '涓夌浉鍒嗙鍣�',
+ type: '鍒嗙璁惧',
+ baseline: { vibration: 7 },
+ initial: { temperature: 38, pressure: 0.95 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(7),
+ temperature: makeSeries(38, 1),
+ pressure: makeSeries(0.95, 2),
+ },
+ },
+])
+
+function currentValue(arr) {
+ return arr[arr.length - 1] ?? 0
+}
+
+function pushWindow(arr, val) {
+ if (arr.length >= windowSize) arr.shift()
+ arr.push(val)
+}
+
+function clamp(val, min, max) { return Math.max(min, Math.min(max, val)) }
+
+function tickDevice(dev) {
+ const vibBase = dev.baseline.vibration
+ // 鎸姩锛氬熀绾柯�2%闅忔満娉㈠姩锛�5%姒傜巼瑙﹀彂8%~12%灏栧嘲妯℃嫙鍛婅
+ const spike = Math.random() < 0.05
+ const vibNoise = vibBase * (spike ? (1 + (Math.random() * 0.08 + 0.04) * (Math.random() < 0.5 ? -1 : 1)) : (1 + (Math.random() - 0.5) * 0.04))
+ const vibVal = Number(vibNoise.toFixed(2))
+ pushWindow(dev.series.vibration, vibVal)
+
+ // 娓╁害锛氱紦鎱㈤殢鏈烘父璧帮紝骞舵坊鍔犲伓鍙戦珮娓╁亸绉�
+ const tPrev = currentValue(dev.series.temperature)
+ const tDrift = tPrev + (Math.random() - 0.5) * 0.8 + (Math.random() < 0.02 ? 6 : 0)
+ const tVal = Number(clamp(tDrift, 15, 95).toFixed(1))
+ pushWindow(dev.series.temperature, tVal)
+
+ // 鍘嬪姏锛氬皬骞呮尝鍔紝鍋跺彂浣庡帇/楂樺帇
+ const pPrev = currentValue(dev.series.pressure)
+ const pDrift = pPrev + (Math.random() - 0.5) * 0.05 + (Math.random() < 0.02 ? (Math.random() < 0.5 ? -0.3 : 0.3) : 0)
+ const pVal = Number(clamp(pDrift, 0.05, 2.0).toFixed(2))
+ pushWindow(dev.series.pressure, pVal)
+
+ // 杈圭紭璁$畻闃堝�煎垽鏂�
+ const vibDelta = Math.abs(vibVal - vibBase) / vibBase
+ const vibAlert = vibDelta > 0.05
+ const tAlert = tVal < 20 || tVal > 80
+ const pAlert = pVal < 0.2 || pVal > 1.5
+
+ const prevHasAlert = dev.hasAlert
+ dev.alerts.vibration = vibAlert
+ dev.alerts.temperature = tAlert
+ dev.alerts.pressure = pAlert
+ dev.hasAlert = vibAlert || tAlert || pAlert
+
+ if (dev.hasAlert && !prevHasAlert) {
+ const reasons = []
+ if (vibAlert) reasons.push(`鎸姩鍋忕卤5% (褰撳墠 ${vibVal} / 鍩虹嚎 ${vibBase})`)
+ if (tAlert) reasons.push(`娓╁害瓒婄晫 (褰撳墠 ${tVal}掳C, 鏈熸湜 20~80掳C) `)
+ if (pAlert) reasons.push(`鍘嬪姏瓒婄晫 (褰撳墠 ${pVal}MPa, 鏈熸湜 0.2~1.5MPa) `)
+ ElNotification({
+ title: `${dev.name} 鍛婅`,
+ message: reasons.join('锛�'),
+ type: vibAlert ? 'error' : 'warning',
+ duration: 5000,
+ })
+ }
+}
+
+let timer = null
+function start() {
+ if (timer) return
+ timer = setInterval(() => {
+ if (!collecting.value) return
+ devices.forEach(tickDevice)
+ lastUpdated.value = Date.now()
+ }, 10000)
+}
+
+function stop() {
+ if (timer) {
+ clearInterval(timer)
+ timer = null
+ }
+}
+
+function toggleCollecting() { collecting.value = !collecting.value }
+
+function resetAll() {
+ devices.forEach(dev => {
+ dev.series.vibration = makeSeries(dev.baseline.vibration)
+ const t0 = dev.initial?.temperature ?? 45
+ const p0 = dev.initial?.pressure ?? 0.8
+ dev.series.temperature = makeSeries(t0, 1)
+ dev.series.pressure = makeSeries(p0, 2)
+ dev.alerts.vibration = false
+ dev.alerts.temperature = false
+ dev.alerts.pressure = false
+ dev.hasAlert = false
+ })
+ lastUpdated.value = Date.now()
+}
+
+onMounted(() => {
+ start()
+})
+
+onBeforeUnmount(() => {
+ stop()
+})
+</script>
+
+<style lang="scss" scoped>
+.iot-monitor {
+ .header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ .title { font-size: 18px; font-weight: 600; }
+ .actions { display: flex; align-items: center; gap: 8px; }
+ .ts { color: #909399; font-size: 12px; }
+ }
+ .rule-alert { margin-bottom: 12px; }
+}
+
+.device-card {
+ margin-bottom: 16px;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+ &.is-alert { border-color: #F56C6C; box-shadow: 0 0 0 2px rgba(245,108,108,0.2) inset; }
+ .card-header {
+ display: flex; flex-direction: column; gap: 4px;
+ .card-title { display: flex; align-items: center; gap: 8px; font-weight: 600; }
+ .meta { color: #909399; font-size: 12px; }
+ }
+ .metrics {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 12px;
+ }
+}
+
+.metric {
+ border: 1px solid #ebeef5;
+ border-radius: 6px;
+ padding: 8px 8px 0 8px;
+ &-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; font-size: 13px; color: #606266; }
+ &-value { font-size: 20px; font-weight: 600; margin: 2px 0 6px 0; }
+}
+
+.metric-alert {
+ border-color: #F56C6C;
+ background: #FFF6F6;
+}
+
+@media (min-width: 1200px) {
+ .device-card .metrics { grid-template-columns: 1fr 1fr 1fr; }
+}
+</style>
+
+
diff --git a/src/views/equipmentManagement/ledger/Form.vue b/src/views/equipmentManagement/ledger/Form.vue
index f7029ee..0951fd8 100644
--- a/src/views/equipmentManagement/ledger/Form.vue
+++ b/src/views/equipmentManagement/ledger/Form.vue
@@ -8,7 +8,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="瑙勬牸鍨嬪彿" prop="deviceModel">
- <el-input v-model="form.deviceModel" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+ <el-input v-model="form.deviceModel" :disabled="(form.deviceModel != null && operationType === 'edit')" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
</el-form-item>
</el-col>
<el-col :span="12">
@@ -119,6 +119,7 @@
name: "璁惧鍙拌处琛ㄥ崟",
});
const formRef = ref(null);
+const operationType = ref('');
const formRules = {
deviceName: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
deviceModel: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }],
@@ -144,6 +145,9 @@
});
const loadForm = async (id) => {
+ if (id) {
+ operationType.value = 'edit'
+ }
const { code, data } = await getLedgerById(id);
if (code == 200) {
form.deviceName = data.deviceName;
diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue
index 16fbbc6..decd235 100644
--- a/src/views/equipmentManagement/ledger/index.vue
+++ b/src/views/equipmentManagement/ledger/index.vue
@@ -76,6 +76,8 @@
size: pagination.pageSize,
total: pagination.total,
}"
+ :isShowSummary="true"
+ :summaryMethod="summaryMethod"
@selection-change="handleSelectionChange"
@pagination="changePage"
>
@@ -103,6 +105,8 @@
import dayjs from "dayjs";
import QRCode from "qrcode";
import { ref } from "vue";
+import { summarizeTable } from "@/utils/summarizeTable";
+import {Search} from "@element-plus/icons-vue";
defineOptions({
name: "璁惧鍙拌处",
@@ -127,7 +131,12 @@
} = usePaginationApi(
getLedgerPage,
{
- searchText: undefined,
+ deviceName: undefined,
+ deviceModel: undefined,
+ supplierName: undefined,
+ unit: undefined,
+ entryDateStart: undefined,
+ entryDateEnd: undefined,
},
[
{
@@ -227,6 +236,20 @@
pagination.pageSize = limit;
onCurrentChange(page);
};
+
+// 鍚堣鏂规硶
+const summaryMethod = (param) => {
+ return summarizeTable(
+ param,
+ ['number', 'taxIncludingPriceTotal', 'unTaxIncludingPriceTotal', 'taxIncludingPriceUnit'],
+ {
+ number: { noDecimal: true },
+ taxIncludingPriceTotal: { decimalPlaces: 2 },
+ unTaxIncludingPriceTotal: { decimalPlaces: 2 }
+ }
+ );
+};
+
const deleteRow = (id) => {
ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ枃浠�, 鏄惁缁х画?", "鎻愮ず", {
confirmButtonText: "纭畾",
diff --git a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
index c82fc8f..f9679a0 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
+++ b/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锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�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锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z-->
+<!-- </div>-->
+<!-- </template>-->
+<!-- </el-upload>-->
+<!-- </el-form-item>-->
+<!-- </el-col>-->
+<!-- </el-row>-->
</el-form>
<template #footer>
<div class="dialog-footer">
diff --git a/src/views/equipmentManagement/measurementEquipment/components/formDia.vue b/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
index 6319cb6..a9e08d1 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
+++ b/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锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�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锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z-->
+<!-- </div>-->
+<!-- </template>-->
+<!-- </el-upload>-->
+<!-- </el-form-item>-->
+<!-- </el-col>-->
+<!-- </el-row>-->
</el-form>
<template #footer>
<div class="dialog-footer">
diff --git a/src/views/equipmentManagement/measurementEquipment/filesDia.vue b/src/views/equipmentManagement/measurementEquipment/filesDia.vue
new file mode 100644
index 0000000..f752496
--- /dev/null
+++ b/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>
\ No newline at end of file
diff --git a/src/views/equipmentManagement/measurementEquipment/index.vue b/src/views/equipmentManagement/measurementEquipment/index.vue
index bb163be..e983a99 100644
--- a/src/views/equipmentManagement/measurementEquipment/index.vue
+++ b/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;
diff --git a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
index e8adc82..309be0e 100644
--- a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -1,5 +1,5 @@
<template>
- <el-drawer v-model="visible" :title="modalOptions.title" direction="ltr">
+ <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
<MaintainForm ref="maintainFormRef" />
<template #footer>
<el-button type="primary" @click="sendForm" :loading="loading">
@@ -7,7 +7,7 @@
</el-button>
<el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
</template>
- </el-drawer>
+ </el-dialog>
</template>
<script setup>
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index 6cae307..3ef3692 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -139,6 +139,7 @@
import { ElMessageBox, ElMessage } from "element-plus";
import dayjs from "dayjs";
import MaintainModal from "./Modal/MaintainModal.vue";
+import {Search} from "@element-plus/icons-vue";
defineOptions({
name: "璁惧鎶ヤ慨",
@@ -163,7 +164,12 @@
} = usePaginationApi(
getRepairPage,
{
- searchText: undefined,
+ deviceName: undefined,
+ deviceModel: undefined,
+ remark: undefined,
+ maintenanceName: undefined,
+ repairTimeStr: undefined,
+ maintenanceTimeStr: undefined,
},
[
{
diff --git a/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
index 1edcf23..1b5a7d4 100644
--- a/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
+++ b/src/views/equipmentManagement/upkeep/Modal/MaintenanceModal.vue
@@ -1,5 +1,5 @@
<template>
- <el-drawer v-model="visible" :title="modalOptions.title" direction="ltr">
+ <el-dialog v-model="visible" :title="modalOptions.title" direction="ltr">
<MaintenanceForm ref="maintenanceFormRef" />
<template #footer>
<el-button type="primary" @click="sendForm" :loading="loading">
@@ -7,7 +7,7 @@
</el-button>
<el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
</template>
- </el-drawer>
+ </el-dialog>
</template>
<script setup>
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index f44d817..484538c 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -127,6 +127,7 @@
import MaintenanceModal from "./Modal/MaintenanceModal.vue";
import dayjs from "dayjs";
import { ElMessageBox, ElMessage } from "element-plus";
+import {Search} from "@element-plus/icons-vue";
defineOptions({
name: "璁惧淇濆吇",
@@ -154,7 +155,12 @@
getTableData,
resetFilters,
onCurrentChange,
-} = usePaginationApi(getUpkeepPage, {}, [
+} = usePaginationApi(getUpkeepPage, {
+ deviceName: undefined,
+ maintenancePlanTime: undefined,
+ maintenanceActuallyTime: undefined,
+ maintenanceActuallyName: undefined,
+}, [
{
label: "璁惧鍚嶇О",
align: "center",
diff --git a/src/views/financialManagement/expenseManagement/index.vue b/src/views/financialManagement/expenseManagement/index.vue
index 32d906b..b1e9fb1 100644
--- a/src/views/financialManagement/expenseManagement/index.vue
+++ b/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>
diff --git a/src/views/financialManagement/revenueManagement/index.vue b/src/views/financialManagement/revenueManagement/index.vue
index 35f3075..285b73f 100644
--- a/src/views/financialManagement/revenueManagement/index.vue
+++ b/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>
diff --git a/src/views/index.vue b/src/views/index.vue
index 95fe72c..67c8654 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -118,11 +118,11 @@
<div class="main-panel">
<div style="display: flex;justify-content: space-between;">
<div class="section-title">搴旀敹搴斾粯缁熻</div>
- <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">
- <el-radio-button label="鎸夊懆" :value="1" />
- <el-radio-button label="鎸夋湀" :value="2" />
- <el-radio-button label="鎸夊搴�" :value="3" />
- </el-radio-group>
+<!-- <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">-->
+<!-- <el-radio-button label="鎸夊懆" :value="1" />-->
+<!-- <el-radio-button label="鎸夋湀" :value="2" />-->
+<!-- <el-radio-button label="鎸夊搴�" :value="3" />-->
+<!-- </el-radio-group>-->
</div>
<Echarts ref="chart"
:color="barColors2"
@@ -138,23 +138,23 @@
<!-- 搴曢儴妯悜涓ゆ爮 -->
<div class="dashboard-row">
- <div class="main-panel">
- <div class="section-title">璐ㄩ噺缁熻</div>
- <div class="quality-cards">
- <div class="quality-card one">鍘熸潗鏂欏凡妫�娴嬫暟 <span>{{qualityStatisticsObject.supplierNum}}浠�</span></div>
- <div class="quality-card two">杩囩▼妫�楠屾暟閲� <span>{{qualityStatisticsObject.processNum}}浠�</span></div>
- <div class="quality-card three">鍑哄巶宸叉鏁伴噺 <span>{{qualityStatisticsObject.factoryNum}}浠�</span></div>
- </div>
- <Echarts ref="chart"
- :chartStyle="chartStyle"
- :grid="grid"
- :legend="barLegend"
- :series="barSeries1"
- :tooltip="tooltip"
- :xAxis="xAxis1"
- :yAxis="yAxis1"
- style="height: 260px"></Echarts>
- </div>
+<!-- <div class="main-panel">-->
+<!-- <div class="section-title">璐ㄩ噺缁熻</div>-->
+<!-- <div class="quality-cards">-->
+<!-- <div class="quality-card one">鍘熸潗鏂欏凡妫�娴嬫暟 <span>{{qualityStatisticsObject.supplierNum}}浠�</span></div>-->
+<!-- <div class="quality-card two">杩囩▼妫�楠屾暟閲� <span>{{qualityStatisticsObject.processNum}}浠�</span></div>-->
+<!-- <div class="quality-card three">鍑哄巶宸叉鏁伴噺 <span>{{qualityStatisticsObject.factoryNum}}浠�</span></div>-->
+<!-- </div>-->
+<!-- <Echarts ref="chart"-->
+<!-- :chartStyle="chartStyle"-->
+<!-- :grid="grid"-->
+<!-- :legend="barLegend"-->
+<!-- :series="barSeries1"-->
+<!-- :tooltip="tooltip"-->
+<!-- :xAxis="xAxis1"-->
+<!-- :yAxis="yAxis1"-->
+<!-- style="height: 260px"></Echarts>-->
+<!-- </div>-->
<div class="main-panel">
<div class="section-title">鍥炴涓庡紑绁ㄥ垎鏋�</div>
<Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries"
@@ -744,15 +744,18 @@
color: #666;
list-style: none;
padding: 0;
+ height: 190px;
+ overflow-y: auto;
+ width: 460px;
}
.line {
position: relative;
- width: 250px;
+ width: 230px;
}
.line::after {
content: '';
position: absolute;
- right: 12px;
+ right: 2px;
top: 0;
bottom: 0;
width: 1px;
diff --git a/src/views/inventoryManagement/dispatchLog/index.vue b/src/views/inventoryManagement/dispatchLog/index.vue
index 504074e..6fe0473 100644
--- a/src/views/inventoryManagement/dispatchLog/index.vue
+++ b/src/views/inventoryManagement/dispatchLog/index.vue
@@ -33,13 +33,14 @@
style="width: 100%"
:summary-method="summarizeMainTable"
height="calc(100vh - 18.5em)"
+ stripe
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column
label="鍑哄簱鏃ユ湡"
prop="createTime"
- min-width="250"
+ min-width="130"
show-overflow-tooltip
/>
<el-table-column
@@ -75,13 +76,13 @@
<el-table-column
label="鍚◣鍗曚环(鍏�)"
prop="taxInclusiveUnitPrice"
- width="100"
+ width="200"
show-overflow-tooltip
/>
<el-table-column
label="鍚◣鎬讳环(鍏�)"
prop="taxInclusiveTotalPrice"
- width="100"
+ width="200"
show-overflow-tooltip
/>
<el-table-column
diff --git a/src/views/inventoryManagement/issueManagement/index.vue b/src/views/inventoryManagement/issueManagement/index.vue
index 4240bef..cc4d8e1 100644
--- a/src/views/inventoryManagement/issueManagement/index.vue
+++ b/src/views/inventoryManagement/issueManagement/index.vue
@@ -16,7 +16,7 @@
<div class="table_list">
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
:expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
- :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
+ :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="鍏ュ簱鏃堕棿" prop="createTime" width="100" show-overflow-tooltip />
diff --git a/src/views/inventoryManagement/receiptManagement/index.vue b/src/views/inventoryManagement/receiptManagement/index.vue
index acf0b68..238f3fd 100644
--- a/src/views/inventoryManagement/receiptManagement/index.vue
+++ b/src/views/inventoryManagement/receiptManagement/index.vue
@@ -16,7 +16,7 @@
<div class="table_list">
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
:expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
- :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
+ :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="鍏ュ簱鏃堕棿" prop="createTime" width="100" show-overflow-tooltip />
@@ -66,6 +66,7 @@
border
v-loading="loadingProducts"
@selection-change="handleSelectionChange"
+ stripe
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column
diff --git a/src/views/inventoryManagement/stockManagement/index.vue b/src/views/inventoryManagement/stockManagement/index.vue
index b4a29eb..06fcaf8 100644
--- a/src/views/inventoryManagement/stockManagement/index.vue
+++ b/src/views/inventoryManagement/stockManagement/index.vue
@@ -16,7 +16,7 @@
<div class="table_list">
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
:expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
- :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
+ :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="搴撳瓨鏃ユ湡" prop="createTime" width="100" show-overflow-tooltip />
diff --git a/src/views/inventoryManagement/stockWarning/index.vue b/src/views/inventoryManagement/stockWarning/index.vue
index 3694265..4193352 100644
--- a/src/views/inventoryManagement/stockWarning/index.vue
+++ b/src/views/inventoryManagement/stockWarning/index.vue
@@ -51,6 +51,7 @@
@selection-change="handleSelectionChange"
style="width: 100%"
height="calc(100vh - 280px)"
+ stripe
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
diff --git a/src/views/login.vue b/src/views/login.vue
index 960f097..5300637 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -181,7 +181,7 @@
<style lang='scss' scoped>
.login {
height: 100%;
- background-image: url("../assets/indexViews/JZYJView.png");
+ background-image: url("../assets/images/login-background.png");
background-size: cover;
position: relative;
}
diff --git a/src/views/monitor/job/index.vue b/src/views/monitor/job/index.vue
index ee291a4..918be40 100644
--- a/src/views/monitor/job/index.vue
+++ b/src/views/monitor/job/index.vue
@@ -87,7 +87,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="浠诲姟缂栧彿" width="100" align="center" prop="jobId" />
<el-table-column label="浠诲姟鍚嶇О" align="center" prop="jobName" :show-overflow-tooltip="true" />
diff --git a/src/views/monitor/job/log.vue b/src/views/monitor/job/log.vue
index b425e45..30ef218 100644
--- a/src/views/monitor/job/log.vue
+++ b/src/views/monitor/job/log.vue
@@ -96,7 +96,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="jobLogList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="jobLogList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="鏃ュ織缂栧彿" width="80" align="center" prop="jobLogId" />
<el-table-column label="浠诲姟鍚嶇О" align="center" prop="jobName" :show-overflow-tooltip="true" />
diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue
index aa42b52..bee51a1 100644
--- a/src/views/monitor/logininfor/index.vue
+++ b/src/views/monitor/logininfor/index.vue
@@ -93,7 +93,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table ref="logininforRef" v-loading="loading" :data="logininforList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
+ <el-table ref="logininforRef" v-loading="loading" :data="logininforList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="璁块棶缂栧彿" align="center" prop="infoId" />
<el-table-column label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue
index 21f6463..7a56ad5 100644
--- a/src/views/monitor/online/index.vue
+++ b/src/views/monitor/online/index.vue
@@ -28,6 +28,7 @@
v-loading="loading"
:data="onlineList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
style="width: 100%;"
+ stripe
>
<el-table-column label="搴忓彿" width="50" type="index" align="center">
<template #default="scope">
diff --git a/src/views/monitor/operlog/index.vue b/src/views/monitor/operlog/index.vue
index 92f9c35..edb57dd 100644
--- a/src/views/monitor/operlog/index.vue
+++ b/src/views/monitor/operlog/index.vue
@@ -107,7 +107,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table ref="operlogRef" v-loading="loading" :data="operlogList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
+ <el-table ref="operlogRef" v-loading="loading" :data="operlogList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange" stripe>
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="鏃ュ織缂栧彿" align="center" prop="operId" />
<el-table-column label="绯荤粺妯″潡" align="center" prop="title" :show-overflow-tooltip="true" />
diff --git a/src/views/personnelManagement/contractManagement/filesDia.vue b/src/views/personnelManagement/contractManagement/filesDia.vue
new file mode 100644
index 0000000..f752496
--- /dev/null
+++ b/src/views/personnelManagement/contractManagement/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>
\ No newline at end of file
diff --git a/src/views/personnelManagement/contractManagement/index.vue b/src/views/personnelManagement/contractManagement/index.vue
index 57d7ec4..98f0272 100644
--- a/src/views/personnelManagement/contractManagement/index.vue
+++ b/src/views/personnelManagement/contractManagement/index.vue
@@ -3,52 +3,81 @@
<div class="search_form">
<div>
<span class="search_title">濮撳悕锛�</span>
- <el-input
- v-model="searchForm.staffName"
- style="width: 240px"
- placeholder="璇疯緭鍏ュ鍚嶆悳绱�"
- @change="handleQuery"
- clearable
- :prefix-icon="Search"
- />
- <span style="margin-left: 10px" class="search_title">鍚堝悓缁撴潫鏃ユ湡锛�</span>
- <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
- placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
- <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
- >鎼滅储</el-button
- >
+ <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="璇疯緭鍏ュ鍚嶆悳绱�" @change="handleQuery"
+ clearable :prefix-icon="Search" />
+ <span style="margin-left: 10px" class="search_title">鍚堝悓缁撴潫鏃ユ湡锛�</span>
+ <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+ placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+ <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
</div>
<div>
<!-- <el-button type="primary" @click="openForm('add')">鏂板鍏ヨ亴</el-button>-->
+<!-- <el-button type="info" @click="handleImport">瀵煎叆</el-button>-->
<el-button @click="handleOut">瀵煎嚭</el-button>
<!-- <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>-->
</div>
</div>
<div class="table_list">
- <PIMTable
- rowKey="id"
- :column="tableColumn"
- :tableData="tableData"
- :page="page"
- :isSelection="true"
- @selection-change="handleSelectionChange"
- :tableLoading="tableLoading"
- @pagination="pagination"
- :total="page.total"
- ></PIMTable>
+ <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true"
+ @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination"
+ :total="page.total"></PIMTable>
</div>
<form-dia ref="formDia" @close="handleQuery"></form-dia>
+
+ <!-- 鍚堝悓瀵煎叆瀵硅瘽妗� -->
+ <el-dialog
+ :title="upload.title"
+ v-model="upload.open"
+ width="400px"
+ append-to-body
+ >
+ <el-upload
+ ref="uploadRef"
+ :limit="1"
+ accept=".xlsx, .xls"
+ :headers="upload.headers"
+ :action="upload.url + '?updateSupport=' + upload.updateSupport"
+ :disabled="upload.isUploading"
+ :on-progress="handleFileUploadProgress"
+ :on-success="handleFileSuccess"
+ :auto-upload="false"
+ drag
+ >
+ <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+ <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+ <template #tip>
+ <div class="el-upload__tip text-center">
+ <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+ <!-- <el-link
+ type="primary"
+ :underline="false"
+ style="font-size: 12px; vertical-align: baseline"
+ @click="importTemplate"
+ >涓嬭浇妯℃澘</el-link
+ > -->
+ </div>
+ </template>
+ </el-upload>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+ <el-button @click="upload.open = false">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ <files-dia ref="filesDia"></files-dia>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
-import {onMounted, ref} from "vue";
+import { onMounted, ref } from "vue";
import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue";
-import {ElMessageBox} from "element-plus";
-import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js";
+import { ElMessageBox } from "element-plus";
+import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js";
import dayjs from "dayjs";
-
+import { getToken } from "@/utils/auth.js";
+import FilesDia from "./filesDia.vue";
const data = reactive({
searchForm: {
staffName: "",
@@ -108,7 +137,7 @@
{
label: "瀹跺涵浣忓潃",
prop: "adress",
- width:200
+ width: 200
},
{
label: "绗竴瀛﹀巻",
@@ -117,12 +146,12 @@
{
label: "涓撲笟",
prop: "profession",
- width:100
+ width: 100
},
{
label: "韬唤璇佸彿",
prop: "identityCard",
- width:200
+ width: 200
},
{
label: "骞撮緞",
@@ -131,7 +160,7 @@
{
label: "鑱旂郴鐢佃瘽",
prop: "phone",
- width:150
+ width: 150
},
{
label: "绱ф�ヨ仈绯讳汉",
@@ -141,7 +170,7 @@
{
label: "绱ф�ヨ仈绯讳汉鐢佃瘽",
prop: "emergencyContactPhone",
- width:150
+ width: 150
},
{
label: "鍚堝悓骞撮檺",
@@ -162,6 +191,7 @@
label: "鎿嶄綔",
align: "center",
fixed: 'right',
+ width: 120,
operation: [
{
name: "璇︽儏",
@@ -170,9 +200,17 @@
openForm("edit", row);
},
},
+ {
+ name: "闄勪欢",
+ type: "text",
+ clickFun: (row) => {
+ openFilesFormDia(row);
+ },
+ },
],
},
]);
+const filesDia = ref()
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
@@ -192,6 +230,13 @@
searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
}
getList();
+};
+// 鎵撳紑闄勪欢寮规
+const openFilesFormDia = (row) => {
+ console.log(row)
+ nextTick(() => {
+ filesDia.value?.openDialog( row,'鍚堝悓')
+ })
};
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
@@ -234,12 +279,47 @@
cancelButtonText: "鍙栨秷",
type: "warning",
})
- .then(() => {
- proxy.download("/staff/staffOnJob/export", {}, "鍚堝悓绠$悊.xlsx");
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
- });
+ .then(() => {
+ proxy.download("/staff/staffOnJob/export", {}, "鍚堝悓绠$悊.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+const upload = reactive({
+ // 鏄惁鏄剧ず寮瑰嚭灞傦紙鍚堝悓瀵煎叆锛�
+ open: false,
+ // 寮瑰嚭灞傛爣棰橈紙鍚堝悓瀵煎叆锛�
+ title: "",
+ // 鏄惁绂佺敤涓婁紶
+ isUploading: false,
+ // 鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�
+ updateSupport: 1,
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/staff/staffOnJob/import",
+});
+/** 瀵煎叆鎸夐挳鎿嶄綔 */
+function handleImport() {
+ upload.title = "鍚堝悓瀵煎叆";
+ upload.open = true;
+}
+/** 鎻愪氦涓婁紶鏂囦欢 */
+function submitFileForm() {
+ console.log(upload.url + '?updateSupport=' + upload.updateSupport)
+ proxy.$refs["uploadRef"].submit();
+}
+/**鏂囦欢涓婁紶涓鐞� */
+const handleFileUploadProgress = (event, file, fileList) => {
+ upload.isUploading = true;
+};
+/** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */
+const handleFileSuccess = (response, file, fileList) => {
+ upload.open = false;
+ upload.isUploading = false;
+ proxy.$refs["uploadRef"].handleRemove(file);
+ getList();
};
onMounted(() => {
getList();
diff --git a/src/views/personnelManagement/onboarding/components/formDia.vue b/src/views/personnelManagement/onboarding/components/formDia.vue
index d0e76cc..7244ab0 100644
--- a/src/views/personnelManagement/onboarding/components/formDia.vue
+++ b/src/views/personnelManagement/onboarding/components/formDia.vue
@@ -124,6 +124,26 @@
</el-form-item>
</el-col>
</el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="璇曠敤鏈燂紙鏈堬級锛�" prop="probationPeriod">
+ <el-input-number v-model="form.probationPeriod" :precision="0" :step="1" min="0" style="width: 100%"/>
+ </el-form-item>
+ </el-col>
+ <!-- <el-col :span="12">
+ <el-form-item label="鍏ヨ亴鏃ユ湡锛�" prop="entryDate">
+ <el-date-picker
+ v-model="form.entryDate"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col> -->
+ </el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
@@ -136,7 +156,7 @@
</template>
<script setup>
-import {ref} from "vue";
+import {ref, reactive, toRefs} from "vue";
import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -162,6 +182,7 @@
contractStartTime: "",
contractEndTime: "",
staffState: "",
+ probationPeriod: 3, // 榛樿璇曠敤鏈�3涓湀
},
rules: {
staffNo: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
diff --git a/src/views/personnelManagement/onboarding/index.vue b/src/views/personnelManagement/onboarding/index.vue
index d36f2e5..d993ee6 100644
--- a/src/views/personnelManagement/onboarding/index.vue
+++ b/src/views/personnelManagement/onboarding/index.vue
@@ -151,8 +151,39 @@
width:150
},
{
- label: "鍚堝悓骞撮檺",
+ label: "璇曠敤鏈燂紙鏈堬級",
+ prop: "probationPeriod",
+ width: 120,
+ },
+ // {
+ // label: "杞鏃ユ湡",
+ // prop: "probationEndDate",
+ // width: 120,
+ // formatData: (row) => {
+ // // 淇敼涓轰娇鐢ㄥ悎鍚屽紑濮嬫棩鏈熻绠楄浆姝f棩鏈�
+ // if (row.contractStartTime && row.probationPeriod) {
+ // // 璁$畻杞鏃ユ湡锛堝悎鍚屽紑濮嬫棩鏈熷姞涓婅瘯鐢ㄦ湡鏈堟暟锛�
+ // return dayjs(row.contractStartTime).add(row.probationPeriod, 'month').format('YYYY-MM-DD');
+ // }
+ // return '';
+ // },
+ // formatType: (row) => {
+ // // 淇敼涓轰娇鐢ㄥ悎鍚屽紑濮嬫棩鏈熸鏌ユ槸鍚︿复杩戣浆姝o紙7澶╁唴锛�
+ // if (row.contractStartTime && row.probationPeriod) {
+ // const probationEndDate = dayjs(row.contractStartTime).add(row.probationPeriod, 'month');
+ // const daysUntilProbationEnd = probationEndDate.diff(dayjs(), 'day');
+
+ // if (daysUntilProbationEnd >= 0 && daysUntilProbationEnd <= 7) {
+ // return 'warning'; // 浣跨敤璀﹀憡鏍峰紡鏍囪涓磋繎杞鐨勫憳宸�
+ // }
+ // }
+ // return '';
+ // }
+ // },
+ {
+ label: "鍚堝悓骞撮檺锛堝勾锛�",
prop: "contractTerm",
+ width: 120,
},
{
label: "鍚堝悓寮�濮嬫棩鏈�",
@@ -222,10 +253,43 @@
tableLoading.value = false;
tableData.value = res.data.records
page.total = res.data.total;
+
+ // 妫�鏌ユ槸鍚︽湁涓磋繎杞鐨勫憳宸ュ苟鎻愰啋
+ checkProbationEnding(tableData.value);
}).catch(err => {
tableLoading.value = false;
})
};
+// 妫�鏌ヤ复杩戣浆姝g殑鍛樺伐骞舵彁閱�
+const checkProbationEnding = (data) => {
+ const probationEndingSoon = [];
+
+ data.forEach(item => {
+ // 淇敼涓轰娇鐢ㄥ悎鍚屽紑濮嬫棩鏈熸鏌�
+ if (item.contractStartTime && item.probationPeriod) {
+ const probationEndDate = dayjs(item.contractStartTime).add(item.probationPeriod, 'month');
+ const daysUntilProbationEnd = probationEndDate.diff(dayjs(), 'day');
+
+ if (daysUntilProbationEnd >= 0 && daysUntilProbationEnd <= 7) {
+ probationEndingSoon.push({
+ staffName: item.staffName,
+ probationEndDate: probationEndDate.format('YYYY-MM-DD'),
+ daysLeft: daysUntilProbationEnd
+ });
+ }
+ }
+ });
+
+ if (probationEndingSoon.length > 0) {
+ let message = '浠ヤ笅鍛樺伐灏嗗湪7澶╁唴杞锛歕n';
+ probationEndingSoon.forEach(item => {
+ message += `${item.staffName}锛�${item.probationEndDate}锛岃繕鏈�${item.daysLeft}澶╋級\n`;
+ });
+
+ // 鏄剧ず鎻愰啋娑堟伅
+ proxy.$modal.msgInfo(message);
+ }
+};
// 琛ㄦ牸閫夋嫨鏁版嵁
const handleSelectionChange = (selection) => {
selectedRows.value = selection;
diff --git a/src/views/procurementManagement/invoiceEntry/index.vue b/src/views/procurementManagement/invoiceEntry/index.vue
index 256377f..4b25c38 100644
--- a/src/views/procurementManagement/invoiceEntry/index.vue
+++ b/src/views/procurementManagement/invoiceEntry/index.vue
@@ -165,7 +165,7 @@
},
},
{
- label: "宸插紑绁ㄩ噾棰�(鍏�)",
+ label: "宸叉潵绁ㄩ噾棰�(鍏�)",
prop: "receiptPaymentAmount",
width:200,
formatData: (val) => {
@@ -173,7 +173,7 @@
},
},
{
- label: "寰呭紑绁ㄩ噾棰�(鍏�)",
+ label: "寰呮潵绁ㄩ噾棰�(鍏�)",
prop: "unReceiptPaymentAmount",
width:200,
formatData: (val) => {
diff --git a/src/views/procurementManagement/invoiceEntry/indexOld.vue b/src/views/procurementManagement/invoiceEntry/indexOld.vue
index 1b4c6b9..52fe222 100644
--- a/src/views/procurementManagement/invoiceEntry/indexOld.vue
+++ b/src/views/procurementManagement/invoiceEntry/indexOld.vue
@@ -37,6 +37,7 @@
:summary-method="summarizeMainTable"
@expand-change="expandChange"
height="calc(100vh - 18.5em)"
+ stripe
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column type="expand">
@@ -46,6 +47,7 @@
border
show-summary
:summary-method="summarizeChildrenTable"
+ stripe
>
<el-table-column
align="center"
@@ -278,6 +280,7 @@
:data="productData"
border
@selection-change="productSelected"
+ stripe
show-summary
style="width: 100%"
:summary-method="summarizeChildrenTable"
diff --git a/src/views/procurementManagement/paymentEntry/index.vue b/src/views/procurementManagement/paymentEntry/index.vue
index 107ee30..3ce2a8d 100644
--- a/src/views/procurementManagement/paymentEntry/index.vue
+++ b/src/views/procurementManagement/paymentEntry/index.vue
@@ -61,6 +61,7 @@
show-summary
v-loading="childrenLoading"
:summary-method="summarizeMainTable2"
+ stripe
>
<el-table-column
align="center"
@@ -221,31 +222,31 @@
</el-select>
</el-form-item>
</el-col>
- <el-col :span="12">
- <el-form-item label="鐧昏浜猴細" prop="registrant">
- <el-input
- v-model="form.registrant"
- placeholder="璇疯緭鍏�"
- clearable
- disabled
- />
- </el-form-item>
- </el-col>
+ <el-col :span="12">
+ <el-form-item label="浠樻鏃ユ湡锛�" prop="paymentDate">
+ <el-date-picker
+ style="width: 100%"
+ v-model="form.paymentDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
</el-row>
<el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="浠樻鏃ユ湡锛�" prop="paymentDate">
- <el-date-picker
- style="width: 100%"
- v-model="form.paymentDate"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨"
- clearable
- />
- </el-form-item>
- </el-col>
+ <el-col :span="12">
+ <el-form-item label="鐧昏浜猴細" prop="registrant">
+ <el-input
+ v-model="form.registrant"
+ placeholder="璇疯緭鍏�"
+ clearable
+ disabled
+ />
+ </el-form-item>
+ </el-col>
<el-col :span="12">
<el-form-item label="鐧昏鏃ユ湡锛�" prop="registrationtDate">
<el-input
diff --git a/src/views/procurementManagement/paymentHistory/index.vue b/src/views/procurementManagement/paymentHistory/index.vue
index 714c670..7c71979 100644
--- a/src/views/procurementManagement/paymentHistory/index.vue
+++ b/src/views/procurementManagement/paymentHistory/index.vue
@@ -63,6 +63,10 @@
const isShowSummarySon = ref(true);
const tableColumn = ref([
{
+ label: "閲囪喘鍚堝悓鍙�",
+ prop: "purchaseContractNumber",
+ },
+ {
label: "浠樻鏃ユ湡",
prop: "paymentDate",
},
diff --git a/src/views/procurementManagement/paymentLedger/index.vue b/src/views/procurementManagement/paymentLedger/index.vue
index 741e6ac..2f001a5 100644
--- a/src/views/procurementManagement/paymentLedger/index.vue
+++ b/src/views/procurementManagement/paymentLedger/index.vue
@@ -29,6 +29,7 @@
height="calc(100vh - 18.5em)"
:highlight-current-row="true"
style="width: 100%"
+ stripe
tooltip-effect="dark"
@row-click="rowClick"
:show-summary="isShowSummary"
diff --git a/src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue b/src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
index 77789c4..f939be4 100644
--- a/src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
+++ b/src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
@@ -53,7 +53,7 @@
defineOptions({
name: "鏉ョエ鍙拌处琛ㄥ崟",
});
-
+const temFutureTickets = ref(0)
const { form, resetForm } = useFormData({
id: undefined,
purchaseContractNumber: undefined, // 閲囪喘鍚堝悓鍙�
@@ -77,6 +77,7 @@
form.ticketsAmount = data.ticketsAmount.toFixed(2);
form.taxInclusiveUnitPrice = data.taxInclusiveUnitPrice;
form.futureTickets = data.futureTickets;
+ temFutureTickets.value = data.futureTickets;
}
};
@@ -86,16 +87,14 @@
proxy.$modal.msgWarning("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
return;
}
-
- if (Number(form.ticketsNum) > Number(form.futureTickets)) {
+ if (Number(form.ticketsNum) > Number(temFutureTickets.value)) {
proxy.$modal.msgWarning("寮�绁ㄦ暟涓嶅緱澶т簬鏈紑绁ㄦ暟");
- form.ticketsNum = form.futureTickets
- return;
+ form.ticketsNum = temFutureTickets.value
}
// 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
- const ticketsAmount = Number(val) * Number(form.taxInclusiveUnitPrice);
- const futureTickets = Number(form.futureTickets) - Number(val);
+ const ticketsAmount = Number(form.ticketsNum) * Number(form.taxInclusiveUnitPrice);
+ const futureTickets = Number(temFutureTickets.value) - Number(form.ticketsNum);
form.futureTickets = Number(futureTickets.toFixed(2));
form.ticketsAmount = Number(ticketsAmount.toFixed(2));
};
diff --git a/src/views/procurementManagement/procurementInvoiceLedger/index.vue b/src/views/procurementManagement/procurementInvoiceLedger/index.vue
index 255f3ac..8ad8799 100644
--- a/src/views/procurementManagement/procurementInvoiceLedger/index.vue
+++ b/src/views/procurementManagement/procurementInvoiceLedger/index.vue
@@ -162,14 +162,19 @@
width: 150,
},
{
- label: "瀹㈡埛鍚嶇О",
- prop: "customerName",
+ label: "椤圭洰鍚嶇О",
+ prop: "projectName",
width: 240,
},
{
label: "渚涘簲鍟嗗悕绉�",
prop: "supplierName",
width: 240,
+ },
+ {
+ label: "浜у搧澶х被",
+ prop: "productCategory",
+ width: 150,
},
{
label: "瑙勬牸鍨嬪彿",
@@ -190,12 +195,17 @@
},
},
{
- label: "寮�绁ㄦ棩鏈�",
+ label: "鏈鏉ョエ鏁�",
+ prop: "ticketsNum",
+ width: 110,
+ },
+ {
+ label: "鏉ョエ鏃ユ湡",
prop: "createdAt",
width: 110,
},
{
- label: "寮�绁ㄩ噾棰�",
+ label: "鏉ョエ閲戦(鍏�)",
prop: "ticketsAmount",
width: 200,
formatData: (cell) => {
diff --git a/src/views/procurementManagement/procurementInvoiceLedger/indexOld.vue b/src/views/procurementManagement/procurementInvoiceLedger/indexOld.vue
index 987e6a4..af38ab1 100644
--- a/src/views/procurementManagement/procurementInvoiceLedger/indexOld.vue
+++ b/src/views/procurementManagement/procurementInvoiceLedger/indexOld.vue
@@ -52,6 +52,7 @@
:summary-method="summarizeMainTable"
@expand-change="expandChange"
height="calc(100vh - 18.5em)"
+ stripe
>
<el-table-column align="center" label="搴忓彿" type="index" width="55" />
<el-table-column type="expand">
@@ -61,6 +62,7 @@
border
show-summary
:summary-method="summarizeChildrenTable"
+ stripe
>
<el-table-column
align="center"
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index b403fd1..4fbf775 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -39,6 +39,7 @@
<div class="table_list">
<div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
<el-button type="primary" @click="openForm('add')">鏂板鍙拌处</el-button>
+ <el-button type="success" @click="openScanAddDialog">鎵爜鏂板</el-button>
<el-button @click="handleOut">瀵煎嚭</el-button>
<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
</div>
@@ -53,6 +54,7 @@
:summary-method="summarizeMainTable"
@expand-change="expandChange"
height="calc(100vh - 18.5em)"
+ stripe
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column type="expand">
@@ -62,6 +64,7 @@
border
show-summary
:summary-method="summarizeChildrenTable"
+ stripe
>
<el-table-column
align="center"
@@ -145,7 +148,7 @@
<el-table-column
fixed="right"
label="鎿嶄綔"
- min-width="60"
+ min-width="150"
align="center"
>
<template #default="scope">
@@ -157,6 +160,14 @@
:disabled="scope.row.receiptPaymentAmount>0 || scope.row.recorderName !== userStore.nickName"
>缂栬緫</el-button
>
+ <el-button
+ link
+ type="success"
+ size="small"
+ @click="showQRCode(scope.row)"
+ >鐢熸垚浜岀淮鐮�</el-button
+ >
+
</template>
</el-table-column>
</el-table>
@@ -296,6 +307,7 @@
border
@selection-change="productSelected"
show-summary
+ stripe
:summary-method="summarizeProTable"
>
<el-table-column align="center" type="selection" width="55" />
@@ -539,13 +551,228 @@
</div>
</template>
</el-dialog>
+
+ <!-- 浜岀淮鐮佹樉绀哄璇濇 -->
+ <el-dialog
+ v-model="qrCodeDialogVisible"
+ title="閲囪喘鍚堝悓鍙蜂簩缁寸爜"
+ width="400px"
+ center
+ >
+ <div style="text-align: center;">
+ <img :src="qrCodeUrl" alt="浜岀淮鐮�" style="width:200px;height:200px;" />
+ <div style="margin: 20px;">
+ <el-button type="primary" @click="downloadQRCode">涓嬭浇浜岀淮鐮佸浘鐗�</el-button>
+ </div>
+ </div>
+ </el-dialog>
+
+ <!-- 鎵爜鏂板瀵硅瘽妗� -->
+ <el-dialog
+ v-model="scanAddDialogVisible"
+ title="鎵爜鏂板閲囪喘鍙拌处"
+ width="70%"
+ @close="closeScanAddDialog"
+ >
+ <el-form
+ :model="scanAddForm"
+ label-width="140px"
+ label-position="top"
+ :rules="scanAddRules"
+ ref="scanAddFormRef"
+ >
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="鎵爜鍐呭锛�">
+ <el-input
+ v-model="scanAddForm.scanContent"
+ type="textarea"
+ :rows="3"
+ placeholder="璇锋壂鎻忎簩缁寸爜鎴栨墜鍔ㄨ緭鍏ラ噰璐悎鍚屼俊鎭�"
+ @input="parseScanContent"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="閲囪喘鍚堝悓鍙凤細" prop="purchaseContractNumber">
+ <el-input
+ v-model="scanAddForm.purchaseContractNumber"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="渚涘簲鍟嗗悕绉帮細" prop="supplierName">
+ <el-input
+ v-model="scanAddForm.supplierName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
+ <el-input
+ v-model="scanAddForm.projectName"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍚堝悓閲戦(鍏�)锛�" prop="contractAmount">
+ <el-input-number
+ v-model="scanAddForm.contractAmount"
+ :precision="2"
+ :step="0.1"
+ clearable
+ style="width: 100%"
+ placeholder="璇疯緭鍏�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浠樻鏂瑰紡锛�">
+ <el-input
+ v-model="scanAddForm.paymentMethod"
+ placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="褰曞叆浜猴細">
+ <el-input v-model="scanAddForm.recorderName" disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�">
+ <el-input
+ v-model="scanAddForm.remark"
+ type="textarea"
+ :rows="2"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitScanAdd">纭鏂板</el-button>
+ <el-button @click="closeScanAddDialog">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 鎵爜鐧昏瀵硅瘽妗� -->
+ <el-dialog
+ v-model="scanDialogVisible"
+ title="鎵爜鐧昏"
+ width="60%"
+ @close="closeScanDialog"
+ >
+ <el-form
+ :model="scanForm"
+ label-width="120px"
+ label-position="left"
+ :rules="scanRules"
+ ref="scanFormRef"
+ >
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="閲囪喘鍚堝悓鍙凤細">
+ <el-input v-model="scanForm.purchaseContractNumber" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="渚涘簲鍟嗗悕绉帮細">
+ <el-input v-model="scanForm.supplierName" disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="椤圭洰鍚嶇О锛�">
+ <el-input v-model="scanForm.projectName" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鎵爜鏃堕棿锛�">
+ <el-input v-model="scanForm.scanTime" disabled />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鎵爜浜猴細">
+ <el-input v-model="scanForm.scannerName" disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鎵爜鐘舵�侊細">
+ <el-tag :type="scanForm.scanStatus === '宸叉壂鐮�' ? 'success' : 'warning'">
+ {{ scanForm.scanStatus }}
+ </el-tag>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="鎵爜澶囨敞锛�">
+ <el-input
+ v-model="scanForm.scanRemark"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ユ壂鐮佸娉ㄤ俊鎭�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="鎵爜璁板綍锛�">
+ <el-table :data="scanRecords" border style="width: 100%" stripe>
+ <el-table-column label="搴忓彿" type="index" width="60" align="center" />
+ <el-table-column label="鎵爜鏃堕棿" prop="scanTime" width="180" />
+ <el-table-column label="鎵爜浜�" prop="scannerName" width="120" />
+ <el-table-column label="鎵爜鐘舵��" prop="scanStatus" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.scanStatus === '宸叉壂鐮�' ? 'success' : 'warning'">
+ {{ scope.row.scanStatus }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="澶囨敞" prop="scanRemark" />
+ </el-table>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="submitScan">纭鎵爜</el-button>
+ <el-button @click="closeScanDialog">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup>
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
-import { ref, onMounted } from "vue";
+import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
@@ -567,6 +794,7 @@
createPurchaseNo,
} from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
+import QRCode from "qrcode";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const productData = ref([]);
@@ -589,6 +817,10 @@
import dayjs from "dayjs";
const userStore = useUserStore();
+
+// 浜岀淮鐮佺浉鍏冲彉閲�
+const qrCodeDialogVisible = ref(false);
+const qrCodeUrl = ref("");
// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
const operationType = ref("");
@@ -812,8 +1044,8 @@
// 涓婁紶鍓嶆牎妫�
function handleBeforeUpload(file) {
// 鏍℃鏂囦欢澶у皬
- if (file.size > 1024 * 1024 * 10) {
- proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+ if (file.size > 1024 * 1024 * 50) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃50MB!");
return false;
}
proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
@@ -1152,6 +1384,194 @@
}
};
+// 鏄剧ず浜岀淮鐮�
+const showQRCode = async (row) => {
+ try {
+ // 鏋勫缓浜岀淮鐮佸唴瀹癸紝鍙寘鍚噰璐悎鍚屽彿锛堢函鏂囨湰锛�
+ const qrContent = row.purchaseContractNumber || '';
+ // 妫�鏌ュ唴瀹规槸鍚︿负绌�
+ if (!qrContent || qrContent.trim() === '') {
+ proxy.$modal.msgWarning("璇ヨ娌℃湁閲囪喘鍚堝悓鍙凤紝鏃犳硶鐢熸垚浜岀淮鐮�");
+ return;
+ }
+ qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
+ width: 200,
+ margin: 2,
+ color: {
+ dark: '#000000',
+ light: '#FFFFFF'
+ }
+ });
+ qrCodeDialogVisible.value = true;
+ } catch (error) {
+ console.error('鐢熸垚浜岀淮鐮佸け璐�:', error);
+ proxy.$modal.msgError("鐢熸垚浜岀淮鐮佸け璐ワ細" + error.message);
+ }
+};
+
+// 涓嬭浇浜岀淮鐮�
+const downloadQRCode = () => {
+ if (!qrCodeUrl.value) {
+ proxy.$modal.msgWarning("浜岀淮鐮佹湭鐢熸垚");
+ return;
+ }
+
+ const a = document.createElement('a');
+ a.href = qrCodeUrl.value;
+ a.download = `閲囪喘鍚堝悓鍙蜂簩缁寸爜_${new Date().getTime()}.png`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
+};
+
+// 鎵爜鏂板瀵硅瘽妗嗙浉鍏冲彉閲�
+const scanAddDialogVisible = ref(false);
+const scanAddForm = reactive({
+ scanContent: "",
+ purchaseContractNumber: "",
+ supplierName: "",
+ projectName: "",
+ contractAmount: "",
+ paymentMethod: "",
+ recorderName: "",
+ scanRemark: "",
+});
+const scanAddRules = {
+ purchaseContractNumber: [{ required: true, message: "璇疯緭鍏ラ噰璐悎鍚屽彿", trigger: "blur" }],
+ supplierName: [{ required: true, message: "璇疯緭鍏ヤ緵搴斿晢鍚嶇О", trigger: "blur" }],
+ projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
+};
+
+// 鎵爜鐧昏瀵硅瘽妗嗙浉鍏冲彉閲�
+const scanDialogVisible = ref(false);
+const scanForm = reactive({
+ purchaseContractNumber: "",
+ supplierName: "",
+ projectName: "",
+ scanTime: "",
+ scannerName: "",
+ scanStatus: "鏈壂鐮�",
+ scanRemark: "",
+});
+const scanRules = {
+ scanRemark: [{ required: true, message: "璇疯緭鍏ユ壂鐮佸娉�", trigger: "blur" }],
+};
+const scanRecords = ref([]);
+
+// 鎵撳紑鎵爜鏂板瀵硅瘽妗�
+const openScanAddDialog = () => {
+ scanAddForm.scanContent = "";
+ scanAddForm.purchaseContractNumber = "";
+ scanAddForm.supplierName = "";
+ scanAddForm.projectName = "";
+ scanAddForm.contractAmount = "";
+ scanAddForm.paymentMethod = "";
+ scanAddForm.recorderName = userStore.nickName;
+ scanAddForm.scanRemark = "";
+ scanAddDialogVisible.value = true;
+};
+
+// 瑙f瀽鎵爜鍐呭锛堟ā鎷熻В鏋愪簩缁寸爜鏁版嵁锛�
+const parseScanContent = (content) => {
+ if (!content) return;
+
+ // 妯℃嫙瑙f瀽浜岀淮鐮佸唴瀹癸紝杩欓噷鍙互鏍规嵁瀹為檯闇�姹傝皟鏁磋В鏋愰�昏緫
+ // 鍋囪鎵爜鍐呭鏍煎紡涓猴細鍚堝悓鍙穦渚涘簲鍟唡椤圭洰|閲戦|浠樻鏂瑰紡
+ const parts = content.split('|');
+ if (parts.length >= 3) {
+ scanAddForm.purchaseContractNumber = parts[0] || "";
+ scanAddForm.supplierName = parts[1] || "";
+ scanAddForm.projectName = parts[2] || "";
+ scanAddForm.contractAmount = parts[3] || "";
+ scanAddForm.paymentMethod = parts[4] || "";
+ }
+};
+
+// 鍏抽棴鎵爜鏂板瀵硅瘽妗�
+const closeScanAddDialog = () => {
+ scanAddDialogVisible.value = false;
+ proxy.resetForm("scanAddFormRef");
+};
+
+// 鎻愪氦鎵爜鏂板
+const submitScanAdd = () => {
+ proxy.$refs["scanAddFormRef"].validate((valid) => {
+ if (valid) {
+ // 鏋勫缓鏂板鏁版嵁
+ const newData = {
+ purchaseContractNumber: scanAddForm.purchaseContractNumber,
+ supplierName: scanAddForm.supplierName,
+ projectName: scanAddForm.projectName,
+ contractAmount: scanAddForm.contractAmount,
+ paymentMethod: scanAddForm.paymentMethod,
+ recorderName: scanAddForm.recorderName,
+ entryDate: getCurrentDate(),
+ remark: scanAddForm.scanRemark,
+ type: 2
+ };
+
+ // 妯℃嫙鏂板鎴愬姛
+ proxy.$modal.msgSuccess("鎵爜鏂板鎴愬姛锛�");
+ closeScanAddDialog();
+
+ // 鍙互閫夋嫨鏄惁鍒锋柊鍒楄〃
+ // getList();
+ }
+ });
+};
+
+// 鎵撳紑鎵爜鐧昏瀵硅瘽妗�
+const openScanDialog = (row) => {
+ scanForm.purchaseContractNumber = row.purchaseContractNumber;
+ scanForm.supplierName = row.supplierName;
+ scanForm.projectName = row.projectName;
+ scanForm.scanTime = getCurrentDateTime();
+ scanForm.scannerName = userStore.nickName;
+ scanForm.scanStatus = "鏈壂鐮�";
+ scanForm.scanRemark = "";
+ scanRecords.value = [];
+ scanDialogVisible.value = true;
+};
+
+// 鍏抽棴鎵爜鐧昏瀵硅瘽妗�
+const closeScanDialog = () => {
+ scanDialogVisible.value = false;
+ proxy.resetForm("scanFormRef");
+};
+
+// 鎻愪氦鎵爜鐧昏
+const submitScan = () => {
+ proxy.$refs["scanFormRef"].validate((valid) => {
+ if (valid) {
+ // 娣诲姞鎵爜璁板綍
+ scanRecords.value.push({
+ ...scanForm,
+ id: Date.now(), // 妯℃嫙ID
+ scanTime: getCurrentDateTime(),
+ });
+ scanForm.scanStatus = "宸叉壂鐮�";
+ scanForm.scanRemark = scanForm.scanRemark || "鏃�";
+ proxy.$modal.msgSuccess("鎵爜鐧昏鎴愬姛锛�");
+ closeScanDialog();
+ }
+ });
+};
+
+// 鑾峰彇褰撳墠鏃ユ湡鏃堕棿
+function getCurrentDateTime() {
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = String(now.getMonth() + 1).padStart(2, "0");
+ const day = String(now.getDate()).padStart(2, "0");
+ const hours = String(now.getHours()).padStart(2, "0");
+ const minutes = String(now.getMinutes()).padStart(2, "0");
+ const seconds = String(now.getSeconds()).padStart(2, "0");
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+}
+
+
+
onMounted(() => {
getList();
});
diff --git a/src/views/productionManagement/operationScheduling/components/formDia.vue b/src/views/productionManagement/operationScheduling/components/formDia.vue
index ad37c4b..b8d1428 100644
--- a/src/views/productionManagement/operationScheduling/components/formDia.vue
+++ b/src/views/productionManagement/operationScheduling/components/formDia.vue
@@ -8,7 +8,7 @@
>
<el-button type="primary" @click="addRow" style="margin-bottom: 10px;">鏂板</el-button>
<span style="font-size: 18px;margin-left: 10px">寰呮帓浜ф暟閲忥細{{pendingNum}}</span>
- <el-table :data="tableData" border style="width: 100%" :summary-method="summarizeMainTable" show-summary :row-key="row => row.id">
+ <el-table :data="tableData" border style="width: 100%" :summary-method="summarizeMainTable" show-summary :row-key="row => row.id" stripe>
<el-table-column label="搴忓彿" width="60">
<template #default="scope">
{{ scope.$index + 1 }}
diff --git a/src/views/productionManagement/productionReporting/index.vue b/src/views/productionManagement/productionReporting/index.vue
index 3ceae74..bcb2d5d 100644
--- a/src/views/productionManagement/productionReporting/index.vue
+++ b/src/views/productionManagement/productionReporting/index.vue
@@ -50,6 +50,7 @@
:data="expandData"
border
show-summary
+ stripe
:summary-method="summarizeMainTable"
v-loading="childrenLoading"
>
diff --git a/src/views/productionManagement/safetyMonitoring/index.vue b/src/views/productionManagement/safetyMonitoring/index.vue
new file mode 100644
index 0000000..56f65e7
--- /dev/null
+++ b/src/views/productionManagement/safetyMonitoring/index.vue
@@ -0,0 +1,873 @@
+<template>
+ <div class="safety-monitoring">
+ <el-row :gutter="20">
+ <!-- 宸︿晶锛氬疄鏃剁洃鎺у尯鍩� -->
+ <el-col :span="16">
+ <el-card class="monitoring-card">
+ <div slot="header" class="card-header">
+ <span>瀹炴椂姘斾綋娴撳害鐩戞帶</span>
+ <el-tag :type="systemStatus === 'normal' ? 'success' : 'danger'">
+ {{ systemStatus === 'normal' ? '绯荤粺姝e父' : '绯荤粺鍛婅' }}
+ </el-tag>
+ </div>
+
+ <!-- 鍌ㄧ綈鍖虹洃鎺� -->
+ <div class="monitoring-section">
+ <h3>鍌ㄧ綈鍖虹洃鎺�</h3>
+ <div class="sensor-grid">
+ <div class="sensor-item" v-for="sensor in tankSensors" :key="sensor.id">
+ <div class="sensor-header">
+ <span>{{ sensor.name }}</span>
+ <el-tag :type="sensor.status === 'normal' ? 'success' : 'danger'" size="small">
+ {{ sensor.status === 'normal' ? '姝e父' : '瓒呮爣' }}
+ </el-tag>
+ </div>
+ <div class="sensor-data">
+ <div class="data-item">
+ <span>鐢茬兎: {{ sensor.methane.toFixed(2) }}%</span>
+ <el-progress
+ :percentage="Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)"
+ :color="getProgressColor(Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100), 80)"
+ :format="formatProgress"
+ :stroke-width="8"
+ />
+ </div>
+ <div class="data-item">
+ <span>纭寲姘�: {{ sensor.h2s.toFixed(2) }}ppm</span>
+ <el-progress
+ :percentage="Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)"
+ :color="getProgressColor(Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100), 80)"
+ :format="formatProgress"
+ :stroke-width="8"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 浜曞彛鍘嬬缉鏈虹洃鎺� -->
+ <div class="monitoring-section">
+ <h3>浜曞彛鍘嬬缉鏈虹洃鎺�</h3>
+ <div class="sensor-grid">
+ <div class="sensor-item" v-for="sensor in compressorSensors" :key="sensor.id">
+ <div class="sensor-header">
+ <span>{{ sensor.name }}</span>
+ <el-tag :type="sensor.status === 'normal' ? 'success' : 'danger'" size="small">
+ {{ sensor.status === 'normal' ? '姝e父' : '瓒呮爣' }}
+ </el-tag>
+ </div>
+ <div class="sensor-data">
+ <div class="data-item">
+ <span>鐢茬兎: {{ sensor.methane.toFixed(2) }}%</span>
+ <el-progress
+ :percentage="Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)"
+ :color="getProgressColor(sensor.methane, 2.5)"
+ :format="formatProgress"
+ :stroke-width="8"
+ />
+ </div>
+ <div class="data-item">
+ <span>纭寲姘�: {{ sensor.h2s.toFixed(2) }}ppm</span>
+ <el-progress
+ :percentage="Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)"
+ :color="getProgressColor(sensor.h2s, 10)"
+ :format="formatProgress"
+ :stroke-width="8"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 瀹炴椂鏇茬嚎鍥� -->
+ <div class="chart-section">
+ <h3>瀹炴椂娴撳害鏇茬嚎</h3>
+ <div class="chart-container">
+ <div ref="chart" class="chart"></div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+
+ <!-- 鍙充晶锛氭帶鍒堕潰鏉� -->
+ <el-col :span="8">
+ <el-card class="control-card">
+ <div slot="header" class="card-header">
+ <span>搴旀�ユ帶鍒堕潰鏉�</span>
+ </div>
+
+ <!-- 鍠锋穻鐘舵�� -->
+ <div class="control-section">
+ <h4>鍠锋穻绯荤粺鐘舵��</h4>
+ <div class="status-grid">
+ <div class="status-item" v-for="sprinkler in sprinklerSystems" :key="sprinkler.id">
+ <div class="status-indicator" :class="sprinkler.status">
+ <i class="el-icon-circle-check" v-if="sprinkler.status === 'active'"></i>
+ <i class="el-icon-circle-close" v-else></i>
+ </div>
+ <span>{{ sprinkler.name }}</span>
+ <el-tag :type="sprinkler.status === 'active' ? 'success' : 'info'" size="small">
+ {{ sprinkler.status === 'active' ? '杩愯涓�' : '寰呮満' }}
+ </el-tag>
+ </div>
+ </div>
+ </div>
+
+ <!-- 搴旀�ヨ褰曟寜閽� -->
+ <h4>搴旀�ョ鐞�</h4>
+
+ <div class="control-section1">
+ <el-button type="primary" @click="showEmergencyRecords" style="margin-bottom: 10px;">
+ 搴旀�ヨ褰�
+ </el-button>
+ <el-button type="warning" @click="triggerEmergency" :disabled="!hasEmergency">
+ 瑙﹀彂搴旀�ュ搷搴�
+ </el-button>
+ </div>
+
+ <!-- 绯荤粺鏃ュ織 -->
+ <div class="control-section">
+ <h4>绯荤粺鏃ュ織</h4>
+ <div class="log-container">
+ <div class="log-item" v-for="log in systemLogs" :key="log.id">
+ <span class="log-time">{{ log.time }}</span>
+ <span class="log-content">{{ log.content }}</span>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <!-- 娉勬紡棰勮寮圭獥 -->
+ <el-dialog
+ title="鈿狅笍 娉勬紡棰勮"
+ :visible.sync="leakWarningVisible"
+ width="500px"
+ :close-on-click-modal="false"
+ :close-on-press-escape="false"
+ class="leak-warning-dialog"
+ >
+ <div class="warning-content">
+ <div class="warning-icon">
+ <i class="el-icon-warning"></i>
+ </div>
+ <div class="warning-text">
+ <h3>妫�娴嬪埌姘斾綋娴撳害瓒呮爣锛�</h3>
+ <p>浣嶇疆锛歿{ currentWarning.location }}</p>
+ <p>瓒呮爣姘斾綋锛歿{ currentWarning.gas }}</p>
+ <p>褰撳墠娴撳害锛歿{ currentWarning.value }}</p>
+ </div>
+ </div>
+ <div slot="footer" class="dialog-footer">
+ <el-button type="danger" @click="acknowledgeWarning">纭鍛婅</el-button>
+ <el-button type="primary" @click="viewDetails">鏌ョ湅璇︽儏</el-button>
+ </div>
+ </el-dialog>
+
+ <!-- 搴旀�ヨ褰曞脊绐� -->
+ <el-dialog
+ title="搴旀�ヨ褰�"
+ :visible.sync="emergencyRecordsVisible"
+ width="800px"
+ >
+ <el-table :data="emergencyRecords" style="width: 100%" stripe>
+ <el-table-column prop="time" label="鏃堕棿" width="180"></el-table-column>
+ <el-table-column prop="location" label="浣嶇疆" width="150"></el-table-column>
+ <el-table-column prop="type" label="绫诲瀷" width="120"></el-table-column>
+ <el-table-column prop="status" label="鐘舵��" width="100">
+ <template slot-scope="scope">
+ <el-tag :type="scope.row.status === 'resolved' ? 'success' : 'warning'">
+ {{ scope.row.status === 'resolved' ? '宸茶В鍐�' : '澶勭悊涓�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="description" label="鎻忚堪"></el-table-column>
+ <el-table-column label="鎿嶄綔" width="120">
+ <template slot-scope="scope">
+ <el-button type="text" @click="viewBlockchainDetails(scope.row)">
+ 鍖哄潡閾捐鎯�
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-dialog>
+
+ <!-- 鍖哄潡閾惧瓨璇佽鎯呭脊绐� -->
+ <el-dialog
+ title="鍖哄潡閾惧瓨璇佽鎯�"
+ :visible.sync="blockchainDetailsVisible"
+ width="900px"
+ >
+ <div class="blockchain-details">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="浜嬩欢ID">{{ currentEvent.id }}</el-descriptions-item>
+ <el-descriptions-item label="鏃堕棿鎴�">{{ currentEvent.timestamp }}</el-descriptions-item>
+ <el-descriptions-item label="浣嶇疆">{{ currentEvent.location }}</el-descriptions-item>
+ <el-descriptions-item label="浜嬩欢绫诲瀷">{{ currentEvent.type }}</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="sensor-data-section">
+ <h4>浼犳劅鍣ㄦ暟鎹�</h4>
+ <el-table :data="currentEvent.sensorData" style="width: 100%" stripe>
+ <el-table-column prop="sensor" label="浼犳劅鍣�"></el-table-column>
+ <el-table-column prop="methane" label="鐢茬兎娴撳害"></el-table-column>
+ <el-table-column prop="h2s" label="纭寲姘㈡祿搴�"></el-table-column>
+ <el-table-column prop="timestamp" label="璁板綍鏃堕棿"></el-table-column>
+ </el-table>
+ </div>
+
+ <div class="action-log-section">
+ <h4>澶勭疆鍔ㄤ綔璁板綍</h4>
+ <el-timeline>
+ <el-timeline-item
+ v-for="action in currentEvent.actions"
+ :key="action.id"
+ :timestamp="action.timestamp"
+ :type="action.type === 'emergency' ? 'danger' : 'primary'"
+ >
+ {{ action.description }}
+ </el-timeline-item>
+ </el-timeline>
+ </div>
+
+ <div class="blockchain-info">
+ <h4>鍖哄潡閾句俊鎭�</h4>
+ <el-descriptions :column="1" border>
+ <el-descriptions-item label="鍖哄潡鍝堝笇">{{ currentEvent.blockHash }}</el-descriptions-item>
+ <el-descriptions-item label="浜ゆ槗鍝堝笇">{{ currentEvent.txHash }}</el-descriptions-item>
+ <el-descriptions-item label="纭鏁�">{{ currentEvent.confirmations }}</el-descriptions-item>
+ </el-descriptions>
+ </div>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+
+export default {
+ name: 'SafetyMonitoring',
+ data() {
+ return {
+ systemStatus: 'normal',
+ leakWarningVisible: false,
+ emergencyRecordsVisible: false,
+ blockchainDetailsVisible: false,
+ currentWarning: {},
+ currentEvent: {},
+ hasEmergency: false,
+
+ // 鍌ㄧ綈鍖轰紶鎰熷櫒鏁版嵁
+ tankSensors: [
+ { id: 1, name: '鍌ㄧ綈T-001', methane: 1.20, h2s: 2.10, status: 'normal' },
+ { id: 2, name: '鍌ㄧ綈T-002', methane: 0.80, h2s: 1.50, status: 'normal' },
+ { id: 3, name: '鍌ㄧ綈T-003', methane: 3.20, h2s: 8.50, status: 'warning' },
+ { id: 4, name: '鍌ㄧ綈T-004', methane: 0.60, h2s: 0.80, status: 'normal' }
+ ],
+
+ // 浜曞彛鍘嬬缉鏈轰紶鎰熷櫒鏁版嵁
+ compressorSensors: [
+ { id: 5, name: '鍘嬬缉鏈篊-001', methane: 2.10, h2s: 3.20, status: 'normal' },
+ { id: 6, name: '鍘嬬缉鏈篊-002', methane: 4.80, h2s: 12.50, status: 'warning' },
+ { id: 7, name: '鍘嬬缉鏈篊-003', methane: 1.80, h2s: 2.80, status: 'normal' }
+ ],
+
+ // 鍠锋穻绯荤粺鐘舵��
+ sprinklerSystems: [
+ { id: 1, name: '鍌ㄧ綈鍖哄柗娣�', status: 'active' },
+ { id: 2, name: '鍘嬬缉鏈哄尯鍠锋穻', status: 'standby' },
+ { id: 3, name: '绱ф�ュ柗娣�', status: 'standby' }
+ ],
+
+ // 绯荤粺鏃ュ織
+ systemLogs: [
+ { id: 1, time: '14:30:25', content: '绯荤粺鍚姩瀹屾垚锛屾墍鏈変紶鎰熷櫒姝e父' },
+ { id: 2, time: '14:35:12', content: '鍌ㄧ綈T-003鐢茬兎娴撳害瓒呮爣锛岃Е鍙戦璀�' },
+ { id: 3, time: '14:35:15', content: '鍚姩鍌ㄧ綈鍖哄柗娣嬬郴缁�' },
+ { id: 4, time: '14:35:20', content: '鍙戦�佺揣鎬ョ枏鏁e箍鎾�' }
+ ],
+
+ // 搴旀�ヨ褰�
+ emergencyRecords: [
+ {
+ id: 'EM001',
+ time: '2024-01-15 14:35:12',
+ location: '鍌ㄧ綈T-003',
+ type: '鐢茬兎瓒呮爣',
+ status: 'resolved',
+ description: '鍌ㄧ綈T-003鐢茬兎娴撳害杈惧埌3.2%锛岃秴杩囧畨鍏ㄩ槇鍊�2.5%'
+ },
+ {
+ id: 'EM002',
+ time: '2024-01-15 14:35:15',
+ location: '鍘嬬缉鏈篊-002',
+ type: '纭寲姘㈣秴鏍�',
+ status: 'processing',
+ description: '鍘嬬缉鏈篊-002纭寲姘㈡祿搴﹁揪鍒�12.5ppm锛岃秴杩囧畨鍏ㄩ槇鍊�10ppm'
+ }
+ ],
+
+ // 鍥捐〃瀹炰緥
+ chart: null,
+
+ // 瀹氭椂鍣�
+ timer: null
+ }
+ },
+
+ mounted() {
+ this.initChart()
+ this.startDataRefresh()
+ this.checkEmergencyStatus()
+ },
+
+ beforeDestroy() {
+ if (this.timer) {
+ clearInterval(this.timer)
+ }
+ if (this.chart) {
+ this.chart.dispose()
+ }
+ },
+
+ methods: {
+ // 缁熶竴杩涘害鏉℃牸寮忓寲涓轰袱浣嶅皬鏁帮紝閬垮厤娴偣璇樊鏄剧ず
+ formatProgress(percentage) {
+ if (percentage == null || isNaN(percentage)) return '0.00%'
+ const val = Math.round(Number(percentage) * 100) / 100
+ return `${val.toFixed(2)}%`
+ },
+ // 鍒濆鍖栧浘琛�
+ initChart() {
+ this.chart = echarts.init(this.$refs.chart)
+ this.updateChart()
+ },
+
+ // 鏇存柊鍥捐〃鏁版嵁
+ updateChart() {
+ const option = {
+ title: {
+ text: '瀹炴椂姘斾綋娴撳害鐩戞帶',
+ left: 'center'
+ },
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'cross'
+ }
+ },
+ legend: {
+ data: ['鍌ㄧ綈鍖虹敳鐑�', '鍌ㄧ綈鍖虹~鍖栨阿', '鍘嬬缉鏈虹敳鐑�', '鍘嬬缉鏈虹~鍖栨阿'],
+ top: 30
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ top: '15%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ data: this.generateTimeData()
+ },
+ yAxis: [
+ {
+ type: 'value',
+ name: '鐢茬兎娴撳害(%)',
+ position: 'left'
+ },
+ {
+ type: 'value',
+ name: '纭寲姘㈡祿搴�(ppm)',
+ position: 'right'
+ }
+ ],
+ series: [
+ {
+ name: '鍌ㄧ綈鍖虹敳鐑�',
+ type: 'line',
+ data: this.generateRandomData(20, 0.5, 3.5),
+ smooth: true,
+ yAxisIndex: 0
+ },
+ {
+ name: '鍌ㄧ綈鍖虹~鍖栨阿',
+ type: 'line',
+ data: this.generateRandomData(20, 0.5, 12),
+ smooth: true,
+ yAxisIndex: 1
+ },
+ {
+ name: '鍘嬬缉鏈虹敳鐑�',
+ type: 'line',
+ data: this.generateRandomData(20, 1.0, 5.0),
+ smooth: true,
+ yAxisIndex: 0
+ },
+ {
+ name: '鍘嬬缉鏈虹~鍖栨阿',
+ type: 'line',
+ data: this.generateRandomData(20, 1.0, 15),
+ smooth: true,
+ yAxisIndex: 1
+ }
+ ]
+ }
+
+ this.chart.setOption(option)
+ },
+
+ // 鐢熸垚鏃堕棿鏁版嵁
+ generateTimeData() {
+ const times = []
+ const now = new Date()
+ for (let i = 19; i >= 0; i--) {
+ const time = new Date(now.getTime() - i * 5 * 60 * 1000)
+ times.push(time.toLocaleTimeString('zh-CN', { hour12: false }))
+ }
+ return times
+ },
+
+ // 鐢熸垚闅忔満鏁版嵁
+ generateRandomData(count, min, max) {
+ const data = []
+ for (let i = 0; i < count; i++) {
+ data.push(+(Math.random() * (max - min) + min).toFixed(2))
+ }
+ return data
+ },
+
+ // 寮�濮嬫暟鎹埛鏂�
+ startDataRefresh() {
+ this.timer = setInterval(() => {
+ this.refreshSensorData()
+ this.updateChart()
+ this.checkEmergencyStatus()
+ }, 5000) // 姣�5绉掑埛鏂颁竴娆�
+ },
+
+ // 鍒锋柊浼犳劅鍣ㄦ暟鎹�
+ refreshSensorData() {
+ // 鏇存柊鍌ㄧ綈鍖轰紶鎰熷櫒鏁版嵁
+ this.tankSensors.forEach(sensor => {
+ sensor.methane = +(Math.random() * 4).toFixed(2)
+ sensor.h2s = +(Math.random() * 15).toFixed(2)
+ sensor.status = this.getSensorStatus(sensor.methane, sensor.h2s)
+ })
+
+ // 鏇存柊鍘嬬缉鏈轰紶鎰熷櫒鏁版嵁
+ this.compressorSensors.forEach(sensor => {
+ sensor.methane = +(Math.random() * 6).toFixed(2)
+ sensor.h2s = +(Math.random() * 20).toFixed(2)
+ sensor.status = this.getSensorStatus(sensor.methane, sensor.h2s)
+ })
+
+ // 妫�鏌ユ槸鍚﹂渶瑕佽Е鍙戦璀�
+ this.checkLeakWarning()
+ },
+
+ // 鑾峰彇浼犳劅鍣ㄧ姸鎬�
+ getSensorStatus(methane, h2s) {
+ const methanePct = Math.min(Math.round(methane * 40 * 100) / 100, 100)
+ const h2sPct = Math.min(Math.round((h2s / 20) * 100 * 100) / 100, 100)
+ if (methanePct >= 80 || h2sPct >= 80) {
+ return 'warning'
+ }
+ return 'normal'
+ },
+
+ // 妫�鏌ユ硠婕忛璀�
+ checkLeakWarning() {
+ const allSensors = [...this.tankSensors, ...this.compressorSensors]
+ const warningSensor = allSensors.find(sensor => this.getSensorStatus(sensor.methane, sensor.h2s) === 'warning')
+
+ if (warningSensor && !this.leakWarningVisible) {
+ this.triggerLeakWarning(warningSensor)
+ }
+ },
+
+ // 瑙﹀彂娉勬紡棰勮
+ triggerLeakWarning(sensor) {
+ const methanePct = Math.min(Math.round(sensor.methane * 40 * 100) / 100, 100)
+ const h2sPct = Math.min(Math.round((sensor.h2s / 20) * 100 * 100) / 100, 100)
+ const isMethaneMajor = methanePct >= h2sPct
+ const overGas = isMethaneMajor ? '鐢茬兎' : '纭寲姘�'
+ const percent = (isMethaneMajor ? methanePct : h2sPct).toFixed(2)
+ this.currentWarning = {
+ location: sensor.name,
+ gas: overGas,
+ value: `${percent}%`
+ }
+
+ this.leakWarningVisible = true
+ this.hasEmergency = true
+
+ // 鑷姩瑙﹀彂搴旀�ュ搷搴�
+ this.autoEmergencyResponse(sensor)
+
+ // 娣诲姞绯荤粺鏃ュ織
+ this.addSystemLog(`妫�娴嬪埌${sensor.name}姘斾綋娴撳害瓒呮爣锛岃Е鍙戞硠婕忛璀)
+ },
+
+ // 鑷姩搴旀�ュ搷搴�
+ autoEmergencyResponse(sensor) {
+ // 鍚姩鍠锋穻绯荤粺
+ if (sensor.name.includes('鍌ㄧ綈')) {
+ this.sprinklerSystems[0].status = 'active'
+ } else if (sensor.name.includes('鍘嬬缉鏈�')) {
+ this.sprinklerSystems[1].status = 'active'
+ }
+
+ // 娣诲姞绯荤粺鏃ュ織
+ this.addSystemLog(`鍚姩${sensor.name}鍖哄煙鍠锋穻绯荤粺`)
+ this.addSystemLog(`鍙戦�佺揣鎬ョ枏鏁e箍鎾璥)
+
+ // 鍒涘缓搴旀�ヨ褰�
+ this.createEmergencyRecord(sensor)
+ },
+
+ // 娣诲姞绯荤粺鏃ュ織
+ addSystemLog(content) {
+ const now = new Date()
+ const time = now.toLocaleTimeString('zh-CN', { hour12: false })
+
+ this.systemLogs.unshift({
+ id: Date.now(),
+ time: time,
+ content: content
+ })
+
+ // 淇濇寔鏈�澶�20鏉℃棩蹇�
+ if (this.systemLogs.length > 20) {
+ this.systemLogs = this.systemLogs.slice(0, 20)
+ }
+ },
+
+ // 鍒涘缓搴旀�ヨ褰�
+ createEmergencyRecord(sensor) {
+ const now = new Date()
+ const record = {
+ id: `EM${Date.now()}`,
+ time: now.toLocaleString('zh-CN'),
+ location: sensor.name,
+ type: sensor.methane > 2.5 ? '鐢茬兎瓒呮爣' : '纭寲姘㈣秴鏍�',
+ status: 'processing',
+ description: `${sensor.name}妫�娴嬪埌${sensor.methane > 2.5 ? '鐢茬兎' : '纭寲姘�'}娴撳害瓒呮爣`
+ }
+
+ this.emergencyRecords.unshift(record)
+ },
+
+ // 鑾峰彇杩涘害鏉¢鑹�
+ getProgressColor(value, threshold) {
+ if (value > threshold) {
+ return '#F56C6C'
+ } else if (value > threshold * 0.8) {
+ return '#E6A23C'
+ }
+ return '#67C23A'
+ },
+
+ // 妫�鏌ュ簲鎬ョ姸鎬�
+ checkEmergencyStatus() {
+ const allSensors = [...this.tankSensors, ...this.compressorSensors]
+ const has = allSensors.some(sensor => this.getSensorStatus(sensor.methane, sensor.h2s) === 'warning')
+ this.hasEmergency = has
+ this.systemStatus = has ? 'warning' : 'normal'
+ },
+
+ // 纭鍛婅
+ acknowledgeWarning() {
+ this.leakWarningVisible = false
+ this.addSystemLog('娉勬紡棰勮宸茬‘璁�')
+ },
+
+ // 鏌ョ湅璇︽儏
+ viewDetails() {
+ this.leakWarningVisible = false
+ // 杩欓噷鍙互璺宠浆鍒拌缁嗛〉闈㈡垨鏄剧ず鏇村淇℃伅
+ },
+
+ // 鏄剧ず搴旀�ヨ褰�
+ showEmergencyRecords() {
+ this.emergencyRecordsVisible = true
+ },
+
+ // 鏌ョ湅鍖哄潡閾捐鎯�
+ viewBlockchainDetails(record) {
+ this.currentEvent = {
+ id: record.id,
+ timestamp: record.time,
+ location: record.location,
+ type: record.type,
+ sensorData: [
+ {
+ sensor: '鐢茬兎浼犳劅鍣�',
+ methane: '3.2%',
+ h2s: '8.5ppm',
+ timestamp: record.time
+ },
+ {
+ sensor: '纭寲姘紶鎰熷櫒',
+ methane: '2.8%',
+ h2s: '12.5ppm',
+ timestamp: record.time
+ }
+ ],
+ actions: [
+ {
+ id: 1,
+ timestamp: record.time,
+ type: 'emergency',
+ description: '妫�娴嬪埌姘斾綋娴撳害瓒呮爣锛岃Е鍙戦璀�'
+ },
+ {
+ id: 2,
+ timestamp: new Date(new Date(record.time).getTime() + 3000).toLocaleString('zh-CN'),
+ type: 'action',
+ description: '鍚姩鍠锋穻绯荤粺闄嶆俯'
+ },
+ {
+ id: 3,
+ timestamp: new Date(new Date(record.time).getTime() + 5000).toLocaleString('zh-CN'),
+ type: 'action',
+ description: '鍙戦�佺揣鎬ョ枏鏁e箍鎾�'
+ }
+ ],
+ blockHash: '0x1234567890abcdef...',
+ txHash: '0xabcdef1234567890...',
+ confirmations: 12
+ }
+
+ this.emergencyRecordsVisible = false
+ this.blockchainDetailsVisible = true
+ },
+
+ // 瑙﹀彂搴旀�ュ搷搴�
+ triggerEmergency() {
+ this.$message.success('搴旀�ュ搷搴斿凡瑙﹀彂')
+ this.addSystemLog('鎵嬪姩瑙﹀彂搴旀�ュ搷搴�')
+ }
+ }
+}
+</script>
+
+<style scoped>
+.safety-monitoring {
+ padding: 20px;
+ background-color: #f5f7fa;
+ min-height: calc(100vh - 84px);
+}
+
+.monitoring-card, .control-card {
+ margin-bottom: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.monitoring-section {
+ margin-bottom: 30px;
+}
+
+.monitoring-section h3 {
+ color: #303133;
+ margin-bottom: 15px;
+ padding-bottom: 8px;
+ border-bottom: 2px solid #409EFF;
+}
+
+.sensor-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 15px;
+}
+
+.sensor-item {
+ background: #fff;
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ padding: 15px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.sensor-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+ font-weight: bold;
+}
+
+.sensor-data .data-item {
+ margin-bottom: 12px;
+}
+
+.sensor-data .data-item span {
+ display: block;
+ margin-bottom: 5px;
+ font-size: 14px;
+ color: #606266;
+}
+
+.chart-section {
+ margin-top: 30px;
+}
+
+.chart-section h3 {
+ color: #303133;
+ margin-bottom: 15px;
+ padding-bottom: 8px;
+ border-bottom: 2px solid #409EFF;
+}
+
+.chart-container {
+ background: #fff;
+ border-radius: 8px;
+ padding: 20px;
+}
+
+.chart {
+ width: 100%;
+ height: 400px;
+}
+
+.control-section {
+ margin-bottom: 25px;
+}
+.control-section1 {
+ display: flex;
+}
+
+.control-section h4 {
+ color: #303133;
+ margin-bottom: 15px;
+ font-size: 16px;
+}
+
+.status-grid {
+ display: grid;
+ gap: 10px;
+}
+
+.status-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px;
+ background: #f8f9fa;
+ border-radius: 6px;
+}
+
+.status-indicator {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.status-indicator.active {
+ color: #67C23A;
+}
+
+.status-indicator.standby {
+ color: #909399;
+}
+
+.log-container {
+ max-height: 200px;
+ overflow-y: auto;
+ background: #f8f9fa;
+ border-radius: 6px;
+ padding: 10px;
+}
+
+.log-item {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 8px;
+ font-size: 12px;
+}
+
+.log-time {
+ color: #909399;
+ min-width: 60px;
+}
+
+.log-content {
+ color: #606266;
+}
+
+/* 娉勬紡棰勮寮圭獥鏍峰紡 */
+.leak-warning-dialog {
+ background: #fff5f5;
+}
+
+.warning-content {
+ text-align: center;
+ padding: 20px 0;
+}
+
+.warning-icon {
+ font-size: 60px;
+ color: #F56C6C;
+ margin-bottom: 20px;
+}
+
+.warning-text h3 {
+ color: #F56C6C;
+ margin-bottom: 15px;
+}
+
+.warning-text p {
+ margin: 8px 0;
+ color: #606266;
+}
+
+/* 鍖哄潡閾捐鎯呮牱寮� */
+.blockchain-details {
+ padding: 20px 0;
+}
+
+.sensor-data-section, .action-log-section, .blockchain-info {
+ margin-top: 25px;
+}
+
+.sensor-data-section h4, .action-log-section h4, .blockchain-info h4 {
+ color: #303133;
+ margin-bottom: 15px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #e4e7ed;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 1200px) {
+ .sensor-grid {
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ }
+}
+
+@media (max-width: 768px) {
+ .safety-monitoring {
+ padding: 10px;
+ }
+
+ .sensor-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .chart {
+ height: 300px;
+ }
+}
+</style>
diff --git a/src/views/qualityManagement/finalInspection/components/formDia.vue b/src/views/qualityManagement/finalInspection/components/formDia.vue
index 34f5990..6547e3f 100644
--- a/src/views/qualityManagement/finalInspection/components/formDia.vue
+++ b/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");
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index e8dcee6..3f2595f 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/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();
});
diff --git a/src/views/qualityManagement/processInspection/components/filesDia.vue b/src/views/qualityManagement/processInspection/components/filesDia.vue
index 66392f3..fb47850 100644
--- a/src/views/qualityManagement/processInspection/components/filesDia.vue
+++ b/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 = [];
diff --git a/src/views/qualityManagement/processInspection/components/formDia.vue b/src/views/qualityManagement/processInspection/components/formDia.vue
index 0ded52e..c24d425 100644
--- a/src/views/qualityManagement/processInspection/components/formDia.vue
+++ b/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");
diff --git a/src/views/qualityManagement/processInspection/index.vue b/src/views/qualityManagement/processInspection/index.vue
index 04ed1f3..6f5ddec 100644
--- a/src/views/qualityManagement/processInspection/index.vue
+++ b/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("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue
index 15a17cd..9907fbe 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue
+++ b/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 = [];
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
index b61f84a..977b420 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
+++ b/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 => {
diff --git a/src/views/qualityManagement/rawMaterialInspection/index.vue b/src/views/qualityManagement/rawMaterialInspection/index.vue
index 16db3eb..7536274 100644
--- a/src/views/qualityManagement/rawMaterialInspection/index.vue
+++ b/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,6 +168,9 @@
clickFun: (row) => {
openForm("edit", row);
},
+ disabled: (row) => {
+ return row.inspectState == 1;
+ }
},
{
name: "闄勪欢",
@@ -182,6 +185,9 @@
clickFun: (row) => {
submit(row.id);
},
+ disabled: (row) => {
+ return row.inspectState == 1;
+ }
},
{
name: "鍒嗛厤妫�楠屽憳",
@@ -193,6 +199,9 @@
proxy.$modal.msgError("妫�楠屽憳宸插瓨鍦�");
}
},
+ disabled: (row) => {
+ return row.inspectState == 1 || row.checkName;
+ }
},
{
name: "涓嬭浇",
@@ -263,12 +272,6 @@
const openForm = (type, row) => {
nextTick(() => {
formDia.value?.openDialog(type, row)
- })
-};
-// 鎵撳紑鏂板妫�楠屽脊妗�
-const openInspectionForm = (type, row) => {
- nextTick(() => {
- inspectionFormDia.value?.openDialog(type, row)
})
};
// 鎵撳紑闄勪欢寮规
@@ -354,19 +357,18 @@
}
const downLoadFile = (row) => {
- downloadQualityInspect({id: row.id}).then(res => {
- // 鍒涘缓 blob 瀵硅薄
- const blob = new Blob([res.data], {type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'})
+ downloadQualityInspect({ id: row.id }).then((blobData) => {
+ const blob = new Blob([blobData], {
+ type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ })
const downloadUrl = window.URL.createObjectURL(blob)
- // 鍒涘缓涓存椂 <a> 鏍囩杩涜涓嬭浇
const link = document.createElement('a')
link.href = downloadUrl
- link.download = '妫�楠屾姤鍛�.docx' // 杩欓噷鍜屽悗绔竴鑷�
+ link.download = '鍘熸潗鏂欐楠屾姤鍛�.docx'
document.body.appendChild(link)
link.click()
- // 娓呯悊
document.body.removeChild(link)
window.URL.revokeObjectURL(downloadUrl)
})
diff --git a/src/views/reportAnalysis/projectProfit/index.vue b/src/views/reportAnalysis/projectProfit/index.vue
index c987e3c..9584776 100644
--- a/src/views/reportAnalysis/projectProfit/index.vue
+++ b/src/views/reportAnalysis/projectProfit/index.vue
@@ -20,6 +20,8 @@
size: pagination.pageSize,
total: pagination.total,
}"
+ :isShowSummary="true"
+ :summaryMethod="summaryMethod"
@pagination="changePage"
></PIMTable>
</div>
@@ -30,6 +32,7 @@
import { usePaginationApi } from "@/hooks/usePaginationApi";
import { getPurchaseList } from "@/api/procurementManagement/projectProfit";
import { onMounted } from "vue";
+import { summarizeTable } from "@/utils/summarizeTable";
defineOptions({
name: "椤圭洰鍒╂鼎",
@@ -98,6 +101,14 @@
onCurrentChange(page);
};
+// 鍚堣鏂规硶
+const summaryMethod = (param) => {
+ return summarizeTable(
+ param,
+ ['contractAmount', 'purchaseAmount', 'balance', 'balanceAmount', 'balanceRatio'],
+ );
+};
+
onMounted(() => {
getTableData();
});
diff --git a/src/views/salesManagement/invoiceLedger/index.vue b/src/views/salesManagement/invoiceLedger/index.vue
index 91900fd..4e8c274 100644
--- a/src/views/salesManagement/invoiceLedger/index.vue
+++ b/src/views/salesManagement/invoiceLedger/index.vue
@@ -27,7 +27,7 @@
</div>
<div class="table_list">
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
- :row-key="(row) => row.id" show-summary :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
+ :row-key="(row) => row.id" show-summary :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)" stripe>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="閿�鍞悎鍚屽彿" prop="salesContractNo" show-overflow-tooltip width="180" />
@@ -43,7 +43,7 @@
<el-table-column label="褰曞叆浜�" prop="invoicePerson" show-overflow-tooltip />
<el-table-column label="褰曞叆鏃ユ湡" prop="createTime" show-overflow-tooltip :formatter="formatDate" width="180" />
<el-table-column label="寮�绁ㄦ棩鏈�" prop="invoiceDate" show-overflow-tooltip width="120" />
- <el-table-column label="鍙戠エ" prop="invoiceFileName" width="120" align="center" show-overflow-tooltip>
+ <el-table-column label="鍙戠エ" prop="invoiceFileName" width="120" align="center" show-overflow-tooltip fixed="right">
<template #default="scope">
<el-button v-if="scope.row.invoiceFileName" text bg type="primary"
@click="handleFile(scope.row.commonFiles)">
@@ -296,8 +296,8 @@
function handleBeforeUpload(file) {
console.log("file", file);
// 鏍℃鏂囦欢澶у皬
- if (file.size > 1024 * 1024 * 10) {
- proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+ if (file.size > 1024 * 1024 * 50) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃50MB!");
return false;
}
// 鍒ゆ柇鏂囦欢鏍煎紡鏄惁绗﹀悎
diff --git a/src/views/salesManagement/invoiceRegistration/index.vue b/src/views/salesManagement/invoiceRegistration/index.vue
index 8ceea47..9d39684 100644
--- a/src/views/salesManagement/invoiceRegistration/index.vue
+++ b/src/views/salesManagement/invoiceRegistration/index.vue
@@ -33,6 +33,12 @@
@change="handleQuery"
/>
</el-form-item>
+ <br/>
+ <el-form-item label="鍚堝悓褰曞叆鏃ユ湡">
+ <el-date-picker style="width: 240px" v-model="searchForm.commonDate" value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD" type="daterange" start-placeholder="寮�濮嬫椂闂�" end-placeholder="缁撴潫鏃堕棿" clearable
+ @change="changeDateRange" @clear="clearRange" />
+ </el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
<el-button @click="resetForm"> 閲嶇疆 </el-button>
@@ -57,6 +63,7 @@
:summary-method="summarizeMainTable"
@expand-change="expandChange"
@selection-change="handleSelectionChange"
+ stripe
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column type="expand">
@@ -66,6 +73,7 @@
border
show-summary
:summary-method="summarizeChildrenTable"
+ stripe
>
<el-table-column
align="center"
@@ -121,6 +129,7 @@
</template>
</el-table-column>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column label="鍚堝悓褰曞叆鏃ユ湡" prop="entryDate" width="120" />
<el-table-column
label="閿�鍞悎鍚屽彿"
prop="salesContractNo"
@@ -461,6 +470,21 @@
const { form, rules } = toRefs(data);
const { form: searchForm, resetForm } = useFormData(data.searchForm);
+
+const changeDateRange = (date) => {
+ if (date) {
+ searchForm.entryDateStart = date[0];
+ searchForm.entryDateEnd = date[1];
+ getList();
+ }
+};
+
+const clearRange = () => {
+ searchForm.commonDate = [];
+ searchForm.entryDateStart = undefined;
+ searchForm.entryDateEnd = undefined;
+ getList();
+};
const formattedNumber = (row, column, cellValue) => {
if (cellValue == 0) {
return parseFloat(cellValue).toFixed(2);
@@ -557,6 +581,7 @@
getSalesLedgerWithProducts({ id: selectedRows.value[0].id }).then((res) => {
form.value = { ...res };
form.value.createTime = dayjs().format("YYYY-MM-DD");
+ form.value.issueDate = dayjs().format("YYYY-MM-DD");
form.value.createUer = userStore.nickName;
productData.value = form.value.productData.map((item) => {
return item;
diff --git a/src/views/salesManagement/receiptPayment/index.vue b/src/views/salesManagement/receiptPayment/index.vue
index f72e546..b37ab85 100644
--- a/src/views/salesManagement/receiptPayment/index.vue
+++ b/src/views/salesManagement/receiptPayment/index.vue
@@ -38,6 +38,12 @@
@change="handleQuery"
/>
</el-form-item>
+ <br/>
+ <el-form-item label="寮�绁ㄦ棩鏈�">
+ <el-date-picker style="width: 240px" v-model="searchForm.commonDate" value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD" type="daterange" start-placeholder="寮�濮嬫椂闂�" end-placeholder="缁撴潫鏃堕棿" clearable
+ @change="changeDateRange" @clear="clearRange" />
+ </el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
</el-form-item>
@@ -65,7 +71,7 @@
:summary-method="summarizeMainTable"
:expand-row-keys="expandedRowKeys"
@expand-change="expandChange"
-
+ stripe
height="calc(100vh - 21.5em)"
>
<el-table-column align="center" type="selection" width="55" />
@@ -76,6 +82,7 @@
border
show-summary
:summary-method="summarizeChildrenTable"
+ stripe
>
<el-table-column
align="center"
@@ -148,6 +155,12 @@
</template>
</el-table-column>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column
+ label="寮�绁ㄦ棩鏈�"
+ prop="invoiceDate"
+ show-overflow-tooltip
+ width="240"
+ />
<el-table-column
label="閿�鍞悎鍚屽彿"
prop="salesContractNo"
@@ -426,6 +439,21 @@
const formattedNumber = (row, column, cellValue) => {
return parseFloat(cellValue).toFixed(2);
};
+
+const changeDateRange = (date) => {
+ if (date) {
+ searchForm.invoiceDateStart = date[0];
+ searchForm.invoiceDateEnd = date[1];
+ getList();
+ }
+};
+
+const clearRange = () => {
+ searchForm.commonDate = [];
+ searchForm.invoiceDateStart = undefined;
+ searchForm.invoiceDateEnd = undefined;
+ getList();
+};
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
const handleQuery = () => {
@@ -514,7 +542,7 @@
return;
}
if (selectedRows.value[0].noReceiptAmount == 0) {
- proxy.$modal.warning("鏃犻渶鍐嶅洖娆�");
+ proxy.$modal.msgWarning("鏃犻渶鍐嶅洖娆�");
return;
}
invoiceInfo({ id: selectedRows.value[0].id }).then((res) => {
diff --git a/src/views/salesManagement/receiptPaymentLedger/index.vue b/src/views/salesManagement/receiptPaymentLedger/index.vue
index 2cec625..3a4af2e 100644
--- a/src/views/salesManagement/receiptPaymentLedger/index.vue
+++ b/src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -27,6 +27,7 @@
:summary-method="summarizeMainTable"
@row-click="rowClickMethod"
height="calc(100vh - 18.5em)"
+ stripe
>
<el-table-column
align="center"
@@ -81,6 +82,7 @@
:data="receiptRecord"
border
:row-key="(row) => row.id"
+ stripe
show-summary
:summary-method="summarizeMainTable1"
height="calc(100vh - 18.5em)"
diff --git a/src/views/salesManagement/salesLedger/fileList.vue b/src/views/salesManagement/salesLedger/fileList.vue
index da37db2..e9e3b87 100644
--- a/src/views/salesManagement/salesLedger/fileList.vue
+++ b/src/views/salesManagement/salesLedger/fileList.vue
@@ -1,6 +1,6 @@
<template>
<el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
- <el-table :data="tableData" border height="40vh">
+ <el-table :data="tableData" border height="40vh" stripe>
<el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
<el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
<template #default="scope">
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index afe56b5..ed8d43d 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -40,11 +40,11 @@
</div>
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
:expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
- :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
+ :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)" stripe>
<el-table-column align="center" type="selection" width="55" />
<el-table-column type="expand">
<template #default="props">
- <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
+ <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable" stripe>
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="浜у搧澶х被" prop="productCategory" />
<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
@@ -66,6 +66,14 @@
<el-table-column label="浠樻鏂瑰紡" prop="paymentMethod" show-overflow-tooltip />
<el-table-column label="鍚堝悓閲戦(鍏�)" prop="contractAmount" width="220" show-overflow-tooltip
:formatter="formattedNumber" />
+ <el-table-column label="宸插紑绁ㄩ噾棰�(鍏�)" prop="invoiceTotal" width="220" show-overflow-tooltip
+ :formatter="formattedNumber" />
+ <el-table-column label="鏈紑绁ㄩ噾棰�(鍏�)" prop="noInvoiceAmountTotal" width="220" show-overflow-tooltip
+ :formatter="formattedNumber" />
+ <el-table-column label="鍥炴閲戦(鍏�)" prop="receiptPaymentAmountTotal" width="220" show-overflow-tooltip
+ :formatter="formattedNumber" />
+ <el-table-column label="寰呭洖娆鹃噾棰�(鍏�)" prop="noReceiptAmount" width="220" show-overflow-tooltip
+ :formatter="formattedNumber" />
<el-table-column label="褰曞叆浜�" prop="entryPersonName" width="100" show-overflow-tooltip />
<el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
<el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
@@ -157,7 +165,7 @@
<el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >鍒犻櫎</el-button>
</el-form-item>
</el-row>
- <el-table :data="productData" border @selection-change="productSelected" show-summary
+ <el-table :data="productData" border @selection-change="productSelected" show-summary stripe
:summary-method="summarizeMainTable">
<el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
@@ -556,6 +564,10 @@
"contractAmount",
"taxInclusiveTotalPrice",
"taxExclusiveTotalPrice",
+ 'invoiceTotal',
+ 'noInvoiceAmountTotal',
+ 'receiptPaymentAmountTotal',
+ 'noReceiptAmount',
]);
};
// 瀛愯〃鍚堣鏂规硶
diff --git a/src/views/system/config/index.vue b/src/views/system/config/index.vue
index 77d9f5a..9dd220d 100644
--- a/src/views/system/config/index.vue
+++ b/src/views/system/config/index.vue
@@ -96,7 +96,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="鍙傛暟涓婚敭" align="center" prop="configId" />
<el-table-column label="鍙傛暟鍚嶇О" align="center" prop="configName" :show-overflow-tooltip="true" />
diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue
index 0a0532a..8d7a26b 100644
--- a/src/views/system/dept/index.vue
+++ b/src/views/system/dept/index.vue
@@ -1,17 +1,17 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
- <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
+ <el-form-item label="鍏徃鍚嶇О" prop="deptName">
<el-input
v-model="queryParams.deptName"
- placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�"
+ placeholder="璇疯緭鍏ュ叕鍙稿悕绉�"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="鐘舵��" prop="status">
- <el-select v-model="queryParams.status" placeholder="閮ㄩ棬鐘舵��" clearable style="width: 200px">
+ <el-select v-model="queryParams.status" placeholder="鍏徃鐘舵��" clearable style="width: 200px">
<el-option
v-for="dict in sys_normal_disable"
:key="dict.value"
@@ -54,8 +54,9 @@
row-key="deptId"
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+ stripe
>
- <el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" width="260"></el-table-column>
+ <el-table-column prop="deptName" label="鍏徃鍚嶇О" width="260"></el-table-column>
<el-table-column prop="orderNum" label="鎺掑簭" width="200"></el-table-column>
<el-table-column prop="status" label="鐘舵��" width="100">
<template #default="scope">
@@ -76,25 +77,25 @@
</el-table-column>
</el-table>
- <!-- 娣诲姞鎴栦慨鏀归儴闂ㄥ璇濇 -->
+ <!-- 娣诲姞鎴栦慨鏀瑰叕鍙稿璇濇 -->
<el-dialog :title="title" v-model="open" width="600px" append-to-body>
<el-form ref="deptRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="24" v-if="form.parentId !== 0">
- <el-form-item label="涓婄骇閮ㄩ棬" prop="parentId">
+ <el-form-item label="涓婄骇鍏徃" prop="parentId">
<el-tree-select
v-model="form.parentId"
:data="deptOptions"
:props="{ value: 'deptId', label: 'deptName', children: 'children' }"
value-key="deptId"
- placeholder="閫夋嫨涓婄骇閮ㄩ棬"
+ placeholder="閫夋嫨涓婄骇鍏徃"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
- <el-input v-model="form.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" />
+ <el-form-item label="鍏徃鍚嶇О" prop="deptName">
+ <el-input v-model="form.deptName" placeholder="璇疯緭鍏ュ叕鍙稿悕绉�" />
</el-form-item>
</el-col>
<el-col :span="12">
@@ -118,7 +119,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="閮ㄩ棬鐘舵��">
+ <el-form-item label="鍏徃鐘舵��">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_normal_disable"
@@ -129,8 +130,8 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="閮ㄩ棬缂栧彿" prop="deptNick">
- <el-input v-model="form.deptNick" placeholder="璇疯緭鍏ラ儴闂ㄧ紪鍙�" maxlength="50" />
+ <el-form-item label="鍏徃缂栧彿" prop="deptNick">
+ <el-input v-model="form.deptNick" placeholder="璇疯緭鍏ュ叕鍙哥紪鍙�" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
@@ -167,18 +168,18 @@
status: undefined
},
rules: {
- parentId: [{ required: true, message: "涓婄骇閮ㄩ棬涓嶈兘涓虹┖", trigger: "blur" }],
- deptName: [{ required: true, message: "閮ㄩ棬鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+ parentId: [{ required: true, message: "涓婄骇鍏徃涓嶈兘涓虹┖", trigger: "blur" }],
+ deptName: [{ required: true, message: "鍏徃鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
orderNum: [{ required: true, message: "鏄剧ず鎺掑簭涓嶈兘涓虹┖", trigger: "blur" }],
email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }],
- deptNick: [{ required: true, message: "閮ㄩ棬缂栧彿涓嶈兘涓虹┖", trigger: "blur" }],
+ deptNick: [{ required: true, message: "鍏徃缂栧彿涓嶈兘涓虹┖", trigger: "blur" }],
},
})
const { queryParams, form, rules } = toRefs(data)
-/** 鏌ヨ閮ㄩ棬鍒楄〃 */
+/** 鏌ヨ鍏徃鍒楄〃 */
function getList() {
loading.value = true
listDept(queryParams.value).then(response => {
@@ -230,7 +231,7 @@
form.value.parentId = row.deptId
}
open.value = true
- title.value = "娣诲姞閮ㄩ棬"
+ title.value = "娣诲姞鍏徃"
}
/** 灞曞紑/鎶樺彔鎿嶄綔 */
@@ -251,7 +252,7 @@
getDept(row.deptId).then(response => {
form.value = response.data
open.value = true
- title.value = "淇敼閮ㄩ棬"
+ title.value = "淇敼鍏徃"
})
}
diff --git a/src/views/system/dict/data.vue b/src/views/system/dict/data.vue
index f4e2c6f..23ca2a9 100644
--- a/src/views/system/dict/data.vue
+++ b/src/views/system/dict/data.vue
@@ -86,7 +86,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="瀛楀吀缂栫爜" align="center" prop="dictCode" />
<el-table-column label="瀛楀吀鏍囩" align="center" prop="dictLabel">
diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue
index 6c67e1b..98ac047 100644
--- a/src/views/system/dict/index.vue
+++ b/src/views/system/dict/index.vue
@@ -101,7 +101,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="瀛楀吀缂栧彿" align="center" prop="dictId" />
<el-table-column label="瀛楀吀鍚嶇О" align="center" prop="dictName" :show-overflow-tooltip="true"/>
diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue
index bccb9e9..2257fb0 100644
--- a/src/views/system/menu/index.vue
+++ b/src/views/system/menu/index.vue
@@ -51,6 +51,7 @@
v-if="refreshTable"
v-loading="loading"
:data="menuList"
+ stripe
row-key="menuId"
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue
index 8043db0..76ddd05 100644
--- a/src/views/system/notice/index.vue
+++ b/src/views/system/notice/index.vue
@@ -68,7 +68,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="搴忓彿" align="center" prop="noticeId" width="100" />
<el-table-column
diff --git a/src/views/system/post/index.vue b/src/views/system/post/index.vue
index 0e80652..39f807f 100644
--- a/src/views/system/post/index.vue
+++ b/src/views/system/post/index.vue
@@ -77,7 +77,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="宀椾綅缂栧彿" align="center" prop="postId" />
<el-table-column label="宀椾綅缂栫爜" align="center" prop="postCode" />
diff --git a/src/views/system/role/authUser.vue b/src/views/system/role/authUser.vue
index a460f3c..86243db 100644
--- a/src/views/system/role/authUser.vue
+++ b/src/views/system/role/authUser.vue
@@ -57,7 +57,7 @@
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
- <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue
index d960674..a32ab4a 100644
--- a/src/views/system/role/index.vue
+++ b/src/views/system/role/index.vue
@@ -92,7 +92,7 @@
</el-row>
<!-- 琛ㄦ牸鏁版嵁 -->
- <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
+ <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange" stripe>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="瑙掕壊缂栧彿" prop="roleId" width="120" />
<el-table-column label="瑙掕壊鍚嶇О" prop="roleName" :show-overflow-tooltip="true" width="150" />
diff --git a/src/views/system/role/selectUser.vue b/src/views/system/role/selectUser.vue
index 3e3d8aa..193c9a5 100644
--- a/src/views/system/role/selectUser.vue
+++ b/src/views/system/role/selectUser.vue
@@ -26,7 +26,7 @@
</el-form-item>
</el-form>
<el-row>
- <el-table @row-click="clickRow" ref="refTable" :data="userList" @selection-change="handleSelectionChange" height="260px">
+ <el-table @row-click="clickRow" ref="refTable" :data="userList" @selection-change="handleSelectionChange" height="260px" stripe>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
diff --git a/src/views/system/user/authRole.vue b/src/views/system/user/authRole.vue
index 3935ab1..85a6f1b 100644
--- a/src/views/system/user/authRole.vue
+++ b/src/views/system/user/authRole.vue
@@ -17,7 +17,7 @@
</el-form>
<h4 class="form-header h4">瑙掕壊淇℃伅</h4>
- <el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)">
+ <el-table v-loading="loading" :row-key="getRowKey" @row-click="clickRow" ref="roleRef" @selection-change="handleSelectionChange" :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)" stripe>
<el-table-column label="搴忓彿" width="55" type="index" align="center">
<template #default="scope">
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index 40fb371..04adf59 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -157,6 +157,7 @@
v-loading="loading"
:data="userList"
@selection-change="handleSelectionChange"
+ stripe
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column
diff --git a/src/views/tool/gen/editTable.vue b/src/views/tool/gen/editTable.vue
index 874fc94..59aa03d 100644
--- a/src/views/tool/gen/editTable.vue
+++ b/src/views/tool/gen/editTable.vue
@@ -5,7 +5,7 @@
<basic-info-form ref="basicInfo" :info="info" />
</el-tab-pane>
<el-tab-pane label="瀛楁淇℃伅" name="columnInfo">
- <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
+ <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight" stripe>
<el-table-column label="搴忓彿" type="index" min-width="5%"/>
<el-table-column
label="瀛楁鍒楀悕"
diff --git a/src/views/tool/gen/importTable.vue b/src/views/tool/gen/importTable.vue
index 23dbf28..ea2ebef 100644
--- a/src/views/tool/gen/importTable.vue
+++ b/src/views/tool/gen/importTable.vue
@@ -26,7 +26,7 @@
</el-form-item>
</el-form>
<el-row>
- <el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
+ <el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px" stripe>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="tableName" label="琛ㄥ悕绉�" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableComment" label="琛ㄦ弿杩�" :show-overflow-tooltip="true"></el-table-column>
diff --git a/src/views/tool/gen/index.vue b/src/views/tool/gen/index.vue
index aaddeb6..4930cef 100644
--- a/src/views/tool/gen/index.vue
+++ b/src/views/tool/gen/index.vue
@@ -106,6 +106,7 @@
ref="genRef"
v-loading="loading"
:data="tableList"
+ stripe
@selection-change="handleSelectionChange"
:default-sort="defaultSort"
@sort-change="handleSortChange"
--
Gitblit v1.9.3