From 9fe3ccfea5f06dd2474480aef7058a8ca907cb29 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期一, 19 一月 2026 16:53:38 +0800
Subject: [PATCH] fix: 新增协同办公、薪资功能。完成库存预警页面编写
---
src/views/collaborativeApproval/notificationManagement/summary/index.vue | 399 +
src/views/personnelManagement/payrollManagement/components/formDia.vue | 2
src/views/personnelManagement/payrollManagement/index.vue | 20
src/views/collaborativeApproval/rulesRegulationsManagement/index.vue | 584 ++
src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue | 320 +
src/views/collaborativeApproval/approvalProcess/index.vue | 262
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue | 394 +
src/views/inventoryManagement/stockWarningLedger/index.vue | 360 +
src/views/collaborativeApproval/sealManagement/index.vue | 801 +++
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue | 194
src/views/collaborativeApproval/purchaseApproval/index.vue | 1064 ++++
src/views/collaborativeApproval/approvalProcess/index5.vue | 22
src/views/collaborativeApproval/planTemplate/index.vue | 867 ++++
src/views/collaborativeApproval/meetingManagement/index.vue | 63
src/views/collaborativeApproval/knowledgeBase/index.vue | 323 -
src/api/inventoryManagement/stockWarningLedger.js | 10
src/views/collaborativeApproval/attendanceManagement/index.vue | 822 +++
src/api/salesManagement/salesQuotation.js | 112
src/views/collaborativeApproval/enterpriseBook/index.vue | 798 +++
src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue | 414 +
src/views/collaborativeApproval/reportGeneration/index.vue | 596 ++
src/views/collaborativeApproval/meetingBoard/index.vue | 228
src/api/collaborativeApproval/meeting.js | 118
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue | 137
src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue | 363 +
src/views/collaborativeApproval/processTracking/index.vue | 498 ++
src/views/collaborativeApproval/notificationManagement/index.vue | 341
src/views/collaborativeApproval/warningSystem/index.vue | 6
src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue | 412 +
src/views/collaborativeApproval/approvalProcess/fileList.vue | 30
src/views/collaborativeApproval/officeSupplies/index.vue | 512 ++
src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue | 495 ++
src/views/collaborativeApproval/rpaManagement/index.vue | 214
src/views/collaborativeApproval/noticeManagement/index.vue | 958 ++-
34 files changed, 11,321 insertions(+), 1,418 deletions(-)
diff --git a/src/api/collaborativeApproval/meeting.js b/src/api/collaborativeApproval/meeting.js
new file mode 100644
index 0000000..20df8b1
--- /dev/null
+++ b/src/api/collaborativeApproval/meeting.js
@@ -0,0 +1,118 @@
+import request from "@/utils/request";
+
+export function getMeetingRoomList(data) {
+ return request({
+ url: "/meeting/roomList",
+ method: "post",
+ data: data,
+ });
+}
+
+export function saveRoom(data) {
+ return request({
+ url: "/meeting/saveRoom",
+ method: "post",
+ data: data,
+ });
+}
+
+export function delRoom(id) {
+ return request({
+ url: "/meeting/delRoom/"+id,
+ method: "delete",
+ });
+}
+
+export function getRoomEnum() {
+ return request({
+ url: "/meeting/roomEnum",
+ method: "get",
+ });
+}
+
+export function getDraftList(data){
+ return request({
+ url: "/meeting/draftList",
+ method: "post",
+ data: data,
+ });
+}
+
+export function saveDraft(data) {
+ return request({
+ url: "/meeting/saveDraft",
+ method: "post",
+ data: data,
+ });
+}
+
+export function delDraft(id) {
+ return request({
+ url: "/meeting/delDraft/"+id,
+ method: "delete",
+ });
+}
+
+export function saveMeetingApplication(data){
+ return request({
+ url: "/meeting/saveMeetingApplication",
+ method: "post",
+ data: data,
+ });
+}
+
+export function getExamineList(data) {
+ return request({
+ url: "/meeting/applicationList",
+ method: "post",
+ data: data,
+ });
+}
+
+
+export function getMeetingUseList(data){
+ return request({
+ url: "/meeting/meetingUseList",
+ method: "post",
+ data: data,
+ });
+}
+
+export function getMeetingPublish(data){
+ return request({
+ url: "/meeting/meetingPublishList",
+ method: "post",
+ data: data
+ });
+}
+
+
+export function getMeetingMinutesByMeetingId(id){
+ return request({
+ url: "/meeting/getMeetingMinutesByMeetingId/"+id,
+ method: "get",
+ });
+}
+
+export function saveMeetingMinutes(data){
+ return request({
+ url: "/meeting/saveMeetingMinutes",
+ method: "post",
+ data: data,
+ });
+}
+
+
+export function getMeetSummary(){
+ return request({
+ url: "/meeting/getMeetSummary",
+ method: "get",
+ });
+}
+
+export function getMeetSummaryItems(){
+ return request({
+ url: "/meeting/getMeetSummaryItems",
+ method: "get",
+ });
+}
diff --git a/src/api/inventoryManagement/stockWarningLedger.js b/src/api/inventoryManagement/stockWarningLedger.js
new file mode 100644
index 0000000..6d14808
--- /dev/null
+++ b/src/api/inventoryManagement/stockWarningLedger.js
@@ -0,0 +1,10 @@
+import request from "@/utils/request";
+
+// 鏌ヨ搴撳瓨棰勮鍙拌处鍒楄〃
+export const getStockWarningLedgerPage = (params) => {
+ return request({
+ url: "/stockWarningLedger/listPage",
+ method: "get",
+ params,
+ });
+};
diff --git a/src/api/salesManagement/salesQuotation.js b/src/api/salesManagement/salesQuotation.js
new file mode 100644
index 0000000..4329dd9
--- /dev/null
+++ b/src/api/salesManagement/salesQuotation.js
@@ -0,0 +1,112 @@
+// 閿�鍞姤浠烽〉闈㈡帴鍙�
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ鎶ヤ环鍗曞垪琛�
+export function getQuotationList(query) {
+ return request({
+ url: "/sales/quotation/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ鎶ヤ环鍗曡鎯�
+export function getQuotationDetail(query) {
+ return request({
+ url: "/sales/quotation/detail",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板鎶ヤ环鍗�
+export function addQuotation(data) {
+ return request({
+ url: "/sales/quotation/add",
+ method: "post",
+ data: data,
+ });
+}
+
+// 淇敼鎶ヤ环鍗�
+export function updateQuotation(data) {
+ return request({
+ url: "/sales/quotation/update",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鍒犻櫎鎶ヤ环鍗�
+export function deleteQuotation(query) {
+ return request({
+ url: "/sales/quotation/delete",
+ method: "delete",
+ data: query,
+ });
+}
+
+// 鍙戦�佹姤浠峰崟
+export function sendQuotation(data) {
+ return request({
+ url: "/sales/quotation/send",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鎶ヤ环鍗曡浆璁㈠崟
+export function convertToOrder(data) {
+ return request({
+ url: "/sales/quotation/convertToOrder",
+ method: "post",
+ data: data,
+ });
+}
+
+// 鏌ヨ瀹㈡埛鍒楄〃
+export function getCustomerList(query) {
+ return request({
+ url: "/basic/customer/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ浜у搧鍒楄〃
+export function getProductList(query) {
+ return request({
+ url: "/basic/product/list",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏌ヨ涓氬姟鍛樺垪琛�
+export function getSalespersonList(query) {
+ return request({
+ url: "/system/user/salespersonList",
+ method: "get",
+ params: query,
+ });
+}
+
+// 瀵煎嚭鎶ヤ环鍗�
+export function exportQuotation(query) {
+ return request({
+ url: "/sales/quotation/export",
+ method: "get",
+ params: query,
+ responseType: "blob",
+ });
+}
+
+// 鎵撳嵃鎶ヤ环鍗�
+export function printQuotation(query) {
+ return request({
+ url: "/sales/quotation/print",
+ method: "get",
+ params: query,
+ responseType: "blob",
+ });
+}
diff --git a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
index 403cab6..c38ee89 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -6,7 +6,7 @@
width="700px"
@close="closeDia"
>
- <el-form :model="form" label-width="140px" label-position="top" ref="formRef">
+ <el-form :model="form" :rules="rules" label-width="140px" label-position="top" ref="formRef">
<el-row>
<el-col :span="24">
<el-form-item label="娴佺▼缂栧彿锛�" prop="approveId">
@@ -32,7 +32,7 @@
</el-form-item>
</el-col>
</el-row>
- <el-row>
+ <el-row v-if="!isQuotationApproval">
<el-col :span="24">
<el-form-item label="瀹℃壒浜嬬敱锛�" prop="approveReason">
<el-input v-model="form.approveReason" placeholder="璇疯緭鍏�" clearable type="textarea" disabled/>
@@ -73,6 +73,54 @@
</el-col>
</el-row>
</el-form>
+
+ <!-- 鎶ヤ环瀹℃壒锛氬睍绀烘姤浠疯鎯咃紙澶嶇敤閿�鍞姤浠封�滄煡鐪嬭鎯呭璇濇鈥濆唴瀹圭粨鏋勶級 -->
+ <div v-if="isQuotationApproval" style="margin: 10px 0 18px;">
+ <el-divider content-position="left">鎶ヤ环璇︽儏</el-divider>
+ <el-skeleton :loading="quotationLoading" animated>
+ <template #template>
+ <el-skeleton-item variant="h3" style="width: 30%" />
+ <el-skeleton-item variant="text" style="width: 100%" />
+ <el-skeleton-item variant="text" style="width: 100%" />
+ </template>
+ <template #default>
+ <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo" description="鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏" />
+ <template v-else>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
+ <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
+ <el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
+ <el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
+ <el-descriptions-item label="鏈夋晥鏈熻嚦">{{ currentQuotation.validDate }}</el-descriptions-item>
+ <el-descriptions-item label="浠樻鏂瑰紡">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
+ <el-descriptions-item label="鎶ヤ环鎬婚" :span="2">
+ <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
+ 楼{{ Number(currentQuotation.totalAmount ?? 0).toFixed(2) }}
+ </span>
+ </el-descriptions-item>
+ </el-descriptions>
+
+ <div style="margin-top: 20px;">
+ <h4>浜у搧鏄庣粏</h4>
+ <el-table :data="currentQuotation.products || []" border style="width: 100%">
+ <el-table-column prop="product" label="浜у搧鍚嶇О" />
+ <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" />
+ <el-table-column prop="unit" label="鍗曚綅" />
+ <el-table-column prop="unitPrice" label="鍗曚环">
+ <template #default="scope">楼{{ Number(scope.row.unitPrice ?? 0).toFixed(2) }}</template>
+ </el-table-column>
+ </el-table>
+ </div>
+
+ <div v-if="currentQuotation.remark" style="margin-top: 20px;">
+ <h4>澶囨敞</h4>
+ <p>{{ currentQuotation.remark }}</p>
+ </div>
+ </template>
+ </template>
+ </el-skeleton>
+ </div>
+
<el-form :model="{ activities }" ref="formRef" label-position="top">
<el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
<el-step
@@ -121,33 +169,16 @@
<template #footer v-if="operationType === 'approval'">
<div class="dialog-footer">
<el-button type="primary" @click="submitForm(2)">涓嶉�氳繃</el-button>
- <el-button type="primary" @click="openSignatureDialog(1)">閫氳繃</el-button>
+ <el-button type="primary" @click="submitForm(1)">閫氳繃</el-button>
<el-button @click="closeDia">鍙栨秷</el-button>
</div>
</template>
- </el-dialog>
- <!-- 鐢靛瓙绛惧悕寮圭獥锛坴ue3-signature-pad锛� -->
- <el-dialog v-model="signatureDialogVisible" title="鐢靛瓙绛惧悕" width="600px" append-to-body>
- <vueEsign
- ref="esign"
- class="mySign"
- :width="800"
- :height="300"
- :isCrop="isCrop"
- :lineWidth="lineWidth"
- :lineColor="lineColor"
- />
- <div style="margin-top:10px;">
- <el-button @click="clearSignature">娓呴櫎</el-button>
- <el-button type="primary" @click="confirmSignature">纭畾</el-button>
- </div>
</el-dialog>
</div>
</template>
<script setup>
-import { getCurrentInstance, reactive, ref, toRefs } from "vue";
-import vueEsign from "vue-esign";
+import { computed, getCurrentInstance, reactive, ref, toRefs } from "vue";
import {
approveProcessDetails,
getDept,
@@ -156,9 +187,16 @@
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
-import { getToken } from "@/utils/auth";
+import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
+
+const props = defineProps({
+ approveType: {
+ type: [Number, String],
+ default: 0
+ }
+})
const dialogFormVisible = ref(false);
const operationType = ref('')
@@ -167,6 +205,10 @@
const userStore = useUserStore()
const productOptions = ref([]);
const userList = ref([])
+const quotationLoading = ref(false)
+const currentQuotation = ref({})
+const isQuotationApproval = computed(() => Number(props.approveType) === 6)
+
const data = reactive({
form: {
approveTime: "",
@@ -176,23 +218,12 @@
approveReason: "",
checkResult: "",
},
+ rules: {
+ // 浣跨敤閮ㄩ棬ID鍋氬繀濉牎楠岋紝閬垮厤鍚嶇О鏈悓姝ュ鑷磋鎶�
+ approveDeptId: [{ required: true, message: "璇烽�夋嫨鐢宠閮ㄩ棬", trigger: "change" }],
+ },
});
-const { form } = toRefs(data);
-const signatureDialogVisible = ref(false);
-const signatureImg = ref('');
-let submitStatus = null; // 涓存椂瀛樺偍閫氳繃/涓嶉�氳繃鐘舵��
-const isCrop = ref("");
-const esign = ref(null);
-const lineWidth = ref(0);
-const lineColor = ref("#000000");
-
-// 涓婁紶閰嶇疆
-const upload = reactive({
- // 涓婁紶鐨勫湴鍧�
- url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
- // 璁剧疆涓婁紶鐨勮姹傚ご閮�
- headers: { Authorization: "Bearer " + getToken() },
-});
+const { form, rules } = toRefs(data);
// 鑺傜偣鏍囬
const getNodeTitle = (index, len) => {
@@ -219,11 +250,27 @@
const openDialog = (type, row) => {
operationType.value = type;
dialogFormVisible.value = true;
+ currentQuotation.value = {}
userListNoPageByTenantId().then((res) => {
userList.value = res.data;
});
form.value = {...row}
getProductOptions()
+
+ // 鎶ヤ环瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨勨�滄姤浠峰崟鍙封�濆幓鏌ユ姤浠峰垪琛�
+ if (isQuotationApproval.value) {
+ const quotationNo = row?.approveReason;
+ if (quotationNo) {
+ quotationLoading.value = true
+ getQuotationList({ quotationNo }).then((res) => {
+ const records = res?.data?.records || []
+ currentQuotation.value = records[0] || {}
+ }).finally(() => {
+ quotationLoading.value = false
+ })
+ }
+ }
+
approveProcessDetails(row.approveId).then((res) => {
activities.value = res.data
// 澧炲姞isApproval瀛楁
@@ -248,77 +295,10 @@
productOptions.value = res.data;
});
};
-// 鎵撳紑绛惧悕寮圭獥
-const openSignatureDialog = (status) => {
- submitStatus = status;
- signatureDialogVisible.value = true;
-};
-// 娓呴櫎绛惧悕
-const clearSignature = () => {
- esign.value.reset();
-};
-// 纭绛惧悕
-const confirmSignature = () => {
- esign.value.generate().then((res) => {
- console.log(res);
- // 灏哹ase64杞崲涓轰簩杩涘埗
- const base64Data = res.split(',')[1]; // 绉婚櫎data:image/png;base64,鍓嶇紑
- const binaryString = atob(base64Data);
- const bytes = new Uint8Array(binaryString.length);
- for (let i = 0; i < binaryString.length; i++) {
- bytes[i] = binaryString.charCodeAt(i);
- }
- signatureImg.value = bytes;
-
- // 鍒涘缓鏂囦欢瀵硅薄鐢ㄤ簬涓婁紶
- const blob = new Blob([bytes], { type: 'image/png' });
- const file = new File([blob], 'signature.png', { type: 'image/png' });
-
- // 鍒涘缓FormData
- const formData = new FormData();
- formData.append('file', file);
-
- // 涓婁紶绛惧悕鍥剧墖
- fetch(upload.url, {
- method: 'POST',
- headers: upload.headers,
- body: formData
- })
- .then(response => response.json())
- .then(data => {
- if (data.code === 200) {
- console.log('data---', data)
- let tempFileIds = [];
- tempFileIds.push(data.data.tempId);
- signatureDialogVisible.value = false;
- clearSignature();
- // 鍙湁閫氳繃鏃舵墠浼犻�掔鍚嶆枃浠禝D
- if (submitStatus === 1) {
- submitForm(submitStatus, tempFileIds);
- } else {
- submitForm(submitStatus);
- }
- } else {
- proxy.$modal.msgError("绛惧悕鍥剧墖涓婁紶澶辫触锛�" + data.msg);
- }
- })
- .catch(error => {
- console.error('涓婁紶澶辫触:', error);
- proxy.$modal.msgError("绛惧悕鍥剧墖涓婁紶澶辫触");
- });
- }).catch((err) => {
- console.log(err);
- proxy.$modal.msgWarning("璇峰厛绛惧悕锛�");
- })
-};
// 鎻愪氦瀹℃壒
-const submitForm = (status, tempFileIds) => {
+const submitForm = (status) => {
const filteredActivities = activities.value.filter(activity => activity.isShen);
filteredActivities[0].approveNodeStatus = status;
- // 鍙湁閫氳繃鏃舵墠闇�瑕佺鍚�
- if (status === 1 && tempFileIds) {
- filteredActivities[0].tempFileIds = tempFileIds;
- }
// 鍒ゆ柇鏄惁涓烘渶鍚庝竴姝�
const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
@@ -330,6 +310,8 @@
const closeDia = () => {
proxy.resetForm("formRef");
dialogFormVisible.value = false;
+ quotationLoading.value = false
+ currentQuotation.value = {}
emit('close')
};
defineExpose({
diff --git a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
index 83585a1..4c9fce3 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -16,11 +16,13 @@
</el-row>
<el-row>
<el-col :span="24">
+ <!-- 鐢宠閮ㄩ棬锛氭牎楠屼娇鐢ㄩ儴闂↖D锛屼究浜庝笅鎷夐�夋嫨鍚庣珛鍗抽�氳繃鏍¢獙 -->
<el-form-item label="鐢宠閮ㄩ棬锛�" prop="approveDeptId">
+<!-- <el-input v-model="form.approveDeptName" placeholder="璇疯緭鍏�" clearable/>-->
<el-select
- disabled
v-model="form.approveDeptId"
placeholder="閫夋嫨閮ㄩ棬"
+ @change="handleDeptChange"
>
<el-option
v-for="user in productOptions"
@@ -34,8 +36,65 @@
</el-row>
<el-row>
<el-col :span="24">
- <el-form-item label="瀹℃壒浜嬬敱锛�" prop="approveReason">
+ <el-form-item :label="props.approveType == 5 ? '閲囪喘璇存槑锛�' : '瀹℃壒浜嬬敱锛�'" prop="approveReason">
<el-input v-model="form.approveReason" placeholder="璇疯緭鍏�" clearable type="textarea" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 璇峰亣鏃堕棿锛堜粎褰� approveType 涓� 2 鏃舵樉绀猴級 -->
+ <el-row :gutter="30" v-if="props.approveType == 2">
+ <el-col :span="12">
+ <el-form-item label="璇峰亣寮�濮嬫椂闂达細" prop="startDate">
+ <el-date-picker
+ v-model="form.startDate"
+ type="date"
+ placeholder="璇烽�夋嫨寮�濮嬫棩鏈�"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璇峰亣缁撴潫鏃堕棿锛�" prop="endDate">
+ <el-date-picker
+ v-model="form.endDate"
+ type="date"
+ placeholder="璇烽�夋嫨缁撴潫鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ clearable
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 鎶ラ攢閲戦锛堜粎褰� approveType 涓� 4 鏃舵樉绀猴級 -->
+ <el-row v-if="props.approveType == 4">
+ <el-col :span="24">
+ <el-form-item label="鎶ラ攢閲戦锛�" prop="price">
+ <el-input-number
+ v-model="form.price"
+ placeholder="璇疯緭鍏ユ姤閿�閲戦"
+ :min="0"
+ :precision="2"
+ :step="0.01"
+ style="width: 100%"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <!-- 鍑哄樊鍦扮偣锛堜粎褰� approveType 涓� 3 鏃舵樉绀猴級 -->
+ <el-row v-if="props.approveType == 3">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞锛�" prop="location">
+ <el-input
+ v-model="form.location"
+ placeholder="璇疯緭鍏ュ娉�"
+ clearable
+ />
</el-form-item>
</el-col>
</el-row>
@@ -88,6 +147,9 @@
<el-select
v-model="form.approveUser"
placeholder="閫夋嫨浜哄憳"
+ filterable
+ default-first-option
+ :reserve-keyword="false"
>
<el-option
v-for="user in userList"
@@ -155,6 +217,8 @@
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
import useUserStore from "@/store/modules/user";
+import { getCurrentDate } from "@/utils/index.js";
+import log from "@/views/monitor/job/log.vue";
const userStore = useUserStore();
const dialogFormVisible = ref(false);
@@ -171,19 +235,29 @@
approveTime: "",
approveId: "",
approveUser: "",
- approveDeptId: "",
+ approveDeptId: "",
+ approveDeptName: "",
approveReason: "",
checkResult: "",
tempFileIds: [],
- approverList: [] // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
+ approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
+ startDate: "", // 璇峰亣寮�濮嬫椂闂�
+ endDate: "", // 璇峰亣缁撴潫鏃堕棿
+ price: null, // 鎶ラ攢閲戦
+ location: "" // 鍑哄樊鍦扮偣
},
rules: {
- approveTime: [{ required: false, message: "璇疯緭鍏�", trigger: "change" },],
+ approveTime: [{ required: false, message: "璇疯緭鍏�", trigger: "change" }],
approveId: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
approveUser: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
- approveDeptId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ // 浣跨敤閮ㄩ棬ID鍋氬繀濉牎楠岋紝閬垮厤鍚嶇О鏈悓姝ュ鑷磋鎶�
+ approveDeptId: [{ required: true, message: "璇烽�夋嫨鐢宠閮ㄩ棬", trigger: "change" }],
approveReason: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
checkResult: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
+ startDate: [{ required: true, message: "璇烽�夋嫨璇峰亣寮�濮嬫椂闂�", trigger: "change" }],
+ endDate: [{ required: true, message: "璇烽�夋嫨璇峰亣缁撴潫鏃堕棿", trigger: "change" }],
+ price: [{ required: true, message: "璇疯緭鍏ユ姤閿�閲戦", trigger: "blur" }],
+ location: [{ required: true, message: "璇疯緭鍏ュ嚭宸湴鐐�", trigger: "blur" }],
},
});
const { form, rules } = toRefs(data);
@@ -208,10 +282,19 @@
function removeApproverNode(index) {
approverNodes.value.splice(index, 1)
}
-
+// 澶勭悊閮ㄩ棬閫夋嫨鍙樺寲
+const handleDeptChange = (deptId) => {
+ if (deptId) {
+ const selectedDept = productOptions.value.find(dept => dept.deptId === deptId);
+ if (selectedDept) {
+ form.value.approveDeptName = selectedDept.deptName;
+ }
+ } else {
+ form.value.approveDeptName = '';
+ }
+};
// 鎵撳紑寮规
const openDialog = (type, row) => {
- console.log('openDialog', type, row)
operationType.value = type;
dialogFormVisible.value = true;
userListNoPageByTenantId().then((res) => {
@@ -278,6 +361,36 @@
proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒")
return
}
+ // 褰� approveType 涓� 2 鏃讹紝鏍¢獙璇峰亣鏃堕棿
+ if (props.approveType == 2) {
+ if (!form.value.startDate) {
+ proxy.$modal.msgError("璇烽�夋嫨璇峰亣寮�濮嬫椂闂达紒")
+ return
+ }
+ if (!form.value.endDate) {
+ proxy.$modal.msgError("璇烽�夋嫨璇峰亣缁撴潫鏃堕棿锛�")
+ return
+ }
+ // 鏍¢獙缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂�
+ if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
+ proxy.$modal.msgError("璇峰亣缁撴潫鏃堕棿涓嶈兘鏃╀簬寮�濮嬫椂闂达紒")
+ return
+ }
+ }
+ // 褰� approveType 涓� 3 鏃讹紝鏍¢獙鍑哄樊鍦扮偣
+ if (props.approveType == 3) {
+ if (!form.value.location || form.value.location.trim() === '') {
+ proxy.$modal.msgError("璇疯緭鍏ュ嚭宸湴鐐癸紒")
+ return
+ }
+ }
+ // 褰� approveType 涓� 4 鏃讹紝鏍¢獙鎶ラ攢閲戦
+ if (props.approveType == 4) {
+ if (!form.value.price || form.value.price <= 0) {
+ proxy.$modal.msgError("璇疯緭鍏ユ湁鏁堢殑鎶ラ攢閲戦锛�")
+ return
+ }
+ }
proxy.$refs.formRef.validate(valid => {
if (valid) {
if (operationType.value === "add" || currentApproveStatus.value == 3) {
@@ -301,14 +414,6 @@
dialogFormVisible.value = false;
emit('close')
};
-// 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
-function getCurrentDate() {
- const today = new Date();
- const year = today.getFullYear();
- const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
- const day = String(today.getDate()).padStart(2, "0");
- return `${year}-${month}-${day}`;
-}
// 涓婁紶鍓嶆牎妫�
function handleBeforeUpload(file) {
diff --git a/src/views/collaborativeApproval/approvalProcess/fileList.vue b/src/views/collaborativeApproval/approvalProcess/fileList.vue
index e9e3b87..5cc65f1 100644
--- a/src/views/collaborativeApproval/approvalProcess/fileList.vue
+++ b/src/views/collaborativeApproval/approvalProcess/fileList.vue
@@ -1,11 +1,12 @@
<template>
- <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose">
- <el-table :data="tableData" border height="40vh" stripe>
+ <el-dialog v-model="dialogVisible" title="闄勪欢" width="40%" :before-close="handleClose" draggable>
+ <el-table :data="tableData" border height="40vh">
<el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
- <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
+ <el-table-column fixed="right" label="鎿嶄綔" width="150" 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>
+ <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
</template>
</el-table-column>
</el-table>
@@ -16,6 +17,8 @@
<script setup>
import { ref } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
+import { ElMessageBox, ElMessage } from 'element-plus'
+import { delCommonFile } from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
@@ -35,6 +38,27 @@
const lookFile = (row) => {
filePreviewRef.value.open(row.url)
}
+// 鍒犻櫎闄勪欢
+const handleDelete = (row) => {
+ ElMessageBox.confirm(`纭鍒犻櫎闄勪欢"${row.name}"鍚楋紵`, '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ delCommonFile([row.id]).then(() => {
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ // 浠庡垪琛ㄤ腑绉婚櫎宸插垹闄ょ殑闄勪欢
+ const index = tableData.value.findIndex(item => item.id === row.id)
+ if (index !== -1) {
+ tableData.value.splice(index, 1)
+ }
+ }).catch(() => {
+ ElMessage.error('鍒犻櫎澶辫触')
+ })
+ }).catch(() => {
+ ElMessage.info('宸插彇娑堝垹闄�')
+ })
+}
defineExpose({
open
})
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index c3b713e..afc2529 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -1,5 +1,16 @@
<template>
<div class="app-container">
+ <!-- 鏍囩椤靛垏鎹笉鍚岀殑瀹℃壒绫诲瀷 -->
+ <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
+ <el-tab-pane label="鍗忓悓瀹℃壒" name="1"></el-tab-pane>
+ <el-tab-pane label="璇峰亣绠$悊" name="2"></el-tab-pane>
+ <el-tab-pane label="閿�鍞鎵�" name="3"></el-tab-pane>
+ <el-tab-pane label="鎶ラ攢瀹℃壒" name="4"></el-tab-pane>
+ <el-tab-pane label="閲囪喘瀹℃壒" name="5"></el-tab-pane>
+ <el-tab-pane label="鎶ヤ环瀹℃壒" name="6"></el-tab-pane>
+ <el-tab-pane label="鍑哄簱瀹℃壒" name="7"></el-tab-pane>
+ </el-tabs>
+
<div class="search_form">
<div>
<span class="search_title">娴佺▼缂栧彿锛�</span>
@@ -24,15 +35,15 @@
>
</div>
<div>
- <el-button type="primary" @click="openForm('add')">鏂板</el-button>
-<!-- <el-button @click="handleOut">瀵煎嚭</el-button>-->
+ <el-button type="primary" @click="openForm('add')" v-if="currentApproveType !== 6">鏂板</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"
+ :column="tableColumnCopy"
:tableData="tableData"
:page="page"
:isSelection="true"
@@ -42,8 +53,8 @@
:total="page.total"
></PIMTable>
</div>
- <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="approveType"></info-form-dia>
- <approval-dia ref="approvalDia" @close="handleQuery"></approval-dia>
+ <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
+ <approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
<FileList ref="fileListRef" />
</div>
</template>
@@ -51,22 +62,33 @@
<script setup>
import FileList from "./fileList.vue";
import { Search } from "@element-plus/icons-vue";
-import {onMounted, ref} from "vue";
+import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
import {ElMessageBox} from "element-plus";
+import { useRoute } from 'vue-router';
import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
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();
+const route = useRoute();
+
+// 褰撳墠閫変腑鐨勬爣绛鹃〉锛岄粯璁や负鍏嚭绠$悊
+const activeTab = ref('1');
+
+// 褰撳墠瀹℃壒绫诲瀷锛屾牴鎹�変腑鐨勬爣绛鹃〉璁$畻
+const currentApproveType = computed(() => {
+ return Number(activeTab.value);
});
-const userStore = useUserStore();
+// 鏍囩椤靛垏鎹㈠鐞�
+const handleTabChange = (tabName) => {
+ // 鍒囨崲鏍囩椤垫椂閲嶇疆鎼滅储鏉′欢鍜屽垎椤碉紝骞堕噸鏂板姞杞芥暟鎹�
+ searchForm.value.approveId = '';
+ searchForm.value.approveStatus = '';
+ page.current = 1;
+ getList();
+};
const data = reactive({
@@ -76,75 +98,101 @@
},
});
const { searchForm } = toRefs(data);
-const tableColumn = ref([
- {
- label: "瀹℃壒鐘舵��",
- prop: "approveStatus",
- dataType: "tag",
- width: 100,
- formatData: (params) => {
- if (params == 0) {
- return "寰呭鏍�";
- } else if (params == 1) {
- return "瀹℃牳涓�";
- } else if (params == 2) {
- return "瀹℃牳瀹屾垚";
- } else if (params == 4) {
- return "宸查噸鏂版彁浜�";
- } else {
- return '涓嶉�氳繃';
- }
+
+// 鍔ㄦ�佽〃鏍煎垪閰嶇疆锛屾牴鎹鎵圭被鍨嬬敓鎴愬垪
+const tableColumnCopy = computed(() => {
+ const isLeaveType = currentApproveType.value === 2; // 璇峰亣绠$悊
+ const isReimburseType = currentApproveType.value === 4; // 鎶ラ攢绠$悊
+ const isQuotationType = currentApproveType.value === 6; // 鎶ヤ环瀹℃壒
+
+ // 鍩虹鍒楅厤缃�
+ const baseColumns = [
+ {
+ label: "瀹℃壒鐘舵��",
+ prop: "approveStatus",
+ dataType: "tag",
+ width: 100,
+ formatData: (params) => {
+ if (params == 0) {
+ return "寰呭鏍�";
+ } else if (params == 1) {
+ return "瀹℃牳涓�";
+ } else if (params == 2) {
+ return "瀹℃牳瀹屾垚";
+ } else if (params == 4) {
+ return "宸查噸鏂版彁浜�";
+ } else {
+ return '涓嶉�氳繃';
+ }
+ },
+ formatType: (params) => {
+ if (params == 0) {
+ return "warning";
+ } else if (params == 1) {
+ return "primary";
+ } else if (params == 2) {
+ return "success";
+ } else if (params == 4) {
+ return "info";
+ } else {
+ return 'danger';
+ }
+ },
},
- formatType: (params) => {
- if (params == 0) {
- return "warning";
- } else if (params == 1) {
- return "primary";
- } else if (params == 2) {
- return "success";
- } else if (params == 4) {
- return "";
- } else {
- return 'danger';
- }
+ {
+ label: "娴佺▼缂栧彿",
+ prop: "approveId",
+ width: 170
},
- },
- {
- label: "娴佺▼缂栧彿",
- prop: "approveId",
- width: 170
- },
- {
- label: "鐢宠閮ㄩ棬",
- prop: "approveDeptName",
- width: 220
- },
- {
- label: "瀹℃壒浜嬬敱",
- prop: "approveReason",
- width: 200
- },
- {
- label: "鐢宠浜�",
- prop: "approveUserName",
- width: 120
- },
- {
- label: "鐢宠鏃ユ湡",
- prop: "approveTime",
- width: 200
- },
- {
- label: "缁撴潫鏃ユ湡",
- prop: "approveOverTime",
- width: 120
- },
- {
+ {
+ label: "鐢宠閮ㄩ棬",
+ prop: "approveDeptName",
+ width: 220
+ },
+ {
+ label: isQuotationType ? "鎶ヤ环鍗曞彿" : "瀹℃壒浜嬬敱",
+ prop: "approveReason",
+ width: 200
+ },
+ {
+ label: "鐢宠浜�",
+ prop: "approveUserName",
+ width: 120
+ }
+ ];
+
+ // 閲戦鍒楋紙浠呮姤閿�绠$悊鏄剧ず锛�
+ if (isReimburseType) {
+ baseColumns.push({
+ label: "閲戦锛堝厓锛�",
+ prop: "price",
+ width: 120
+ });
+ }
+
+ // 鏃ユ湡鍒楋紙鏍规嵁绫诲瀷鍔ㄦ�侀厤缃級
+ baseColumns.push(
+ {
+ label: isLeaveType ? "寮�濮嬫棩鏈�" : "鐢宠鏃ユ湡",
+ prop: isLeaveType ? "startDate" : "approveTime",
+ width: 200
+ },
+ {
+ label: "缁撴潫鏃ユ湡",
+ prop: isLeaveType ? "endDate" : "approveOverTime",
+ width: 120
+ }
+ );
+
+ // 褰撳墠瀹℃壒浜哄垪
+ baseColumns.push({
label: "褰撳墠瀹℃壒浜�",
prop: "approveUserCurrentName",
width: 120
- },
- {
+ });
+
+ // 鎿嶄綔鍒�
+ baseColumns.push({
dataType: "action",
label: "鎿嶄綔",
align: "center",
@@ -157,7 +205,7 @@
clickFun: (row) => {
openForm("edit", row);
},
- disabled: (row) => row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
+ disabled: (row) => currentApproveType.value === 6 || row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
},
{
name: "瀹℃牳",
@@ -165,7 +213,7 @@
clickFun: (row) => {
openApprovalDia("approval", row);
},
- disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
+ disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
},
{
name: "璇︽儏",
@@ -182,8 +230,10 @@
},
},
],
- },
-]);
+ });
+
+ return baseColumns;
+});
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
@@ -214,7 +264,7 @@
};
const getList = () => {
tableLoading.value = true;
- approveProcessListPage({...page, ...searchForm.value,approveType:props.approveType}).then(res => {
+ approveProcessListPage({...page, ...searchForm.value, approveType: currentApproveType.value}).then(res => {
tableLoading.value = false;
tableData.value = res.data.records
page.total = res.data.total;
@@ -222,6 +272,33 @@
tableLoading.value = false;
})
};
+// 瀵煎嚭
+const handleOut = () => {
+ const type = currentApproveType.value
+ const urlMap = {
+ 0: "/approveProcess/exportZero",
+ 1: "/approveProcess/exportOne",
+ 2: "/approveProcess/exportTwo",
+ 3: "/approveProcess/exportThree",
+ 4: "/approveProcess/exportFour",
+ 5: "/approveProcess/exportFive",
+ 6: "/approveProcess/exportSix",
+ 7: "/approveProcess/exportSeven",
+ }
+ const url = urlMap[type] || urlMap[0]
+ const nameMap = {
+ 0: "鍗忓悓瀹℃壒绠$悊琛�",
+ 1: "鍏嚭绠$悊瀹℃壒琛�",
+ 2: "璇峰亣绠$悊瀹℃壒琛�",
+ 3: "鍑哄樊绠$悊瀹℃壒琛�",
+ 4: "鎶ラ攢绠$悊瀹℃壒琛�",
+ 5: "閲囪喘鐢宠瀹℃壒琛�",
+ 6: "鎶ヤ环瀹℃壒琛�",
+ 7: "鍑哄簱瀹℃壒琛�",
+ }
+ const fileName = nameMap[type] || nameMap[0]
+ proxy.download(url, {}, `${fileName}.xlsx`)
+}
// 琛ㄦ牸閫夋嫨鏁版嵁
const handleSelectionChange = (selection) => {
selectedRows.value = selection;
@@ -265,8 +342,27 @@
});
};
onMounted(() => {
+ // 鏍规嵁URL鍙傛暟璁剧疆鏍囩椤靛拰鏌ヨ鏉′欢
+ const approveType = route.query.approveType;
+ const approveId = route.query.approveId;
+
+ if (approveType) {
+ // 璁剧疆鏍囩椤碉紙approveType 瀵瑰簲 activeTab 鐨� name锛�
+ activeTab.value = String(approveType);
+ }
+
+ if (approveId) {
+ // 璁剧疆娴佺▼缂栧彿鏌ヨ鏉′欢
+ searchForm.value.approveId = String(approveId);
+ }
+
+ // 鏌ヨ鍒楄〃
getList();
});
</script>
-<style scoped></style>
+<style scoped>
+.approval-tabs {
+ margin-bottom: 10px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/approvalProcess/index5.vue b/src/views/collaborativeApproval/approvalProcess/index5.vue
new file mode 100644
index 0000000..2e0f4f3
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/index5.vue
@@ -0,0 +1,22 @@
+<template>
+ <div class="container">
+ <!-- 寮曞叆index.vue缁勪欢骞朵紶閫掑弬鏁� -->
+ <ApprovalProcessIndex :approveType="5" />
+ </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
index 8db29fc..f6a3e3c 100644
--- a/src/views/collaborativeApproval/attendanceManagement/index.vue
+++ b/src/views/collaborativeApproval/attendanceManagement/index.vue
@@ -5,8 +5,8 @@
<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 :data="holidayData" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="name" label="鍋囨湡鍚嶇О" />
<el-table-column prop="type" label="鍋囨湡绫诲瀷">
<template #default="scope">
@@ -37,9 +37,13 @@
<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 :data="annualData" border style="width: 100%; margin-top: 20px;">
+ <el-table-column prop="employeeType" label="鍛樺伐绫诲瀷">
+ <template #default="scope">
+ <el-tag :type="getTagType(scope.row.employeeType)">{{ getTypeLabel(scope.row.employeeType) }}</el-tag>
+ </template>
+ </el-table-column>
<el-table-column prop="workYears" label="宸ヤ綔骞撮檺" />
<el-table-column prop="annualDays" label="骞村亣澶╂暟" align="center" />
<el-table-column prop="maxCarryOver" label="鏈�澶х粨杞ぉ鏁�" align="center" />
@@ -64,8 +68,8 @@
<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 :data="overtimeData" border style="width: 100%; margin-top: 20px;">
<el-table-column prop="name" label="瑙勫垯鍚嶇О" />
<el-table-column prop="type" label="鍔犵彮绫诲瀷" >
<template #default="scope">
@@ -96,15 +100,15 @@
<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 :data="worktimeData" border style="width: 100%; margin-top: 20px;">
<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 :type="scope.row.flexibleStart === 'true' ? 'success' : 'info'">
+ {{ scope.row.flexibleStart === 'true' ? '鏄�' : '鍚�' }}
</el-tag>
</template>
</el-table-column>
@@ -125,6 +129,52 @@
</el-table>
</div>
</el-tab-pane>
+
+ <!-- 鎵撳崱璁板綍 -->
+ <el-tab-pane label="鎵撳崱璁板綍" name="attendance">
+ <div class="tab-content">
+ <div style="margin-bottom: 20px;">
+ <el-date-picker
+ v-model="attendanceDate"
+ type="date"
+ placeholder="閫夋嫨鏃ユ湡"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ style="margin-right: 10px;"
+ @change="filterAttendanceData"
+ />
+ <el-select
+ v-model="attendanceStatus"
+ placeholder="閫夋嫨鐘舵��"
+ style="width: 120px; margin-right: 10px;"
+ @change="filterAttendanceData"
+ >
+ <el-option label="鍏ㄩ儴" value="" />
+ <el-option label="姝e父" value="normal" />
+ <el-option label="杩熷埌" value="late" />
+ <el-option label="鏃╅��" value="early" />
+ <el-option label="缂哄嫟" value="absent" />
+ </el-select>
+ <el-button type="primary" @click="exportAttendance">瀵煎嚭璁板綍</el-button>
+ </div>
+
+ <el-table :data="filteredAttendanceData" border style="width: 100%;">
+ <el-table-column prop="employeeName" label="鍛樺伐濮撳悕" width="120" />
+ <el-table-column prop="department" label="閮ㄩ棬" width="120" />
+ <el-table-column prop="date" label="鏃ユ湡" width="120" />
+ <el-table-column prop="clockInTime" label="涓婄彮鎵撳崱" width="120" />
+ <el-table-column prop="clockOutTime" label="涓嬬彮鎵撳崱" width="120" />
+ <el-table-column prop="workHours" label="宸ヤ綔鏃堕暱" width="100" align="center" />
+ <el-table-column prop="status" label="鐘舵��" width="100" align="center">
+ <template #default="scope">
+ <el-tag :type="getAttendanceTagType(scope.row.status)">{{ getAttendanceStatusLabel(scope.row.status) }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="location" label="鎵撳崱鍦扮偣" width="150" />
+ <el-table-column prop="remark" label="澶囨敞" min-width="150" />
+ </el-table>
+ </div>
+ </el-tab-pane>
</el-tabs>
<!-- 閫氱敤寮圭獥 -->
@@ -133,23 +183,29 @@
<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-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="姝e紡鍛樺伐" value="regular" />
<el-option label="璇曠敤鏈熷憳宸�" value="probation" />
- <el-option label="瀹炰範鐢�" value="intern" />
+ <el-option label="瀹炰範鐢�" value="intern" /> -->
+ <el-option
+ v-for="option in getTypeOptions()"
+ :key="option.value"
+ :label="option.label"
+ :value="option.value"
+ />
</el-select>
</el-form-item>
@@ -191,7 +247,7 @@
@change="validateTimeField('startTime')"
/>
</el-form-item>
-
+
<el-form-item label="缁撴潫鏃堕棿" prop="endTime" v-if="currentType === 'overtime'">
<el-time-picker
v-model="form.endTime"
@@ -237,14 +293,14 @@
<el-input-number v-model="form.flexibleMinutes" :min="0" :max="120" style="width: 100%" />
</el-form-item>
- <el-form-item label="鐘舵��" prop="status">
+ <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>
@@ -258,6 +314,7 @@
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
+import { listHolidaySettings, addHolidaySettings, updateHolidaySettings, delHolidaySettings, listAnnualLeaveSettingList, addAnnualLeaveSetting, updateAnnualLeaveSetting, delAnnualLeaveSetting, listOvertimeSettingList, addOvertimeSetting, updateOvertimeSetting, delOvertimeSetting, listWorkingHoursSettingList, addWorkingHoursSetting, updateWorkingHoursSetting, delWorkingHoursSetting } from '@/api/collaborativeApproval/attendanceManagement.js'
// 褰撳墠婵�娲荤殑鏍囩椤�
const activeTab = ref('holiday')
@@ -269,12 +326,29 @@
const currentAction = ref('')
const currentEditId = ref('')
const formRef = ref()
+const page = {
+ current: 1,
+ size: 20,
+ total: 0,
+ }
+const holidayData = ref([])
+const annualData = ref([])
+const overtimeData = ref([])
+const worktimeData = ref([])
+
+// 鎵撳崱璁板綍鐩稿叧鏁版嵁
+const attendanceData = ref([])
+const filteredAttendanceData = ref([])
+const attendanceDate = ref('')
+const attendanceStatus = ref('')
// 琛ㄥ崟鏁版嵁
const form = reactive({
name: '',
type: '',
dateRange: [],
+ startDate: '',
+ endDate: '',
days: 0,
employeeType: '',
workYears: '',
@@ -300,9 +374,9 @@
workYears: [{ required: true, message: '璇疯緭鍏ュ伐浣滃勾闄�', trigger: 'blur' }],
annualDays: [{ required: true, message: '璇疯緭鍏ュ勾鍋囧ぉ鏁�', trigger: 'blur' }],
maxCarryOver: [{ required: true, message: '璇疯緭鍏ユ渶澶х粨杞ぉ鏁�', trigger: 'blur' }],
- startTime: [{
- required: true,
- message: '璇烽�夋嫨寮�濮嬫椂闂�',
+ startTime: [{
+ required: true,
+ message: '璇烽�夋嫨寮�濮嬫椂闂�',
trigger: 'change',
validator: (rule, value, callback) => {
if (!value) {
@@ -312,9 +386,9 @@
}
}
}],
- endTime: [{
- required: true,
- message: '璇烽�夋嫨缁撴潫鏃堕棿',
+ endTime: [{
+ required: true,
+ message: '璇烽�夋嫨缁撴潫鏃堕棿',
trigger: 'change',
validator: (rule, value, callback) => {
if (!value) {
@@ -324,9 +398,9 @@
}
}
}],
- workStartTime: [{
- required: true,
- message: '璇烽�夋嫨涓婄彮鏃堕棿',
+ workStartTime: [{
+ required: true,
+ message: '璇烽�夋嫨涓婄彮鏃堕棿',
trigger: 'change',
validator: (rule, value, callback) => {
if (!value) {
@@ -336,9 +410,9 @@
}
}
}],
- workEndTime: [{
- required: true,
- message: '璇烽�夋嫨涓嬬彮鏃堕棿',
+ workEndTime: [{
+ required: true,
+ message: '璇烽�夋嫨涓嬬彮鏃堕棿',
trigger: 'change',
validator: (rule, value, callback) => {
if (!value) {
@@ -350,37 +424,12 @@
}],
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'
+ weekday: 'primary', weekend: 'warning', holiday: 'danger', night: 'info',
+ regular: 'success', probation: 'info', intern: 'danger'
}
return tagMap[type] || 'info'
}
@@ -388,9 +437,31 @@
const getTypeLabel = (type) => {
const labelMap = {
legal: '娉曞畾鑺傚亣鏃�', adjustment: '璋冧紤鏃�', special: '鐗规畩鍋囨湡', company: '鍏徃鍋囨湡',
- weekday: '宸ヤ綔鏃ュ姞鐝�', weekend: '鍛ㄦ湯鍔犵彮', holiday: '鑺傚亣鏃ュ姞鐝�', night: '娣卞鍔犵彮'
+ weekday: '宸ヤ綔鏃ュ姞鐝�', weekend: '鍛ㄦ湯鍔犵彮', holiday: '鑺傚亣鏃ュ姞鐝�', night: '娣卞鍔犵彮',
+ regular: '姝e紡鍛樺伐', probation: '璇曠敤鏈熷憳宸�', intern: '瀹炰範鐢�'
}
return labelMap[type] || type
+}
+
+// 鎵撳崱璁板綍鐩稿叧宸ュ叿鍑芥暟
+const getAttendanceTagType = (status) => {
+ const tagMap = {
+ normal: 'success',
+ late: 'warning',
+ early: 'warning',
+ absent: 'danger'
+ }
+ return tagMap[status] || 'info'
+}
+
+const getAttendanceStatusLabel = (status) => {
+ const labelMap = {
+ normal: '姝e父',
+ late: '杩熷埌',
+ early: '鏃╅��',
+ absent: '缂哄嫟'
+ }
+ return labelMap[status] || status
}
const getTypeOptions = () => {
@@ -408,6 +479,12 @@
{ label: '鑺傚亣鏃ュ姞鐝�', value: 'holiday' },
{ label: '娣卞鍔犵彮', value: 'night' }
]
+ } else if (currentType.value === 'annual') {
+ return [
+ { label: '姝e紡鍛樺伐', value: 'regular' },
+ { label: '璇曠敤鏈熷憳宸�', value: 'probation' },
+ { label: '瀹炰範鐢�', value: 'intern' }
+ ]
}
return []
}
@@ -418,12 +495,14 @@
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])
-
+ form.startDate = start.toISOString().split('T')[0]
+ form.endDate = end.toISOString().split('T')[0]
+
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
@@ -434,14 +513,14 @@
}
// 楠岃瘉鏃堕棿鏍煎紡
-const validateTime = (time) => {
- if (!time) return ''
- if (typeof time === 'string') return time
- if (time instanceof Date) {
- return time.toTimeString().slice(0, 5)
- }
- return ''
-}
+// 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) => {
@@ -464,7 +543,7 @@
try {
currentType.value = type
currentAction.value = action
-
+
if (action === 'add') {
dialogTitle.value = `鏂板${getTypeName(type)}`
currentEditId.value = ''
@@ -474,7 +553,7 @@
currentEditId.value = row.id
fillForm(row)
}
-
+
dialogVisible.value = true
} catch (error) {
console.error('鎵撳紑寮圭獥澶辫触:', error)
@@ -497,6 +576,8 @@
name: '',
type: '',
dateRange: [],
+ startDate: '',
+ endDate: '',
days: 0,
employeeType: '',
workYears: '',
@@ -519,6 +600,8 @@
name: row.name,
type: row.type,
dateRange: [new Date(row.startDate), new Date(row.endDate)],
+ startDate: row.startDate,
+ endDate: row.endDate,
days: row.days,
status: row.status
})
@@ -558,15 +641,15 @@
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) {
@@ -576,85 +659,438 @@
}
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)
+ const params = {
+ name: form.name,
+ type: form.type,
+ startDate: form.startDate,
+ endDate: form.endDate,
+ days: form.days,
+ status: form.status
+ }
+ addHolidaySettings(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ // dialogVisible.value = false;
+ getHolidaySettingsList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} else if (currentType.value === 'annual') {
- annualData.value.push(newItem)
+ // annualData.value.push(newItem)
+ const params = {
+ employeeType: form.employeeType,
+ workYears: form.workYears,
+ annualDays: form.annualDays,
+ maxCarryOver: form.maxCarryOver,
+ status: form.status
+ }
+ addAnnualLeaveSetting(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ // dialogVisible.value = false;
+ getAnnualLeaveSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} else if (currentType.value === 'overtime') {
- newItem.startTime = form.startTime || ''
- newItem.endTime = form.endTime || ''
- overtimeData.value.push(newItem)
+ const params = {
+ name: form.name,
+ type: form.type,
+ startTime: form.startTime || '',
+ endTime: form.endTime || '',
+ rate: form.rate,
+ status: form.status
+ }
+ addOvertimeSetting(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ // dialogVisible.value = false;
+ getOvertimeSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ // 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 params = {
+ name: form.name,
+ startTime: form.workStartTime || '',
+ endTime: form.workEndTime || '',
+ flexibleStart: form.flexibleStart,
+ flexibleMinutes: form.flexibleMinutes,
+ status: form.status
+ }
+ addWorkingHoursSetting(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ getWorkingHoursSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ // 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
- }
+ const params = {
+ id: currentEditId.value,
+ 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
}
+ updateHolidaySettings(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ // dialogVisible.value = false;
+ getHolidaySettingsList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} 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
- }
+ const params = {
+ id: currentEditId.value,
+ employeeType: form.employeeType,
+ workYears: form.workYears,
+ annualDays: form.annualDays,
+ maxCarryOver: form.maxCarryOver,
+ status: form.status
}
+ updateAnnualLeaveSetting(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ getAnnualLeaveSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} 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
- }
+ const params = {
+ id: currentEditId.value,
+ name: form.name,
+ type: form.type,
+ startTime: form.startTime || '',
+ endTime: form.endTime || '',
+ rate: form.rate,
+ status: form.status
}
+ updateOvertimeSetting(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ getOvertimeSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+
+ // 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 params = {
+ id: currentEditId.value,
+ name: form.name,
+ startTime: form.workStartTime || '',
+ endTime: form.workEndTime || '',
+ flexibleStart: form.flexibleStart,
+ flexibleMinutes: form.flexibleMinutes,
+ status: form.status
}
+ updateWorkingHoursSetting(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ getWorkingHoursSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ // 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 filterAttendanceData = () => {
+ let filtered = attendanceData.value
+
+ // 鎸夋棩鏈熻繃婊�
+ if (attendanceDate.value) {
+ filtered = filtered.filter(item => item.date === attendanceDate.value)
+ }
+
+ // 鎸夌姸鎬佽繃婊�
+ if (attendanceStatus.value) {
+ filtered = filtered.filter(item => item.status === attendanceStatus.value)
+ }
+
+ filteredAttendanceData.value = filtered
+}
+
+// 瀵煎嚭鎵撳崱璁板綍
+const exportAttendance = () => {
+ ElMessage.success('瀵煎嚭鍔熻兘寮�鍙戜腑...')
+}
+
+// 鍒濆鍖栨墦鍗¤褰曞亣鏁版嵁
+const initAttendanceData = () => {
+ const mockData = [
+ {
+ id: 1,
+ employeeName: '闄堝織寮�',
+ department: '鎶�鏈儴',
+ date: '2025-08-15',
+ clockInTime: '09:00:00',
+ clockOutTime: '18:00:00',
+ workHours: '8.0h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 2,
+ employeeName: '鏉庨洩姊�',
+ department: '甯傚満閮�',
+ date: '2025-08-16',
+ clockInTime: '08:58:00',
+ clockOutTime: '18:05:00',
+ workHours: '8.12h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 3,
+ employeeName: '鐜嬪缓鍗�',
+ department: '浜轰簨閮�',
+ date: '2025-08-16',
+ clockInTime: '09:02:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.97h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 4,
+ employeeName: '璧垫檽涓�',
+ department: '璐㈠姟閮�',
+ date: '2025-09-02',
+ clockInTime: '08:55:00',
+ clockOutTime: '18:10:00',
+ workHours: '8.25h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 5,
+ employeeName: '寮犲浗搴�',
+ department: '鎶�鏈儴',
+ date: '2025-09-02',
+ clockInTime: '09:00:00',
+ clockOutTime: '18:30:00',
+ workHours: '8.5h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: '鍔犵彮'
+ },
+ {
+ id: 6,
+ employeeName: '鍒樻槑杈�',
+ department: '杩愯惀閮�',
+ date: '2025-09-03',
+ clockInTime: '09:05:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.92h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 7,
+ employeeName: '瀛欎附鍗�',
+ department: '璁捐閮�',
+ date: '2025-09-03',
+ clockInTime: '08:59:00',
+ clockOutTime: '18:02:00',
+ workHours: '8.05h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 8,
+ employeeName: '鍛ㄥ缓鍐�',
+ department: '閿�鍞儴',
+ date: '2025-09-04',
+ clockInTime: '09:15:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.75h',
+ status: 'late',
+ location: '鍏徃鎬婚儴',
+ remark: '浜ら�氬牭濉�'
+ },
+ {
+ id: 9,
+ employeeName: '鍚村皬鑺�',
+ department: '瀹㈡湇閮�',
+ date: '2025-09-04',
+ clockInTime: '09:01:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.98h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 10,
+ employeeName: '椹枃鏉�',
+ department: '鎶�鏈儴',
+ date: '2025-09-05',
+ clockInTime: '08:57:00',
+ clockOutTime: '17:30:00',
+ workHours: '7.55h',
+ status: 'early',
+ location: '鍏徃鎬婚儴',
+ remark: '鏈夋�ヤ簨鎻愬墠绂诲紑'
+ },
+ {
+ id: 11,
+ employeeName: '鏋楁檽涓�',
+ department: '琛屾斂閮�',
+ date: '2025-09-05',
+ clockInTime: '09:03:00',
+ clockOutTime: '18:08:00',
+ workHours: '8.08h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 12,
+ employeeName: '榛勭編鐜�',
+ department: '璐㈠姟閮�',
+ date: '2025-09-06',
+ clockInTime: '',
+ clockOutTime: '',
+ workHours: '0h',
+ status: 'absent',
+ location: '',
+ remark: '璇风梾鍋�'
+ },
+ {
+ id: 13,
+ employeeName: '閮戞捣娑�',
+ department: '甯傚満閮�',
+ date: '2025-08-14',
+ clockInTime: '09:00:00',
+ clockOutTime: '18:00:00',
+ workHours: '8.0h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 14,
+ employeeName: '璋附濞�',
+ department: '浜轰簨閮�',
+ date: '2025-08-20',
+ clockInTime: '08:58:00',
+ clockOutTime: '18:03:00',
+ workHours: '8.08h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 15,
+ employeeName: '浣曞織浼�',
+ department: '鎶�鏈儴',
+ date: '2025-08-21',
+ clockInTime: '09:10:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.83h',
+ status: 'late',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 16,
+ employeeName: '璁搁泤鑺�',
+ department: '璁捐閮�',
+ date: '2025-08-22',
+ clockInTime: '09:01:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.98h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 17,
+ employeeName: '閭撳缓骞�',
+ department: '杩愯惀閮�',
+ date: '2025-09-10',
+ clockInTime: '08:59:00',
+ clockOutTime: '18:05:00',
+ workHours: '8.1h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ },
+ {
+ id: 18,
+ employeeName: '鏇惧皬绾�',
+ department: '瀹㈡湇閮�',
+ date: '2025-09-11',
+ clockInTime: '09:02:00',
+ clockOutTime: '18:00:00',
+ workHours: '7.97h',
+ status: 'normal',
+ location: '鍏徃鎬婚儴',
+ remark: ''
+ }
+ ]
+
+ attendanceData.value = mockData
+ filteredAttendanceData.value = mockData
}
// 鍒犻櫎椤圭洰
@@ -664,21 +1100,115 @@
cancelButtonText: '鍙栨秷',
type: 'warning'
}).then(() => {
+ let ids = [];
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('鍒犻櫎鎴愬姛')
+ if (type === 'holiday') {
+ ids.push(row.id)
+ delHolidaySettings(ids).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ ids = []
+ getHolidaySettingsList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
}
+ else if (type === 'annual') {
+ ids.push(row.id)
+ delAnnualLeaveSetting(ids).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ ids = []
+ getAnnualLeaveSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ }
+ else if (type === 'overtime') {
+ ids.push(row.id)
+ delOvertimeSetting(ids).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ ids = []
+ getOvertimeSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ }
+ else if (type === 'worktime') {
+ ids.push(row.id)
+ delWorkingHoursSetting(ids).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ ids = []
+ getWorkingHoursSettingList()
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ }
+
+ // const index = dataArray.findIndex(item => item.id === row.id)
+ // if (index > -1) {
+ // dataArray.splice(index, 1)
+ // ElMessage.success('鍒犻櫎鎴愬姛')
+ // }
})
}
+// 鑾峰彇鍋囨湡璁剧疆鍒楄〃
+const getHolidaySettingsList = () => {
+ // tableLoading.value = true;
+ listHolidaySettings({...page.value})
+ .then(res => {
+ // tableLoading.value = false;
+ holidayData.value = res.data.records
+ page.total = res.data.total;
+ }).catch(err => {
+ // tableLoading.value = false;
+ })
+};
+// 鑾峰彇骞村亣瑙勫垯鍒楄〃
+const getAnnualLeaveSettingList = () => {
+ listAnnualLeaveSettingList({...page.value})
+ .then(res => {
+ // console.log(res.data)
+ annualData.value = res.data.records
+ page.total = res.data.total;
+ }).catch(err => {
+ })
+};
+// 鑾峰彇鍔犵彮瑙勫垯鍒楄〃
+const getOvertimeSettingList = () => {
+
+ listOvertimeSettingList({...page.value})
+ .then(res => {
+ // console.log(res.data)
+ overtimeData.value = res.data.records
+ page.total = res.data.total;
+ }).catch(err => {
+ })
+};
+// 鑾峰彇宸ヤ綔鏃堕棿瑙勫垯鍒楄〃
+const getWorkingHoursSettingList = () => {
+
+ listWorkingHoursSettingList({...page.value})
+ .then(res => {
+ // console.log(res.data)
+ worktimeData.value = res.data.records
+ page.total = res.data.total;
+ }).catch(err => {
+ })
+};
onMounted(() => {
+ getHolidaySettingsList()
+ getAnnualLeaveSettingList()
+ getOvertimeSettingList()
+ getWorkingHoursSettingList()
+ initAttendanceData()
console.log('鑰冨嫟绠$悊椤甸潰鍔犺浇瀹屾垚')
})
diff --git a/src/views/collaborativeApproval/enterpriseBook/index.vue b/src/views/collaborativeApproval/enterpriseBook/index.vue
new file mode 100644
index 0000000..0b33a32
--- /dev/null
+++ b/src/views/collaborativeApproval/enterpriseBook/index.vue
@@ -0,0 +1,798 @@
+<template>
+ <div class="app-container">
+ <!-- 澶撮儴瀵艰埅 -->
+ <!-- <div class="header">
+ <h2>浼佷笟閫氳褰曠鐞�</h2>
+ <p>绠$悊涓汉銆佸叕鍏卞拰鍗曚綅鐨勮仈绯绘柟寮�</p>
+ </div> -->
+
+ <!-- 鏍囩椤靛垏鎹� -->
+ <el-tabs v-model="activeTab" @tab-change="handleTabChange" type="border-card">
+ <el-tab-pane label="涓汉閫氳褰�" name="personal">
+ <div class="tab-content">
+ <!-- 鎼滅储妗� -->
+ <el-input
+ v-model="personalSearch.staffName"
+ placeholder="鎼滅储鑱旂郴浜�"
+ clearable
+ prefix-icon="Search"
+ class="search-input"
+ @keyup.enter="getPersonalContactsList"
+ />
+ <el-button style="margin: 0 0 20px 20px;" type="primary" @click="showAddContactDialog=true">娣诲姞鑱旂郴浜�</el-button>
+ <!-- 鑱旂郴浜哄垪琛� -->
+ <div class="contact-list">
+ <div
+ v-for="contact in personalContacts"
+ :key="contact.id"
+ class="contact-card"
+ @click="showContactDetail(contact)"
+ >
+ <div class="contact-avatar">{{ contact.staffName.charAt(0) }}</div>
+ <div class="contact-info">
+ <h4>{{ contact.staffName }}</h4>
+ <p>{{ contact.profession }} - {{ contact.postJob }}</p>
+ <div class="contact-phone">{{ contact.phone }}</div>
+ </div>
+ <div class="contact-actions">
+ <!-- <el-button
+ type="text"
+ icon="Phone"
+ @click.stop="callContact(contact)"
+ ></el-button> -->
+ <el-button
+ type="text"
+ icon="Message"
+ @click.stop="messageContact(contact)"
+ ></el-button>
+ <el-button
+ type="text"
+ icon="Delete"
+ @click.stop="removeFromPersonalContacts(contact.id)"
+ ></el-button>
+ </div>
+ </div>
+
+ <!-- 绌虹姸鎬�
+ <div v-if="personalContacts.length === 0 && !loading" class="empty-state">
+ <el-empty description="鏆傛棤鑱旂郴浜�" />
+ <el-button type="primary" @click="showAddContactDialog=true">娣诲姞鑱旂郴浜�</el-button>
+ </div> -->
+ </div>
+ </div>
+ </el-tab-pane>
+
+ <el-tab-pane label="鍏叡閫氳褰�" name="public">
+ <div class="tab-content">
+ <!-- 鎼滅储妗� -->
+ <el-input
+ v-model="publicSearch.staffName"
+ placeholder="鎼滅储鍏叡鑱旂郴浜�"
+ clearable
+ prefix-icon="Search"
+ class="search-input"
+ @keyup.enter="getPublicContactsList"
+ />
+
+ <!-- 鑱旂郴浜哄垪琛� publicContacts-->
+ <div class="contact-list">
+ <div
+ v-for="contact in EmployeeList"
+ :key="contact.id"
+ class="contact-card"
+ @click="showContactDetail(contact)"
+ >
+ <div class="contact-avatar">{{ contact.staffName.charAt(0) }}</div>
+ <div class="contact-info">
+ <h4>{{ contact.staffName }}</h4>
+ <p>{{ contact.postJob }} - {{ contact.profession }}</p>
+ <div class="contact-phone">{{ contact.phone }}</div>
+ </div>
+ <div class="contact-actions">
+ <!-- <el-button
+ type="text"
+ icon="Phone"
+ @click.stop="callContact(contact)"
+ ></el-button> -->
+ <el-button
+ type="text"
+ icon="Message"
+ @click.stop="messageContact(contact)"
+ ></el-button>
+ <el-button
+ type="text"
+ icon="Delete"
+ :type="isInPersonalContacts(contact.id) ? 'primary' : ''"
+ @click.stop="togglePersonalContact(contact)"
+ ></el-button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+
+ <el-tab-pane label="鍗曚綅閫氳褰�" name="company">
+ <div class="tab-content">
+ <div class="company-contacts-layout">
+ <!-- 宸︿晶閮ㄩ棬鏍� -->
+ <div class="department-tree">
+ <!-- <h3>閮ㄩ棬缁撴瀯</h3>
+ <el-tree
+ :data="departmentTree"
+ :props="{ label: 'deptName', children: 'children' }"
+ node-key="deptId"
+ ref="departmentTreeRef"
+ highlight-current
+ default-expand-all
+ @node-click="handleDepartmentClick"
+ /> -->
+ <el-col >
+ <div class="head-container">
+ <el-input
+ v-model="deptName"
+ placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�"
+ clearable
+ prefix-icon="Search"
+ style="margin-bottom: 20px"
+ />
+ </div>
+ <div class="head-container">
+ <el-tree
+ :data="departmentTree"
+ :props="{ label: 'label', children: 'children' }"
+ :expand-on-click-node="false"
+ :filter-node-method="filterNode"
+ ref="deptTreeRef"
+ node-key="id"
+ highlight-current
+ default-expand-all
+ @node-click="handleDepartmentClick"
+ />
+ </div>
+ </el-col>
+ </div>
+
+ <!-- 鍙充晶閮ㄩ棬鎴愬憳 -->
+ <div class="department-members">
+ <h3>{{ currentDepartment?.label || '鍏ㄩ儴鎴愬憳' }}</h3>
+ <el-input
+ v-model="companySearch.staffName"
+ placeholder="鎼滅储閮ㄩ棬鎴愬憳"
+ clearable
+ prefix-icon="Search"
+ class="search-input"
+ @keyup.enter="getCompanyContactsList"
+ />
+
+ <div class="contact-list">
+ <div
+ v-for="contact in companyContacts"
+ :key="contact.id"
+ class="contact-card"
+ @click="showContactDetail(contact)"
+ >
+ <div class="contact-avatar">{{ contact.staffName.charAt(0) }}</div>
+ <div class="contact-info">
+ <h4>{{ contact.staffName }}</h4>
+ <p>{{ contact.profession }}</p>
+ <div class="contact-phone">{{ contact.phone }}</div>
+ </div>
+ <div class="contact-actions">
+
+ <el-button
+ type="text"
+ icon="Message"
+ @click.stop="messageContact(contact)"
+ ></el-button>
+ <el-button
+ type="text"
+ icon="Delete"
+ :type="isInPersonalContacts(contact.id) ? 'primary' : ''"
+ @click.stop="togglePersonalContact(contact)"
+ ></el-button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+
+ <!-- 鑱旂郴浜鸿鎯呭脊绐� -->
+ <el-dialog
+ v-model="showDetailDialog"
+ title="鑱旂郴浜鸿鎯�"
+ width="400px"
+ >
+ <div v-if="selectedContact" class="contact-detail">
+ <div class="detail-avatar">{{ selectedContact.staffName?.charAt(0) }}</div>
+ <h3>{{ selectedContact.staffName }}</h3>
+ <p class="detail-position">{{ selectedContact.profession }} - {{ selectedContact.postJob }}</p>
+
+ <div class="detail-info">
+ <div class="info-item">
+ <span class="label">缂栧彿锛�</span>
+ <span class="value">{{ selectedContact.staffNo }}</span>
+ </div>
+ <div class="info-item">
+ <span class="label">鎵嬫満鍙风爜锛�</span>
+ <span class="value">{{ selectedContact.phone }}</span>
+ </div>
+ <div class="info-item">
+ <span class="label">閭锛�</span>
+ <span class="value">{{ selectedContact.sex }}</span>
+ </div>
+ <div class="info-item">
+ <span class="label">浣忓潃锛�</span>
+ <span class="value">{{ selectedContact.adress || '鏆傛棤' }}</span>
+ </div>
+ </div>
+ </div>
+ <template #footer>
+ <el-button @click="showDetailDialog = false">鍏抽棴</el-button>
+ <el-button
+ type="primary"
+ v-if="activeTab !== 'personal'"
+ @click="togglePersonalContact(selectedContact); showDetailDialog = false"
+ >
+ {{ isInPersonalContacts(selectedContact?.id) ? '浠庝釜浜洪�氳褰曠Щ闄�' : '娣诲姞鍒颁釜浜洪�氳褰�' }}
+ </el-button>
+ </template>
+ </el-dialog>
+
+ <!-- 娣诲姞鑱旂郴浜哄脊绐� -->
+ <el-dialog
+ v-model="showAddContactDialog"
+ title="娣诲姞鑱旂郴浜�"
+ width="500px"
+ >
+ <el-form :model="addContactForm" ref="addContactFormRef" label-width="80px">
+ <!-- <el-form-item label="濮撳悕" prop="name">
+ <el-input v-model="addContactForm.name" placeholder="璇疯緭鍏ュ鍚�" />
+ </el-form-item>
+ <el-form-item label="鎵嬫満鍙风爜" prop="phone">
+ <el-input v-model="addContactForm.phone" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" />
+ </el-form-item>
+ <el-form-item label="閭" prop="email">
+ <el-input v-model="addContactForm.email" placeholder="璇疯緭鍏ラ偖绠�" />
+ </el-form-item>
+ <el-form-item label="閮ㄩ棬" prop="department">
+ <el-input v-model="addContactForm.department" placeholder="璇疯緭鍏ラ儴闂�" />
+ </el-form-item> -->
+ <el-form-item label="濮撳悕" prop="name">
+ <!-- <select v-model="addContactForm.contactId">
+ <option v-for="item in EmployeeList" :key="item.id" :value="item.id">{{ item.staffName }}</option>
+ </select> -->
+ <el-select v-model="addContactForm.contactId" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option
+ v-for="option in EmployeeList"
+ :key="option.id"
+ :label="option.staffName"
+ :value="option.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="showAddContactDialog = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="addContact">纭畾</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, reactive, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+ getPersonalContacts,
+ addPersonalContact,
+ removePersonalContact,
+ getPublicContacts,
+ getCompanyContacts,
+ getDepartmentTree,
+ getEmployeeDetail
+} from '@/api/collaborativeApproval/enterpriseBook.js'
+import { getUserProfile } from '@/api/system/user.js'
+import {staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
+import {
+ changeUserStatus,
+ listUser,
+ resetUserPwd,
+ delUser,
+ getUser,
+ updateUser,
+ addUser,
+ deptTreeSelect,
+} from "@/api/system/user";
+
+// 鏍囩椤电姸鎬�
+const activeTab = ref('personal')
+const loading = ref(false)
+const EmployeeList = ref([])
+const page = reactive({
+ pageNum: 1,
+ pageSize: 10,
+ total: 0,
+})
+// 涓汉閫氳褰曟暟鎹�
+const personalContacts = ref([])
+const personalSearch = ref({
+ staffName: '',
+})
+
+// 鍏叡閫氳褰曟暟鎹�
+const publicContacts = ref([])
+const publicSearch = ref({
+ staffName: '',
+ staffState: 1
+})
+
+// 鍗曚綅閫氳褰曟暟鎹�
+const companyContacts = ref([])
+const companySearch = ref({
+ staffName: '',
+ staffState: 1
+})
+const departmentTree = ref([])
+const departmentTreeRef = ref(null)
+const currentDepartment = ref(null)
+
+// 寮圭獥鐘舵��
+const showDetailDialog = ref(false)
+const showAddContactDialog = ref(false)
+const selectedContact = ref(null)
+
+// 娣诲姞鑱旂郴浜鸿〃鍗�
+const addContactForm = reactive({
+ contactId: '',
+ name: '',
+ phone: '',
+ email: '',
+ department: '',
+ position: ''
+})
+const addContactFormRef = ref(null)
+
+// 鍒濆鍖栨暟鎹�
+onMounted(() => {
+ getEmployeeList()
+ getPersonalContactsList()
+ if (activeTab.value === 'public') {
+ getPublicContactsList()
+ } else if (activeTab.value === 'company') {
+ getDepartmentTreeData()
+ getCompanyContactsList()
+ }
+})
+
+// 澶勭悊鏍囩椤靛垏鎹�
+const handleTabChange = (tabName) => {
+ if (tabName === 'public') {
+ getPublicContactsList()
+ } else if (tabName === 'company') {
+ getDepartmentTreeData()
+ getCompanyContactsList()
+ }
+}
+
+// 鑾峰彇涓汉閫氳褰曞垪琛�
+const getPersonalContactsList = async () => {
+ loading.value = true
+ getPersonalContacts(page,personalSearch.value).then(res => {
+ personalContacts.value = res.data.records
+ })
+ loading.value = false
+}
+
+// 鑾峰彇鍏叡閫氳褰曞垪琛�
+const getPublicContactsList = async () => {
+ loading.value = true
+ getEmployeeList()
+ // publicContacts.value = generateMockPublicContacts()
+ loading.value = false
+}
+ //鑾峰彇鍛樺伐鍒楄〃
+const getEmployeeList = async () => {
+ staffJoinListPage(publicSearch.value).then(res => {
+ console.log(res.data.records)
+ EmployeeList.value = res.data.records
+ }).catch(err => {})
+}
+// 鑾峰彇鍗曚綅閫氳褰曞垪琛�
+const getCompanyContactsList = async () => {
+ loading.value = true
+ staffJoinListPage(companySearch.value).then(res => {
+ // console.log(res.data.records)
+ companyContacts.value = res.data.records
+ }).catch(err => {})
+
+ loading.value = false
+ loading.value = false
+ // }
+}
+
+// 鑾峰彇閮ㄩ棬鏍戠粨鏋�
+const getDepartmentTreeData = async () => {
+ deptTreeSelect().then((response) => {
+ // console.log("Tree",response.data)
+ departmentTree.value = response.data;
+ // enabledDeptOptions.value = filterDisabledDept(
+ // JSON.parse(JSON.stringify(response.data))
+ // );
+ });
+}
+// /** 杩囨护绂佺敤鐨勯儴闂� */
+// function filterDisabledDept(deptList) {
+// return deptList.filter((dept) => {
+// if (dept.disabled) {
+// return false;
+// }
+// if (dept.children && dept.children.length) {
+// dept.children = filterDisabledDept(dept.children);
+// }
+// return true;
+// });
+// }
+// 澶勭悊閮ㄩ棬鐐瑰嚮
+const handleDepartmentClick = (data) => {
+ // console.log("鐐瑰嚮",data)
+ companySearch.value = {
+ ...companySearch.value,
+ deptId: data.id,
+ }
+ // currentDepartment.value = data.id
+ // 鑾峰彇璇ラ儴闂ㄧ殑鎴愬憳鍒楄〃
+
+ getCompanyContactsList()
+}
+
+// 鏄剧ず鑱旂郴浜鸿鎯�
+const showContactDetail = async (contact) => {
+ selectedContact.value = contact
+ showDetailDialog.value = true
+}
+
+// 鎷ㄦ墦鐢佃瘽
+const callContact = (contact) => {
+ ElMessage.info(`姝e湪鎷ㄦ墦 ${contact.name} 鐨勭數璇�: ${contact.phone}`)
+}
+
+// 鍙戦�佹秷鎭�
+const messageContact = (contact) => {
+ ElMessage.info(`姝e湪鍙戦�佹秷鎭粰 ${contact.name}`)
+}
+
+
+// 娣诲姞鑱旂郴浜�
+const addContact = async () => {
+
+ try {
+ // 琛ㄥ崟楠岃瘉
+ // if (!addContactForm.name || !addContactForm.phone) {
+ // ElMessage.warning('璇峰~鍐欏鍚嶅拰鎵嬫満鍙风爜')
+ // return
+ // }
+
+ const res = await addPersonalContact(addContactForm)
+ if (res.code === 200) {
+ ElMessage.success('娣诲姞鎴愬姛')
+ showAddContactDialog.value = false
+ getPersonalContactsList()
+ // 閲嶇疆琛ㄥ崟
+ Object.keys(addContactForm).forEach(key => {
+ addContactForm[key] = ''
+ })
+ }
+ } catch (error) {
+ ElMessage.error('娣诲姞澶辫触')
+ // 妯℃嫙娣诲姞鎴愬姛
+ personalContacts.value.push({
+ ...addContactForm,
+ id: Date.now(),
+ createTime: new Date().toISOString()
+ })
+ ElMessage.success('娣诲姞鎴愬姛')
+ showAddContactDialog.value = false
+ // 閲嶇疆琛ㄥ崟
+ Object.keys(addContactForm).forEach(key => {
+ addContactForm[key] = ''
+ })
+ }
+}
+
+// 浠庝釜浜洪�氳褰曠Щ闄�
+const removeFromPersonalContacts = async (contactId) => {
+ ElMessageBox.confirm(
+ '纭畾瑕佷粠涓汉閫氳褰曚腑绉婚櫎璇ヨ仈绯讳汉鍚楋紵',
+ '鎻愮ず',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ ).then(async () => {
+ try {
+ const res = await removePersonalContact(contactId)
+ if (res.code === 200) {
+ ElMessage.success('绉婚櫎鎴愬姛')
+ getPersonalContactsList()
+ }
+ } catch (error) {
+ ElMessage.error('绉婚櫎澶辫触')
+ // 妯℃嫙绉婚櫎鎴愬姛
+ // personalContacts.value = personalContacts.value.filter(item => item.id !== contactId)
+ ElMessage.success('绉婚櫎鎴愬姛')
+ }
+ })
+}
+
+// 鍒囨崲涓汉閫氳褰�
+const togglePersonalContact = async (contact) => {
+ const isInPersonal = isInPersonalContacts(contact.id)
+ const contactId = contact.id
+ if (isInPersonal) {
+ // 浠庝釜浜洪�氳褰曠Щ闄�
+ //鏍规嵁contactId鏌ユ壘personalContacts涓搴旂殑椤癸紝鐒跺悗鍒犻櫎璇ラ」
+ const index = personalContacts.value.findIndex(item => item.contactId === contactId)
+ const personId = personalContacts.value[index].id
+ // console.log(personId)
+ await removeFromPersonalContacts(personId)
+ } else {
+ // 娣诲姞鍒颁釜浜洪�氳褰�
+ try {
+ const res = await addPersonalContact({contactId: contactId})
+ if (res.code === 200) {
+ ElMessage.success('娣诲姞鎴愬姛')
+ getPersonalContactsList()
+ }
+ } catch (error) {
+ ElMessage.error('娣诲姞澶辫触')
+ // 妯℃嫙娣诲姞鎴愬姛
+ // personalContacts.value.push({
+ // ...contact,
+ // id: contact.id || Date.now(),
+ // createTime: new Date().toISOString()
+ // })
+ // ElMessage.success('娣诲姞鎴愬姛')
+ }
+ }
+}
+
+// 妫�鏌ユ槸鍚﹀湪涓汉閫氳褰曚腑
+const isInPersonalContacts = (contactId) => {
+ return personalContacts.value.some(item => item.contactId === contactId)
+}
+
+// 鐢熸垚妯℃嫙閮ㄩ棬鏍戞暟鎹�
+const generateMockDepartmentTree = () => {
+ return [
+ {
+ deptId: 1,
+ deptName: '鎶�鏈儴',
+ children: [
+ {
+ deptId: 101,
+ deptName: '鍓嶇缁�'
+ },
+ {
+ deptId: 102,
+ deptName: '鍚庣缁�'
+ },
+ {
+ deptId: 103,
+ deptName: '娴嬭瘯缁�'
+ }
+ ]
+ },
+ {
+ deptId: 2,
+ deptName: '浜у搧閮�'
+ },
+ {
+ deptId: 3,
+ deptName: '浜轰簨閮�'
+ },
+ {
+ deptId: 4,
+ deptName: '璐㈠姟閮�'
+ }
+ ]
+}
+
+// 鐢熸垚妯℃嫙鍗曚綅閫氳褰曟暟鎹�
+// const generateMockCompanyContacts = (deptName) => {
+// const allContacts = getEmployeeList()
+
+// if (deptName) {
+// return allContacts.filter(contact => contact.postJob === deptName)
+// }
+// return allContacts
+// }
+
+</script>
+
+<style scoped>
+.header {
+ margin-bottom: 20px;
+ padding: 15px;
+ background: #f5f7fa;
+ border-radius: 8px;
+}
+
+.header h2 {
+ margin: 0 0 5px 0;
+ color: #303133;
+}
+
+.header p {
+ margin: 0;
+ color: #909399;
+ font-size: 14px;
+}
+
+.tab-content {
+ padding: 15px;
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+}
+
+.search-input {
+ margin-bottom: 20px;
+ width: 300px;
+}
+
+.contact-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px;
+}
+
+.contact-card {
+ display: flex;
+ align-items: center;
+ padding: 15px;
+ width: 500px;
+ background: #f8f9fa;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.contact-card:hover {
+ background: #e9ecef;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.contact-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 48px;
+ height: 48px;
+ background: #409eff;
+ color: #fff;
+ font-size: 20px;
+ font-weight: bold;
+ border-radius: 50%;
+ margin-right: 15px;
+}
+
+.contact-info {
+ flex: 1;
+}
+
+.contact-info h4 {
+ margin: 0 0 5px 0;
+ color: #303133;
+ font-size: 16px;
+}
+
+.contact-info p {
+ margin: 0 0 5px 0;
+ color: #606266;
+ font-size: 14px;
+}
+
+.contact-phone {
+ color: #409eff;
+ font-size: 14px;
+}
+
+.contact-actions {
+ display: flex;
+ gap: 5px;
+}
+
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 60px 20px;
+ color: #909399;
+}
+
+.company-contacts-layout {
+ display: flex;
+ gap: 20px;
+}
+
+.department-tree {
+ width: 250px;
+ padding: 15px;
+ background: #f8f9fa;
+ border-radius: 8px;
+}
+
+.department-tree h3 {
+ margin: 0 0 15px 0;
+ padding-bottom: 10px;
+ border-bottom: 1px solid #e4e7ed;
+ color: #303133;
+ font-size: 16px;
+}
+
+.department-members {
+ flex: 1;
+}
+
+.department-members h3 {
+ margin: 0 0 15px 0;
+ color: #303133;
+ font-size: 16px;
+}
+
+.contact-detail {
+ text-align: center;
+}
+
+.detail-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 80px;
+ height: 80px;
+ background: #409eff;
+ color: #fff;
+ font-size: 32px;
+ font-weight: bold;
+ border-radius: 50%;
+ margin: 0 auto 20px;
+}
+
+.contact-detail h3 {
+ margin: 0 0 10px 0;
+ color: #303133;
+ font-size: 20px;
+}
+
+.detail-position {
+ margin: 0 0 30px 0;
+ color: #606266;
+ font-size: 14px;
+}
+
+.detail-info {
+ text-align: left;
+}
+
+.info-item {
+ margin-bottom: 15px;
+}
+
+.info-item .label {
+ display: inline-block;
+ width: 100px;
+ color: #909399;
+ font-size: 14px;
+}
+
+.info-item .value {
+ color: #303133;
+ font-size: 14px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/knowledgeBase/index.vue b/src/views/collaborativeApproval/knowledgeBase/index.vue
index f944859..aeb1ba4 100644
--- a/src/views/collaborativeApproval/knowledgeBase/index.vue
+++ b/src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -13,22 +13,24 @@
/>
<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-option
+ v-for="item in knowledgeTypeOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
</el-select>
<el-button type="primary" @click="handleQuery" style="margin-left: 10px">
鎼滅储
</el-button>
</div>
<div>
+ <el-button @click="handleExport" style="margin-right: 10px">瀵煎嚭</el-button>
<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"
@@ -60,11 +62,12 @@
<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-option
+ v-for="item in knowledgeTypeOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
</el-select>
</el-form-item>
</el-col>
@@ -221,7 +224,7 @@
<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>
+ <!-- <el-button type="success" @click="markAsFavorite">鏀惰棌@</el-button> -->
</span>
</template>
</el-dialog>
@@ -230,9 +233,10 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
-import { onMounted, ref, reactive, toRefs } from "vue";
+import { onMounted, ref, reactive, toRefs, getCurrentInstance, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import { listKnowledgeBase, delKnowledgeBase,addKnowledgeBase,updateKnowledgeBase } from "@/api/collaborativeApproval/knowledgeBase.js";
// 琛ㄥ崟楠岃瘉瑙勫垯
const rules = {
@@ -259,7 +263,7 @@
tableLoading: false,
page: {
current: 1,
- size: 100,
+ size: 20,
total: 0,
},
tableData: [],
@@ -268,7 +272,7 @@
title: "",
type: "",
scenario: "",
- efficiency: "medium",
+ efficiency: "",
problem: "",
solution: "",
keyPoints: "",
@@ -282,11 +286,11 @@
currentKnowledge: {}
});
-const {
- searchForm,
- tableLoading,
- page,
- tableData,
+const {
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
selectedIds,
form,
dialogVisible,
@@ -311,24 +315,10 @@
prop: "type",
dataType: "tag",
formatData: (params) => {
- const typeMap = {
- contract: "鍚堝悓鐗规壒",
- approval: "瀹℃壒妗堜緥",
- solution: "瑙e喅鏂规",
- experience: "缁忛獙鎬荤粨",
- guide: "鎿嶄綔鎸囧崡"
- };
- return typeMap[params] || params;
+ return getKnowledgeTypeLabel(params);
},
formatType: (params) => {
- const typeMap = {
- contract: "success",
- approval: "warning",
- solution: "primary",
- experience: "info",
- guide: "danger"
- };
- return typeMap[params] || "info";
+ return getKnowledgeTypeTagType(params);
}
},
{
@@ -399,111 +389,6 @@
}
]);
-// 妯℃嫙鏁版嵁
-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();
@@ -513,7 +398,6 @@
// 寮�濮嬭嚜鍔ㄥ埛鏂�
const startAutoRefresh = () => {
setInterval(() => {
- generateNewData();
getList();
}, 600000); // 10鍒嗛挓鍒锋柊涓�娆� (10 * 60 * 1000 = 600000ms)
};
@@ -526,24 +410,14 @@
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;
+ listKnowledgeBase({...page.value, ...searchForm.value})
+ .then(res => {
tableLoading.value = false;
- }, 500);
+ tableData.value = res.data.records
+ page.total = res.data.total;
+ }).catch(err => {
+ tableLoading.value = false;
+ })
};
// 鍒嗛〉澶勭悊
@@ -568,7 +442,7 @@
title: "",
type: "",
scenario: "",
- efficiency: "medium",
+ efficiency: "",
problem: "",
solution: "",
keyPoints: "",
@@ -578,6 +452,7 @@
} else if (type === "edit" && row) {
dialogTitle.value = "缂栬緫鐭ヨ瘑";
Object.assign(form.value, {
+ id: row.id,
title: row.title,
type: row.type,
scenario: row.scenario,
@@ -612,14 +487,7 @@
// 鑾峰彇绫诲瀷鏍囩鏂囨湰
const getTypeLabel = (type) => {
- const typeMap = {
- contract: "鍚堝悓鐗规壒",
- approval: "瀹℃壒妗堜緥",
- solution: "瑙e喅鏂规",
- experience: "缁忛獙鎬荤粨",
- guide: "鎿嶄綔鎸囧崡"
- };
- return typeMap[type] || type;
+ return getKnowledgeTypeLabel(type);
};
// 鑾峰彇鏁堢巼鏍囩绫诲瀷
@@ -665,15 +533,15 @@
// 澶嶅埗鐭ヨ瘑
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}
+ 鐭ヨ瘑鏍囬锛�${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("鐭ヨ瘑鍐呭宸插鍒跺埌鍓创鏉�");
@@ -682,62 +550,32 @@
});
};
-// 鏀惰棌鐭ヨ瘑
-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("鐭ヨ瘑鍒涘缓鎴愬姛");
+ addKnowledgeBase({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} 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("鐭ヨ瘑鏇存柊鎴愬姛");
- }
+ updateKnowledgeBase({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
}
-
- dialogVisible.value = false;
- getList();
} catch (error) {
console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
}
@@ -749,27 +587,42 @@
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);
+ // console.log(selectedIds.value);
+ delKnowledgeBase(selectedIds.value).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ selectedIds.value = [];
+ getList();
}
- });
-
- ElMessage.success("鍒犻櫎鎴愬姛");
- selectedIds.value = [];
- getList();
+ })
}).catch(() => {
// 鐢ㄦ埛鍙栨秷
});
};
+
+// 瀵煎嚭
+const { proxy } = getCurrentInstance()
+const { knowledge_type } = proxy.useDict("knowledge_type")
+
+// 瀛楀吀宸ュ叿
+const knowledgeTypeOptions = computed(() => knowledge_type?.value || [])
+const getKnowledgeTypeLabel = (val) => {
+ const item = knowledgeTypeOptions.value.find(i => String(i.value) === String(val))
+ return item ? item.label : val
+}
+const getKnowledgeTypeTagType = (val) => {
+ const item = knowledgeTypeOptions.value.find(i => String(i.value) === String(val))
+ return item?.elTagType || "info"
+}
+const handleExport = () => {
+ proxy.download('/knowledgeBase/export', { ...searchForm.value }, '鐭ヨ瘑搴�.xlsx')
+}
</script>
<style scoped>
diff --git a/src/views/collaborativeApproval/meetingBoard/index.vue b/src/views/collaborativeApproval/meetingBoard/index.vue
index 63c74f9..dfbb922 100644
--- a/src/views/collaborativeApproval/meetingBoard/index.vue
+++ b/src/views/collaborativeApproval/meetingBoard/index.vue
@@ -16,7 +16,7 @@
</el-card>
<el-card class="stat-card">
<div class="stat-content">
- <div class="stat-number">{{ stats.ongoing }}</div>
+ <div class="stat-number">{{ stats.underWay }}</div>
<div class="stat-label">杩涜涓�</div>
</div>
</el-card>
@@ -28,7 +28,7 @@
</el-card>
<el-card class="stat-card">
<div class="stat-content">
- <div class="stat-number">{{ stats.upcoming }}</div>
+ <div class="stat-number">{{ stats.toStart }}</div>
<div class="stat-label">鍗冲皢寮�濮�</div>
</div>
</el-card>
@@ -45,11 +45,11 @@
</el-tag>
</div>
<div class="meeting-time">
- <el-icon><Clock /></el-icon>
- {{ formatTime(meeting.startTime) }} - {{ formatTime(meeting.endTime) }}
+ {{dayjs(meeting.startTime).format("YYYY-MM-DD")}}<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>
@@ -66,79 +66,18 @@
</div>
<div class="meeting-agenda">
- <h4>璁▼瀹夋帓</h4>
+ <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 class="editor-container">
+ <div
+ v-html="meeting.content"
+ />
</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>
@@ -146,63 +85,21 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue'
+import Editor from "@/components/Editor/index.vue";
+import {getMeetSummaryItems,getMeetSummary} from '@/api/collaborativeApproval/meeting.js'
+import dayjs from "dayjs";
// 缁熻鏁版嵁
-const stats = reactive({
- total: 12,
- ongoing: 3,
- completed: 7,
- upcoming: 2
+const stats = ref({
+ total: 0,
+ underWay: 0,
+ completed: 0,
+ toStart: 0
})
// 浼氳鏁版嵁
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' }
- ]
- }
+
])
// 瀵硅瘽妗嗙浉鍏�
@@ -218,9 +115,9 @@
// 鑾峰彇鐘舵�佺被鍨�
const getStatusType = (status) => {
const statusMap = {
- 'ongoing': 'success',
- 'upcoming': 'warning',
- 'completed': 'info'
+ '2': 'success',
+ '1': 'warning',
+ '0': 'info'
}
return statusMap[status] || 'info'
}
@@ -228,9 +125,9 @@
// 鑾峰彇鐘舵�佹枃鏈�
const getStatusText = (status) => {
const statusMap = {
- 'ongoing': '杩涜涓�',
- 'upcoming': '鍗冲皢寮�濮�',
- 'completed': '宸插畬鎴�'
+ '2': '杩涜涓�',
+ '1': '鍗冲皢寮�濮�',
+ '0': '宸插畬鎴�'
}
return statusMap[status] || '鏈煡'
}
@@ -261,65 +158,16 @@
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
}
-// 鍒涘缓浼氳
-const createMeeting = () => {
- dialogVisible.value = true
- // 閲嶇疆琛ㄥ崟
- Object.assign(meetingForm, {
- title: '',
- timeRange: [],
- location: '',
- host: '',
- description: ''
+
+onMounted( async () => {
+ let [resp1,resp2] = await Promise.all([getMeetSummary(),getMeetSummaryItems()])
+ stats.value = resp1.data
+ meetings.value = resp2.data.map(item => {
+ return {
+ ...item,
+ participants: JSON.parse(item.participants)
+ }
})
-}
-
-// 鎻愪氦浼氳
-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>
@@ -480,19 +328,19 @@
.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
+</style>
diff --git a/src/views/collaborativeApproval/meetingManagement/index.vue b/src/views/collaborativeApproval/meetingManagement/index.vue
new file mode 100644
index 0000000..69b0275
--- /dev/null
+++ b/src/views/collaborativeApproval/meetingManagement/index.vue
@@ -0,0 +1,63 @@
+<template>
+ <div class="app-container">
+ <div class="tabs-wrapper">
+ <el-tabs
+ v-model="activeTab"
+ class="meeting-tabs"
+ @tab-change="handleTabChange"
+ >
+ <el-tab-pane label="浼氳璁剧疆" name="setting" />
+ <el-tab-pane label="浼氳鍒楄〃" name="index" />
+ <el-tab-pane label="浼氳鐢宠" name="application" />
+ <el-tab-pane label="浼氳瀹℃壒" name="examine" />
+ <el-tab-pane label="浼氳鍙戝竷" name="publish" />
+ <el-tab-pane label="浼氳鎬荤粨" name="summary" />
+ </el-tabs>
+ </div>
+
+ <div class="tab-content">
+ <keep-alive>
+ <component :is="currentComponent" />
+ </keep-alive>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue'
+import MeetSetting from '../notificationManagement/meetSetting/index.vue'
+import MeetIndex from '../notificationManagement/meetIndex/index.vue'
+import MeetApplication from '../notificationManagement/meetApplication/index.vue'
+import MeetExamine from '../notificationManagement/meetExamine/index.vue'
+import MeetPublish from '../notificationManagement/meetPublish/index.vue'
+import MeetSummary from '../notificationManagement/summary/index.vue'
+
+const activeTab = ref('setting')
+
+const tabComponentMap = {
+ setting: MeetSetting,
+ index: MeetIndex,
+ application: MeetApplication,
+ examine: MeetExamine,
+ publish: MeetPublish,
+ summary: MeetSummary
+}
+
+const currentComponent = computed(() => tabComponentMap[activeTab.value] || MeetSetting)
+
+function handleTabChange(name) {
+ activeTab.value = name
+}
+</script>
+
+<style scoped lang="scss">
+
+.tabs-wrapper {
+ margin-bottom: 10px;
+}
+
+.tab-content {
+ min-height: 400px;
+}
+</style>
+
diff --git a/src/views/collaborativeApproval/noticeManagement/index.vue b/src/views/collaborativeApproval/noticeManagement/index.vue
index daa4cd7..d92adea 100644
--- a/src/views/collaborativeApproval/noticeManagement/index.vue
+++ b/src/views/collaborativeApproval/noticeManagement/index.vue
@@ -3,159 +3,116 @@
<!-- 鎼滅储琛ㄥ崟 -->
<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>
+ <el-button type="info" @click="openNoticeTypeDialog">鍏憡绫诲瀷閰嶇疆</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>
+ <el-tabs v-model="activeNoticeTypeTab" @tab-change="handleNoticeTypeTabChange">
+ <el-tab-pane
+ v-for="noticeType in noticeTypeList"
+ :key="noticeType.id"
+ :label="noticeType.noticeType"
+ :name="String(noticeType.id)"
+ >
+ <template #label>
+ <span>{{ noticeType.noticeType }}
+ <span class="tab-count" v-if="getNoticeCountByType(noticeType.id) > 0">
+ ({{ getNoticeCountByType(noticeType.id) }})
+ </span>
+ </span>
+ </template>
+
+ <div class="notice-section">
+ <div class="notice-cards">
+ <div
+ v-for="notice in getNoticesByType(noticeType.id)"
+ :key="notice.id"
+ class="notice-card"
+ :class="{ 'urgent': notice.priority === '3' }"
+ >
+ <div class="card-header">
+ <div class="card-title">
+ <el-icon class="notice-icon">
+ <Calendar/>
+ </el-icon>
+ {{ notice.title }}
+ </div>
+ <div class="card-actions">
+ <el-button link type="primary" @click="handleEdit(notice)" :disabled="isNoticeExpired(notice)" v-if="notice.status !== 1">缂栬緫</el-button>
+ <el-button link type="success" @click="handlePublish(notice)" v-if="notice.status === 0">鍙戝竷</el-button>
+ <el-button link type="danger" @click="handleDelete(notice.id)" v-if="notice.status !== 1">鍒犻櫎</el-button>
+ </div>
+ </div>
+ <div class="card-content">
+ <p>{{ notice.content }}</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-' + getNoticeStatus(notice)">
+ {{ getStatusText(getNoticeStatus(notice)) }}
+ </span>
+ </div>
+ <div class="card-info">
+ <span class="creator">{{ notice.createUserName }}</span>
+ <span class="expiration" v-if="notice.expirationDate">鎴鏃ユ湡锛歿{ notice.expirationDate }}</span>
+ </div>
+ </div>
+ <div class="card-remark" v-if="notice.remark">
+ <el-icon>
+ <InfoFilled/>
+ </el-icon>
+ <span>{{ notice.remark }}</span>
+ </div>
</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>
+
+ <pagination
+ v-if="getNoticePageByType(noticeType.id).total > 0"
+ :total="getNoticePageByType(noticeType.id).total"
+ :page="getNoticePageByType(noticeType.id).current"
+ :limit="getNoticePageByType(noticeType.id).size"
+ @pagination="(val) => handleNoticeCurrentChange(noticeType.id, val)"
+ />
+
+ <!-- 绌虹姸鎬� -->
+ <div class="empty-state" v-if="getNoticesByType(noticeType.id).length === 0">
+ <el-empty description="鏆傛棤閫氱煡鍏憡"/>
</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>
+ </el-tab-pane>
+ </el-tabs>
</div>
<!-- 鏂板/缂栬緫瀵硅瘽妗� -->
- <el-dialog
- :title="dialogTitle"
- v-model="dialogVisible"
- width="800px"
- append-to-body
- @close="resetForm"
+ <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 label="鍏憡鏍囬" prop="title">
+ <el-input v-model="form.title" 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-form-item label="鍏憡绫诲瀷" prop="type">
+ <el-select v-model="form.type" placeholder="璇烽�夋嫨鍏憡绫诲瀷" style="width: 100%">
+ <el-option
+ v-for="item in noticeTypeList"
+ :key="item.id"
+ :label="item.noticeType"
+ :value="item.id"
+ />
</el-select>
</el-form-item>
</el-col>
@@ -164,32 +121,39 @@
<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 :value="0">鑽夌</el-radio>
+ <el-radio :value="1">姝e紡鍙戝竷</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-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="12">
+ <el-form-item label="杩囨湡鏃堕棿" prop="expirationDate">
+ <el-date-picker v-model="form.expirationDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="date"
+ placeholder="璇烽�夋嫨" clearable />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row>
<el-col :span="24">
- <el-form-item label="鍏憡鍐呭" prop="noticeContent">
+ <el-form-item label="鍏憡鍐呭" prop="content">
<el-input
- v-model="form.noticeContent"
- type="textarea"
- :rows="6"
- placeholder="璇疯緭鍏ュ叕鍛婂唴瀹�"
- maxlength="500"
- show-word-limit
+ v-model="form.content"
+ type="textarea"
+ :rows="6"
+ placeholder="璇疯緭鍏ュ叕鍛婂唴瀹�"
+ maxlength="500"
+ show-word-limit
/>
</el-form-item>
</el-col>
@@ -198,12 +162,12 @@
<el-col :span="24">
<el-form-item label="澶囨敞">
<el-input
- v-model="form.remark"
- type="textarea"
- :rows="3"
- placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
- maxlength="200"
- show-word-limit
+ v-model="form.remark"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+ maxlength="200"
+ show-word-limit
/>
</el-form-item>
</el-col>
@@ -216,177 +180,168 @@
</div>
</template>
</el-dialog>
+
+ <!-- 鍏憡绫诲瀷閰嶇疆寮规 -->
+ <el-dialog
+ v-model="noticeTypeDialogVisible"
+ title="鍏憡绫诲瀷閰嶇疆"
+ width="800px"
+ @close="handleNoticeTypeDialogClose"
+ >
+ <div class="notice-type-container">
+ <div class="notice-type-header">
+ <el-button type="primary" @click="handleAddNoticeType">鏂板绫诲瀷</el-button>
+ </div>
+ <el-table :data="noticeTypeList" border style="width: 100%">
+ <el-table-column prop="id" label="ID" width="80" align="center"/>
+ <el-table-column prop="noticeType" label="鍏憡绫诲瀷" align="center">
+ <template #default="scope">
+ <el-input
+ v-if="scope.row.editing"
+ v-model="scope.row.noticeType"
+ placeholder="璇疯緭鍏ュ叕鍛婄被鍨�"
+ />
+ <span v-else>{{ scope.row.noticeType }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="200" align="center">
+ <template #default="scope">
+ <el-button
+ v-if="scope.row.editing"
+ link
+ type="primary"
+ size="small"
+ @click="handleSaveNoticeType(scope.row)"
+ >
+ 淇濆瓨
+ </el-button>
+ <el-button
+ v-if="scope.row.editing"
+ link
+ type="info"
+ size="small"
+ @click="handleCancelEdit(scope.row)"
+ >
+ 鍙栨秷
+ </el-button>
+ <el-button
+ v-if="!scope.row.editing"
+ link
+ type="primary"
+ size="small"
+ @click="handleEditNoticeType(scope.row)"
+ >
+ 缂栬緫
+ </el-button>
+ <el-button
+ v-if="!scope.row.editing"
+ link
+ type="danger"
+ size="small"
+ @click="handleDeleteNoticeType(scope.row)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </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 {Calendar, InfoFilled} from "@element-plus/icons-vue";
+import {onMounted, ref, reactive, toRefs, computed} from "vue";
+import {ElMessage, ElMessageBox} from "element-plus";
+import {useRoute} from "vue-router";
import useUserStore from "@/store/modules/user";
+import {
+ addNotice,
+ delNotice,
+ getCount,
+ listNotice,
+ updateNotice,
+ listNoticeType,
+ addNoticeType,
+ delNoticeType
+} from "../../../api/collaborativeApproval/noticeManagement.js";
+import pagination from "../../../components/PIMTable/Pagination.vue";
const userStore = useUserStore();
+const route = useRoute();
// 鍝嶅簲寮忔暟鎹�
const data = reactive({
searchForm: {
- noticeTitle: "",
- noticeType: "",
- status: "",
+ title: "",
+ type: undefined,
+ status: undefined,
},
form: {
id: undefined,
- noticeTitle: "",
- noticeType: "",
- noticeContent: "",
- status: "0",
- priority: "1",
+ title: "",
+ type: null,
+ content: "",
+ status: 0,
+ priority: 1,
remark: "",
- createBy: "",
- createTime: "",
+ expirationDate: "",
},
rules: {
- noticeTitle: [
- { required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }
+ title: [
+ {required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur"}
],
- noticeType: [
- { required: true, message: "璇烽�夋嫨鍏憡绫诲瀷", trigger: "change" }
+ type: [
+ {required: true, message: "璇烽�夋嫨鍏憡绫诲瀷", trigger: "change"}
],
- noticeContent: [
- { required: true, message: "鍏憡鍐呭涓嶈兘涓虹┖", trigger: "blur" }
+ content: [
+ {required: true, message: "鍏憡鍐呭涓嶈兘涓虹┖", trigger: "blur"}
+ ],
+ expirationDate: [
+ {required: true, message: "璇烽�夋嫨鏃ユ湡", trigger: "change"}
]
}
});
-const { searchForm, form, rules } = toRefs(data);
+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 noticeTypeDialogVisible = ref(false);
+const noticeTypeList = ref([]);
+const activeNoticeTypeTab = ref('');
+
+// 閫氱煡鏁版嵁 - 浣跨敤 Map 瀛樺偍锛宬ey 涓虹被鍨� id
+const noticesMap = ref({});
+const noticePagesMap = ref({});
+
// 璁$畻灞炴��
const filteredNotices = computed(() => {
let filtered = [...mockData];
-
+
if (searchForm.value.noticeTitle) {
- filtered = filtered.filter(item =>
- item.noticeTitle.includes(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
+ filtered = filtered.filter(item =>
+ item.noticeType === searchForm.value.noticeType
);
}
if (searchForm.value.status !== "") {
- filtered = filtered.filter(item =>
- item.status === 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");
});
// 鏂规硶瀹氫箟
@@ -396,20 +351,44 @@
const resetQuery = () => {
searchForm.value = {
- noticeTitle: "",
- noticeType: "",
+ title: "",
+ type: "",
status: ""
};
};
const getPriorityText = (priority) => {
- const priorityMap = { "1": "鏅��", "2": "閲嶈", "3": "绱ф��" };
+ const priorityMap = {"1": "鏅��", "2": "閲嶈", "3": "绱ф��"};
return priorityMap[priority] || "鏅��";
};
const getStatusText = (status) => {
- const statusMap = { "0": "鑽夌", "1": "宸插彂甯�", "2": "宸蹭笅绾�" };
+ const statusMap = {"0": "鑽夌", "1": "宸插彂甯�", "2": "宸茶繃鏈�"};
return statusMap[status] || "鏈煡";
+};
+
+const isNoticeExpired = (notice) => {
+ if (!notice || !notice.expirationDate) {
+ return false;
+ }
+
+ const expiration = new Date(notice.expirationDate);
+
+ if (Number.isNaN(expiration.getTime())) {
+ return false;
+ }
+
+ expiration.setHours(23, 59, 59, 999);
+
+ return new Date() > expiration;
+};
+
+const getNoticeStatus = (notice) => {
+ const normalizedStatus = notice && notice.status !== undefined && notice.status !== null
+ ? String(notice.status)
+ : "0";
+
+ return isNoticeExpired(notice) ? "2" : normalizedStatus;
};
const openForm = (type) => {
@@ -417,44 +396,64 @@
dialogTitle.value = "鏂板鍏憡";
form.value = {
id: undefined,
- noticeTitle: "",
- noticeType: "",
- noticeContent: "",
- status: "0",
- priority: "1",
+ title: "",
+ type: undefined,
+ content: "",
+ status: 0,
+ priority: 1,
remark: "",
- createBy: userStore.name || "褰撳墠鐢ㄦ埛",
- createTime: new Date().toLocaleString()
+ expirationDate: "",
};
}
dialogVisible.value = true;
};
const handleEdit = (row) => {
+ if (isNoticeExpired(row)) {
+ ElMessage.warning("宸茶繃鏈熺殑鍏憡涓嶅彲缂栬緫");
+ return;
+ }
dialogTitle.value = "缂栬緫鍏憡";
- form.value = { ...row };
+ 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"
- }
+ "纭鍒犻櫎杩欐潯鍏憡鍚楋紵",
+ "鎻愮ず",
+ {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }
).then(() => {
- const index = mockData.findIndex(item => item.id === id);
- if (index > -1) {
- mockData.splice(index, 1);
+ delNotice(id).then(res => {
ElMessage.success("鍒犻櫎鎴愬姛");
- }
+ resetTable()
+ })
+ });
+};
+
+const handlePublish = (notice) => {
+ ElMessageBox.confirm(
+ "纭鍙戝竷杩欐潯鍏憡鍚楋紵",
+ "鎻愮ず",
+ {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "info"
+ }
+ ).then(() => {
+ updateNotice({
+ ...notice,
+ status: 1
+ }).then(res => {
+ if (res.code === 200) {
+ ElMessage.success("鍙戝竷鎴愬姛");
+ resetTable()
+ }
+ })
});
};
@@ -463,24 +462,85 @@
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("淇敼鎴愬姛");
+ updateNotice(form.value).then(res => {
+ ElMessage.success("淇敼鎴愬姛");
+ resetTable()
+ })
} 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("鏂板鎴愬姛");
+ addNotice(form.value).then(res => {
+ ElMessage.success("鏂板鎴愬姛");
+ resetTable()
+ })
}
dialogVisible.value = false;
}
+ });
+};
+
+// 鍒濆鍖栨煇涓被鍨嬬殑鍒嗛〉鏁版嵁
+const initNoticePage = (typeId) => {
+ if (!noticePagesMap.value[typeId]) {
+ noticePagesMap.value[typeId] = {
+ total: 0,
+ current: 1,
+ size: 10
+ };
+ }
+ if (!noticesMap.value[typeId]) {
+ noticesMap.value[typeId] = [];
+ }
+};
+
+// 鑾峰彇鏌愪釜绫诲瀷鐨勯�氱煡鍒楄〃
+const getNoticesByType = (typeId) => {
+ return noticesMap.value[typeId] || [];
+};
+
+// 鑾峰彇鏌愪釜绫诲瀷鐨勫垎椤垫暟鎹�
+const getNoticePageByType = (typeId) => {
+ return noticePagesMap.value[typeId] || { total: 0, current: 1, size: 10 };
+};
+
+// 鑾峰彇鏌愪釜绫诲瀷鐨勬暟閲�
+const getNoticeCountByType = (typeId) => {
+ return getNoticePageByType(typeId).total || 0;
+};
+
+// 鑾峰彇鏌愪釜绫诲瀷鐨勯�氱煡鏁版嵁
+const fetchNoticesByType = (typeId) => {
+ initNoticePage(typeId);
+ const pageData = noticePagesMap.value[typeId];
+ listNotice({...pageData, type: typeId}).then(res => {
+ if (res.code === 200) {
+ noticesMap.value[typeId] = res.data.records || [];
+ noticePagesMap.value[typeId].total = res.data.total || 0;
+ }
+ });
+};
+
+// 澶勭悊鍒嗛〉鍙樺寲
+const handleNoticeCurrentChange = (typeId, val) => {
+ initNoticePage(typeId);
+ noticePagesMap.value[typeId].size = val.limit;
+ noticePagesMap.value[typeId].current = val.page;
+ fetchNoticesByType(typeId);
+};
+
+// 澶勭悊 tab 鍒囨崲
+const handleNoticeTypeTabChange = (tabName) => {
+ activeNoticeTypeTab.value = tabName;
+ const typeId = Number(tabName);
+ fetchNoticesByType(typeId);
+};
+
+const resetTable = () => {
+ // 閲嶇疆鎵�鏈夌被鍨嬬殑鍒嗛〉骞堕噸鏂拌幏鍙栨暟鎹�
+ noticeTypeList.value.forEach(type => {
+ initNoticePage(type.id);
+ noticePagesMap.value[type.id].current = 1;
+ noticePagesMap.value[type.id].size = 10;
+ fetchNoticesByType(type.id);
});
};
@@ -488,9 +548,187 @@
formRef.value?.resetFields();
};
+// 鍏憡绫诲瀷閰嶇疆鐩稿叧鏂规硶
+const openNoticeTypeDialog = () => {
+ noticeTypeDialogVisible.value = true;
+ fetchNoticeTypeList();
+};
+
+const fetchNoticeTypeList = () => {
+ return listNoticeType().then(res => {
+ if (res.code === 200) {
+ noticeTypeList.value = res.data.map(item => ({
+ ...item,
+ editing: false
+ }));
+
+ // 妫�鏌ヨ矾鐢卞弬鏁颁腑鐨� type
+ const routeType = route.query.type;
+ let targetTypeId = null;
+
+ if (routeType) {
+ // 濡傛灉璺敱鍙傛暟涓湁 type锛屾煡鎵惧搴旂殑绫诲瀷
+ const typeId = Number(routeType);
+ const foundType = noticeTypeList.value.find(item => item.id === typeId);
+ if (foundType) {
+ targetTypeId = typeId;
+ }
+ }
+
+ // 濡傛灉鏈夌被鍨嬫暟鎹�
+ if (noticeTypeList.value.length > 0) {
+ // 濡傛灉璺敱鍙傛暟鎸囧畾浜嗙被鍨嬩笖瀛樺湪锛屼娇鐢ㄨ矾鐢卞弬鏁扮殑绫诲瀷
+ // 鍚﹀垯濡傛灉娌℃湁閫変腑 tab锛岄粯璁ら�変腑绗竴涓�
+ if (targetTypeId !== null) {
+ activeNoticeTypeTab.value = String(targetTypeId);
+ fetchNoticesByType(targetTypeId);
+ } else if (!activeNoticeTypeTab.value) {
+ activeNoticeTypeTab.value = String(noticeTypeList.value[0].id);
+ fetchNoticesByType(noticeTypeList.value[0].id);
+ }
+ }
+ }
+ });
+};
+
+const handleAddNoticeType = () => {
+ const newItem = {
+ id: undefined,
+ noticeType: '',
+ editing: true
+ };
+ noticeTypeList.value.push(newItem);
+};
+
+const handleEditNoticeType = (row) => {
+ // 淇濆瓨鍘熷鍊�
+ row.originalNoticeType = row.noticeType;
+ row.editing = true;
+};
+
+const handleSaveNoticeType = (row) => {
+ if (!row.noticeType || row.noticeType.trim() === '') {
+ ElMessage.warning('鍏憡绫诲瀷涓嶈兘涓虹┖');
+ return;
+ }
+
+ const data = {
+ noticeType: row.noticeType.trim()
+ };
+
+ if (row.id) {
+ // 缂栬緫妯″紡 - 鍏堝垹闄ゅ啀娣诲姞锛堝洜涓哄彧鏈� add 鍜� del 鎺ュ彛锛�
+ delNoticeType(row.id).then(res => {
+ if (res.code === 200) {
+ addNoticeType(data).then(addRes => {
+ if (addRes.code === 200) {
+ ElMessage.success('缂栬緫鎴愬姛');
+ row.editing = false;
+ delete row.originalNoticeType;
+ fetchNoticeTypeList().then(() => {
+ // 濡傛灉褰撳墠閫変腑鐨勭被鍨嬭缂栬緫锛岄渶瑕侀噸鏂拌幏鍙栨暟鎹�
+ if (activeNoticeTypeTab.value === String(row.id)) {
+ fetchNoticesByType(addRes.data?.id || row.id);
+ }
+ });
+ }
+ });
+ }
+ });
+ } else {
+ // 鏂板妯″紡
+ addNoticeType(data).then(res => {
+ if (res.code === 200) {
+ ElMessage.success('鏂板鎴愬姛');
+ row.editing = false;
+ fetchNoticeTypeList();
+ }
+ });
+ }
+};
+
+const handleDeleteNoticeType = (row) => {
+ // 濡傛灉娌℃湁id锛岃鏄庢槸鏂板浣嗘湭淇濆瓨鐨勮锛岀洿鎺ヤ粠鍓嶇鍒犻櫎
+ if (!row.id) {
+ const index = noticeTypeList.value.indexOf(row);
+ if (index > -1) {
+ noticeTypeList.value.splice(index, 1);
+ }
+ return;
+ }
+
+ // 濡傛灉鏈塱d锛岃皟鐢ㄥ悗绔帴鍙e垹闄�
+ ElMessageBox.confirm(
+ "纭鍒犻櫎杩欎釜鍏憡绫诲瀷鍚楋紵",
+ "鎻愮ず",
+ {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }
+ ).then(() => {
+ delNoticeType(row.id).then(res => {
+ if (res.code === 200) {
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ // 濡傛灉鍒犻櫎鐨勬槸褰撳墠閫変腑鐨勭被鍨嬶紝鍒囨崲鍒扮涓�涓被鍨�
+ if (activeNoticeTypeTab.value === String(row.id)) {
+ fetchNoticeTypeList().then(() => {
+ if (noticeTypeList.value.length > 0) {
+ activeNoticeTypeTab.value = String(noticeTypeList.value[0].id);
+ fetchNoticesByType(noticeTypeList.value[0].id);
+ } else {
+ activeNoticeTypeTab.value = '';
+ }
+ });
+ } else {
+ fetchNoticeTypeList();
+ }
+ }
+ });
+ });
+};
+
+const handleCancelEdit = (row) => {
+ if (!row.id) {
+ // 濡傛灉鏄柊澧炰絾鏈繚瀛樼殑琛岋紝绉婚櫎瀹�
+ const index = noticeTypeList.value.indexOf(row);
+ if (index > -1) {
+ noticeTypeList.value.splice(index, 1);
+ }
+ } else {
+ // 濡傛灉鏄紪杈戜腑鐨勮锛屽彇娑堢紪杈戠姸鎬佸苟鎭㈠鍘熷��
+ row.editing = false;
+ if (row.originalNoticeType !== undefined) {
+ row.noticeType = row.originalNoticeType;
+ delete row.originalNoticeType;
+ }
+ }
+};
+
+const handleNoticeTypeDialogClose = () => {
+ // 鍏抽棴寮规鏃讹紝鍙栨秷鎵�鏈夌紪杈戠姸鎬�
+ noticeTypeList.value.forEach(item => {
+ if (item.editing && !item.id) {
+ // 濡傛灉鏄柊澧炰絾鏈繚瀛樼殑琛岋紝绉婚櫎瀹�
+ const index = noticeTypeList.value.indexOf(item);
+ if (index > -1) {
+ noticeTypeList.value.splice(index, 1);
+ }
+ } else if (item.editing) {
+ // 濡傛灉鏄紪杈戜腑鐨勮锛屽彇娑堢紪杈戠姸鎬佸苟鎭㈠鍘熷��
+ item.editing = false;
+ if (item.originalNoticeType !== undefined) {
+ item.noticeType = item.originalNoticeType;
+ delete item.originalNoticeType;
+ }
+ }
+ });
+};
+
// 鐢熷懡鍛ㄦ湡
onMounted(() => {
- // 椤甸潰鍔犺浇瀹屾垚
+ // 鍏堣幏鍙栧叕鍛婄被鍨嬪垪琛紝鐒跺悗鏍规嵁绫诲瀷鑾峰彇閫氱煡鏁版嵁
+ fetchNoticeTypeList();
});
</script>
@@ -569,12 +807,16 @@
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
-.holiday-card {
- border-left-color: #67c23a;
+.notice-icon {
+ color: #409eff;
+ margin-right: 8px;
+ font-size: 18px;
}
-.maintenance-card {
- border-left-color: #e6a23c;
+.tab-count {
+ color: #909399;
+ font-size: 12px;
+ margin-left: 4px;
}
.urgent {
@@ -598,17 +840,6 @@
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;
@@ -624,6 +855,9 @@
color: #606266;
line-height: 1.6;
font-size: 14px;
+ word-break: break-all;
+ white-space: pre-wrap;
+ overflow-wrap: break-word;
}
.card-footer {
@@ -645,13 +879,35 @@
font-weight: 500;
}
-.priority-1 { background: #f0f9ff; color: #0369a1; }
-.priority-2 { background: #fef3c7; color: #d97706; }
-.priority-3 { background: #fef2f2; color: #dc2626; }
+.priority-1 {
+ background: #f0f9ff;
+ color: #0369a1;
+}
-.status-0 { background: #f3f4f6; color: #6b7280; }
-.status-1 { background: #d1fae5; color: #059669; }
-.status-2 { background: #fef3c7; color: #d97706; }
+.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;
@@ -664,6 +920,10 @@
.creator {
font-weight: 500;
margin-bottom: 2px;
+}
+
+.expiration {
+ margin-top: 2px;
}
.card-remark {
@@ -687,17 +947,27 @@
text-align: right;
}
+.notice-type-container {
+ padding: 10px 0;
+}
+
+.notice-type-header {
+ margin-bottom: 15px;
+ display: flex;
+ justify-content: flex-end;
+}
+
/* 鍝嶅簲寮忚璁� */
@media (max-width: 768px) {
.notice-cards {
grid-template-columns: 1fr;
}
-
+
.search_form {
flex-direction: column;
gap: 15px;
}
-
+
.search_form > div {
width: 100%;
}
diff --git a/src/views/collaborativeApproval/notificationManagement/index.vue b/src/views/collaborativeApproval/notificationManagement/index.vue
index 288acf1..fb3f7a8 100644
--- a/src/views/collaborativeApproval/notificationManagement/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/index.vue
@@ -87,6 +87,8 @@
v-model="form.expireDate"
type="date"
placeholder="璇烽�夋嫨鏈夋晥鏈�"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
@@ -152,6 +154,8 @@
<el-date-picker
v-model="meetingForm.startTime"
type="datetime"
+ value-format="YYYY-MM-DD HH:mm:ss"
+ format="YYYY-MM-DD HH:mm:ss"
placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
style="width: 100%"
/>
@@ -318,7 +322,9 @@
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";
+import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js";
+import { listNotification, addNotification, updateNotification, delNotification,addOnlineMeeting,addFileSharing } from "@/api/collaborativeApproval/notificationManagement.js";
+import { id } from "element-plus/es/locales.mjs";
// 琛ㄥ崟楠岃瘉瑙勫垯
const rules = {
@@ -364,7 +370,7 @@
tableLoading: false,
page: {
current: 1,
- size: 100,
+ size: 20,
total: 0,
},
tableData: [],
@@ -373,7 +379,7 @@
form: {
title: "",
type: "",
- priority: "medium",
+ priority: "",
content: "",
departments: [],
expireDate: "",
@@ -403,11 +409,11 @@
fileList: []
});
-const {
- searchForm,
- tableLoading,
- page,
- tableData,
+const {
+ searchForm,
+ tableLoading,
+ page,
+ tableData,
selectedIds,
form,
dialogVisible,
@@ -543,7 +549,6 @@
clickFun: (row) => {
publishNotification(row);
},
- // disabled: (row) => row.status === "published"
},
{
name: "鎾ゅ洖",
@@ -551,52 +556,10 @@
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}鏀惧亣瀹夋帓鐨勯�氱煡",
@@ -631,7 +594,7 @@
employeesLoading.value = true;
// 浼樺厛浣跨敤绯荤粺鐢ㄦ埛鎺ュ彛锛堟寜绉熸埛鑾峰彇锛�
const userResponse = await userListNoPageByTenantId();
-
+
if (userResponse.data) {
employees.value = userResponse.data.map(user => ({
label: user.nickName || user.userName || '鏈煡濮撳悕',
@@ -643,12 +606,12 @@
})).filter(user => user.status === '0'); // 鍙樉绀烘甯哥姸鎬佺殑鐢ㄦ埛
} else {
// 濡傛灉绯荤粺鐢ㄦ埛鎺ュ彛澶辫触锛屼娇鐢ㄥ憳宸ュ彴璐︽帴鍙�
- const response = await staffOnJobListPage({
- pageNum: 1,
- pageSize: 1000,
+ 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 || '鏈煡濮撳悕',
@@ -683,7 +646,7 @@
}
groups[dept].push(employee);
});
-
+
// 鎸夐儴闂ㄥ悕绉版帓搴忥紝纭繚鏄剧ず椤哄簭涓�鑷�
return Object.keys(groups)
.sort()
@@ -697,7 +660,7 @@
const filterEmployees = (query) => {
if (query !== '') {
const lowerQuery = query.toLowerCase();
- return employees.value.filter(employee =>
+ return employees.value.filter(employee =>
employee.label.toLowerCase().includes(lowerQuery) ||
employee.dept.toLowerCase().includes(lowerQuery) ||
(employee.phone && employee.phone.includes(query)) ||
@@ -712,18 +675,18 @@
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})`);
};
@@ -737,7 +700,7 @@
const getEmployeeInfo = (employeeId) => {
const employee = employees.value.find(emp => emp.value === employeeId);
if (!employee) return null;
-
+
return {
name: employee.label,
dept: employee.dept,
@@ -776,7 +739,7 @@
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
@@ -788,15 +751,15 @@
.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,
@@ -809,15 +772,15 @@
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}`);
};
@@ -844,24 +807,14 @@
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;
+ listNotification({...page.value, ...searchForm.value})
+ .then(res => {
tableLoading.value = false;
- }, 500);
+ tableData.value = res.data.records
+ page.value.total = res.data.total;
+ }).catch(err => {
+ tableLoading.value = false;
+ })
};
// 鍒嗛〉澶勭悊
@@ -883,23 +836,27 @@
dialogTitle.value = "鏂板閫氱煡";
// 閲嶇疆琛ㄥ崟
Object.assign(form.value, {
+ id: "",
title: "",
type: "",
- priority: "medium",
+ priority: "",
content: "",
departments: [],
expireDate: "",
+ status: "draft",
syncMethods: []
});
} else if (type === "edit" && row) {
dialogTitle.value = "缂栬緫閫氱煡";
Object.assign(form.value, {
+ id: row.id,
title: row.title,
type: row.type,
priority: row.priority,
content: row.content || "",
departments: row.departments || [],
expireDate: row.expireDate || "",
+ status: row.status,
syncMethods: row.syncMethods || []
});
}
@@ -944,43 +901,30 @@
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("閫氱煡鍒涘缓鎴愬姛");
+ addNotification({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} 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("閫氱煡鏇存柊鎴愬姛");
- }
+ updateNotification({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
}
-
- dialogVisible.value = false;
- getList();
} catch (error) {
console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
}
@@ -990,7 +934,7 @@
const createMeeting = async () => {
try {
await meetingFormRef.value.validate();
-
+
// 妯℃嫙鍒涘缓浼氳
const meetingInfo = {
title: meetingForm.value.title,
@@ -998,22 +942,28 @@
duration: meetingForm.value.duration,
participants: meetingForm.value.participants,
description: meetingForm.value.description,
- platform: meetingForm.value.platform,
- meetingId: `MTG${Date.now()}`
+ platform: meetingForm.value.platform
};
-
+ // 鏂板浼氳
+ addOnlineMeeting({...meetingInfo}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("浼氳娣诲姞鎴愬姛");
+ meetingDialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
// 妯℃嫙鍙戦�佸埌浼佷笟寰俊/閽夐拤
- const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "鏈煡骞冲彴";
-
- ElMessage.success(`浼氳鍒涘缓鎴愬姛锛佷細璁甀D: ${meetingInfo.meetingId}锛屽皢閫氳繃${platformName}鍙戦�侀�氱煡`);
- meetingDialogVisible.value = false;
-
- // 鑾峰彇鍙備細浜哄憳淇℃伅
+ // const platformName = meetingPlatforms.find(p => p.value === meetingForm.value.platform)?.label || "鏈煡骞冲彴";
+ // ElMessage.success(`浼氳鍒涘缓鎴愬姛锛佷細璁甀D: ${meetingInfo.meetingId}锛屽皢閫氳繃${platformName}鍙戦�侀�氱煡`);
+
+ // 鑾峰彇鍙備細浜哄憳淇℃伅
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);
@@ -1024,23 +974,29 @@
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}浜篳,
+ 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()
+ syncMethods: [meetingForm.value.platform]
};
-
- mockData.unshift(meetingNotification);
- getList();
+ addNotification({...meetingNotification}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ // dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ // mockData.unshift(meetingNotification);
+ // getList();
} catch (error) {
console.error("浼氳琛ㄥ崟楠岃瘉澶辫触:", error);
}
@@ -1053,16 +1009,16 @@
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);
+ fileShareForm.value.files.push(fileInfo.name);
return false; // 闃绘鑷姩涓婁紶
};
@@ -1082,27 +1038,34 @@
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;
-
+ addFileSharing({...shareInfo}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏂囦欢鍏变韩鎴愬姛");
+ fileShareDialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+
+ // ElMessage.success(`鏂囦欢鍏变韩鎴愬姛锛佸叡浜獻D: ${shareInfo.shareId}锛屽凡閫氱煡鐩稿叧閮ㄩ棬`);
+
+
// 灏嗘枃浠跺叡浜俊鎭坊鍔犲埌閫氱煡鍒楄〃
const fileShareNotification = {
- id: (mockData.length + 1).toString(),
title: `[鏂囦欢鍏变韩] ${shareInfo.title}`,
type: "temporary",
priority: "medium",
@@ -1111,11 +1074,19 @@
departments: shareInfo.departments,
expireDate: "",
syncMethods: ["wechat", "dingtalk"],
- createTime: new Date().toLocaleString()
};
-
- mockData.unshift(fileShareNotification);
- getList();
+ addNotification({...fileShareNotification}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ // dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+
+ // mockData.unshift(fileShareNotification);
+ // getList();
} catch (error) {
console.error("鏂囦欢鍏变韩琛ㄥ崟楠岃瘉澶辫触:", error);
}
@@ -1123,33 +1094,75 @@
// 鍙戝竷閫氱煡
const publishNotification = (row) => {
- row.status = "published";
- ElMessage.success("閫氱煡鍙戝竷鎴愬姛");
- getList();
+ Object.assign(form.value, {
+ id: row.id,
+ title: row.title,
+ type: row.type,
+ priority: row.priority,
+ content: row.content || "",
+ departments: row.departments || [],
+ expireDate: row.expireDate || "",
+ status: row.status,
+ syncMethods: row.syncMethods || []
+ });
+ form.value.status = "published";
+ updateNotification({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("閫氱煡鍙戝竷鎴愬姛");
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
};
// 鎾ゅ洖閫氱煡
const revokeNotification = (row) => {
- row.status = "draft";
- ElMessage.success("閫氱煡宸叉挙鍥�");
- getList();
+ Object.assign(form.value, {
+ id: row.id,
+ title: row.title,
+ type: row.type,
+ priority: row.priority,
+ content: row.content || "",
+ departments: row.departments || [],
+ expireDate: row.expireDate || "",
+ status: row.status,
+ syncMethods: row.syncMethods || []
+ });
+ form.value.status = "draft";
+ updateNotification({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("閫氱煡宸叉挙鍥�");
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
};
// 鍒犻櫎閫氱煡
const handleDelete = () => {
- if (selectedIds.value.length === 0) {
+ let ids = [];
+ if (selectedIds.value.length > 0) {
+ ids = selectedIds.value;
+ }else{
ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑閫氱煡");
return;
}
-
ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
confirmButtonText: "纭",
cancelButtonText: "鍙栨秷",
type: "warning",
}).then(() => {
- ElMessage.success("鍒犻櫎鎴愬姛");
- selectedIds.value = [];
- getList();
+ delNotification(ids).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ selectedIds.value = [];
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
}).catch(() => {
// 鐢ㄦ埛鍙栨秷
});
diff --git a/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue b/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
new file mode 100644
index 0000000..7781593
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
@@ -0,0 +1,394 @@
+<template>
+ <div>
+
+ <!-- 鐢宠绫诲瀷閫夋嫨 -->
+ <el-card class="type-card">
+ <div class="type-selector">
+ <div
+ v-for="type in applicationTypes"
+ :key="type.value"
+ class="type-item"
+ :class="{ active: currentType === type.value }"
+ @click="changeType(type.value)"
+ >
+ <div class="type-icon">
+ <el-icon :size="24"><component :is="type.icon"/></el-icon>
+ </div>
+ <div class="type-info">
+ <div class="type-name">{{ type.name }}</div>
+ <div class="type-desc">{{ type.desc }}</div>
+ </div>
+ </div>
+ </div>
+ </el-card>
+
+ <!-- 浼氳鐢宠琛ㄥ崟 -->
+ <el-card>
+ <div class="form-header">
+ <h3>{{ getCurrentTypeName() }}鐢宠</h3>
+ </div>
+
+ <el-form
+ ref="meetingFormRef"
+ :model="meetingForm"
+ :rules="rules"
+ label-width="100px"
+ >
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浼氳涓婚" prop="title">
+ <el-input v-model="meetingForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�"/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浼氳瀹�" prop="roomId">
+ <el-select v-model="meetingForm.roomId" placeholder="璇烽�夋嫨浼氳瀹�" style="width: 100%">
+ <el-option
+ v-for="room in meetingRooms"
+ :key="room.id"
+ :label="`${room.name} (${room.location})`"
+ :value="room.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="涓绘寔浜�" prop="host">
+ <el-input v-model="meetingForm.host" placeholder="璇疯緭鍏ヤ富鎸佷汉濮撳悕"/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浼氳鏃ユ湡" prop="meetingDate">
+ <el-date-picker
+ v-model="meetingForm.meetingDate"
+ type="date"
+ placeholder="璇烽�夋嫨浼氳鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ :disabled-date="disabledDate"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <!-- 绌哄垪锛屼繚鎸佸竷灞� -->
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="寮�濮嬫椂闂�" prop="startTime">
+ <el-select
+ v-model="meetingForm.startTime"
+ placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="time in timeOptions"
+ :key="time.value"
+ :label="time.label"
+ :value="time.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="缁撴潫鏃堕棿" prop="endTime">
+ <el-select
+ v-model="meetingForm.endTime"
+ placeholder="璇烽�夋嫨缁撴潫鏃堕棿"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="time in timeOptions"
+ :key="time.value"
+ :label="time.label"
+ :value="time.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-form-item label="鍙備細浜哄憳" prop="participants">
+ <el-select
+ v-model="meetingForm.participants"
+ multiple
+ filterable
+ placeholder="璇烽�夋嫨鍙備細浜哄憳"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="person in employees"
+ :key="person.id"
+ :label="`${person.staffName} (${person.postJob})`"
+ :value="person.id"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="浼氳璇存槑" prop="description">
+ <el-input
+ v-model="meetingForm.description"
+ type="textarea"
+ :rows="4"
+ placeholder="璇疯緭鍏ヤ細璁鏄�"
+ />
+ </el-form-item>
+ </el-form>
+
+ <div class="form-footer">
+ <el-button @click="resetForm">閲嶇疆</el-button>
+ <el-button type="primary" @click="submitForm">鎻愪氦</el-button>
+ </div>
+ </el-card>
+ </div>
+</template>
+
+<script setup>
+import {ref, reactive, onMounted} from 'vue'
+import {ElMessage} from 'element-plus'
+import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue'
+import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
+import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
+
+// 褰撳墠鐢宠绫诲瀷
+const currentType = ref('department') // approval: 瀹℃壒娴佺▼, department: 閮ㄩ棬绾�, notification: 閫氱煡鍙戝竷
+
+// 鐢宠绫诲瀷閫夐」
+const applicationTypes = ref([
+ {
+ value: 'approval',
+ name: '瀹℃壒娴佺▼浼氳',
+ desc: '闇�瑕佺粡杩囧绾у鎵圭殑浼氳鐢宠',
+ icon: Document
+ },
+ {
+ value: 'department',
+ name: '閮ㄩ棬绾т細璁�',
+ desc: '閮ㄩ棬鍐呴儴浼氳鐢宠娴佺▼',
+ icon: Promotion
+ },
+ {
+ value: 'notification',
+ name: '浼氳閫氱煡',
+ desc: '鏃犻渶瀹℃壒鐩存帴鍙戝竷鐨勪細璁�氱煡',
+ icon: Bell
+ }
+])
+
+// 琛ㄥ崟鏁版嵁
+const meetingForm = reactive({
+ title: '',
+ type: '',
+ roomId: '',
+ host: '',
+ meetingDate: '',
+ startTime: '',
+ endTime: '',
+ participants: [],
+ description: ''
+})
+
+// 琛ㄥ崟鏍¢獙瑙勫垯
+const rules = {
+ title: [{required: true, message: '璇疯緭鍏ヤ細璁富棰�', trigger: 'blur'}],
+ roomId: [{required: true, message: '璇烽�夋嫨浼氳瀹�', trigger: 'change'}],
+ host: [{required: true, message: '璇疯緭鍏ヤ富鎸佷汉', trigger: 'blur'}],
+ meetingDate: [{required: true, message: '璇烽�夋嫨浼氳鏃ユ湡', trigger: 'change'}],
+ startTime: [{required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change'}],
+ endTime: [{required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change'}],
+ participants: [{required: true, message: '璇烽�夋嫨鍙備細浜哄憳', trigger: 'change'}]
+}
+
+// 琛ㄥ崟寮曠敤
+const meetingFormRef = ref(null)
+
+// 浼氳瀹ゅ垪琛�
+const meetingRooms = ref([])
+
+// 鍛樺伐鍒楄〃
+const employees = ref([])
+
+// 鏃堕棿閫夐」锛堜互鍗婂皬鏃朵负闂撮殧锛�
+const timeOptions = ref([])
+
+// 鍒濆鍖栨椂闂撮�夐」
+const initTimeOptions = () => {
+ const options = []
+ for (let hour = 8; hour <= 18; hour++) {
+ // 姣忎釜灏忔椂娣诲姞涓や釜閫夐」锛氭暣鐐瑰拰鍗婄偣
+ options.push({
+ value: `${hour.toString().padStart(2, '0')}:00`,
+ label: `${hour.toString().padStart(2, '0')}:00`
+ })
+
+ if (hour < 18) { // 18:00涔嬪悗娌℃湁鍗婄偣閫夐」
+ options.push({
+ value: `${hour.toString().padStart(2, '0')}:30`,
+ label: `${hour.toString().padStart(2, '0')}:30`
+ })
+ }
+ }
+ timeOptions.value = options
+}
+
+// 绂佺敤鏃ユ湡锛堢鐢ㄤ粖澶╀箣鍓嶇殑鏃ユ湡锛�
+const disabledDate = (time) => {
+ // 绂佺敤浠婂ぉ涔嬪墠鐨勬棩鏈�
+ return time.getTime() < Date.now() - 86400000
+}
+
+// 鍒囨崲鐢宠绫诲瀷
+const changeType = (type) => {
+ currentType.value = type
+}
+
+// 鑾峰彇褰撳墠绫诲瀷鍚嶇О
+const getCurrentTypeName = () => {
+ const type = applicationTypes.value.find(t => t.value === currentType.value)
+ return type ? type.name : ''
+}
+
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+ meetingFormRef.value?.resetFields()
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ meetingFormRef.value?.validate((valid) => {
+ if (valid) {
+
+ let formData = {...meetingForm}
+ formData.applicationType = currentType.value
+ formData.startTime = `${meetingForm.meetingDate} ${meetingForm.startTime}:00`
+ formData.endTime = `${meetingForm.meetingDate} ${meetingForm.endTime}:00`
+ formData.participants = JSON.stringify(formData.participants)
+ console.log(formData)
+ saveMeetingApplication(formData).then(() => {
+
+ // 妯℃嫙鎻愪氦鎿嶄綔
+ ElMessage.success(`${getCurrentTypeName()}鎻愪氦鎴愬姛`)
+
+ // 鏍规嵁涓嶅悓绫诲瀷鎵ц涓嶅悓鎿嶄綔
+ switch (currentType.value) {
+ case 'approval':
+ ElMessage.info('浼氳宸叉彁浜ゅ鎵规祦绋�')
+ break
+ case 'department':
+ ElMessage.info('閮ㄩ棬绾т細璁敵璇峰凡鎻愪氦')
+ break
+ case 'notification':
+ ElMessage.info('浼氳閫氱煡宸插彂甯�')
+ break
+ }
+ resetForm()
+ })
+
+ }
+ })
+}
+
+// 椤甸潰鍔犺浇鏃跺垵濮嬪寲
+onMounted(() => {
+ initTimeOptions()
+ getRoomEnum().then(res => {
+ meetingRooms.value = res.data
+ })
+ getStaffOnJob().then(res => {
+ employees.value = res.data.sort((a, b) => a.postJob.localeCompare(b.postJob))
+ })
+})
+</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;
+}
+
+.type-card {
+ margin-bottom: 20px;
+}
+
+.type-selector {
+ display: flex;
+ gap: 20px;
+}
+
+.type-item {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ padding: 20px;
+ border: 1px solid #ebeef5;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.type-item:hover {
+ border-color: #409eff;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.type-item.active {
+ border-color: #409eff;
+ background-color: #ecf5ff;
+}
+
+.type-icon {
+ margin-right: 15px;
+ color: #409eff;
+}
+
+.type-name {
+ font-size: 16px;
+ font-weight: 500;
+ color: #303133;
+ margin-bottom: 5px;
+}
+
+.type-desc {
+ font-size: 14px;
+ color: #909399;
+}
+
+.form-header {
+ margin-bottom: 20px;
+ padding-bottom: 15px;
+ border-bottom: 1px solid #ebeef5;
+}
+
+.form-header h3 {
+ margin: 0;
+ color: #303133;
+}
+
+.form-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ margin-top: 30px;
+ padding-top: 20px;
+ border-top: 1px solid #ebeef5;
+}
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue b/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
new file mode 100644
index 0000000..11d1774
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
@@ -0,0 +1,495 @@
+<template>
+ <div>
+ <!-- 椤甸潰鏍囬 -->
+ <div class="page-header">
+ <h2>浼氳鑽夌</h2>
+ <el-button type="primary" @click="handleAdd">
+ <el-icon><Plus /></el-icon>
+ 鏂板缓鑽夌
+ </el-button>
+ </div>
+
+ <!-- 鎼滅储鍖哄煙 -->
+ <el-card class="search-card">
+ <el-form :model="searchForm" label-width="100px" inline>
+ <el-form-item label="浼氳涓婚">
+ <el-input v-model="searchForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�" clearable />
+ </el-form-item>
+ <el-form-item label="浼氳鏃ユ湡">
+ <el-date-picker
+ v-model="searchForm.meetingDate"
+ type="date"
+ placeholder="璇烽�夋嫨浼氳鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </el-card>
+
+ <!-- 鑽夌鍒楄〃 -->
+ <el-card>
+ <el-table v-loading="loading" :data="draftList" border>
+ <el-table-column prop="title" label="浼氳涓婚" align="center" min-width="200" show-overflow-tooltip />
+ <el-table-column prop="room" label="浼氳瀹�" align="center" width="120" />
+ <el-table-column prop="host" label="涓绘寔浜�" align="center" width="120" />
+ <el-table-column prop="meetingTime" label="浼氳鏃堕棿" align="center" width="180">
+ <template #default="scope">
+ {{ formatDateTime(scope.row.meetingTime) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="participants" label="鍙備細浜烘暟" align="center" width="100">
+ <template #default="scope">
+ {{ scope.row.participants }}浜�
+ </template>
+ </el-table-column>
+ <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" align="center" width="180" />
+ <el-table-column label="鎿嶄綔" align="center" width="200" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" link @click="viewDraft(scope.row)">鏌ョ湅</el-button>
+ <el-button type="primary" link @click="editDraft(scope.row)">缂栬緫</el-button>
+ <el-button type="danger" link @click="deleteDraft(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </el-card>
+
+ <!-- 浼氳鑽夌璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳鑽夌璇︽儏"
+ v-model="detailDialogVisible"
+ width="800px"
+ >
+ <div v-if="currentDraft">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="浼氳涓婚">{{ currentDraft.title }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳缂栧彿">{{ currentDraft.meetingId }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳瀹�">{{ currentDraft.room }}</el-descriptions-item>
+ <el-descriptions-item label="涓绘寔浜�">{{ currentDraft.host }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2">
+ {{ formatDateTime(currentDraft.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="鍒涘缓鏃堕棿">{{ currentDraft.createTime }}</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="content-section mt-20">
+ <h4>鍙備細浜哄憳</h4>
+ <div class="participants-list">
+ {{ currentDraft.participantList }}
+ </div>
+ </div>
+
+ <div class="content-section mt-20">
+ <h4>浼氳璇存槑</h4>
+ <div class="meeting-description">{{ currentDraft.description }}</div>
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="detailDialogVisible = false">鍏� 闂�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 鏂板缓/缂栬緫鑽夌瀵硅瘽妗� -->
+ <el-dialog
+ :title="dialogTitle"
+ v-model="editDialogVisible"
+ width="700px"
+ >
+ <el-form :model="meetingForm" :rules="rules" ref="meetingFormRef" label-width="100px">
+ <el-form-item label="浼氳涓婚" prop="title">
+ <el-input v-model="meetingForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�" />
+ </el-form-item>
+ <el-form-item label="浼氳瀹�" prop="room">
+ <el-select v-model="meetingForm.roomId" placeholder="璇烽�夋嫨浼氳瀹�" style="width: 100%">
+ <el-option v-for="(v,k) in roomList" :label="v.name" :value="v.id" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="涓绘寔浜�" prop="host">
+ <el-input v-model="meetingForm.host" placeholder="璇疯緭鍏ヤ富鎸佷汉" />
+ </el-form-item>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="浼氳鏃ユ湡" prop="meetingDate">
+ <el-date-picker
+ v-model="meetingForm.meetingDate"
+ type="date"
+ placeholder="璇烽�夋嫨浼氳鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ :disabled-date="disabledDate"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <!-- 绌哄垪锛屼繚鎸佸竷灞� -->
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="寮�濮嬫椂闂�" prop="startTime">
+ <el-select
+ v-model="meetingForm.startTime"
+ placeholder="璇烽�夋嫨寮�濮嬫椂闂�"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="time in timeOptions"
+ :key="time.value"
+ :label="time.label"
+ :value="time.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="缁撴潫鏃堕棿" prop="endTime">
+ <el-select
+ v-model="meetingForm.endTime"
+ placeholder="璇烽�夋嫨缁撴潫鏃堕棿"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="time in timeOptions"
+ :key="time.value"
+ :label="time.label"
+ :value="time.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="鍙備細浜烘暟" prop="participants">
+ <el-input
+ v-model="meetingForm.participants"
+ type="number"
+ placeholder="璇疯緭鍏ュ弬浼氫汉鏁�"
+ />
+ </el-form-item>
+ <el-form-item label="鍙備細浜哄憳" prop="participants">
+ <el-input
+ v-model="meetingForm.participantList"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ弬浼氫汉鍛橈紝鐢ㄩ�楀彿鍒嗛殧"
+ />
+ </el-form-item>
+ <el-form-item label="浼氳璇存槑">
+ <el-input
+ v-model="meetingForm.description"
+ type="textarea"
+ :rows="4"
+ placeholder="璇疯緭鍏ヤ細璁鏄�"
+ />
+ </el-form-item>
+ </el-form>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="editDialogVisible = false">鍙� 娑�</el-button>
+ <el-button type="primary" @click="submitForm">淇� 瀛�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus } from '@element-plus/icons-vue'
+import Pagination from '@/components/Pagination/index.vue'
+import {getRoomEnum,getDraftList,saveDraft,delDraft} from '@/api/collaborativeApproval/meeting.js'
+import dayjs from "dayjs";
+// 鏁版嵁鍒楄〃鍔犺浇鐘舵��
+const loading = ref(false)
+
+// 鎬绘潯鏁�
+const total = ref(0)
+
+// 鑽夌鍒楄〃鏁版嵁
+const draftList = ref([])
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ title: '',
+ meetingDate: ''
+})
+
+// 鏄惁鏄剧ず瀵硅瘽妗�
+const detailDialogVisible = ref(false)
+const editDialogVisible = ref(false)
+
+const roomList = ref([])
+
+// 瀵硅瘽妗嗘爣棰�
+const dialogTitle = ref('')
+
+// 褰撳墠鏌ョ湅鐨勮崏绋�
+const currentDraft = ref(null)
+
+// 琛ㄥ崟寮曠敤
+const meetingFormRef = ref(null)
+
+// 鏃堕棿閫夐」锛堜互鍗婂皬鏃朵负闂撮殧锛屽伐浣滄椂闂�8:00-18:00锛�
+const timeOptions = ref([])
+
+// 琛ㄥ崟鏁版嵁
+const meetingForm = reactive({
+ id: '',
+ meetingId: '',
+ title: '',
+ roomId: '',
+ host: '',
+ meetingDate: '',
+ startTime: '',
+ endTime: '',
+ participants: 0,
+ participantList: '',
+ description: '',
+ createTime: ''
+})
+
+// 琛ㄥ崟鏍¢獙瑙勫垯
+const rules = {
+ title: [{ required: true, message: '璇疯緭鍏ヤ細璁富棰�', trigger: 'blur' }],
+ roomId: [{ required: true, message: '璇烽�夋嫨浼氳瀹�', trigger: 'change' }],
+ host: [{ required: true, message: '璇疯緭鍏ヤ富鎸佷汉', trigger: 'blur' }],
+ meetingDate: [{ required: true, message: '璇烽�夋嫨浼氳鏃ユ湡', trigger: 'change' }],
+ startTime: [{ required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change' }],
+ endTime: [{ required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change' }]
+}
+
+// 鍒濆鍖栨椂闂撮�夐」锛堜互鍗婂皬鏃朵负闂撮殧锛屽伐浣滄椂闂�8:00-18:00锛�
+const initTimeOptions = () => {
+ const options = []
+ for (let hour = 8; hour <= 18; hour++) {
+ // 姣忎釜灏忔椂娣诲姞涓や釜閫夐」锛氭暣鐐瑰拰鍗婄偣
+ options.push({
+ value: `${hour.toString().padStart(2, '0')}:00`,
+ label: `${hour.toString().padStart(2, '0')}:00`
+ })
+
+ if (hour < 18) { // 18:00涔嬪悗娌℃湁鍗婄偣閫夐」
+ options.push({
+ value: `${hour.toString().padStart(2, '0')}:30`,
+ label: `${hour.toString().padStart(2, '0')}:30`
+ })
+ }
+ }
+ timeOptions.value = options
+}
+
+// 绂佺敤鏃ユ湡锛堢鐢ㄤ粖澶╀箣鍓嶇殑鏃ユ湡锛�
+const disabledDate = (time) => {
+ // 绂佺敤浠婂ぉ涔嬪墠鐨勬棩鏈�
+ return time.getTime() < Date.now() - 86400000
+}
+
+// 鏌ヨ鏁版嵁
+const getList = async () => {
+ loading.value = true
+
+ let resp = await getDraftList({...queryParams,...searchForm})
+ queryParams.current = resp.data.current
+ draftList.value = resp.data.records.map(it=>{
+ it.room = roomList.value.find(room=>it.roomId===room.id).name ?? ""
+ it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format("HH:mm")} ~ ${dayjs(it.endTime).format("HH:mm")}`
+ return it
+ })
+
+ loading.value = false
+
+}
+
+// 鎼滅储鎸夐挳鎿嶄綔
+const handleSearch = () => {
+ queryParams.pageNum = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储琛ㄥ崟
+const resetSearch = () => {
+ Object.assign(searchForm, {
+ title: '',
+ createTime: []
+ })
+ handleSearch()
+}
+
+// 娣诲姞鎸夐挳鎿嶄綔
+const handleAdd = () => {
+ dialogTitle.value = '鏂板缓鑽夌'
+ resetForm()
+ editDialogVisible.value = true
+}
+
+// 鏌ョ湅鑽夌璇︽儏
+const viewDraft = (row) => {
+ currentDraft.value = row
+ detailDialogVisible.value = true
+}
+
+// 缂栬緫鑽夌
+const editDraft = (row) => {
+ dialogTitle.value = '缂栬緫鑽夌'
+ Object.assign(meetingForm, {
+ id: row.id,
+ meetingId: row.meetingId,
+ title: row.title,
+ room: row.room,
+ roomId: row.id,
+ host: row.host,
+ meetingDate: row.meetingTime.split(' ')[0],
+ startTime: row.meetingTime.split(' ')[1],
+ endTime: row.meetingTime.split(' ')[3],
+ participants: row.participants,
+ participantList: row.participantList,
+ description: row.description,
+ createTime: row.createTime
+ })
+ editDialogVisible.value = true
+}
+
+// 鍒犻櫎鑽夌
+const deleteDraft = (row) => {
+ ElMessageBox.confirm(
+ `纭鍒犻櫎浼氳鑽夌 "${row.title}"?`,
+ '鍒犻櫎鑽夌',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ ).then(() => {
+ delDraft(row.id).then(resp=>{
+ ElMessage.success('鑽夌鍒犻櫎鎴愬姛')
+ getList()
+ })
+
+ }).catch(() => {})
+}
+
+// 閲嶇疆琛ㄥ崟
+const resetForm = () => {
+ Object.assign(meetingForm, {
+ id: '',
+ meetingId: '',
+ title: '',
+ room: '',
+ host: '',
+ meetingDate: '',
+ startTime: '',
+ endTime: '',
+ participants: 0,
+ participantList: '',
+ description: '',
+ createTime: ''
+ })
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ meetingFormRef.value.validate((valid) => {
+ if (valid) {
+ let formData = {...meetingForm}
+ formData.startTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.startTime).format("YYYY-MM-DD HH:mm:ss")
+ formData.endTime = dayjs(meetingForm.meetingDate + ' ' + meetingForm.endTime).format("YYYY-MM-DD HH:mm:ss")
+ saveDraft(formData).then(()=>{
+ ElMessage.success('淇濆瓨鎴愬姛')
+ editDialogVisible.value = false
+ getList()
+ })
+ }
+ })
+}
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (dateTime) => {
+ if (!dateTime) return ''
+ return dateTime.replace(' ', '\n')
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(() => {
+ initTimeOptions()
+ getList()
+ getRoomEnum().then((res) => {
+ roomList.value = res.data
+ })
+})
+</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;
+}
+
+.search-card {
+ margin-bottom: 20px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+.content-section h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+.participants-list {
+ min-height: 40px;
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+}
+
+.meeting-description {
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+ white-space: pre-wrap;
+}
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
new file mode 100644
index 0000000..83747a0
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
@@ -0,0 +1,414 @@
+<template>
+ <div>
+
+ <el-form :model="searchForm" inline>
+ <el-form-item label="浼氳涓婚">
+ <el-input v-model="searchForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�" clearable/>
+ </el-form-item>
+ <el-form-item label="鐢宠浜�">
+ <el-input v-model="searchForm.applicant" placeholder="璇疯緭鍏ョ敵璇蜂汉" clearable/>
+ </el-form-item>
+ <el-form-item label="瀹℃壒鐘舵��">
+ <el-select style="width: 100px" v-model="searchForm.status" placeholder="璇烽�夋嫨瀹℃壒鐘舵��" clearable>
+ <el-option label="寰呭鎵�" value="0"/>
+ <el-option label="宸查�氳繃" value="1"/>
+ <el-option label="鏈鎵�" value="2"/>
+ <el-option label="宸插彇娑�" value="3"/>
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 浼氳瀹℃壒鍒楄〃 -->
+ <el-card>
+ <el-table v-loading="loading" :data="approvalList" border :height="tableHeight">
+ <el-table-column prop="title" label="浼氳涓婚" align="center" min-width="200" show-overflow-tooltip/>
+ <el-table-column prop="applicant" label="鐢宠浜�" align="center" width="120"/>
+ <el-table-column prop="host" label="涓荤悊浜�" align="center" width="120"/>
+ <el-table-column prop="meetingTime" label="浼氳鏃堕棿" align="center" width="180">
+ <template #default="scope">
+ {{ formatDateTime(scope.row.meetingTime) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="location" label="浼氳鍦扮偣" align="center" width="150"/>
+ <el-table-column prop="participants" label="鍙備細浜烘暟" align="center" width="100">
+ <template #default="scope">
+ {{ scope.row.participants.length }}浜�
+ </template>
+ </el-table-column>
+ <el-table-column prop="status" label="瀹℃壒鐘舵��" align="center" width="120">
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" align="center" width="200" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" link @click="viewDetail(scope.row)">鏌ョ湅</el-button>
+ <el-button
+ v-if="scope.row.status == '0'"
+ type="primary"
+ link
+ @click="handleApproval(scope.row)"
+ >
+ 瀹℃壒
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </el-card>
+
+ <!-- 浼氳璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳璇︽儏"
+ v-model="detailDialogVisible"
+ width="800px"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions label-width="100px" class="meeting-desc" :column="2" border>
+ <el-descriptions-item label="浼氳涓婚" label-class-name="nowrap-label">{{
+ currentMeeting.title
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�" label-class-name="nowrap-label">{{
+ currentMeeting.applicant
+ }}</el-descriptions-item>
+ <el-descriptions-item label="涓荤悊浜�" label-class-name="nowrap-label">{{
+ currentMeeting.host
+ }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2" label-class-name="nowrap-label">
+ {{ formatDateTime(currentMeeting.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="浼氳鍦扮偣" label-class-name="nowrap-label">{{
+ currentMeeting.location
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟" label-class-name="nowrap-label">{{
+ currentMeeting.participants.length
+ }}浜�</el-descriptions-item>
+ <el-descriptions-item label="瀹℃壒鐘舵��" label-class-name="nowrap-label">
+ <el-tag :type="getStatusType(currentMeeting.status)">
+ {{ getStatusText(currentMeeting.status) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏃堕棿" label-class-name="nowrap-label">{{
+ currentMeeting.createTime
+ }}</el-descriptions-item>
+ <el-descriptions-item style="max-height: 400px" label="浼氳璇存槑" :span="2"
+ label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
+ </el-descriptions>
+
+
+ <div class="content-section mt-20">
+ <h4>鍙備細浜哄憳</h4>
+ <div class="participants-list">
+ <el-tag
+ v-for="participant in currentMeeting.participants"
+ :key="participant.id"
+ style="margin-right: 10px; margin-bottom: 10px;"
+ >
+ {{ participant.name }}
+ </el-tag>
+ </div>
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="detailDialogVisible = false">鍏� 闂�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 浼氳瀹℃壒瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳瀹℃壒"
+ v-model="approvalDialogVisible"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="浼氳涓婚">{{ currentMeeting.title }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�">{{ currentMeeting.applicant }}</el-descriptions-item>
+ <el-descriptions-item label="涓荤悊浜�">{{ currentMeeting.host }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2">
+ {{ formatDateTime(currentMeeting.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="浼氳鍦扮偣">{{ currentMeeting.location }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟">{{ currentMeeting.participants.length }}浜�</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="content-section mt-20">
+ <h4>鍙備細浜哄憳</h4>
+ <div class="participants-list">
+ <el-tag
+ v-for="participant in currentMeeting.participants"
+ :key="participant.id"
+ style="margin-right: 10px; margin-bottom: 10px;"
+ >
+ {{ participant.name }}
+ </el-tag>
+ </div>
+ </div>
+
+ <div v-show="false" class="approval-opinion mt-20">
+ <h4>瀹℃壒鎰忚</h4>
+ <el-input
+ v-model="approvalOpinion"
+ type="textarea"
+ placeholder="璇疯緭鍏ュ鎵规剰瑙�"
+ :rows="4"
+ />
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="approvalDialogVisible = false">鍙� 娑�</el-button>
+ <el-button type="danger" @click="submitApproval('2')">涓嶉�氳繃</el-button>
+ <el-button type="primary" @click="submitApproval('1')">閫� 杩�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {ref, reactive, onMounted} from 'vue'
+import {ElMessage, ElMessageBox} from 'element-plus'
+import Pagination from '@/components/Pagination/index.vue'
+import {getRoomEnum, getExamineList,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
+import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
+import dayjs from "dayjs";
+
+// 鏁版嵁鍒楄〃鍔犺浇鐘舵��
+const loading = ref(false)
+
+// 鎬绘潯鏁�
+const total = ref(0)
+
+// 琛ㄦ牸楂樺害锛堟牴鎹獥鍙i珮搴﹁嚜閫傚簲锛�
+const tableHeight = ref(window.innerHeight - 380)
+const roomEnum = ref([])
+const staffList = ref([])
+// 瀹℃壒鍒楄〃鏁版嵁
+const approvalList = ref([])
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ title: '',
+ applicant: '',
+ status: ''
+})
+
+// 鏄惁鏄剧ず瀵硅瘽妗�
+const detailDialogVisible = ref(false)
+const approvalDialogVisible = ref(false)
+
+// 褰撳墠鏌ョ湅鐨勪細璁�
+const currentMeeting = ref(null)
+
+// 瀹℃壒鎰忚
+const approvalOpinion = ref('')
+
+// 鏌ヨ鏁版嵁
+const getList = async () => {
+ loading.value = true
+ let resp = await getExamineList({...searchForm, ...queryParams})
+ approvalList.value = resp.data.records.map(it => {
+ let room = roomEnum.value.find(room => it.roomId === room.id)
+ it.location = `${room.name}(${room.location})`
+ let staffs = JSON.parse(it.participants)
+ it.staffCount = staffs.size
+ it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
+ it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => {
+ return {
+ id: staff.id,
+ name: `${staff.staffName}(${staff.postJob})`
+ }
+ })
+
+
+ return it
+ })
+ total.value = resp.data.total
+ loading.value = false
+}
+
+// 鎼滅储鎸夐挳鎿嶄綔
+const handleSearch = () => {
+ queryParams.pageNum = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储琛ㄥ崟
+const resetSearch = () => {
+ Object.assign(searchForm, {
+ title: '',
+ applicant: '',
+ status: ''
+ })
+ handleSearch()
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetail = (row) => {
+ currentMeeting.value = row
+ detailDialogVisible.value = true
+}
+
+// 澶勭悊瀹℃壒
+const handleApproval = (row) => {
+ currentMeeting.value = row
+ approvalOpinion.value = ''
+ approvalDialogVisible.value = true
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const statusMap = {
+ '0': 'info', // 寰呭鎵�
+ '1': 'success', // 宸查�氳繃
+ '2': 'warning', // 鏈�氳繃
+ '3': 'danger' // 鍙栨秷
+ }
+ return statusMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const statusMap = {
+ '0': '寰呭鎵�',
+ '1': '宸查�氳繃',
+ '2': '鏈�氳繃',
+ '3': '宸插彇娑�'
+ }
+ return statusMap[status] || '鏈煡'
+}
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (dateTime) => {
+ if (!dateTime) return ''
+ return dateTime.replace(' ', '\n')
+}
+
+// 鎻愪氦瀹℃壒
+const submitApproval = (status) => {
+ // if (status === 'approved' && !approvalOpinion.value.trim()) {
+ // ElMessage.warning('璇峰~鍐欏鎵规剰瑙�')
+ // return
+ // }
+
+ ElMessageBox.confirm(
+ `纭${status === '1' ? '閫氳繃' : '涓嶉�氳繃'}璇ヤ細璁敵璇凤紵`,
+ '瀹℃壒纭',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ ).then(() => {
+ saveMeetingApplication({
+ id: currentMeeting.value.id,
+ status: status
+ }).then(resp=>{
+ // 鏇存柊浼氳鐘舵��
+ currentMeeting.value.status = status
+
+ ElMessage.success('瀹℃壒鎻愪氦鎴愬姛')
+ approvalDialogVisible.value = false
+ getList()
+ })
+
+ }).catch(() => {
+ })
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(async () => {
+ const [resp1, resp2]= await Promise.all([getRoomEnum(), getStaffOnJob()])
+ roomEnum.value = resp1.data
+ staffList.value = resp2.data
+
+ await getList()
+})
+</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;
+}
+
+.search-card {
+ margin-bottom: 20px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+.content-section h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+.participants-list {
+ min-height: 40px;
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+}
+
+.approval-opinion h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.nowrap-label {
+ white-space: nowrap !important;
+}
+
+.description-content {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ line-height: 1.6;
+ padding: 10px;
+ background-color: #f5f7fa;
+ border-radius: 4px;
+ min-height: 60px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue b/src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue
new file mode 100644
index 0000000..9b5325f
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetIndex/index.vue
@@ -0,0 +1,363 @@
+<template>
+ <div>
+ <el-form :model="queryForm" label-width="80px" inline>
+ <el-form-item label="鏌ヨ鏃ユ湡">
+ <el-date-picker
+ v-model="queryForm.meetingDate"
+ type="date"
+ placeholder="璇烽�夋嫨鏃ユ湡"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ :clearable="false"
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鏌ヨ</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 浼氳瀹や娇鐢ㄦ儏鍐� -->
+ <el-card class="table-container" :loading="loading">
+ <div class="time-table">
+ <!-- 琛ㄥご -->
+ <div class="table-header">
+ <div class="header-cell room-header">浼氳瀹�</div>
+ <div
+ v-for="timeSlot in timeSlots"
+ :key="timeSlot.value"
+ class="header-cell time-header"
+ >
+ {{ timeSlot.label }}
+ </div>
+ </div>
+
+ <!-- 琛ㄦ牸鍐呭 -->
+ <div class="table-body">
+ <div
+ v-for="room in roomUsage"
+ :key="room.id"
+ class="table-row"
+ >
+ <div class="cell room-cell">{{ room.name }}</div>
+ <div class="cells-container">
+ <template v-for="(cell, index) in generateMeetingCells(room)" :key="index">
+ <div
+ class="cell content-cell"
+ :class="[cell.type, `status-${cell.meeting?.status || '0'}`]"
+ :style="{ flex: cell.span-0.2 }"
+ @click="viewMeetingDetails(cell)"
+ >
+ <div v-if="cell.type === 'meeting'" class="meeting-content">
+ <div class="meeting-title">{{ cell.meeting.title }}</div>
+ <div class="meeting-time">{{ cell.startTime }}-{{ cell.endTime }}</div>
+ </div>
+ <div v-else class="free-content">
+ 绌洪棽
+ </div>
+ </div>
+ </template>
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-card>
+
+ <!-- 浼氳璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳璇︽儏"
+ v-model="detailDialogVisible"
+ width="800px"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions :column="1" border>
+ <el-descriptions-item label="浼氳涓婚">{{ currentMeeting.title }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳瀹�">{{ currentMeeting.room }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿">{{ currentMeeting.time }}</el-descriptions-item>
+ <el-descriptions-item label="涓绘寔浜�">{{ currentMeeting.host }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟">{{ currentMeeting.participants }}浜�</el-descriptions-item>
+ <el-descriptions-item label="浼氳璇存槑">{{ currentMeeting.description }}</el-descriptions-item>
+ </el-descriptions>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="detailDialogVisible = false">鍏� 闂�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {ref, reactive, onMounted} from 'vue'
+import {ElMessage} from 'element-plus'
+import {getMeetingUseList} from "@/api/collaborativeApproval/meeting.js"
+import dayjs from "dayjs";
+
+// 鏌ヨ琛ㄥ崟
+const queryForm = reactive({
+ meetingDate: dayjs().format('YYYY-MM-DD')
+})
+let loading = ref(false)
+// 鏃堕棿娈碉紙浠ュ崐灏忔椂涓洪棿闅旓級
+const timeSlots = ref([])
+
+// 浼氳瀹や娇鐢ㄦ儏鍐�
+const roomUsage = ref([])
+
+// 褰撳墠鏌ョ湅鐨勪細璁�
+const currentMeeting = ref(null)
+
+// 鏄惁鏄剧ず璇︽儏瀵硅瘽妗�
+const detailDialogVisible = ref(false)
+
+// 鍒濆鍖栨椂闂存Ы锛堜互鍗婂皬鏃朵负闂撮殧锛屼粠8:00鍒�18:00锛�
+const initTimeSlots = () => {
+ const slots = []
+ for (let hour = 8; hour < 18; hour++) {
+ // 姣忎釜灏忔椂娣诲姞涓や釜鏃堕棿娈碉細鏁寸偣鍜屽崐鐐�
+ slots.push({
+ label: `${hour.toString().padStart(2, '0')}:00`,
+ value: `${hour.toString().padStart(2, '0')}:00`
+ })
+
+ if (hour < 18) { // 鍒�17:30涓烘
+ slots.push({
+ label: `${hour.toString().padStart(2, '0')}:30`,
+ value: `${hour.toString().padStart(2, '0')}:30`
+ })
+ }
+ }
+ timeSlots.value = slots
+}
+
+// 鐢熸垚浼氳瀹ょ殑鏃堕棿鍗曞厓鏍�
+const generateMeetingCells = (room) => {
+ const cells = []
+ const meetings = room.meetings || []
+ const occupiedSlots = new Set()
+
+ // 澶勭悊姣忎釜浼氳
+ for (const meeting of meetings) {
+
+ const startIdx = timeSlots.value.findIndex(slot => slot.value === meeting.startTime)
+ let endIdx = timeSlots.value.findIndex(slot => slot.value === meeting.endTime)
+ if (endIdx === -1) {
+ endIdx = timeSlots.value.length
+ }
+ console.log('endIdx111', endIdx)
+ if (startIdx !== -1 && endIdx !== -1) {
+ // 鏍囪琚崰鐢ㄧ殑鏃堕棿娈�
+ for (let i = startIdx; i < endIdx; i++) {
+ occupiedSlots.add(timeSlots.value[i].value)
+ }
+
+ // 鍒涘缓浼氳鍗曞厓鏍�
+ cells.push({
+ type: 'meeting',
+ meeting: meeting,
+ span: endIdx - startIdx,
+ startTime: meeting.startTime,
+ endTime: meeting.endTime
+ })
+ }
+ }
+
+ // 澶勭悊绌洪棽鏃堕棿娈�
+ for (let i = 0; i < timeSlots.value.length; i++) {
+ const slot = timeSlots.value[i]
+ if (!occupiedSlots.has(slot.value)) {
+ // 鏌ユ壘杩炵画鐨勭┖闂叉椂闂存
+ let span = 1
+ while (i + span < timeSlots.value.length &&
+ !occupiedSlots.has(timeSlots.value[i + span].value)) {
+ occupiedSlots.add(timeSlots.value[i + span].value)
+ span++
+ }
+
+ cells.push({
+ type: 'free',
+ span: span,
+ time: slot.value
+ })
+ }
+ }
+
+ // 鎸夋椂闂存帓搴�
+ cells.sort((a, b) => {
+ const timeA = a.startTime || a.time
+ const timeB = b.startTime || b.time
+ return timeSlots.value.findIndex(s => s.value === timeA) -
+ timeSlots.value.findIndex(s => s.value === timeB)
+ })
+ console.log('cells', cells)
+ return cells
+}
+
+// 鏌ョ湅浼氳璇︽儏
+const viewMeetingDetails = (cell) => {
+ if (cell && cell.type === 'meeting') {
+ currentMeeting.value = cell.meeting
+ detailDialogVisible.value = true
+ } else {
+ ElMessage.info('璇ユ椂闂存浼氳瀹ょ┖闂�')
+ }
+}
+
+// 鏌ヨ鎸夐挳鎿嶄綔
+const handleSearch = async () => {
+ loading.value = true
+ let resp = await getMeetingUseList({...queryForm})
+ roomUsage.value = resp.data
+ loading.value = false
+}
+
+// 閲嶇疆鎼滅储琛ㄥ崟
+const resetSearch = () => {
+ queryForm.date = dayjs().format('YYYY-MM-DD')
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(() => {
+ // 鍒濆鍖栨椂闂存Ы
+ initTimeSlots()
+
+ // 榛樿鏌ヨ浠婂ぉ鐨勬暟鎹�
+ const today = new Date()
+ queryForm.date = today.toISOString().split('T')[0]
+ handleSearch()
+})
+</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;
+}
+
+.search-card {
+ margin-bottom: 20px;
+}
+
+.table-container {
+ padding: 0;
+}
+
+.time-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.table-header {
+ display: flex;
+ border: 1px solid;
+}
+
+.table-row {
+ display: flex;
+ border: 1px solid #ebeef5;
+ border-top: none;
+}
+
+.header-cell {
+ padding: 12px 5px;
+ text-align: center;
+ font-weight: bold;
+ border-right: 1px solid;
+ min-height: 20px;
+}
+
+.room-header {
+ width: 120px;
+}
+
+.time-header {
+ flex: 1;
+}
+
+.cell {
+ padding: 15px 5px;
+ text-align: center;
+ border-right: 1px solid;
+ min-height: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ word-break: break-word;
+ line-height: 1.2;
+}
+
+.room-cell {
+ width: 120px;
+ font-weight: bold;
+}
+
+.cells-container {
+ flex: 1;
+ display: flex;
+}
+
+.content-cell {
+ min-height: 60px;
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.content-cell:hover {
+ opacity: 0.8;
+}
+
+.free {
+ color: #f56c6c;
+}
+
+.meeting {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.status-1 {
+ background-color: #fef0f0;
+ color: #d14646;
+}
+
+.status-0 {
+ background-color: #c7ddc8;
+ color: rgba(230, 162, 60, 0.29);
+}
+
+.meeting-content {
+ width: 100%;
+}
+
+.meeting-title {
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.meeting-time {
+ font-size: 12px;
+}
+
+.free-content {
+ color: #909399;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
new file mode 100644
index 0000000..2351293
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
@@ -0,0 +1,412 @@
+<template>
+ <div>
+
+ <el-form :model="searchForm" inline>
+ <el-form-item label="浼氳涓婚">
+ <el-input v-model="searchForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�" clearable/>
+ </el-form-item>
+ <el-form-item label="鐢宠浜�">
+ <el-input v-model="searchForm.applicant" placeholder="璇疯緭鍏ョ敵璇蜂汉" clearable/>
+ </el-form-item>
+ <el-form-item label="鍙戝竷鐘舵��">
+ <el-select style="width: 100px" v-model="searchForm.status" placeholder="璇烽�夋嫨鍙戝竷鐘舵��" clearable>
+ <el-option label="寰呭彂甯�" value="0"/>
+ <el-option label="宸插彂甯�" value="1"/>
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 浼氳鍙戝竷鍒楄〃 -->
+ <el-card>
+ <el-table v-loading="loading" :data="approvalList" border :height="tableHeight">
+ <el-table-column prop="title" label="浼氳涓婚" align="center" min-width="200" show-overflow-tooltip/>
+ <el-table-column prop="applicant" label="鐢宠浜�" align="center" width="120"/>
+ <el-table-column prop="host" label="涓荤悊浜�" align="center" width="120"/>
+ <el-table-column prop="meetingTime" label="浼氳鏃堕棿" align="center" width="180">
+ <template #default="scope">
+ {{ formatDateTime(scope.row.meetingTime) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="location" label="浼氳鍦扮偣" align="center" width="150"/>
+ <el-table-column prop="participants" label="鍙備細浜烘暟" align="center" width="100">
+ <template #default="scope">
+ {{ scope.row.participants.length }}浜�
+ </template>
+ </el-table-column>
+ <el-table-column prop="status" label="鍙戝竷鐘舵��" align="center" width="120">
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" align="center" width="200" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" link @click="viewDetail(scope.row)">鏌ョ湅</el-button>
+ <el-button
+ v-if="scope.row.status == '0'"
+ type="primary"
+ link
+ @click="handleApproval(scope.row)"
+ >
+ 鍙戝竷
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </el-card>
+
+ <!-- 浼氳璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳璇︽儏"
+ v-model="detailDialogVisible"
+ width="800px"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions label-width="100px" class="meeting-desc" :column="2" border>
+ <el-descriptions-item label="浼氳涓婚" label-class-name="nowrap-label">{{
+ currentMeeting.title
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�" label-class-name="nowrap-label">{{
+ currentMeeting.applicant
+ }}</el-descriptions-item>
+ <el-descriptions-item label="涓荤悊浜�" label-class-name="nowrap-label">{{
+ currentMeeting.host
+ }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2" label-class-name="nowrap-label">
+ {{ formatDateTime(currentMeeting.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="浼氳鍦扮偣" label-class-name="nowrap-label">{{
+ currentMeeting.location
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟" label-class-name="nowrap-label">{{
+ currentMeeting.participants.length
+ }}浜�</el-descriptions-item>
+ <el-descriptions-item label="鍙戝竷鐘舵��" label-class-name="nowrap-label">
+ <el-tag :type="getStatusType(currentMeeting.status)">
+ {{ getStatusText(currentMeeting.status) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏃堕棿" label-class-name="nowrap-label">{{
+ currentMeeting.createTime
+ }}</el-descriptions-item>
+ <el-descriptions-item style="max-height: 400px" label="浼氳璇存槑" :span="2"
+ label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
+ </el-descriptions>
+
+
+ <div class="content-section mt-20">
+ <h4>鍙備細浜哄憳</h4>
+ <div class="participants-list">
+ <el-tag
+ v-for="participant in currentMeeting.participants"
+ :key="participant.id"
+ style="margin-right: 10px; margin-bottom: 10px;"
+ >
+ {{ participant.name }}
+ </el-tag>
+ </div>
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="detailDialogVisible = false">鍏� 闂�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 浼氳鍙戝竷瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳鍙戝竷"
+ v-model="approvalDialogVisible"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="浼氳涓婚">{{ currentMeeting.title }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�">{{ currentMeeting.applicant }}</el-descriptions-item>
+ <el-descriptions-item label="涓荤悊浜�">{{ currentMeeting.host }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2">
+ {{ formatDateTime(currentMeeting.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="浼氳鍦扮偣">{{ currentMeeting.location }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟">{{ currentMeeting.participants.length }}浜�</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="content-section mt-20">
+ <h4>鍙備細浜哄憳</h4>
+ <div class="participants-list">
+ <el-tag
+ v-for="participant in currentMeeting.participants"
+ :key="participant.id"
+ style="margin-right: 10px; margin-bottom: 10px;"
+ >
+ {{ participant.name }}
+ </el-tag>
+ </div>
+ </div>
+
+ <div class="approval-opinion mt-20">
+ <h4>鍙戝竷鎰忚</h4>
+ <el-input
+ v-model="publishComment"
+ type="textarea"
+ placeholder="璇疯緭鍏ュ彂甯冩剰瑙�"
+ :rows="4"
+ />
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="approvalDialogVisible = false">鍙� 娑�</el-button>
+<!-- <el-button type="danger" @click="submitApproval('2')">涓嶉�氳繃</el-button>-->
+ <el-button type="primary" @click="submitApproval('1')">鍙� 甯�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {ref, reactive, onMounted} from 'vue'
+import {ElMessage, ElMessageBox} from 'element-plus'
+import Pagination from '@/components/Pagination/index.vue'
+import {getRoomEnum, getMeetingPublish,saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
+import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
+import dayjs from "dayjs";
+
+// 鏁版嵁鍒楄〃鍔犺浇鐘舵��
+const loading = ref(false)
+
+// 鎬绘潯鏁�
+const total = ref(0)
+
+// 琛ㄦ牸楂樺害锛堟牴鎹獥鍙i珮搴﹁嚜閫傚簲锛�
+const tableHeight = ref(window.innerHeight - 380)
+const roomEnum = ref([])
+const staffList = ref([])
+// 鍙戝竷鍒楄〃鏁版嵁
+const approvalList = ref([])
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ title: '',
+ applicant: '',
+ status: ''
+})
+
+// 鏄惁鏄剧ず瀵硅瘽妗�
+const detailDialogVisible = ref(false)
+const approvalDialogVisible = ref(false)
+
+// 褰撳墠鏌ョ湅鐨勪細璁�
+const currentMeeting = ref(null)
+
+// 鍙戝竷鎰忚
+const publishComment = ref('')
+
+// 鏌ヨ鏁版嵁
+const getList = async () => {
+ loading.value = true
+ let resp = await getMeetingPublish({...searchForm, ...queryParams})
+ approvalList.value = resp.data.records.map(it => {
+ let room = roomEnum.value.find(room => it.roomId === room.id)
+ it.location = `${room.name}(${room.location})`
+ let staffs = JSON.parse(it.participants)
+ it.staffCount = staffs.size
+ it.status = it.publishStatus
+ it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
+ it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => {
+ return {
+ id: staff.id,
+ name: `${staff.staffName}(${staff.postJob})`
+ }
+ })
+
+
+ return it
+ })
+ total.value = resp.data.total
+ loading.value = false
+}
+
+// 鎼滅储鎸夐挳鎿嶄綔
+const handleSearch = () => {
+ queryParams.pageNum = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储琛ㄥ崟
+const resetSearch = () => {
+ Object.assign(searchForm, {
+ title: '',
+ applicant: '',
+ status: ''
+ })
+ handleSearch()
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetail = (row) => {
+ currentMeeting.value = row
+ detailDialogVisible.value = true
+}
+
+// 澶勭悊鍙戝竷
+const handleApproval = (row) => {
+ currentMeeting.value = row
+ publishComment.value = ''
+ approvalDialogVisible.value = true
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const statusMap = {
+ '0': 'info', // 寰呭彂甯�
+ '1': 'success', // 宸查�氳繃
+ '2': 'danger', // 鏈�氳繃
+ }
+ return statusMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const statusMap = {
+ '0': '寰呭彂甯�',
+ '1': '宸插彂甯�',
+ '2': '宸插彇娑�',
+ }
+ return statusMap[status] || '鏈煡'
+}
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (dateTime) => {
+ if (!dateTime) return ''
+ return dateTime.replace(' ', '\n')
+}
+
+// 鎻愪氦鍙戝竷
+const submitApproval = (status) => {
+ // if (status === 'approved' && !publishComment.value.trim()) {
+ // ElMessage.warning('璇峰~鍐欏彂甯冩剰瑙�')
+ // return
+ // }
+
+ ElMessageBox.confirm(
+ `纭${status === '1' ? '鍙戝竷' : '鍙栨秷'}璇ヤ細璁紵`,
+ '鍙戝竷纭',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ ).then(() => {
+ saveMeetingApplication({
+ id: currentMeeting.value.id,
+ publishStatus: status,
+ publishComment: publishComment.value
+ }).then(resp=>{
+ // 鏇存柊浼氳鐘舵��
+ currentMeeting.value.status = status
+
+ ElMessage.success('鍙戝竷鎻愪氦鎴愬姛')
+ approvalDialogVisible.value = false
+ getList()
+ })
+
+ }).catch(() => {
+ })
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(async () => {
+ const [resp1, resp2]= await Promise.all([getRoomEnum(), getStaffOnJob()])
+ roomEnum.value = resp1.data
+ staffList.value = resp2.data
+
+ await getList()
+})
+</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;
+}
+
+.search-card {
+ margin-bottom: 20px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+.content-section h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+.participants-list {
+ min-height: 40px;
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+}
+
+.approval-opinion h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.nowrap-label {
+ white-space: nowrap !important;
+}
+
+.description-content {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ line-height: 1.6;
+ padding: 10px;
+ background-color: #f5f7fa;
+ border-radius: 4px;
+ min-height: 60px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue b/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
new file mode 100644
index 0000000..ad4931a
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
@@ -0,0 +1,320 @@
+<template>
+ <div>
+ <!-- 鎼滅储鍖哄煙 -->
+ <el-form :model="searchForm" label-width="100px" class="search-form">
+ <el-form-item label="浼氳瀹ゅ悕绉�">
+ <el-input v-model="searchForm.name" placeholder="璇疯緭鍏ヤ細璁鍚嶇О" clearable />
+ </el-form-item>
+ <el-form-item label="浣嶇疆">
+ <el-input v-model="searchForm.location" placeholder="璇疯緭鍏ヤ綅缃�" clearable />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ <el-form-item class="search-actions">
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="primary" @click="handleAdd">
+ <el-icon><Plus /></el-icon>
+ 鏂板浼氳瀹�
+ </el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 浼氳瀹ゅ垪琛� -->
+ <el-card>
+ <el-table v-loading="loading" :data="meetingRoomList" border :height="tableHeight">
+ <el-table-column prop="name" label="浼氳瀹ゅ悕绉�" align="center" />
+ <el-table-column prop="location" label="浣嶇疆" align="center" />
+ <el-table-column prop="capacity" label="瀹圭撼浜烘暟" align="center" />
+ <el-table-column prop="equipment" label="璁惧閰嶇疆" align="center">
+ <template #default="scope">
+ <el-tag v-for="item in scope.row.equipment" :key="item" style="margin-right: 5px; margin-bottom: 5px;">
+ {{ item }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="status" label="鐘舵��" align="center" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
+ {{ scope.row.status === 1 ? '鍚敤' : '绂佺敤' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" align="center" width="200">
+ <template #default="scope">
+ <el-button type="primary" link @click="handleEdit(scope.row)">缂栬緫</el-button>
+ <el-button type="danger" link @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </el-card>
+
+ <!-- 娣诲姞/缂栬緫瀵硅瘽妗� -->
+ <el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" @close="cancel">
+ <el-form ref="meetingRoomFormRef" :model="meetingRoomForm" :rules="rules" label-width="100px">
+ <el-form-item label="浼氳瀹ゅ悕绉�" prop="name">
+ <el-input v-model="meetingRoomForm.name" placeholder="璇疯緭鍏ヤ細璁鍚嶇О" />
+ </el-form-item>
+ <el-form-item label="浣嶇疆" prop="location">
+ <el-input v-model="meetingRoomForm.location" placeholder="璇疯緭鍏ヤ細璁浣嶇疆" />
+ </el-form-item>
+ <el-form-item label="瀹圭撼浜烘暟" prop="capacity">
+ <el-input-number v-model="meetingRoomForm.capacity" :min="1" placeholder="璇疯緭鍏ュ绾充汉鏁�" />
+ </el-form-item>
+ <el-form-item label="璁惧閰嶇疆" prop="equipment">
+ <el-select v-model="meetingRoomForm.equipment" multiple placeholder="璇烽�夋嫨璁惧閰嶇疆" style="width: 100%">
+ <el-option
+ v-for="item in equipmentOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐘舵��" prop="status">
+ <el-radio-group v-model="meetingRoomForm.status">
+ <el-radio :label="1">鍚敤</el-radio>
+ <el-radio :label="0">绂佺敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="meetingRoomForm.remark" type="textarea" placeholder="璇疯緭鍏ュ娉�" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="cancel">鍙� 娑�</el-button>
+ <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus } from '@element-plus/icons-vue'
+import Pagination from '@/components/Pagination/index.vue'
+import {getMeetingRoomList,saveRoom,delRoom} from '@/api/collaborativeApproval/meeting.js'
+
+// 鏁版嵁鍒楄〃鍔犺浇鐘舵��
+const loading = ref(false)
+
+// 鎬绘潯鏁�
+const total = ref(0)
+
+// 琛ㄦ牸楂樺害锛堟牴鎹獥鍙i珮搴﹁嚜閫傚簲锛�
+const tableHeight = ref(window.innerHeight - 380)
+
+// 浼氳瀹ゅ垪琛ㄦ暟鎹�
+const meetingRoomList = ref([])
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ name: '',
+ location: ''
+})
+
+// 瀵硅瘽妗嗘爣棰�
+const dialogTitle = ref('')
+
+// 鏄惁鏄剧ず瀵硅瘽妗�
+const dialogVisible = ref(false)
+
+// 璁惧閰嶇疆閫夐」
+const equipmentOptions = ref([
+ { value: '鎶曞奖浠�', label: '鎶曞奖浠�' },
+ { value: '鐢佃', label: '鐢佃' },
+ { value: '闊冲搷', label: '闊冲搷' },
+ { value: '鐢佃瘽', label: '鐢佃瘽' },
+ { value: '瑙嗛浼氳绯荤粺', label: '瑙嗛浼氳绯荤粺' },
+ { value: '鐧芥澘', label: '鐧芥澘' },
+ { value: '鍐欏瓧鏉�', label: '鍐欏瓧鏉�' },
+ { value: '鏃犵嚎缃戠粶', label: '鏃犵嚎缃戠粶' }
+])
+
+// 琛ㄥ崟鏁版嵁
+const meetingRoomForm = reactive({
+ id: undefined,
+ name: '',
+ location: '',
+ capacity: 10,
+ equipment: [],
+ status: 1,
+ remark: ''
+})
+
+// 琛ㄥ崟鏍¢獙瑙勫垯
+const rules = {
+ name: [{ required: true, message: '浼氳瀹ゅ悕绉颁笉鑳戒负绌�', trigger: 'blur' }],
+ location: [{ required: true, message: '浣嶇疆涓嶈兘涓虹┖', trigger: 'blur' }],
+ capacity: [{ required: true, message: '瀹圭撼浜烘暟涓嶈兘涓虹┖', trigger: 'blur' }]
+}
+
+// 琛ㄥ崟寮曠敤
+const meetingRoomFormRef = ref(null)
+
+// 鏌ヨ鏁版嵁
+const getList = async () => {
+ loading.value = true
+
+ let resp = await getMeetingRoomList({...searchForm,...queryParams})
+ meetingRoomList.value = resp.data.records.map(it=>{
+ it.equipment = it.equipment.split(',')
+ return it;
+ })
+ total.value = resp.data.total
+ loading.value = false
+
+}
+
+// 鎼滅储鎸夐挳鎿嶄綔
+const handleSearch = () => {
+ queryParams.current = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储琛ㄥ崟
+const resetSearch = () => {
+ Object.assign(searchForm, {
+ name: '',
+ location: ''
+ })
+ handleSearch()
+}
+
+// 娣诲姞鎸夐挳鎿嶄綔
+const handleAdd = () => {
+ dialogTitle.value = '娣诲姞浼氳瀹�'
+ dialogVisible.value = true
+}
+
+// 淇敼鎸夐挳鎿嶄綔
+const handleEdit = (row) => {
+ dialogTitle.value = '淇敼浼氳瀹�'
+ Object.assign(meetingRoomForm, row)
+ dialogVisible.value = true
+}
+
+// 鍒犻櫎鎸夐挳鎿嶄綔
+const handleDelete = (row) => {
+ ElMessageBox.confirm(
+ `鏄惁纭鍒犻櫎浼氳瀹� "${row.name}"?`,
+ '璀﹀憡',
+ {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }
+ ).then(() => {
+ // 妯℃嫙鍒犻櫎鎿嶄綔
+ delRoom(row.id).then(resp=>{
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ getList()
+ })
+
+ }).catch(() => {})
+}
+
+// 鍙栨秷鎸夐挳
+const cancel = () => {
+ dialogVisible.value = false
+ reset()
+}
+
+// 琛ㄥ崟閲嶇疆
+const reset = () => {
+ Object.assign(meetingRoomForm, {
+ id: undefined,
+ name: '',
+ location: '',
+ capacity: 10,
+ equipment: [],
+ status: 1,
+ remark: ''
+ })
+ meetingRoomFormRef.value?.resetFields()
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+ meetingRoomFormRef.value?.validate((valid) => {
+ if (valid) {
+ // 妯℃嫙鎻愪氦鎿嶄綔
+
+ let formData = {... meetingRoomForm}
+ formData.equipment = formData.equipment.join(',')
+ saveRoom(formData).then(resp=>{
+ ElMessage.success('淇濆瓨鎴愬姛')
+ dialogVisible.value = false
+ getList()
+ })
+ }
+ })
+}
+
+// 瀵煎嚭
+const { proxy } = getCurrentInstance()
+const handleExport = () => {
+ proxy.download('/meeting/export', { ...searchForm }, '浼氳瀹よ缃�.xlsx')
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(() => {
+ getList()
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+}
+
+.search-form {
+ display: flex;
+ /* align-items: center; */
+}
+
+.search-actions {
+ margin-left: auto;
+}
+
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.page-header h2 {
+ margin: 0;
+ color: #303133;
+}
+
+.search-card {
+ margin-bottom: 20px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/notificationManagement/summary/index.vue b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
new file mode 100644
index 0000000..a00316d
--- /dev/null
+++ b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -0,0 +1,399 @@
+<template>
+ <div>
+
+ <el-form :model="searchForm" inline>
+ <el-form-item label="浼氳涓婚">
+ <el-input v-model="searchForm.title" placeholder="璇疯緭鍏ヤ細璁富棰�" clearable />
+ </el-form-item>
+ <el-form-item label="鐢宠浜�">
+ <el-input v-model="searchForm.applicant" placeholder="璇疯緭鍏ョ敵璇蜂汉" clearable />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+ <el-button @click="resetSearch">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 浼氳鍒楄〃 -->
+ <el-card>
+ <el-table v-loading="loading" :data="meetingList" border :height="tableHeight">
+ <el-table-column prop="title" label="浼氳涓婚" align="center" min-width="200" show-overflow-tooltip />
+ <el-table-column prop="applicant" label="鐢宠浜�" align="center" width="120" />
+ <el-table-column prop="host" label="涓绘寔浜�" align="center" width="120" />
+ <el-table-column prop="meetingTime" label="浼氳鏃堕棿" align="center" width="180">
+ <template #default="scope">
+ {{ formatDateTime(scope.row.meetingTime) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="location" label="浼氳鍦扮偣" align="center" width="150" />
+ <el-table-column prop="participants" label="鍙備細浜烘暟" align="center" width="100">
+ <template #default="scope">
+ {{ scope.row.participants.length }}浜�
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" align="center" width="200" fixed="right">
+ <template #default="scope">
+ <el-button type="primary" link @click="viewDetail(scope.row)">鏌ョ湅</el-button>
+ <el-button
+ type="primary"
+ link
+ @click="addMinutes(scope.row)"
+ >
+ 娣诲姞绾
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </el-card>
+
+ <!-- 浼氳璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ title="浼氳璇︽儏"
+ v-model="detailDialogVisible"
+ width="800px"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions label-width="100px" class="meeting-desc" :column="2" border>
+ <el-descriptions-item label="浼氳涓婚" label-class-name="nowrap-label">{{
+ currentMeeting.title
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�" label-class-name="nowrap-label">{{
+ currentMeeting.applicant
+ }}</el-descriptions-item>
+ <el-descriptions-item label="涓绘寔浜�" label-class-name="nowrap-label">{{
+ currentMeeting.host
+ }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2" label-class-name="nowrap-label">
+ {{ formatDateTime(currentMeeting.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="浼氳鍦扮偣" label-class-name="nowrap-label">{{
+ currentMeeting.location
+ }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟" label-class-name="nowrap-label">{{
+ currentMeeting.participants.length
+ }}浜�</el-descriptions-item>
+ <el-descriptions-item label="瀹℃壒鐘舵��" label-class-name="nowrap-label">
+ <el-tag :type="getStatusType(currentMeeting.status)">
+ {{ getStatusText(currentMeeting.status) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏃堕棿" label-class-name="nowrap-label">{{
+ currentMeeting.createTime
+ }}</el-descriptions-item>
+ <el-descriptions-item style="max-height: 400px" label="浼氳璇存槑" :span="2"
+ label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="content-section mt-20">
+ <h4>鍙備細浜哄憳</h4>
+ <div class="participants-list">
+ <el-tag
+ v-for="participant in currentMeeting.participants"
+ :key="participant.id"
+ style="margin-right: 10px; margin-bottom: 10px;"
+ >
+ {{ participant.name }}
+ </el-tag>
+ </div>
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="detailDialogVisible = false">鍏� 闂�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 娣诲姞浼氳绾瀵硅瘽妗� -->
+ <el-dialog
+ title="娣诲姞浼氳绾"
+ v-model="minutesDialogVisible"
+ width="80%"
+ @close="handleCloseMinutesDialog"
+ >
+ <div v-if="currentMeeting">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="浼氳涓婚">{{ currentMeeting.title }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�">{{ currentMeeting.applicant }}</el-descriptions-item>
+ <el-descriptions-item label="涓绘寔浜�">{{ currentMeeting.host }}</el-descriptions-item>
+ <el-descriptions-item label="浼氳鏃堕棿" :span="2">
+ {{ formatDateTime(currentMeeting.meetingTime) }}
+ </el-descriptions-item>
+ <el-descriptions-item label="浼氳鍦扮偣">{{ currentMeeting.location }}</el-descriptions-item>
+ <el-descriptions-item label="鍙備細浜烘暟">{{ currentMeeting.participants.length }}浜�</el-descriptions-item>
+ </el-descriptions>
+
+ <div class="content-section mt-20">
+ <h4>浼氳绾鍐呭</h4>
+ <div class="editor-container">
+ <Editor
+ v-model="minutesContent"
+ :min-height="400"
+ />
+ </div>
+ </div>
+ </div>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="minutesDialogVisible = false">鍙� 娑�</el-button>
+ <el-button type="primary" @click="submitMinutes">淇� 瀛�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import Pagination from '@/components/Pagination/index.vue'
+import Editor from '@/components/Editor/index.vue'
+import { getRoomEnum, getMeetingPublish ,getMeetingMinutesByMeetingId,saveMeetingMinutes} from '@/api/collaborativeApproval/meeting.js'
+import { getStaffOnJob } from "@/api/personnelManagement/onboarding.js"
+import dayjs from "dayjs"
+
+// 鏁版嵁鍒楄〃鍔犺浇鐘舵��
+const loading = ref(false)
+
+// 鎬绘潯鏁�
+const total = ref(0)
+
+// 琛ㄦ牸楂樺害锛堟牴鎹獥鍙i珮搴﹁嚜閫傚簲锛�
+const tableHeight = ref(window.innerHeight - 380)
+const roomEnum = ref([])
+const staffList = ref([])
+
+// 浼氳鍒楄〃鏁版嵁
+const meetingList = ref([])
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ current: 1,
+ size: 10
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ title: '',
+ applicant: '',
+ // status: '1' // 榛樿鍙樉绀哄凡閫氳繃瀹℃壒鐨勪細璁�
+})
+
+// 鏄惁鏄剧ず瀵硅瘽妗�
+const detailDialogVisible = ref(false)
+const minutesDialogVisible = ref(false)
+
+// 褰撳墠鏌ョ湅鐨勪細璁�
+const currentMeeting = ref(null)
+
+// 浼氳绾鍐呭
+const minutesContent = ref('')
+const minutesContentId = ref('')
+
+// 鏌ヨ鏁版嵁
+const getList = async () => {
+ loading.value = true
+ let resp = await getMeetingPublish({ ...searchForm, ...queryParams })
+ meetingList.value = resp.data.records.map(it => {
+ let room = roomEnum.value.find(room => it.roomId === room.id)
+ it.location = `${room.name}(${room.location})`
+ let staffs = JSON.parse(it.participants)
+ it.staffCount = staffs.size
+ it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
+ it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => {
+ return {
+ id: staff.id,
+ name: `${staff.staffName}(${staff.postJob})`
+ }
+ })
+
+ return it
+ })
+ total.value = resp.data.total
+ loading.value = false
+}
+
+// 鎼滅储鎸夐挳鎿嶄綔
+const handleSearch = () => {
+ queryParams.current = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储琛ㄥ崟
+const resetSearch = () => {
+ Object.assign(searchForm, {
+ title: '',
+ applicant: '',
+ // status: '1'
+ })
+ handleSearch()
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetail = (row) => {
+ currentMeeting.value = row
+ detailDialogVisible.value = true
+}
+
+// 娣诲姞浼氳绾
+const addMinutes = async (row) => {
+ let resp = await getMeetingMinutesByMeetingId(row.id)
+ currentMeeting.value = row
+ if (resp.data){
+ minutesContent.value = resp.data.content
+ minutesContentId.value = resp.data.id
+ }else {
+ minutesContent.value = `<h2>${row.title}浼氳绾</h2>
+<p><strong>浼氳鏃堕棿锛�</strong>${row.meetingTime}</p>
+<p><strong>浼氳鍦扮偣锛�</strong>${row.location}</p>
+<p><strong>涓绘寔浜猴細</strong>${row.host}</p>
+<p><strong>鍙備細浜哄憳锛�</strong></p>
+<ol>
+ ${row.participants.map(p => `<li>${p.name}</li>`).join('')}
+</ol>
+<p><strong>浼氳鍐呭锛�</strong></p>
+<ol>
+ <li>璁涓�锛�
+ <ul>
+ <li>璁ㄨ鍐呭锛�</li>
+ <li>鍐宠浜嬮」锛�</li>
+ </ul>
+ </li>
+ <li>璁浜岋細
+ <ul>
+ <li>璁ㄨ鍐呭锛�</li>
+ <li>鍐宠浜嬮」锛�</li>
+ </ul>
+ </li>
+</ol>
+<p><strong>澶囨敞锛�</strong></p>`
+ }
+
+ minutesDialogVisible.value = true
+}
+
+// 鎻愪氦浼氳绾
+const submitMinutes = () => {
+ if (!minutesContent.value) {
+ ElMessage.warning('璇疯緭鍏ヤ細璁邯瑕佸唴瀹�')
+ return
+ }
+ saveMeetingMinutes({
+ id: minutesContentId.value,
+ content: minutesContent.value,
+ meetingId: currentMeeting.value.id,
+ title: currentMeeting.value.title
+ }).then(resp=>{
+ console.log('浼氳绾鍐呭:', minutesContent.value)
+ ElMessage.success('浼氳绾淇濆瓨鎴愬姛')
+ minutesDialogVisible.value = false
+ })
+
+}
+
+// 鍏抽棴浼氳绾瀵硅瘽妗�
+const handleCloseMinutesDialog = () => {
+ minutesContent.value = ''
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const statusMap = {
+ '0': 'info', // 寰呭鎵�
+ '1': 'success', // 宸查�氳繃
+ '2': 'warning', // 鏈�氳繃
+ '3': 'danger' // 鍙栨秷
+ }
+ return statusMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const statusMap = {
+ '0': '寰呭鎵�',
+ '1': '宸查�氳繃',
+ '2': '鏈�氳繃',
+ '3': '宸插彇娑�'
+ }
+ return statusMap[status] || '鏈煡'
+}
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (dateTime) => {
+ if (!dateTime) return ''
+ return dateTime.replace(' ', '\n')
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(async () => {
+ const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()])
+ roomEnum.value = resp1.data
+ staffList.value = resp2.data
+
+ await getList()
+})
+</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;
+}
+
+.search-card {
+ margin-bottom: 20px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+.content-section h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+.participants-list {
+ min-height: 40px;
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+}
+
+.nowrap-label {
+ white-space: nowrap !important;
+}
+
+.editor-container {
+ border: 1px solid #dcdfe6;
+ border-radius: 4px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/officeSupplies/index.vue b/src/views/collaborativeApproval/officeSupplies/index.vue
new file mode 100644
index 0000000..a2d1c6d
--- /dev/null
+++ b/src/views/collaborativeApproval/officeSupplies/index.vue
@@ -0,0 +1,512 @@
+<template>
+ <div class="app-container">
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>鍔炲叕鐗╄祫鐢宠绠$悊</span>
+ <el-button type="primary" @click="openShow()">
+ <el-icon><Plus /></el-icon>
+ 鏂板缓鐢宠
+ </el-button>
+ </div>
+ </template>
+
+ <!-- 鎼滅储鍖哄煙 -->
+ <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch">
+ <el-form-item label="鐢宠缂栧彿" prop="code">
+ <el-input
+ v-model="queryParams.code"
+ placeholder="璇疯緭鍏ョ敵璇风紪鍙�"
+ clearable
+ style="width: 200px"
+ @keyup.enter="handleQuery"
+ />
+ </el-form-item>
+ <el-form-item label="鐢宠浜�" prop="applicant">
+ <el-input
+ v-model="queryParams.applicant"
+ 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-option label="寰呭鎵�" value="1" />
+ <el-option label="宸查�氳繃" value="3" />
+ <el-option label="宸叉嫆缁�" value="2" />
+ <el-option label="宸插彂鏀�" value="4" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">
+ <el-icon><Search /></el-icon>
+ 鎼滅储
+ </el-button>
+ <el-button @click="resetQuery">
+ <el-icon><Refresh /></el-icon>
+ 閲嶇疆
+ </el-button>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleExport">
+ <el-icon><Download /></el-icon>
+ 瀵煎嚭
+ </el-button>
+ </el-form-item>
+ </el-form>
+
+ <!-- 琛ㄦ牸鍖哄煙 -->
+ <el-table
+ v-loading="loading"
+ :data="officeList"
+ @selection-change="handleSelectionChange"
+ style="width: 100%"
+ >
+ <el-table-column type="selection" width="55" align="center" />
+ <el-table-column label="鐢宠缂栧彿" align="center" prop="code" width="180" />
+ <el-table-column label="鐢宠浜�" align="center" prop="applicant" width="120" />
+ <el-table-column label="閮ㄩ棬" align="center" prop="dept" width="120" />
+ <el-table-column label="鐗╄祫绫诲瀷" align="center" prop="materialType" width="120">
+ <template #default="scope">
+ <el-tag v-if="scope.row.materialType === 1" type="info">鍏朵粬</el-tag>
+ <el-tag v-if="scope.row.materialType === 2" type="success">娓呮磥鐢ㄥ搧</el-tag>
+ <el-tag v-if="scope.row.materialType === 3" type="warning">鐢靛瓙璁惧</el-tag>
+ <el-tag v-if="scope.row.materialType === 4" type="danger">鍔炲叕鐢ㄥ搧</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鐢宠鏁伴噺" align="center" prop="applyNum" width="100" />
+ <el-table-column label="鐢宠鍘熷洜" align="center" prop="reason" min-width="200" show-overflow-tooltip />
+ <el-table-column label="鐢宠鐘舵��" align="center" prop="status" width="100">
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鐢宠鏃堕棿" align="center" prop="applyTime" width="180" />
+ <el-table-column label="瀹℃壒浜�" align="center" prop="approval" width="120" />
+ <el-table-column label="瀹℃壒鏃堕棿" align="center" prop="approvalTime" width="180" />
+ <el-table-column label="鍙戞斁鏃堕棿" align="center" prop="issueTime" width="180" />
+ <el-table-column label="鎿嶄綔" align="center" fixed="right" class-name="small-padding fixed-width" width="200">
+ <template #default="scope">
+ <el-button
+ v-if="scope.row.status === 1"
+ type="primary"
+ link
+ @click="handleApprove(scope.row)"
+ >
+ 瀹℃壒
+ </el-button>
+ <el-button
+ v-if="scope.row.status === 3"
+ type="success"
+ link
+ @click="handleIssue(scope.row)"
+ >
+ 鍙戞斁
+ </el-button>
+ <el-button
+ type="info"
+ link
+ @click="handleDetail(scope.row)"
+ >
+ 璇︽儏
+ </el-button>
+ <el-button
+ v-if="scope.row.status === 2"
+ type="danger"
+ link
+ @click="handleDelete(scope.row)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 鍒嗛〉 -->
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ v-model:page="queryParams.current"
+ v-model:limit="queryParams.size"
+ @pagination="getList"
+ />
+ </el-card>
+
+ <!-- 鐢宠瀵硅瘽妗� -->
+ <el-dialog
+ v-model="showApplyDialog"
+ title="鍔炲叕鐗╄祫鐢宠"
+ width="600px"
+ append-to-body
+ >
+ <el-form ref="applyFormRef" :model="applyForm" :rules="applyRules" label-width="100px">
+ <el-form-item label="鐢宠浜�" prop="applicant">
+ <el-input v-model="applyForm.applicant" placeholder="璇疯緭鍏ョ敵璇蜂汉鍚嶇О" />
+ </el-form-item>
+ <el-form-item label="閮ㄩ棬" prop="dept">
+ <el-input v-model="applyForm.dept" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" />
+ </el-form-item>
+ <el-form-item label="鐗╄祫绫诲瀷" prop="materialType">
+ <el-select v-model="applyForm.materialType" placeholder="璇烽�夋嫨鐗╄祫绫诲瀷" style="width: 100%">
+ <el-option label="鍔炲叕鐢ㄥ搧" value="4" />
+ <el-option label="鐢靛瓙璁惧" value="3" />
+ <el-option label="娓呮磥鐢ㄥ搧" value="2" />
+ <el-option label="鍏朵粬" value="1" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍏蜂綋鐗╁搧" prop="itemName">
+ <el-input v-model="applyForm.itemName" placeholder="璇疯緭鍏ュ叿浣撶墿鍝佸悕绉�" />
+ </el-form-item>
+ <el-form-item label="鐢宠鏁伴噺" prop="applyNum">
+ <el-input-number v-model="applyForm.applyNum" :min="1" :max="999" style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="鐢宠鍘熷洜" prop="reason">
+ <el-input
+ v-model="applyForm.reason"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ョ敵璇峰師鍥�"
+ />
+ </el-form-item>
+ <el-form-item label="绱ф�ョ▼搴�" prop="urgency">
+ <el-radio-group v-model="applyForm.urgency">
+ <el-radio label="1">鏅��</el-radio>
+ <el-radio label="2">绱ф��</el-radio>
+ <el-radio label="3">闈炲父绱ф��</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="showApplyDialog = false">鍙� 娑�</el-button>
+ <el-button type="primary" @click="submitApply">纭� 瀹�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 瀹℃壒瀵硅瘽妗� -->
+ <el-dialog
+ v-model="showApproveDialog"
+ title="瀹℃壒鐢宠"
+ width="500px"
+ append-to-body
+ >
+ <el-form ref="approveFormRef" :model="approveForm" :rules="approveRules" label-width="100px">
+ <el-form-item label="瀹℃壒缁撴灉" prop="approveResult">
+ <el-radio-group v-model="approveForm.approveResult">
+ <el-radio label="3">閫氳繃</el-radio>
+ <el-radio label="2">鎷掔粷</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="瀹℃壒鎰忚" prop="approvalOpinions">
+ <el-input
+ v-model="approveForm.approvalOpinions"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ鎵规剰瑙�"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="showApproveDialog = false">鍙� 娑�</el-button>
+ <el-button type="primary" @click="submitApprove">纭� 瀹�</el-button>
+ </div>
+ </template>
+ </el-dialog>
+
+ <!-- 璇︽儏瀵硅瘽妗� -->
+ <el-dialog
+ v-model="showDetailDialog"
+ title="鐢宠璇︽儏"
+ width="700px"
+ append-to-body
+ >
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鐢宠缂栧彿">{{ currentDetail.code }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�">{{ currentDetail.applicant }}</el-descriptions-item>
+ <el-descriptions-item label="閮ㄩ棬">{{ currentDetail.dept }}</el-descriptions-item>
+ <el-descriptions-item label="鐗╄祫绫诲瀷">{{ currentDetail.materialType }}</el-descriptions-item>
+ <el-descriptions-item label="鍏蜂綋鐗╁搧">{{ currentDetail.itemName }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏁伴噺">{{ currentDetail.applyNum }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠鍘熷洜" :span="2">{{ currentDetail.reason }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠鐘舵��">
+ <el-tag :type="getStatusType(currentDetail.status)">
+ {{ getStatusText(currentDetail.status) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏃堕棿">{{ currentDetail.applyTime }}</el-descriptions-item>
+ <el-descriptions-item label="瀹℃壒浜�">{{ currentDetail.approval || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="瀹℃壒鏃堕棿">{{ currentDetail.approvalTime || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="瀹℃壒鎰忚" :span="2">{{ currentDetail.approvalOpinions || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戞斁鏃堕棿">{{ currentDetail.issueTime || '-' }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戞斁浜�">{{ currentDetail.issueUser || '-' }}</el-descriptions-item>
+ </el-descriptions>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {listPage,add,update,deleteOff} from "@/api/collaborativeApproval/officeSupplies.js"
+import {ref, reactive, onMounted, getCurrentInstance} from 'vue'
+import Cookies from 'js-cookie'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus, Search, Refresh, Download, Check } from '@element-plus/icons-vue'
+
+// 鍝嶅簲寮忔暟鎹�
+const loading = ref(false)
+const showSearch = ref(true)
+const showApplyDialog = ref(false)
+const showApproveDialog = ref(false)
+const showDetailDialog = ref(false)
+const multipleSelection = ref([])
+const officeList = ref([])
+const total = ref(0)
+const suppliesList = ref([])
+const currentDetail = ref({})
+
+// 鏌ヨ鍙傛暟
+const queryParams = reactive({
+ current: 1,
+ size: 10,
+ code: '',
+ applicant: '',
+ status: ''
+})
+
+// 鐢宠琛ㄥ崟
+const applyForm = reactive({
+ applicant: '',
+ dept: '',
+ materialType: '',
+ itemName: '',
+ applyNum: 1,
+ reason: '',
+ urgency: '1'
+})
+
+// 瀹℃壒琛ㄥ崟
+const approveForm = reactive({
+ approveResult: '3',
+ approvalOpinions: ''
+})
+
+// 琛ㄥ崟鏍¢獙瑙勫垯
+const applyRules = {
+ applicant: [{ required: true, message: '璇烽�夋嫨鐗╄祫绫诲瀷', trigger: 'blur' }],
+ dept: [{ required: true, message: '璇烽�夋嫨鐗╄祫绫诲瀷', trigger: 'blur' }],
+ materialType: [{ required: true, message: '璇烽�夋嫨鐗╄祫绫诲瀷', trigger: 'change' }],
+ itemName: [{ required: true, message: '璇疯緭鍏ュ叿浣撶墿鍝佸悕绉�', trigger: 'blur' }],
+ applyNum: [{ required: true, message: '璇疯緭鍏ョ敵璇锋暟閲�', trigger: 'blur' }],
+ reason: [{ required: true, message: '璇疯緭鍏ョ敵璇峰師鍥�', trigger: 'blur' }]
+}
+
+const approveRules = {
+ approveResult: [{ required: true, message: '璇烽�夋嫨瀹℃壒缁撴灉', trigger: 'change' }],
+ approvalOpinions: [{ required: true, message: '璇疯緭鍏ュ鎵规剰瑙�', trigger: 'blur' }]
+}
+
+const openShow = () => {
+ showApplyDialog.value = true
+ resetApplyForm()
+}
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = () => {
+ loading.value = true
+ listPage(queryParams).then(res => {
+ total.value = res.data.total
+ loading.value = false
+ officeList.value = res.data.records
+ })
+}
+
+// 鏌ヨ
+const handleQuery = () => {
+ queryParams.current = 1
+ getList()
+}
+
+// 閲嶇疆鏌ヨ
+const resetQuery = () => {
+ queryParams.code = ''
+ queryParams.applicant = ''
+ queryParams.status = ''
+ handleQuery()
+}
+
+// 澶氶��
+const handleSelectionChange = (selection) => {
+ multipleSelection.value = selection
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const statusMap = {
+ 1: 'warning',
+ 3: 'success',
+ 2: 'danger',
+ 4: 'info'
+ }
+ return statusMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const statusMap = {
+ 1: '寰呭鎵�',
+ 3: '宸查�氳繃',
+ 2: '宸叉嫆缁�',
+ 4: '宸插彂鏀�'
+ }
+ return statusMap[status] || status
+}
+
+// 鎻愪氦鐢宠
+const submitApply = () => {
+ add(applyForm).then(() => {
+ ElMessage.success('鐢宠鎴愬姛')
+ getList()
+ showApplyDialog.value = false
+ resetApplyForm()
+ })
+
+
+
+}
+
+//閲嶇疆琛ㄥ崟
+const resetApplyForm = () => {
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(applyForm, {
+ applicant: '',
+ dept: '',
+ materialType: '',
+ itemName: '',
+ applyNum: 1,
+ reason: '',
+ urgency: '1'
+ })
+}
+
+// 瀹℃壒
+const handleApprove = (row) => {
+ currentDetail.value = row
+ showApproveDialog.value = true
+}
+
+const 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')
+ const sends = String(date.getSeconds()).padStart(2, '0')
+ return `${year}-${month}-${day} ${hours}:${minutes}:${sends}`
+}
+
+// 鎻愪氦瀹℃壒
+const submitApprove = () => {
+ currentDetail.value.status = approveForm.approveResult
+ // 浠巆ookie涓幏鍙栧綋鍓嶇櫥褰曠敤鎴峰悕绉�
+ currentDetail.value.approval = Cookies.get('username')
+ currentDetail.value.approvalTime = formatDate(new Date())
+ currentDetail.value.approvalOpinions = approveForm.approvalOpinions
+ update(currentDetail.value).then((res) => {
+ if(res.code === 200){
+ showApproveDialog.value = false
+ ElMessage.success('瀹℃壒瀹屾垚')
+ getList()
+
+ // 閲嶇疆琛ㄥ崟
+ Object.assign(approveForm, {
+ approveResult: '3',
+ approvalOpinions: ''
+ })
+ }
+ })
+
+}
+
+// 鍙戞斁
+const handleIssue = (row) => {
+ row.status = 4
+ row.issueTime = formatDate(new Date())
+ row.issueUser = Cookies.get('username')
+ update(row).then((res) =>{
+ if(res.code === 200){
+ ElMessage.success('鍙戞斁瀹屾垚')
+ getList()
+ }
+ })
+}
+
+// 鏌ョ湅璇︽儏
+const handleDetail = (row) => {
+ currentDetail.value = row
+ showDetailDialog.value = true
+}
+
+// 鍒犻櫎
+const handleDelete = (row) => {
+ ElMessageBox.confirm('纭鍒犻櫎璇ョ敵璇峰悧锛�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ let ids = [row.id]
+ deleteOff(ids).then((res) =>{
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ getList()
+ })
+ })
+}
+const { proxy } = getCurrentInstance();
+// 瀵煎嚭
+const handleExport = () => {
+ ElMessageBox.confirm("鎵�鏈夌殑鍐呭灏嗚瀵煎嚭锛屾槸鍚︾‘璁ゅ鍑猴紵", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/officeSupplies/export", {}, "鍔炲叕鐗╄祫.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+}
+
+// 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+onMounted(() => {
+ getList()
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.mb8 {
+ margin-bottom: 8px;
+}
+
+.dialog-footer {
+ text-align: right;
+}
+
+:deep(.el-descriptions__label) {
+ width: 120px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/planTemplate/index.vue b/src/views/collaborativeApproval/planTemplate/index.vue
new file mode 100644
index 0000000..0af6d8b
--- /dev/null
+++ b/src/views/collaborativeApproval/planTemplate/index.vue
@@ -0,0 +1,867 @@
+<template>
+ <div class="app-container">
+ <!-- 椤堕儴鎿嶄綔鏍� -->
+ <div class="header-actions">
+ <div class="left-actions">
+ <el-select v-model="currentLevel" placeholder="閫夋嫨璁″垝绾у埆" style="width: 150px" @change="handleLevelChange">
+ <el-option label="涓汉璁″垝" value="personal" />
+ <el-option label="灏忕粍璁″垝" value="group" />
+ <el-option label="閮ㄩ棬璁″垝" value="department" />
+ <el-option label="鍏徃璁″垝" value="company" />
+ </el-select>
+ <el-select v-model="currentPeriod" placeholder="閫夋嫨鏃堕棿鍛ㄦ湡" style="width: 120px; margin-left: 10px" @change="handlePeriodChange">
+ <el-option label="鍛ㄨ鍒�" value="week" />
+ <el-option label="鏈堣鍒�" value="month" />
+ <el-option label="骞磋鍒�" value="year" />
+ </el-select>
+ <el-date-picker
+ v-model="currentDate"
+ :type="datePickerType"
+ placeholder="閫夋嫨鏃ユ湡"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ style="width: 180px; margin-left: 10px"
+ @change="handleDateChange"
+ />
+ </div>
+ <div class="right-actions">
+ <el-button type="primary" @click="handleAddPlan">鏂板璁″垝</el-button>
+ <el-button @click="handleExport">瀵煎嚭璁″垝</el-button>
+ <!-- <el-button @click="handleShare">鍏变韩璁″垝@</el-button> -->
+ </div>
+ </div>
+
+ <!-- 璁″垝姒傝鍗$墖 -->
+ <div class="overview-cards">
+ <el-row :gutter="20">
+ <el-col :span="6">
+ <el-card class="overview-card">
+ <div class="card-content">
+ <div class="card-icon personal">
+ <el-icon><User /></el-icon>
+ </div>
+ <div class="card-info">
+ <div class="card-title">涓汉璁″垝</div>
+ <div class="card-number">{{ overviewData.personal.total }}</div>
+ <div class="card-progress">
+ <el-progress :percentage="overviewData.personal.completion" :stroke-width="6" />
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="overview-card">
+ <div class="card-content">
+ <div class="card-icon group">
+ <el-icon><UserFilled /></el-icon>
+ </div>
+ <div class="card-info">
+ <div class="card-title">灏忕粍璁″垝</div>
+ <div class="card-number">{{ overviewData.group.total }}</div>
+ <div class="card-progress">
+ <el-progress :percentage="overviewData.group.completion" :stroke-width="6" />
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="overview-card">
+ <div class="card-content">
+ <div class="card-icon department">
+ <el-icon><OfficeBuilding /></el-icon>
+ </div>
+ <div class="card-info">
+ <div class="card-title">閮ㄩ棬璁″垝</div>
+ <div class="card-number">{{ overviewData.department.total }}</div>
+ <div class="card-progress">
+ <el-progress :percentage="overviewData.department.completion" :stroke-width="6" />
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ <el-col :span="6">
+ <el-card class="overview-card">
+ <div class="card-content">
+ <div class="card-icon company">
+ <el-icon><House /></el-icon>
+ </div>
+ <div class="card-info">
+ <div class="card-title">鍏徃璁″垝</div>
+ <div class="card-number">{{ overviewData.company.total }}</div>
+ <div class="card-progress">
+ <el-progress :percentage="overviewData.company.completion" :stroke-width="6" />
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 璁″垝鍒楄〃 -->
+ <div class="plan-content">
+ <el-card>
+ <template #header>
+ <div class="card-header">
+ <span>{{ getCurrentLevelText() }} - {{ getCurrentPeriodText() }}</span>
+ <div>
+ <el-button size="small" @click="handleRefresh">鍒锋柊</el-button>
+ <!-- <el-button size="small" @click="handleFilter">绛涢�堾</el-button> -->
+ </div>
+ </div>
+ </template>
+
+ <div class="plan-list">
+ <div v-for="plan in planList" :key="plan.id" class="plan-item">
+ <div class="plan-header">
+ <div class="plan-title">
+ <el-tag :type="getPriorityType(plan.priority)" size="small">{{ getPriorityText(plan.priority) }}</el-tag>
+ <span class="title-text">{{ plan.title }}</span>
+ </div>
+ <div class="plan-actions">
+ <el-button size="small" @click="handleEditPlan(plan)">缂栬緫</el-button>
+ <el-button size="small" @click="handleViewDetail(plan)">璇︽儏</el-button>
+ <el-dropdown @command="(command) => handleMoreAction(plan, command)">
+ <el-button size="small">
+ 鏇村<el-icon class="el-icon--right"><ArrowDown /></el-icon>
+ </el-button>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <!-- <el-dropdown-item command="share">鍏变韩@</el-dropdown-item> -->
+ <el-dropdown-item command="copy">澶嶅埗</el-dropdown-item>
+ <el-dropdown-item command="delete" divided>鍒犻櫎</el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ </div>
+ </div>
+
+ <div class="plan-content">
+ <div class="plan-description">{{ plan.description }}</div>
+ <div class="plan-meta">
+ <div class="meta-item">
+ <el-icon><Calendar /></el-icon>
+ <span>{{ plan.startDate }} - {{ plan.endDate }}</span>
+ </div>
+ <div class="meta-item">
+ <el-icon><User /></el-icon>
+ <span>{{ plan.assignee }}</span>
+ </div>
+ <div class="meta-item">
+ <el-icon><Clock /></el-icon>
+ <span>杩涘害: {{ plan.progress }}%</span>
+ </div>
+ <div class="meta-item">
+ <el-icon><Flag /></el-icon>
+ <span>{{ getStatusText(plan.status) }}</span>
+ </div>
+ </div>
+
+ <div class="plan-progress">
+ <el-progress
+ :percentage="plan.progress"
+ :color="getProgressColor(plan.progress)"
+ :stroke-width="8"
+ />
+ </div>
+
+ <div class="plan-tags">
+ <el-tag v-for="tag in plan.tags" :key="tag" size="small" style="margin-right: 5px">
+ {{ tag }}
+ </el-tag>
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-card>
+ </div>
+
+ <!-- 鏂板/缂栬緫璁″垝瀵硅瘽妗� -->
+ <el-dialog
+ v-model="planDialogVisible"
+ :title="operationType === 'add' ? '鍙戝竷璁″垝' : '缂栬緫璁″垝'"
+ width="600px"
+ @close="handleDialogClose"
+ >
+ <el-form :model="planForm" :rules="planRules" ref="planFormRef" label-width="100px">
+ <el-form-item label="璁″垝鏍囬" prop="title">
+ <el-input v-model="planForm.title" placeholder="璇疯緭鍏ヨ鍒掓爣棰�" />
+ </el-form-item>
+ <el-form-item label="璁″垝鎻忚堪" prop="description">
+ <el-input
+ v-model="planForm.description"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ヨ鍒掓弿杩�"
+ />
+ </el-form-item>
+ <el-form-item label="璁″垝绾у埆" prop="level">
+ <el-select v-model="planForm.level" placeholder="閫夋嫨璁″垝绾у埆" style="width: 100%">
+ <el-option label="涓汉璁″垝" value="personal" />
+ <el-option label="灏忕粍璁″垝" value="group" />
+ <el-option label="閮ㄩ棬璁″垝" value="department" />
+ <el-option label="鍏徃璁″垝" value="company" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鏃堕棿鍛ㄦ湡" prop="period">
+ <el-select v-model="planForm.period" placeholder="閫夋嫨鏃堕棿鍛ㄦ湡" style="width: 100%">
+ <el-option label="鍛ㄨ鍒�" value="week" />
+ <el-option label="鏈堣鍒�" value="month" />
+ <el-option label="骞磋鍒�" value="year" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="寮�濮嬫椂闂�" prop="startDate">
+ <el-date-picker
+ v-model="planForm.startDate"
+ type="date"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ placeholder="閫夋嫨寮�濮嬫椂闂�"
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item label="缁撴潫鏃堕棿" prop="endDate">
+ <el-date-picker
+ v-model="planForm.endDate"
+ type="date"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ placeholder="閫夋嫨缁撴潫鏃堕棿"
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item label="璐熻矗浜�" prop="assignee">
+ <el-input v-model="planForm.assignee" placeholder="璇疯緭鍏ヨ礋璐d汉" />
+ </el-form-item>
+ <el-form-item label="浼樺厛绾�" prop="priority">
+ <el-select v-model="planForm.priority" placeholder="閫夋嫨浼樺厛绾�" style="width: 100%">
+ <el-option label="楂�" value="high" />
+ <el-option label="涓�" value="medium" />
+ <el-option label="浣�" value="low" />
+ </el-select>
+ </el-form-item>
+ <!-- <el-form-item label="鏍囩">
+ <el-input v-model="planForm.tags" placeholder="璇疯緭鍏ユ爣绛撅紝鐢ㄩ�楀彿鍒嗛殧" />
+ </el-form-item> -->
+ <el-form-item label="鏍囩" prop="tags">
+ <!-- <el-checkbox-group v-model="planForm.tags">
+ <el-checkbox label="all"></el-checkbox>
+ <el-checkbox label="manager">绠$悊灞�</el-checkbox>
+ <el-checkbox label="hr">浜轰簨閮ㄩ棬</el-checkbox>
+ <el-checkbox label="finance">璐㈠姟閮ㄩ棬</el-checkbox>
+ <el-checkbox label="tech">鎶�鏈儴闂�</el-checkbox>
+ </el-checkbox-group> -->
+ <el-select
+ v-model="planForm.tags"
+ 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="status">
+ <el-select v-model="planForm.status" placeholder="閫夋嫨鐘舵��" style="width: 100%">
+ <el-option label="鏈紑濮�" value="not_started" />
+ <el-option label="杩涜涓�" value="in_progress" />
+ <el-option label="宸插畬鎴�" value="completed" />
+ <el-option label="宸叉殏鍋�" value="paused" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="杩涘害" prop="progress">
+ <el-input-number
+ v-model="planForm.progress"
+ min="0"
+ max="100"
+ step="1"
+ placeholder="璇疯緭鍏ヨ繘搴�"
+ style="width: 100%"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="planDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="handleSavePlan">淇濆瓨</el-button>
+ </span>
+ </template>
+ </el-dialog>
+ <!-- 璁″垝璇︽儏瀵硅瘽妗� -->
+ <el-dialog v-model="showPlanDetailDialog" title="璁″垝璇︽儏" width="700px">
+ <div v-if="currentPlanDetail" class="mb10">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="璁″垝鏍囬">{{ currentPlanDetail.title }}</el-descriptions-item>
+ <el-descriptions-item label="璁″垝鎻忚堪">{{ currentPlanDetail.description }}</el-descriptions-item>
+ <el-descriptions-item label="璁″垝绾у埆">{{ getCurrentLevelText(currentPlanDetail.level) }}</el-descriptions-item>
+ <el-descriptions-item label="鏃堕棿鍛ㄦ湡">{{ getCurrentPeriodText(currentPlanDetail.period) }}</el-descriptions-item>
+ <el-descriptions-item label="寮�濮嬫椂闂�">{{ currentPlanDetail.startDate }}</el-descriptions-item>
+ <el-descriptions-item label="缁撴潫鏃堕棿">{{ currentPlanDetail.endDate }}</el-descriptions-item>
+ <el-descriptions-item label="璐熻矗浜�">{{ currentPlanDetail.assignee }}</el-descriptions-item>
+ <el-descriptions-item label="浼樺厛绾�">{{ getPriorityText(currentPlanDetail.priority) }}</el-descriptions-item>
+ <el-descriptions-item label="鏍囩">{{ currentPlanDetail.tags.join(', ') }}</el-descriptions-item>
+ <el-descriptions-item label="鐘舵��">{{ getStatusText(currentPlanDetail.status) }}</el-descriptions-item>
+ <el-descriptions-item label="杩涘害">{{ currentPlanDetail.progress }}%</el-descriptions-item>
+ </el-descriptions>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+const { proxy } = getCurrentInstance();
+import {
+ User,
+ UserFilled,
+ OfficeBuilding,
+ House,
+ Calendar,
+ Clock,
+ Flag,
+ ArrowDown
+} from '@element-plus/icons-vue'
+import { listDutyPlan, addDutyPlan, updateDutyPlan, delDutyPlan,NumDutyPlan,exportDutyPlan } from '@/api/collaborativeApproval/planTemplate.js'
+
+// 鍝嶅簲寮忔暟鎹�
+const operationType = ref('add')
+const currentLevel = ref('personal')
+const currentPeriod = ref('week')
+const currentDate = ref(new Date())
+const planDialogVisible = ref(false)
+const dialogTitle = ref('鏂板璁″垝')
+const planFormRef = ref()
+const showPlanDetailDialog = ref(false)
+const currentPlanDetail = ref(null)
+
+// 琛ㄥ崟鏁版嵁
+const planForm = reactive({
+ id: '',
+ title: '',
+ description: '',
+ level: 'personal',
+ period: 'week',
+ startDate: '',
+ endDate: '',
+ assignee: '',
+ priority: 'medium',
+ tags: [],
+ status: '',
+ progress: 0
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const planRules = {
+ title: [{ required: true, message: '璇疯緭鍏ヨ鍒掓爣棰�', trigger: 'blur' }],
+ description: [{ required: true, message: '璇疯緭鍏ヨ鍒掓弿杩�', trigger: 'blur' }],
+ level: [{ required: true, message: '璇烽�夋嫨璁″垝绾у埆', trigger: 'change' }],
+ period: [{ required: true, message: '璇烽�夋嫨鏃堕棿鍛ㄦ湡', trigger: 'change' }],
+ startDate: [{ required: true, message: '璇烽�夋嫨寮�濮嬫椂闂�', trigger: 'change' }],
+ endDate: [{ required: true, message: '璇烽�夋嫨缁撴潫鏃堕棿', trigger: 'change' }],
+ assignee: [{ required: true, message: '璇疯緭鍏ヨ礋璐d汉', trigger: 'blur' }],
+ priority: [{ required: true, message: '璇烽�夋嫨浼樺厛绾�', trigger: 'change' }]
+}
+const departments = ["浜у搧", "鍒嗘瀽", "璋冪爺",'鎶�鏈�', '鏋舵瀯', '璁捐','甯傚満', '鎺ㄥ箍', '钀ラ攢'];
+// 姒傝鏁版嵁
+const overviewData = reactive({
+ personal: { total: 0, completion: 0 },
+ group: { total: 0, completion: 0 },
+ department: { total: 0, completion: 0 },
+ company: { total: 0, completion: 0 }
+})
+
+// 璁″垝鍒楄〃鏁版嵁
+const planList = ref([])
+
+// 璁$畻灞炴��
+const datePickerType = computed(() => {
+ switch (currentPeriod.value) {
+ case 'week':
+ return 'week'
+ case 'month':
+ return 'month'
+ case 'year':
+ return 'year'
+ default:
+ return 'date'
+ }
+})
+
+// 鏂规硶
+const handleLevelChange = (value) => {
+ console.log('璁″垝绾у埆鍙樻洿:', value)
+ getPlanList()
+ // 杩欓噷鍙互鏍规嵁绾у埆绛涢�夋暟鎹�
+}
+
+const handlePeriodChange = (value) => {
+ console.log('鏃堕棿鍛ㄦ湡鍙樻洿:', value)
+ getPlanList()
+ // 杩欓噷鍙互鏍规嵁鍛ㄦ湡绛涢�夋暟鎹�
+}
+
+const handleDateChange = (value) => {
+ console.log('鏃ユ湡鍙樻洿:', value)
+ getPlanList()
+ // 杩欓噷鍙互鏍规嵁鏃ユ湡绛涢�夋暟鎹�
+}
+
+const handleAddPlan = () => {
+ operationType.value = 'add'
+ dialogTitle.value = '鏂板璁″垝'
+ planDialogVisible.value = true
+ // 閲嶇疆琛ㄥ崟
+ Object.keys(planForm).forEach(key => {
+ planForm[key] = ''
+ })
+ planForm.level = 'personal'
+ planForm.period = 'week'
+ planForm.priority = 'medium'
+ planForm.status = 'not_started'
+ planForm.progress = 0
+}
+
+const handleEditPlan = (plan) => {
+ operationType.value = 'edit'
+ dialogTitle.value = '缂栬緫璁″垝'
+ planDialogVisible.value = true
+ Object.assign(planForm, plan)
+ // // 濉厖琛ㄥ崟鏁版嵁
+ // Object.keys(planForm).forEach(key => {
+ // if (key === 'tags') {
+ // planForm[key] = plan[key].join(', ')
+ // } else {
+ // planForm[key] = plan[key]
+ // }
+ // })
+}
+
+const handleViewDetail = (plan) => {
+ currentPlanDetail.value = plan
+ showPlanDetailDialog.value = true
+ // ElMessage.info(`鏌ョ湅璁″垝璇︽儏: ${plan.title}`)
+}
+
+const handleMoreAction = async(plan,command) => {
+ let ids = [];
+ ids.push(plan.id);
+ console.log("ids",ids)
+ switch (command) {
+ case 'share':
+ ElMessage.success('璁″垝宸插叡浜�')
+ break
+ case 'copy':
+ const knowledgeText = `
+ 璁″垝鏍囬锛�${plan.title}
+ 璁″垝鎻忚堪锛�${plan.description}
+ 璁″垝绾у埆锛�${getCurrentLevelText(plan.level)}
+ 鏃堕棿鍛ㄦ湡锛�${getCurrentPeriodText(plan.period)}
+ 寮�濮嬫椂闂达細${plan.startDate}
+ 缁撴潫鏃堕棿锛�${plan.endDate}
+ 璐熻矗浜猴細${plan.assignee}
+ 浼樺厛绾э細${getPriorityText(plan.priority)}
+ 鏍囩锛�${plan.tags.join(', ')}
+ 鐘舵�侊細${getStatusText(plan.status)}
+ 杩涘害锛�${plan.progress}%
+ `.trim();
+
+ // 澶嶅埗鍒板壀璐存澘
+ navigator.clipboard.writeText(knowledgeText).then(() => {
+ ElMessage.success("鐭ヨ瘑鍐呭宸插鍒跺埌鍓创鏉�");
+ }).catch(() => {
+ ElMessage.error("澶嶅埗澶辫触锛岃鎵嬪姩澶嶅埗");
+ });
+ // ElMessage.success('璁″垝宸插鍒�')
+ break
+ case 'delete':
+ ElMessageBox.confirm('纭畾瑕佸垹闄よ繖涓鍒掑悧锛�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+
+ delDutyPlan(ids).then(res => {
+ if (res.code === 200) {
+ ElMessage.success('璁″垝宸插垹闄�')
+ ids.value = [];
+ getPlanList()
+ }
+ })
+ })
+ break
+ }
+}
+//
+const handleSavePlan = async () => {
+ try {
+ await planFormRef.value.validate()
+ if (operationType.value === 'add') {
+ addDutyPlan(planForm).then(res => {
+ if (res.code === 200) {
+ ElMessage.success('璁″垝淇濆瓨鎴愬姛')
+ planDialogVisible.value = false
+ }
+ getPlanList()
+ })
+ } else {
+
+ updateDutyPlan(planForm).then(res => {
+ if (res.code === 200) {
+ ElMessage.success('璁″垝淇濆瓨鎴愬姛')
+ planDialogVisible.value = false
+ }
+ getPlanList()
+ })
+ }
+ } catch (error) {
+ console.log('琛ㄥ崟楠岃瘉澶辫触:', error)
+ }
+}
+
+const handleDialogClose = () => {
+ planFormRef.value?.resetFields()
+}
+
+const handleRefresh = () => {
+ getPlanList()
+ // ElMessage.success('鏁版嵁宸插埛鏂�')
+}
+
+const handleFilter = () => {
+ ElMessage.info('鎵撳紑绛涢�夐潰鏉�')
+}
+
+const handleExport = () => {
+ ElMessageBox.confirm("鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ // exportDutyPlan().then(res => {
+
+ // })
+ proxy.download("/dutyPlan/export", {}, "璁″垝绠$悊.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+const handleShare = () => {
+ ElMessage.success('璁″垝宸插叡浜�')
+}
+
+const getCurrentLevelText = () => {
+ const levelMap = {
+ personal: '涓汉璁″垝',
+ group: '灏忕粍璁″垝',
+ department: '閮ㄩ棬璁″垝',
+ company: '鍏徃璁″垝'
+ }
+ return levelMap[currentLevel.value] || '涓汉璁″垝'
+}
+
+const getCurrentPeriodText = () => {
+ const periodMap = {
+ week: '鍛ㄨ鍒�',
+ month: '鏈堣鍒�',
+ year: '骞磋鍒�'
+ }
+ return periodMap[currentPeriod.value] || '鍛ㄨ鍒�'
+}
+
+const getPriorityType = (priority) => {
+ const typeMap = {
+ high: 'danger',
+ medium: 'warning',
+ low: 'info'
+ }
+ return typeMap[priority] || 'info'
+}
+
+const getPriorityText = (priority) => {
+ const textMap = {
+ high: '楂�',
+ medium: '涓�',
+ low: '浣�'
+ }
+ return textMap[priority] || '涓�'
+}
+
+const getStatusText = (status) => {
+ const statusMap = {
+ not_started: '鏈紑濮�',
+ in_progress: '杩涜涓�',
+ completed: '宸插畬鎴�',
+ paused: '宸叉殏鍋�'
+ }
+ return statusMap[status] || '鏈煡'
+}
+
+const getProgressColor = (progress) => {
+ if (progress >= 80) return '#67C23A'
+ if (progress >= 50) return '#E6A23C'
+ return '#F56C6C'
+}
+//鑾峰彇鏁版嵁鍒楄〃
+const getPlanList = async () => {
+ const params = {
+ level: currentLevel.value,
+ period: currentPeriod.value,
+ queryDate:currentDate.value
+ }
+ listDutyPlan(params).then(res => {
+ if (res.code === 200) {
+ planList.value = res.data.records
+ }
+ }).catch(err => {
+ console.log(err)
+ })
+}
+//鑾峰彇鏁版嵁
+const getPlanNum = async () => {
+ NumDutyPlan().then(res => {
+ if (res.code === 200) {
+ // console.log(res.data)
+ //璁茬粨鏋滈噷闈㈢殑鏁版嵁鏍规嵁level 璧嬪�肩粰overviewData
+ res.data.forEach(item => {
+ overviewData[item.level].total = item.num
+ overviewData[item.level].completion = item.completion
+ })
+
+ }
+ }).catch(err => {
+ console.log(err)
+ })
+}
+
+onMounted(() => {
+ getPlanList()
+ getPlanNum()
+ console.log('澶氱骇璁″垝妯℃澘椤甸潰宸插姞杞�')
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+ background-color: #f5f5f5;
+ min-height: 100vh;
+}
+
+.header-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ background: white;
+ padding: 20px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.left-actions {
+ display: flex;
+ align-items: center;
+}
+
+.right-actions {
+ display: flex;
+ gap: 10px;
+}
+
+.overview-cards {
+ margin-bottom: 20px;
+}
+
+.overview-card {
+ height: 120px;
+}
+
+.card-content {
+ display: flex;
+ align-items: center;
+ height: 100%;
+}
+
+.card-icon {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 15px;
+ font-size: 24px;
+ color: white;
+}
+
+.card-icon.personal {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.card-icon.group {
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+}
+
+.card-icon.department {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+}
+
+.card-icon.company {
+ background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
+}
+
+.card-info {
+ flex: 1;
+}
+
+.card-title {
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 5px;
+}
+
+.card-number {
+ font-size: 24px;
+ font-weight: bold;
+ color: #333;
+ margin-bottom: 10px;
+}
+
+.card-progress {
+ width: 100%;
+}
+
+.plan-content {
+ background: white;
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-weight: bold;
+ color: #333;
+}
+
+.header-actions {
+ display: flex;
+ gap: 10px;
+}
+
+.plan-list {
+ padding: 20px 0;
+}
+
+.plan-item {
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ margin-bottom: 15px;
+ padding: 20px;
+ transition: all 0.3s ease;
+}
+
+.plan-item:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+}
+
+.plan-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.plan-title {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.title-text {
+ font-size: 16px;
+ font-weight: bold;
+ color: #333;
+}
+
+.plan-actions {
+ display: flex;
+ gap: 10px;
+}
+
+.plan-content {
+ margin-bottom: 15px;
+}
+
+.plan-description {
+ color: #666;
+ margin-bottom: 15px;
+ line-height: 1.6;
+}
+
+.plan-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+ margin-bottom: 15px;
+}
+
+.meta-item {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ color: #666;
+ font-size: 14px;
+}
+
+.plan-progress {
+ margin-bottom: 15px;
+}
+
+.plan-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+ .header-actions {
+ flex-direction: column;
+ gap: 15px;
+ }
+
+ .left-actions {
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+
+ .plan-meta {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .plan-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+}
+</style>
diff --git a/src/views/collaborativeApproval/processTracking/index.vue b/src/views/collaborativeApproval/processTracking/index.vue
new file mode 100644
index 0000000..67f5106
--- /dev/null
+++ b/src/views/collaborativeApproval/processTracking/index.vue
@@ -0,0 +1,498 @@
+<template>
+ <div class="process-tracking">
+ <div class="header">
+ <h2>杩囩▼杩借釜</h2>
+ <el-button type="primary" @click="refreshData">鍒锋柊鏁版嵁</el-button>
+ </div>
+
+ <!-- 椤圭洰鐘舵�佺粺璁� -->
+ <div class="status-cards">
+ <el-row :gutter="20">
+ <el-col :span="6" v-for="(item, index) in statusStats" :key="index">
+ <el-card class="status-card" :class="item.type">
+ <div class="card-content">
+ <div class="card-icon">
+ <el-icon :size="24">
+ <component :is="item.icon" />
+ </el-icon>
+ </div>
+ <div class="card-info">
+ <div class="card-title">{{ item.label }}</div>
+ <div class="card-count">{{ item.count }}</div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 椤圭洰鍒楄〃 -->
+ <el-card class="project-list">
+ <template #header>
+ <div class="card-header">
+ <span>椤圭洰鍒楄〃</span>
+ <el-button type="text" @click="toggleView">
+ {{ viewMode === 'table' ? '鍒囨崲鍒扮敇鐗瑰浘' : '鍒囨崲鍒板垪琛�' }}
+ </el-button>
+ </div>
+ </template>
+
+ <!-- 琛ㄦ牸瑙嗗浘 -->
+ <div v-if="viewMode === 'table'">
+ <el-table :data="projectList" style="width: 100%">
+ <el-table-column prop="name" label="椤圭洰鍚嶇О" />
+ <el-table-column prop="manager" label="璐熻矗浜�"/>
+ <el-table-column label="鐘舵��" >
+ <template #default="{ row }">
+ <el-tag :type="getStatusType(row.status)">
+ {{ getStatusText(row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="杩涘害" width="150">
+ <template #default="{ row }">
+ <el-progress :percentage="row.progress" :status="getProgressStatus(row.status)" />
+ </template>
+ </el-table-column>
+ <el-table-column prop="startDate" label="寮�濮嬫椂闂�" width="120" />
+ <el-table-column prop="endDate" label="缁撴潫鏃堕棿" width="120" />
+ <el-table-column label="鎿嶄綔" width="150">
+ <template #default="{ row }">
+ <el-button type="text" @click="updateStatus(row)">鏇存柊鐘舵��</el-button>
+ <el-button type="text" @click="viewDetails(row)">璇︽儏</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+
+ <!-- 鐢樼壒鍥捐鍥� -->
+ <div v-else class="gantt-container">
+ <div ref="ganttChart" style="width: 100%; height: 400px;"></div>
+ </div>
+ </el-card>
+
+ <!-- 鐘舵�佹洿鏂板璇濇 -->
+ <el-dialog v-model="statusDialogVisible" title="鏇存柊椤圭洰鐘舵��" width="400px">
+ <el-form :model="statusForm" label-width="80px">
+ <el-form-item label="椤圭洰鍚嶇О">
+ <el-input v-model="statusForm.name" disabled />
+ </el-form-item>
+ <el-form-item label="褰撳墠鐘舵��">
+ <el-select v-model="statusForm.status" placeholder="璇烽�夋嫨鐘舵��">
+ <el-option label="鏈紑濮�" value="not_started" />
+ <el-option label="杩涜涓�" value="in_progress" />
+ <el-option label="宸插畬鎴�" value="completed" />
+ <el-option label="寤舵湡" value="delayed" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="杩涘害">
+ <el-slider v-model="statusForm.progress" :max="100" />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="statusDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="confirmStatusUpdate">纭</el-button>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, nextTick } from 'vue'
+import { ElMessage } from 'element-plus'
+import { Clock, Loading, Check, Warning } from '@element-plus/icons-vue'
+import * as echarts from 'echarts'
+
+// 鍝嶅簲寮忔暟鎹�
+const viewMode = ref('table')
+const statusDialogVisible = ref(false)
+const ganttChart = ref(null)
+let chartInstance = null
+
+// 鐘舵�佺粺璁℃暟鎹�
+const statusStats = reactive([
+ { label: '鏈紑濮�', count: 3, type: 'not-started', icon: Clock },
+ { label: '杩涜涓�', count: 5, type: 'in-progress', icon: Loading },
+ { label: '宸插畬鎴�', count: 8, type: 'completed', icon: Check },
+ { label: '寤舵湡', count: 2, type: 'delayed', icon: Warning }
+])
+
+// 椤圭洰鍒楄〃鏁版嵁
+const projectList = reactive([
+ {
+ id: 1,
+ name: '姹囨槦閽欎笟鐢熶骇绾挎墿寤洪」鐩�',
+ manager: '闄堝織寮�',
+ status: 'completed',
+ progress: 100,
+ startDate: '2024-01-01',
+ endDate: '2024-01-15'
+ },
+ {
+ id: 2,
+ name: '鏂板瀷鐜繚閽欑矇宸ヨ壓鐮斿彂',
+ manager: '鏋楅洩宄�',
+ status: 'in_progress',
+ progress: 75,
+ startDate: '2024-01-10',
+ endDate: '2024-01-25'
+ },
+ {
+ id: 3,
+ name: '姹囨槦閽欎笟ERP绯荤粺鍗囩骇',
+ manager: '鐜嬮泤鐞�',
+ status: 'in_progress',
+ progress: 60,
+ startDate: '2024-01-20',
+ endDate: '2024-02-10'
+ },
+ {
+ id: 4,
+ name: '鐭垮北寮�閲囪鍙瘉缁湡鐢宠',
+ manager: '璧典紵涓�',
+ status: 'in_progress',
+ progress: 45,
+ startDate: '2024-01-25',
+ endDate: '2024-02-15'
+ },
+ {
+ id: 5,
+ name: '鐜繚璁惧鍗囩骇鏀归��',
+ manager: '鏉庝匠娆�',
+ status: 'delayed',
+ progress: 30,
+ startDate: '2024-01-15',
+ endDate: '2024-01-30'
+ },
+ {
+ id: 6,
+ name: '骞村害瀹夊叏鐢熶骇鍩硅璁″垝',
+ manager: '寮犲缓鍥�',
+ status: 'not_started',
+ progress: 0,
+ startDate: '2024-02-01',
+ endDate: '2024-02-20'
+ }
+])
+
+// 鐘舵�佹洿鏂拌〃鍗�
+const statusForm = reactive({
+ id: null,
+ name: '',
+ status: '',
+ progress: 0
+})
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const typeMap = {
+ not_started: 'info',
+ in_progress: 'warning',
+ completed: 'success',
+ delayed: 'danger'
+ }
+ return typeMap[status] || 'info'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+ const textMap = {
+ not_started: '鏈紑濮�',
+ in_progress: '杩涜涓�',
+ completed: '宸插畬鎴�',
+ delayed: '寤舵湡'
+ }
+ return textMap[status] || '鏈煡'
+}
+
+// 鑾峰彇杩涘害鐘舵��
+const getProgressStatus = (status) => {
+ if (status === 'completed') return 'success'
+ if (status === 'delayed') return 'exception'
+ return null
+}
+
+// 鍒囨崲瑙嗗浘妯″紡
+const toggleView = () => {
+ viewMode.value = viewMode.value === 'table' ? 'gantt' : 'table'
+ if (viewMode.value === 'gantt') {
+ nextTick(() => {
+ initGanttChart()
+ })
+ }
+}
+
+// 鍒濆鍖栫敇鐗瑰浘
+const initGanttChart = () => {
+ if (!ganttChart.value) return
+
+ if (chartInstance) {
+ chartInstance.dispose()
+ }
+
+ chartInstance = echarts.init(ganttChart.value)
+
+ // 鍑嗗鐢樼壒鍥炬暟鎹�
+ const data = projectList.map((project, index) => ({
+ name: project.name,
+ value: [
+ index,
+ new Date(project.startDate).getTime(),
+ new Date(project.endDate).getTime(),
+ project.progress
+ ],
+ itemStyle: {
+ color: getGanttColor(project.status)
+ }
+ }))
+
+ const option = {
+ title: {
+ text: '椤圭洰鐢樼壒鍥�',
+ left: 'center'
+ },
+ tooltip: {
+ formatter: (params) => {
+ const project = projectList[params.value[0]]
+ return `
+ <div>
+ <strong>${project.name}</strong><br/>
+ 璐熻矗浜�: ${project.manager}<br/>
+ 鐘舵��: ${getStatusText(project.status)}<br/>
+ 杩涘害: ${project.progress}%<br/>
+ 寮�濮嬫椂闂�: ${project.startDate}<br/>
+ 缁撴潫鏃堕棿: ${project.endDate}
+ </div>
+ `
+ }
+ },
+ grid: {
+ left: '15%',
+ right: '10%',
+ top: '15%',
+ bottom: '15%'
+ },
+ xAxis: {
+ type: 'time',
+ axisLabel: {
+ formatter: (value) => {
+ return echarts.format.formatTime('MM-dd', value)
+ }
+ }
+ },
+ yAxis: {
+ type: 'category',
+ data: projectList.map(p => p.name),
+ inverse: true
+ },
+ series: [{
+ type: 'custom',
+ renderItem: (params, api) => {
+ const categoryIndex = api.value(0)
+ const start = api.coord([api.value(1), categoryIndex])
+ const end = api.coord([api.value(2), categoryIndex])
+ const height = api.size([0, 1])[1] * 0.6
+
+ return {
+ type: 'rect',
+ shape: {
+ x: start[0],
+ y: start[1] - height / 2,
+ width: end[0] - start[0],
+ height: height
+ },
+ style: api.style()
+ }
+ },
+ data: data
+ }]
+ }
+
+ chartInstance.setOption(option)
+}
+
+// 鑾峰彇鐢樼壒鍥鹃鑹�
+const getGanttColor = (status) => {
+ const colorMap = {
+ not_started: '#909399',
+ in_progress: '#E6A23C',
+ completed: '#67C23A',
+ delayed: '#F56C6C'
+ }
+ return colorMap[status] || '#909399'
+}
+
+// 鏇存柊鐘舵��
+const updateStatus = (project) => {
+ statusForm.id = project.id
+ statusForm.name = project.name
+ statusForm.status = project.status
+ statusForm.progress = project.progress
+ statusDialogVisible.value = true
+}
+
+// 纭鐘舵�佹洿鏂�
+const confirmStatusUpdate = () => {
+ const project = projectList.find(p => p.id === statusForm.id)
+ if (project) {
+ project.status = statusForm.status
+ project.progress = statusForm.progress
+
+ // 鏇存柊缁熻鏁版嵁
+ updateStatusStats()
+
+ // 濡傛灉鏄敇鐗瑰浘瑙嗗浘锛岄噸鏂版覆鏌�
+ if (viewMode.value === 'gantt') {
+ nextTick(() => {
+ initGanttChart()
+ })
+ }
+
+ ElMessage.success('鐘舵�佹洿鏂版垚鍔�')
+ }
+ statusDialogVisible.value = false
+}
+
+// 鏇存柊鐘舵�佺粺璁�
+const updateStatusStats = () => {
+ const stats = {
+ not_started: 0,
+ in_progress: 0,
+ completed: 0,
+ delayed: 0
+ }
+
+ projectList.forEach(project => {
+ stats[project.status]++
+ })
+
+ statusStats[0].count = stats.not_started
+ statusStats[1].count = stats.in_progress
+ statusStats[2].count = stats.completed
+ statusStats[3].count = stats.delayed
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetails = (project) => {
+ ElMessage.info(`鏌ョ湅椤圭洰璇︽儏: ${project.name}`)
+}
+
+// 鍒锋柊鏁版嵁
+const refreshData = () => {
+ // 妯℃嫙瀹炴椂鏇存柊
+ const randomProject = projectList[Math.floor(Math.random() * projectList.length)]
+ if (randomProject.status === 'in_progress') {
+ randomProject.progress = Math.min(100, randomProject.progress + Math.floor(Math.random() * 10))
+ if (randomProject.progress === 100) {
+ randomProject.status = 'completed'
+ }
+ }
+
+ updateStatusStats()
+
+ if (viewMode.value === 'gantt') {
+ nextTick(() => {
+ initGanttChart()
+ })
+ }
+
+ ElMessage.success('鏁版嵁宸插埛鏂�')
+}
+
+onMounted(() => {
+ updateStatusStats()
+})
+</script>
+
+<style scoped>
+.process-tracking {
+ padding: 20px;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.header h2 {
+ margin: 0;
+ color: #303133;
+}
+
+.status-cards {
+ margin-bottom: 20px;
+}
+
+.status-card {
+ cursor: pointer;
+ transition: all 0.3s;
+}
+
+.status-card:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.card-content {
+ display: flex;
+ align-items: center;
+}
+
+.card-icon {
+ margin-right: 15px;
+ padding: 10px;
+ border-radius: 8px;
+}
+
+.status-card.not-started .card-icon {
+ background-color: #f4f4f5;
+ color: #909399;
+}
+
+.status-card.in-progress .card-icon {
+ background-color: #fdf6ec;
+ color: #E6A23C;
+}
+
+.status-card.completed .card-icon {
+ background-color: #f0f9ff;
+ color: #67C23A;
+}
+
+.status-card.delayed .card-icon {
+ background-color: #fef0f0;
+ color: #F56C6C;
+}
+
+.card-info {
+ flex: 1;
+}
+
+.card-title {
+ font-size: 14px;
+ color: #909399;
+ margin-bottom: 5px;
+}
+
+.card-count {
+ font-size: 24px;
+ font-weight: bold;
+ color: #303133;
+}
+
+.project-list {
+ margin-top: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.gantt-container {
+ min-height: 400px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/purchaseApproval/index.vue b/src/views/collaborativeApproval/purchaseApproval/index.vue
new file mode 100644
index 0000000..af17bbe
--- /dev/null
+++ b/src/views/collaborativeApproval/purchaseApproval/index.vue
@@ -0,0 +1,1064 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <div>
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="渚涘簲鍟嗗悕绉帮細">
+ <el-input v-model="searchForm.supplierName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="閲囪喘鍚堝悓鍙凤細">
+ <el-input
+ v-model="searchForm.purchaseContractNumber"
+ style="width: 240px"
+ placeholder="璇疯緭鍏�"
+ @change="handleQuery"
+ clearable
+ :prefix-icon="Search"
+ />
+ </el-form-item>
+ <el-form-item label="閿�鍞悎鍚屽彿锛�">
+ <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item label="椤圭洰鍚嶇О锛�">
+ <el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+ @change="handleQuery" />
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+
+ </div>
+ <div class="table_list">
+ <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
+ <el-button @click="handleOut">瀵煎嚭</el-button>
+ <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+ </div>
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ @selection-change="handleSelectionChange"
+ :expand-row-keys="expandedRowKeys"
+ :row-key="(row) => row.id"
+ show-summary
+ :summary-method="summarizeMainTable"
+ @expand-change="expandChange"
+ height="calc(100vh - 18.5em)"
+ :row-class-name="tableRowClassName"
+ >
+ <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-column
+ align="center"
+ label="搴忓彿"
+ type="index"
+ width="60"
+ />
+ <el-table-column label="浜у搧澶х被" prop="productCategory" />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+ <el-table-column label="鍗曚綅" prop="unit" />
+ <el-table-column label="鏁伴噺" prop="quantity" />
+ <el-table-column label="绋庣巼(%)" prop="taxRate" />
+ <el-table-column
+ label="鍚◣鍗曚环(鍏�)"
+ prop="taxInclusiveUnitPrice"
+ :formatter="formattedNumber"
+ />
+ <el-table-column
+ label="鍚◣鎬讳环(鍏�)"
+ prop="taxInclusiveTotalPrice"
+ :formatter="formattedNumber"
+ />
+ <el-table-column
+ label="涓嶅惈绋庢�讳环(鍏�)"
+ prop="taxExclusiveTotalPrice"
+ :formatter="formattedNumber"
+ />
+ </el-table>
+ </template>
+ </el-table-column>
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column
+ label="閲囪喘鍚堝悓鍙�"
+ prop="purchaseContractNumber"
+ width="200"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="閿�鍞悎鍚屽彿"
+ prop="salesContractNo"
+ width="200"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="渚涘簲鍟嗗悕绉�"
+ width="240"
+ prop="supplierName"
+ show-overflow-tooltip
+ />
+ <el-table-column label="璁㈠崟鐘舵��" width="100" align="center">
+ <template #default="scope">
+ <el-tag v-if="scope.row.isInvalid" type="danger" size="small">澶辨晥</el-tag>
+ <el-tag v-else type="success" size="small">姝e父</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="椤圭洰鍚嶇О"
+ prop="projectName"
+ width="420"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="瀹℃壒鐘舵��"
+ prop="approvalStatus"
+ width="200"
+ show-overflow-tooltip
+ >
+ <template #default="scope">
+ <el-tag
+ size="small"
+ >
+ {{ approvalStatusText[scope.row.approvalStatus] || '鏈煡鐘舵��' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="浠樻鏂瑰紡"
+ width="100"
+ prop="paymentMethod"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="鍚堝悓閲戦(鍏�)"
+ prop="contractAmount"
+ width="200"
+ show-overflow-tooltip
+ :formatter="formattedNumber"
+ />
+ <el-table-column
+ label="褰曞叆浜�"
+ prop="recorderName"
+ width="100"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ label="褰曞叆鏃ユ湡"
+ prop="entryDate"
+ width="100"
+ show-overflow-tooltip
+ />
+ <el-table-column
+ fixed="right"
+ label="鎿嶄綔"
+ min-width="150"
+ align="center"
+ >
+ <template #default="scope">
+ <el-button
+ link
+ type="primary"
+ size="small"
+ @click="approvePurchase(scope.row)"
+ :disabled="scope.row.approvalStatus !== 0"
+ >瀹℃壒</el-button
+ >
+ <el-button
+ link
+ type="primary"
+ size="small"
+ @click="rejectPurchase(scope.row)"
+ :disabled="scope.row.approvalStatus !== 0"
+ >鎷掔粷瀹℃壒</el-button
+ >
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { getToken } from "@/utils/auth";
+import pagination from "@/components/PIMTable/Pagination.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";
+import {
+ getSalesLedgerWithProducts,
+ addOrUpdateSalesLedgerProduct,
+ delProduct,
+ delLedgerFile,
+ getProductInfoByContractNo,
+} from "@/api/salesManagement/salesLedger.js";
+import {
+ addOrEditPurchase,
+ delPurchase,
+ getSalesNo,
+ purchaseListPage,
+ productList,
+ getPurchaseById,
+ getOptions,
+ createPurchaseNo, updateApprovalStatus,
+} from "@/api/procurementManagement/procurementLedger.js";
+import useFormData from "@/hooks/useFormData.js";
+import QRCode from "qrcode";
+
+
+const { proxy } = getCurrentInstance();
+const tableData = ref([]);
+const productData = ref([]);
+const selectedRows = ref([]);
+const productSelectedRows = ref([]);
+const modelOptions = ref([]);
+const userList = ref([]);
+const productOptions = ref([]);
+const salesContractList = ref([]);
+const supplierList = ref([]);
+const tableLoading = ref(false);
+const page = reactive({
+ current: 1,
+ size: 100,
+});
+const total = ref(0);
+const fileList = ref([]);
+import useUserStore from "@/store/modules/user";
+import { modelList, productTreeList } from "@/api/basicData/product.js";
+import dayjs from "dayjs";
+import { getCurrentDate } from "@/utils/index.js";
+
+const userStore = useUserStore();
+
+// 浜岀淮鐮佺浉鍏冲彉閲�
+const qrCodeDialogVisible = ref(false);
+const qrCodeUrl = ref("");
+
+// 璁㈠崟瀹℃壒鐘舵�佹樉绀烘枃鏈�
+const approvalStatusText = {
+ 0: '寰呭鎵�',
+ 1: '瀹℃壒閫氳繃',
+ 2: '瀹℃壒澶辫触'
+};
+
+// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+const operationType = ref("");
+const dialogFormVisible = ref(false);
+const data = reactive({
+ searchForm: {
+ supplierName: "", // 渚涘簲鍟嗗悕绉�
+ purchaseContractNumber: "", // 閲囪喘鍚堝悓缂栧彿
+ salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
+ projectName: "", // 椤圭洰鍚嶇О
+ entryDate: null, // 褰曞叆鏃ユ湡
+ entryDateStart: undefined,
+ entryDateEnd: undefined,
+ },
+ form: {
+ purchaseContractNumber: "",
+ salesLedgerId: "",
+ projectName: "",
+ recorderId: "",
+ entryDate: "",
+ productData: [],
+ supplierName: "",
+ supplierId: "",
+ paymentMethod: "",
+ executionDate: "",
+ approvalStatus: "0",
+ },
+ rules: {
+ purchaseContractNumber: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ ],
+ projectName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ supplierId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+});
+const { form, rules } = toRefs(data);
+const { form: searchForm } = useFormData(data.searchForm);
+
+// 浜у搧琛ㄥ崟寮规鏁版嵁
+const productFormVisible = ref(false);
+const productOperationType = ref("");
+const productOperationIndex = ref("");
+const currentId = ref("");
+const productFormData = reactive({
+ productForm: {
+ productId: "",
+ productCategory: "",
+ productModelId: "",
+ specificationModel: "",
+ unit: "",
+ quantity: "",
+ taxInclusiveUnitPrice: "",
+ taxRate: "",
+ taxInclusiveTotalPrice: "",
+ taxExclusiveTotalPrice: "",
+ invoiceType: "",
+ warnNum: "",
+ },
+ productRules: {
+ productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ taxInclusiveUnitPrice: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ ],
+ taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ warnNum: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+ taxInclusiveTotalPrice: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ ],
+ taxExclusiveTotalPrice: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ ],
+ invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+ },
+});
+const { productForm, productRules } = toRefs(productFormData);
+const upload = reactive({
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+});
+
+const changeDaterange = (value) => {
+ if (value) {
+ searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+ searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+ } else {
+ searchForm.entryDateStart = undefined;
+ searchForm.entryDateEnd = undefined;
+ }
+ handleQuery();
+};
+
+const formattedNumber = (row, column, cellValue) => {
+ return parseFloat(cellValue).toFixed(2);
+};
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+ page.current = 1;
+ getList();
+};
+// 瀛愯〃鍚堣鏂规硶
+const summarizeChildrenTable = (param) => {
+ return proxy.summarizeTable(
+ param,
+ [
+ "taxInclusiveUnitPrice",
+ "taxInclusiveTotalPrice",
+ "taxExclusiveTotalPrice",
+ "ticketsNum",
+ "ticketsAmount",
+ "futureTickets",
+ "futureTicketsAmount",
+ ],
+ {
+ ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+ }
+ );
+};
+const paginationChange = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getList();
+};
+const getList = () => {
+ tableLoading.value = true;
+ const { entryDate, ...rest } = searchForm;
+ purchaseListPage({ ...rest, ...page })
+ .then((res) => {
+ tableLoading.value = false;
+ // tableData.value = res.data.records;
+ // 澶勭悊鏁版嵁锛屾坊鍔犲け鏁堢姸鎬佹爣璁�
+ tableData.value = res.data.records.map(record => ({
+ ...record,
+ isInvalid: record.isWhite === 1
+ }));
+ tableData.value.map((item) => {
+ item.children = [];
+ });
+ total.value = res.data.total;
+ expandedRowKeys.value = [];
+ })
+ .catch(() => {
+ tableLoading.value = false;
+ });
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+};
+const productSelected = (selectedRows) => {
+ productSelectedRows.value = selectedRows;
+};
+const expandedRowKeys = ref([]);
+// 灞曞紑琛�
+const expandChange = (row, expandedRows) => {
+ if (expandedRows.length > 0) {
+ expandedRowKeys.value = [];
+ try {
+ productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
+ const index = tableData.value.findIndex((item) => item.id === row.id);
+ if (index > -1) {
+ tableData.value[index].children = res.data;
+ }
+ expandedRowKeys.value.push(row.id);
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ } else {
+ expandedRowKeys.value = [];
+ }
+};
+// 涓昏〃鍚堣鏂规硶
+const summarizeMainTable = (param) => {
+ return proxy.summarizeTable(param, ["contractAmount"]);
+};
+// 瀛愯〃鍚堣鏂规硶
+const summarizeProTable = (param) => {
+ return proxy.summarizeTable(param, [
+ "taxInclusiveUnitPrice",
+ "taxInclusiveTotalPrice",
+ "taxExclusiveTotalPrice",
+ ]);
+};
+// 鎵撳紑寮规
+const openForm = (type, row) => {
+ operationType.value = type;
+ form.value = {};
+ productData.value = [];
+ fileList.value = [];
+ if (operationType.value == "add") {
+ createPurchaseNo().then((res) => {
+ form.value.purchaseContractNumber = res.data;
+ });
+ }
+ userListNoPage().then((res) => {
+ userList.value = res.data;
+ });
+ getSalesNo().then((res) => {
+ salesContractList.value = res;
+ });
+ getOptions().then((res) => {
+ // 渚涘簲鍟嗚繃婊ゅ嚭isWhite=0 鐨勬暟鎹�
+ supplierList.value = res.data.filter((item) => item.isWhite == 0);
+ });
+ form.value.recorderId = userStore.id;
+ form.value.entryDate = getCurrentDate();
+ if (type === "edit") {
+ currentId.value = row.id;
+ getPurchaseById({ id: row.id, type: 2 }).then((res) => {
+ form.value = { ...res };
+ productData.value = form.value.productData;
+ if (form.value.salesLedgerFiles) {
+ fileList.value = form.value.salesLedgerFiles;
+ } else {
+ fileList.value = [];
+ }
+ });
+ }
+ dialogFormVisible.value = true;
+};
+// 涓婁紶鍓嶆牎妫�
+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) {
+ file.tempId = res.data.tempId;
+ proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+ } else {
+ proxy.$modal.msgError(res.msg);
+ proxy.$refs.fileUpload.handleRemove(file);
+ }
+}
+// 绉婚櫎鏂囦欢
+function handleRemove(file) {
+ console.log("handleRemove", file.id);
+ if (file.size > 1024 * 1024 * 10) {
+ // 浠呭墠绔竻鐞嗭紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙e拰鎻愮ず
+ return;
+ }
+ if (operationType.value === "edit") {
+ let ids = [];
+ ids.push(file.id);
+ delLedgerFile(ids).then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ });
+ }
+}
+// 鎻愪氦琛ㄥ崟
+const submitForm = (n) => {
+ proxy.$refs["formRef"].validate((valid) => {
+ if (valid) {
+ if (productData.value.length > 0) {
+ form.value.productData = proxy.HaveJson(productData.value);
+ } else {
+ proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
+ return;
+ }
+ let tempFileIds = [];
+ if (fileList.value.length > 0) {
+ tempFileIds = fileList.value.map((item) => item.tempId);
+ }
+ form.value.tempFileIds = tempFileIds;
+ form.value.type = 2;
+ form.value.approvalStatus = n;
+ addOrEditPurchase(form.value).then((res) => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeDia();
+ getList();
+ });
+ }
+ });
+};
+// 鍏抽棴寮规
+const closeDia = () => {
+ proxy.resetForm("formRef");
+ dialogFormVisible.value = false;
+};
+// 鎵撳紑浜у搧寮规
+const openProductForm = (type, row, index) => {
+ productOperationType.value = type;
+ productOperationIndex.value = index;
+ productForm.value = {};
+ proxy.resetForm("productFormRef");
+ if (type === "edit") {
+ productForm.value = { ...row };
+ }
+ productFormVisible.value = true;
+ getProductOptions();
+};
+const getProductOptions = () => {
+ productTreeList().then((res) => {
+ productOptions.value = convertIdToValue(res);
+ });
+};
+const getModels = (value) => {
+ if (value) {
+ productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
+ modelList({ id: value }).then((res) => {
+ modelOptions.value = res;
+ });
+ } else {
+ productForm.value.productCategory = "";
+ modelOptions.value = [];
+ }
+};
+const getProductModel = (value) => {
+ const index = modelOptions.value.findIndex((item) => item.id === value);
+ if (index !== -1) {
+ productForm.value.specificationModel = modelOptions.value[index].model;
+ productForm.value.unit = modelOptions.value[index].unit;
+ } else {
+ productForm.value.specificationModel = null;
+ productForm.value.unit = null;
+ }
+};
+const findNodeById = (nodes, productId) => {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].value === productId) {
+ return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣鐨刲abel
+ }
+ if (nodes[i].children && nodes[i].children.length > 0) {
+ const foundNode = findNodeById(nodes[i].children, productId);
+ if (foundNode) {
+ return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝鐩存帴杩斿洖锛堝凡缁忔槸label瀛楃涓诧級
+ }
+ }
+ }
+ return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+};
+function convertIdToValue(data) {
+ return data.map((item) => {
+ const { id, children, ...rest } = item;
+ const newItem = {
+ ...rest,
+ value: id, // 灏� id 鏀逛负 value
+ };
+ if (children && children.length > 0) {
+ newItem.children = convertIdToValue(children);
+ }
+
+ return newItem;
+ });
+}
+// 鎻愪氦浜у搧琛ㄥ崟
+const submitProduct = () => {
+ proxy.$refs["productFormRef"].validate((valid) => {
+ if (valid) {
+ if (operationType.value === "edit") {
+ submitProductEdit();
+ } else {
+ if (productOperationType.value === "add") {
+ productData.value.push({ ...productForm.value });
+ console.log("productData.value---", productData.value);
+ } else {
+ productData.value[productOperationIndex.value] = {
+ ...productForm.value,
+ };
+ }
+ closeProductDia();
+ }
+ }
+ });
+};
+const submitProductEdit = () => {
+ productForm.value.salesLedgerId = currentId.value;
+ productForm.value.type = 2;
+ addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ closeProductDia();
+ getPurchaseById({ id: currentId.value, type: 2 }).then((res) => {
+ productData.value = res.productData;
+ });
+ });
+};
+// 鍒犻櫎浜у搧
+const deleteProduct = () => {
+ if (productSelectedRows.value.length === 0) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ if (operationType.value === "add") {
+ productSelectedRows.value.forEach((selectedRow) => {
+ const index = productData.value.findIndex(
+ (product) => product.id === selectedRow.id
+ );
+ if (index !== -1) {
+ productData.value.splice(index, 1);
+ }
+ });
+ } else {
+ let ids = [];
+ if (productSelectedRows.value.length > 0) {
+ ids = productSelectedRows.value.map((item) => item.id);
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ delProduct(ids).then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ closeProductDia();
+ getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
+ (res) => {
+ productData.value = res.productData;
+ }
+ );
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ }
+};
+// 鍏抽棴浜у搧寮规
+const closeProductDia = () => {
+ proxy.resetForm("productFormRef");
+ productFormVisible.value = false;
+};
+// 瀹℃壒閫氳繃鏂规硶
+const approvePurchase = (row) => {
+ ElMessageBox.confirm(`纭閫氳繃閲囪喘鍚堝悓鍙蜂负 ${row.purchaseContractNumber} 鐨勫鎵癸紵`, '瀹℃壒纭', {
+ confirmButtonText: '纭',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning',
+ }).then(() => {
+ updateApprovalStatus({ id: row.id, approvalStatus: 1}).then((res)=>{
+ proxy.$modal.msgSuccess('瀹℃壒鎴愬姛');
+ getList();
+ })
+ }).catch(() => {
+ proxy.$modal.msg('宸插彇娑堝鎵�');
+ });
+};
+
+// 瀹℃壒鎷掔粷鏂规硶
+const rejectPurchase = (row) => {
+ ElMessageBox.confirm(`纭鎷掔粷閲囪喘鍚堝悓鍙蜂负 ${row.purchaseContractNumber} 鐨勫鎵癸紵`, '瀹℃壒纭', {
+ confirmButtonText: '纭',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning',
+ }).then(() => {
+ updateApprovalStatus({ id: row.id, approvalStatus: 2}).then((res)=>{
+ proxy.$modal.msgSuccess('瀹℃壒鎴愬姛');
+ getList();
+ })
+ }).catch(() => {
+ proxy.$modal.msg('宸插彇娑堝鎵�');
+ });
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/purchase/ledger/export", {}, "閲囪喘鍙拌处.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+// 鍒犻櫎
+const handleDelete = () => {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+ const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
+ if (unauthorizedData.length > 0) {
+ proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+ return;
+ }
+ ids = selectedRows.value.map((item) => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ delPurchase(ids).then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+const mathNum = () => {
+ if (!productForm.value.taxRate) {
+ proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ return;
+ }
+ if (!productForm.value.taxInclusiveUnitPrice) {
+ return;
+ }
+ if (!productForm.value.quantity) {
+ return;
+ }
+ // 鍚◣鎬讳环璁$畻
+ productForm.value.taxInclusiveTotalPrice =
+ proxy.calculateTaxIncludeTotalPrice(
+ productForm.value.taxInclusiveUnitPrice,
+ productForm.value.quantity
+ );
+ if (productForm.value.taxRate) {
+ // 涓嶅惈绋庢�讳环璁$畻
+ productForm.value.taxExclusiveTotalPrice =
+ proxy.calculateTaxExclusiveTotalPrice(
+ productForm.value.taxInclusiveTotalPrice,
+ productForm.value.taxRate
+ );
+ }
+};
+const reverseMathNum = (field) => {
+ if (!productForm.value.taxRate) {
+ proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+ return;
+ }
+ const taxRate = Number(productForm.value.taxRate);
+ if (!taxRate) return;
+ if (field === 'taxInclusiveTotalPrice') {
+ // 宸茬煡鍚◣鎬讳环鍜屾暟閲忥紝鍙嶇畻鍚◣鍗曚环
+ if (productForm.value.quantity) {
+ productForm.value.taxInclusiveUnitPrice =
+ (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
+ }
+ // 宸茬煡鍚◣鎬讳环鍜屽惈绋庡崟浠凤紝鍙嶇畻鏁伴噺
+ else if (productForm.value.taxInclusiveUnitPrice) {
+ productForm.value.quantity =
+ (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
+ }
+ // 鍙嶇畻涓嶅惈绋庢�讳环
+ productForm.value.taxExclusiveTotalPrice =
+ (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
+ } else if (field === 'taxExclusiveTotalPrice') {
+ // 鍙嶇畻鍚◣鎬讳环
+ productForm.value.taxInclusiveTotalPrice =
+ (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
+ // 宸茬煡鏁伴噺锛屽弽绠楀惈绋庡崟浠�
+ if (productForm.value.quantity) {
+ productForm.value.taxInclusiveUnitPrice =
+ (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
+ }
+ // 宸茬煡鍚◣鍗曚环锛屽弽绠楁暟閲�
+ else if (productForm.value.taxInclusiveUnitPrice) {
+ productForm.value.quantity =
+ (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
+ }
+ }
+};
+// 閿�鍞悎鍚岄�夋嫨鏀瑰彉鏂规硶
+const salesLedgerChange = async (row) => {
+ console.log("row", row);
+ var index = salesContractList.value.findIndex((item) => item.id == row);
+ console.log("index", index);
+ if (index > -1) {
+ form.value.projectName = salesContractList.value[index].projectName;
+ await querygProductInfoByContractNo();
+ }
+};
+
+const querygProductInfoByContractNo = async () => {
+ const { code, data } = await getProductInfoByContractNo({
+ contractNo: form.value.salesLedgerId,
+ });
+ if (code == 200) {
+ productData.value = data;
+ }
+};
+
+// 鏄剧ず浜岀淮鐮�
+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}`;
+}
+
+// 娣诲姞琛岀被鍚嶆柟娉�
+const tableRowClassName = ({ row }) => {
+ return row.isInvalid ? 'invalid-row' : '';
+};
+
+onMounted(() => {
+ getList();
+});
+</script>
+
+<style scoped lang="scss">
+.invalid-row {
+ opacity: 0.6;
+ background-color: #f5f7fa;
+}
+</style>
diff --git a/src/views/collaborativeApproval/reportGeneration/index.vue b/src/views/collaborativeApproval/reportGeneration/index.vue
new file mode 100644
index 0000000..c160ad7
--- /dev/null
+++ b/src/views/collaborativeApproval/reportGeneration/index.vue
@@ -0,0 +1,596 @@
+<template>
+ <div class="report-generation">
+ <!-- 椤甸潰澶撮儴 -->
+ <div class="page-header">
+ <h2>椤圭洰鎬荤粨鎶ュ憡鐢熸垚</h2>
+ <div class="header-actions">
+ <el-button v-if="!reportGenerated" type="primary" @click="generateReport" :loading="generating">
+ <el-icon><Document /></el-icon>
+ 鐢熸垚鎶ュ憡
+ </el-button>
+ <el-button v-if="reportGenerated" type="primary" @click="resetConfig">
+ <el-icon><Refresh /></el-icon>
+ 鐢熸垚鏂版姤鍛�
+ </el-button>
+ <el-button @click="exportReport" :disabled="!reportGenerated">
+ <el-icon><Download /></el-icon>
+ 瀵煎嚭鎶ュ憡
+ </el-button>
+ </div>
+ </div>
+
+ <!-- 鎶ュ憡閰嶇疆鍖哄煙 -->
+ <el-card class="config-card" v-if="!reportGenerated">
+ <template #header>
+ <span>鎶ュ憡閰嶇疆</span>
+ </template>
+ <el-form :model="reportConfig" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="椤圭洰鍚嶇О">
+ <el-select v-model="reportConfig.projectId" placeholder="璇烽�夋嫨椤圭洰">
+ <el-option
+ v-for="project in projectList"
+ :key="project.id"
+ :label="project.name"
+ :value="project.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鎶ュ憡鍛ㄦ湡">
+ <el-date-picker
+ v-model="reportConfig.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ format="YYYY-MM-DD"
+ value-format="YYYY-MM-DD"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </el-card>
+
+ <!-- 鎶ュ憡鍐呭灞曠ず鍖哄煙 -->
+ <div v-if="reportGenerated" class="report-content">
+ <!-- 椤圭洰鍩烘湰淇℃伅 -->
+ <el-card class="report-section">
+ <template #header>
+ <span>椤圭洰鍩烘湰淇℃伅</span>
+ </template>
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="椤圭洰鍚嶇О">{{ reportData.projectInfo.name }}</el-descriptions-item>
+ <el-descriptions-item label="椤圭洰缁忕悊">{{ reportData.projectInfo.manager }}</el-descriptions-item>
+ <el-descriptions-item label="寮�濮嬫椂闂�">{{ reportData.projectInfo.startDate }}</el-descriptions-item>
+ <el-descriptions-item label="缁撴潫鏃堕棿">{{ reportData.projectInfo.endDate }}</el-descriptions-item>
+ <el-descriptions-item label="椤圭洰鐘舵��">
+ <el-tag :type="getStatusType(reportData.projectInfo.status)">
+ {{ reportData.projectInfo.status }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鎬婚绠�">{{ reportData.projectInfo.budget }}</el-descriptions-item>
+ </el-descriptions>
+ </el-card>
+
+ <!-- 浠诲姟瀹屾垚鐜囩粺璁� -->
+ <el-card class="report-section">
+ <template #header>
+ <span>浠诲姟瀹屾垚鐜囩粺璁�</span>
+ </template>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <div class="completion-stats">
+ <div class="stat-item">
+ <div class="stat-label">鎬讳换鍔℃暟</div>
+ <div class="stat-value">{{ reportData.taskStats.total }}</div>
+ </div>
+ <div class="stat-item">
+ <div class="stat-label">宸插畬鎴�</div>
+ <div class="stat-value completed">{{ reportData.taskStats.completed }}</div>
+ </div>
+ <div class="stat-item">
+ <div class="stat-label">杩涜涓�</div>
+ <div class="stat-value in-progress">{{ reportData.taskStats.inProgress }}</div>
+ </div>
+ <div class="stat-item">
+ <div class="stat-label">鏈紑濮�</div>
+ <div class="stat-value pending">{{ reportData.taskStats.pending }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="completion-rate">
+ <el-progress
+ type="circle"
+ :percentage="reportData.taskStats.completionRate"
+ :width="150"
+ :stroke-width="8"
+ >
+ <template #default="{ percentage }">
+ <span class="percentage-value">{{ percentage }}%</span>
+ <div class="percentage-label">瀹屾垚鐜�</div>
+ </template>
+ </el-progress>
+ </div>
+ </el-col>
+ </el-row>
+ </el-card>
+
+ <!-- 闂璁板綍缁熻 -->
+ <el-card class="report-section">
+ <template #header>
+ <span>闂璁板綍缁熻</span>
+ </template>
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <div class="issue-summary">
+ <div class="summary-item">
+ <div class="summary-label">鎬婚棶棰樻暟</div>
+ <div class="summary-value">{{ reportData.issueStats.total }}</div>
+ </div>
+ <div class="summary-item">
+ <div class="summary-label">宸茶В鍐�</div>
+ <div class="summary-value resolved">{{ reportData.issueStats.resolved }}</div>
+ </div>
+ <div class="summary-item">
+ <div class="summary-label">寰呰В鍐�</div>
+ <div class="summary-value pending">{{ reportData.issueStats.pending }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="16">
+ <el-table :data="reportData.issueStats.topIssues" size="small">
+ <el-table-column prop="title" label="涓昏闂" />
+ <el-table-column prop="severity" label="涓ラ噸绋嬪害" width="100">
+ <template #default="{ row }">
+ <el-tag :type="getSeverityType(row.severity)" size="small">
+ {{ row.severity }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="status" label="鐘舵��" width="80">
+ <template #default="{ row }">
+ <el-tag :type="row.status === '宸茶В鍐�' ? 'success' : 'warning'" size="small">
+ {{ row.status }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-col>
+ </el-row>
+ </el-card>
+
+ <!-- 寤惰鍒嗘瀽 -->
+ <el-card class="report-section">
+ <template #header>
+ <span>寤惰鍒嗘瀽</span>
+ </template>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <div class="delay-stats">
+ <div class="delay-item">
+ <div class="delay-label">寤惰浠诲姟鏁�</div>
+ <div class="delay-value">{{ reportData.delayAnalysis.delayedTasks }}</div>
+ </div>
+ <div class="delay-item">
+ <div class="delay-label">骞冲潎寤惰澶╂暟</div>
+ <div class="delay-value">{{ reportData.delayAnalysis.avgDelayDays }}</div>
+ </div>
+ <div class="delay-item">
+ <div class="delay-label">鏈�澶у欢璇ぉ鏁�</div>
+ <div class="delay-value">{{ reportData.delayAnalysis.maxDelayDays }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="delay-reasons">
+ <h4>涓昏寤惰鍘熷洜</h4>
+ <ul>
+ <li v-for="reason in reportData.delayAnalysis.reasons" :key="reason.reason">
+ {{ reason.reason }} ({{ reason.count }}娆�)
+ </li>
+ </ul>
+ </div>
+ </el-col>
+ </el-row>
+ </el-card>
+
+ <!-- 鍥㈤槦缁╂晥 -->
+ <el-card class="report-section">
+ <template #header>
+ <span>鍥㈤槦缁╂晥</span>
+ </template>
+ <el-table :data="reportData.teamPerformance" size="small">
+ <el-table-column prop="name" label="鎴愬憳濮撳悕" />
+ <el-table-column prop="completedTasks" label="瀹屾垚浠诲姟鏁�" />
+ <el-table-column prop="completionRate" label="瀹屾垚鐜�" width="100">
+ <template #default="{ row }">
+ <el-progress :percentage="row.completionRate" :show-text="false" />
+ <span style="margin-left: 10px;">{{ row.completionRate }}%</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="performance" label="缁╂晥璇勭骇" width="100">
+ <template #default="{ row }">
+ <el-tag :type="getPerformanceType(row.performance)">
+ {{ row.performance }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-card>
+
+ <!-- 鎬荤粨涓庡缓璁� -->
+ <el-card class="report-section">
+ <template #header>
+ <span>鎬荤粨涓庡缓璁�</span>
+ </template>
+ <div class="summary-content">
+ <h4>椤圭洰鎬荤粨</h4>
+ <p>{{ reportData.summary.conclusion }}</p>
+
+ <h4>鏀硅繘寤鸿</h4>
+ <ul>
+ <li v-for="suggestion in reportData.summary.suggestions" :key="suggestion">
+ {{ suggestion }}
+ </li>
+ </ul>
+ </div>
+ </el-card>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { Document, Download, Refresh } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+
+// 鍝嶅簲寮忔暟鎹�
+const generating = ref(false)
+const reportGenerated = ref(false)
+
+// 鎶ュ憡閰嶇疆
+const reportConfig = reactive({
+ projectId: '',
+ dateRange: []
+})
+
+// 椤圭洰鍒楄〃
+const projectList = ref([
+ { id: 1, name: '浜у搧搴撳瓨绠$悊绯荤粺' },
+ { id: 2, name: '瀹㈡埛鍏崇郴绠$悊骞冲彴' },
+ { id: 3, name: '璐㈠姟绠$悊绯荤粺鍗囩骇' },
+ { id: 4, name: '绉诲姩绔簲鐢ㄥ紑鍙�' }
+])
+
+// 鎶ュ憡鏁版嵁
+const reportData = ref({})
+
+// 妯℃嫙鎶ュ憡鏁版嵁
+const mockReportData = {
+ projectInfo: {
+ name: '浜у搧搴撳瓨绠$悊绯荤粺',
+ manager: '寮犱笁',
+ startDate: '2024-01-01',
+ endDate: '2024-03-31',
+ status: '宸插畬鎴�',
+ budget: '楼500,000'
+ },
+ taskStats: {
+ total: 45,
+ completed: 38,
+ inProgress: 5,
+ pending: 2,
+ completionRate: 84
+ },
+ issueStats: {
+ total: 12,
+ resolved: 9,
+ pending: 3,
+ topIssues: [
+ { title: '鏁版嵁搴撹繛鎺ヨ秴鏃�', severity: '楂�', status: '宸茶В鍐�' },
+ { title: '鍓嶇椤甸潰鍝嶅簲鎱�', severity: '涓�', status: '宸茶В鍐�' },
+ { title: '鏉冮檺楠岃瘉寮傚父', severity: '楂�', status: '寰呰В鍐�' },
+ { title: '鎶ヨ〃瀵煎嚭鍔熻兘缂哄け', severity: '涓�', status: '寰呰В鍐�' }
+ ]
+ },
+ delayAnalysis: {
+ delayedTasks: 8,
+ avgDelayDays: 3.5,
+ maxDelayDays: 12,
+ reasons: [
+ { reason: '闇�姹傚彉鏇�', count: 3 },
+ { reason: '鎶�鏈毦棰�', count: 2 },
+ { reason: '璧勬簮涓嶈冻', count: 2 },
+ { reason: '澶栭儴渚濊禆', count: 1 }
+ ]
+ },
+ teamPerformance: [
+ { name: '鏉庡洓', completedTasks: 12, completionRate: 92, performance: '浼樼' },
+ { name: '鐜嬩簲', completedTasks: 10, completionRate: 85, performance: '鑹ソ' },
+ { name: '璧靛叚', completedTasks: 8, completionRate: 78, performance: '鑹ソ' },
+ { name: '閽变竷', completedTasks: 8, completionRate: 72, performance: '涓�鑸�' }
+ ],
+ summary: {
+ conclusion: '鏈」鐩暣浣撴墽琛屾儏鍐佃壇濂斤紝浠诲姟瀹屾垚鐜囪揪鍒�84%锛屽洟闃熷崗浣滄晥鐜囪緝楂樸�備富瑕侀棶棰橀泦涓湪鎶�鏈疄鐜板拰闇�姹傚彉鏇存柟闈紝閫氳繃鍙婃椂娌熼�氬拰鎶�鏈敾鍏筹紝澶ч儴鍒嗛棶棰樺凡寰楀埌瑙e喅銆�',
+ suggestions: [
+ '鍔犲己闇�姹傚垎鏋愰樁娈电殑宸ヤ綔锛屽噺灏戝悗鏈熼渶姹傚彉鏇�',
+ '寤虹珛鎶�鏈毦棰橀璀︽満鍒讹紝鎻愬墠璇嗗埆鍜岃В鍐虫妧鏈闄�',
+ '浼樺寲鍥㈤槦璧勬簮閰嶇疆锛屾彁楂樻暣浣撳伐浣滄晥鐜�',
+ '瀹屽杽椤圭洰绠$悊娴佺▼锛屽姞寮鸿繃绋嬬洃鎺�'
+ ]
+ }
+}
+
+// 鐢熸垚鎶ュ憡
+const generateReport = async () => {
+ if (!reportConfig.projectId) {
+ ElMessage.warning('璇烽�夋嫨椤圭洰')
+ return
+ }
+
+ generating.value = true
+
+ // 妯℃嫙鐢熸垚鎶ュ憡鐨勮繃绋�
+ setTimeout(() => {
+ reportData.value = mockReportData
+ reportGenerated.value = true
+ generating.value = false
+ ElMessage.success('鎶ュ憡鐢熸垚鎴愬姛')
+ }, 2000)
+}
+
+// 瀵煎嚭鎶ュ憡
+const exportReport = () => {
+ ElMessage.success('鎶ュ憡瀵煎嚭鍔熻兘寮�鍙戜腑...')
+}
+
+// 閲嶇疆閰嶇疆锛岀敓鎴愭柊鎶ュ憡
+const resetConfig = () => {
+ reportGenerated.value = false
+ reportData.value = {}
+ // 閲嶇疆閰嶇疆涓洪粯璁ゅ��
+ reportConfig.projectId = 1
+ const endDate = new Date()
+ const startDate = new Date()
+ startDate.setDate(startDate.getDate() - 30)
+ reportConfig.dateRange = [
+ startDate.toISOString().split('T')[0],
+ endDate.toISOString().split('T')[0]
+ ]
+ ElMessage.success('宸查噸缃厤缃紝鍙互鐢熸垚鏂版姤鍛�')
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+ const statusMap = {
+ '宸插畬鎴�': 'success',
+ '杩涜涓�': 'warning',
+ '鏈紑濮�': 'info',
+ '宸叉殏鍋�': 'danger'
+ }
+ return statusMap[status] || 'info'
+}
+
+// 鑾峰彇涓ラ噸绋嬪害绫诲瀷
+const getSeverityType = (severity) => {
+ const severityMap = {
+ '楂�': 'danger',
+ '涓�': 'warning',
+ '浣�': 'success'
+ }
+ return severityMap[severity] || 'info'
+}
+
+// 鑾峰彇缁╂晥绫诲瀷
+const getPerformanceType = (performance) => {
+ const performanceMap = {
+ '浼樼': 'success',
+ '鑹ソ': 'primary',
+ '涓�鑸�': 'warning',
+ '寰呮敼杩�': 'danger'
+ }
+ return performanceMap[performance] || 'info'
+}
+
+// 缁勪欢鎸傝浇鏃跺垵濮嬪寲
+onMounted(() => {
+ // 璁剧疆榛樿椤圭洰
+ reportConfig.projectId = 1
+ // 璁剧疆榛樿鏃ユ湡鑼冨洿锛堟渶杩�30澶╋級
+ const endDate = new Date()
+ const startDate = new Date()
+ startDate.setDate(startDate.getDate() - 30)
+ reportConfig.dateRange = [
+ startDate.toISOString().split('T')[0],
+ endDate.toISOString().split('T')[0]
+ ]
+})
+</script>
+
+<style scoped>
+.report-generation {
+ padding: 20px;
+}
+
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.page-header h2 {
+ margin: 0;
+ color: #303133;
+}
+
+.header-actions {
+ display: flex;
+ gap: 10px;
+}
+
+.config-card {
+ margin-bottom: 20px;
+}
+
+.report-content {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.report-section {
+ margin-bottom: 20px;
+}
+
+.completion-stats {
+ display: flex;
+ justify-content: space-around;
+ padding: 20px 0;
+}
+
+.stat-item {
+ text-align: center;
+}
+
+.stat-label {
+ font-size: 14px;
+ color: #909399;
+ margin-bottom: 8px;
+}
+
+.stat-value {
+ font-size: 24px;
+ font-weight: bold;
+ color: #303133;
+}
+
+.stat-value.completed {
+ color: #67c23a;
+}
+
+.stat-value.in-progress {
+ color: #e6a23c;
+}
+
+.stat-value.pending {
+ color: #909399;
+}
+
+.completion-rate {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 200px;
+}
+
+.percentage-value {
+ font-size: 20px;
+ font-weight: bold;
+ color: #409eff;
+}
+
+.percentage-label {
+ font-size: 12px;
+ color: #909399;
+ margin-top: 4px;
+}
+
+.issue-summary {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding: 20px 0;
+}
+
+.summary-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.summary-label {
+ font-size: 14px;
+ color: #606266;
+}
+
+.summary-value {
+ font-size: 18px;
+ font-weight: bold;
+ color: #303133;
+}
+
+.summary-value.resolved {
+ color: #67c23a;
+}
+
+.summary-value.pending {
+ color: #e6a23c;
+}
+
+.delay-stats {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding: 20px 0;
+}
+
+.delay-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.delay-label {
+ font-size: 14px;
+ color: #606266;
+}
+
+.delay-value {
+ font-size: 18px;
+ font-weight: bold;
+ color: #e6a23c;
+}
+
+.delay-reasons h4 {
+ margin: 0 0 15px 0;
+ color: #303133;
+}
+
+.delay-reasons ul {
+ margin: 0;
+ padding-left: 20px;
+}
+
+.delay-reasons li {
+ margin-bottom: 8px;
+ color: #606266;
+}
+
+.summary-content h4 {
+ margin: 0 0 10px 0;
+ color: #303133;
+}
+
+.summary-content p {
+ line-height: 1.6;
+ color: #606266;
+ margin-bottom: 20px;
+}
+
+.summary-content ul {
+ margin: 0;
+ padding-left: 20px;
+}
+
+.summary-content li {
+ margin-bottom: 8px;
+ color: #606266;
+ line-height: 1.5;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/rpaManagement/index.vue b/src/views/collaborativeApproval/rpaManagement/index.vue
index 51cef73..9e5b504 100644
--- a/src/views/collaborativeApproval/rpaManagement/index.vue
+++ b/src/views/collaborativeApproval/rpaManagement/index.vue
@@ -22,6 +22,7 @@
</el-button>
</div>
<div>
+ <el-button @click="handleExport" style="margin-right: 10px">瀵煎嚭</el-button>
<el-button type="primary" @click="openForm('add')">鏂板</el-button>
<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
</div>
@@ -89,10 +90,10 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
-import { onMounted, ref, reactive, toRefs } from "vue";
+import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
-
+import {listRpa, addRpa, updateRpa, delRpa, delRpaBatch} from "@/api/collaborativeApproval/rpaManagement.js";
// 鍝嶅簲寮忔暟鎹�
const data = reactive({
searchForm: {
@@ -100,20 +101,17 @@
status: "",
},
form: {
- id: "",
programName: "",
- status: "stopped",
- description: "",
- createTime: "",
+ status: "",
+ description: ""
},
dialogVisible: false,
dialogTitle: "",
dialogType: "add",
- selectedIds: [],
tableLoading: false,
page: {
current: 1,
- size: 100,
+ size: 20,
total: 0,
},
tableData: [],
@@ -123,6 +121,8 @@
// 琛ㄥ崟寮曠敤
const formRef = ref();
+// 閫夋嫨鐨勮鏁版嵁
+const selectedRows = ref([]);
// 琛ㄥ崟楠岃瘉瑙勫垯
const rules = {
@@ -179,7 +179,7 @@
label: "鎿嶄綔",
align: "center",
fixed: "right",
- width: 230,
+ width: 150,
operation: [
{
name: "缂栬緫",
@@ -188,50 +188,26 @@
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'
- }
+ // {
+ // 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(() => {
@@ -240,32 +216,20 @@
// 鏌ヨ鏁版嵁
const handleQuery = () => {
- page.value.current = 1;
+ // 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;
+ listRpa({...page.value, ...searchForm.value})
+ .then(res => {
tableLoading.value = false;
- }, 500);
+ tableData.value = res.data.records
+ page.total = res.data.total;
+ }).catch(err => {
+ tableLoading.value = false;
+ })
};
// 鍒嗛〉澶勭悊
@@ -277,23 +241,16 @@
// 閫夋嫨鍙樺寲澶勭悊
const handleSelectionChange = (selection) => {
- selectedIds.value = selection.map(item => item.id);
+ selectedRows.value = selection;
};
// 鎵撳紑琛ㄥ崟
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 };
@@ -303,33 +260,38 @@
// 鎻愪氦琛ㄥ崟
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娣诲姞鎴愬姛");
+ addRpa({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("娣诲姞鎴愬姛");
+ form.value = {
+ programName: "",
+ status: "",
+ description: ""
+ },
+ dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
} else {
// 缂栬緫RPA
- const index = mockData.findIndex(item => item.id === form.value.id);
- if (index !== -1) {
- mockData[index] = { ...form.value };
- ElMessage.success("RPA鏇存柊鎴愬姛");
- }
+ updateRpa({...form.value}).then(res => {
+ if(res.code == 200){
+ ElMessage.success("鏇存柊鎴愬姛");
+ dialogVisible.value = false;
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
}
-
- dialogVisible.value = false;
- getList();
} catch (error) {
console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
}
@@ -368,33 +330,37 @@
// 鍒犻櫎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(() => {
- // 鐢ㄦ埛鍙栨秷
- });
+ if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map((item) => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ delRpa(ids).then((res) => {
+ if(res.code == 200){
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ getList();
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg);
+ })
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
};
+
+// 瀵煎嚭鍔熻兘
+const { proxy } = getCurrentInstance()
+const handleExport = () => {
+ proxy.download('/rpaProcessAutomation/export', { ...searchForm.value }, 'RPA绠$悊.xlsx')
+}
</script>
<style scoped></style>
diff --git a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
new file mode 100644
index 0000000..91a6468
--- /dev/null
+++ b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
@@ -0,0 +1,584 @@
+<template>
+ <div class="app-container">
+
+ <!-- 瑙勭珷鍒跺害绠$悊-->
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>瑙勭珷鍒跺害鍙戝竷</span>
+ </div>
+ </template>
+ <div class="tab-content">
+ <el-row :gutter="20" class="mb-20">
+ <span class="ml-10">鍒跺害鏍囬锛�</span>
+ <el-col :span="6">
+ <el-input v-model="regulationSearchForm.title" placeholder="璇疯緭鍏ュ埗搴︽爣棰�" clearable />
+ </el-col>
+ <span class="search_title">鍒跺害鍒嗙被锛�</span>
+ <el-col :span="4">
+ <el-select v-model="regulationSearchForm.category" placeholder="鍒跺害鍒嗙被" clearable>
+ <el-option label="浜轰簨鍒跺害" value="hr" />
+ <el-option label="璐㈠姟鍒跺害" value="finance" />
+ <el-option label="瀹夊叏鍒跺害" value="safety" />
+ <el-option label="鎶�鏈埗搴�" value="tech" />
+ </el-select>
+ </el-col>
+ <el-col :span="8">
+ <el-button type="primary" @click="searchRegulations">鎼滅储</el-button>
+ <el-button @click="resetRegulationSearch">閲嶇疆</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="success" @click="handleAdd">
+ 鍙戝竷鍒跺害
+ </el-button>
+ </el-col>
+ </el-row>
+
+ <el-table :data="regulations" border v-loading="tableLoading" style="width: 100%">
+ <el-table-column prop="regulationNum" label="鍒跺害缂栧彿" width="120" />
+ <el-table-column prop="title" label="鍒跺害鏍囬" min-width="150" />
+ <el-table-column prop="category" label="鍒嗙被" width="120">
+ <template #default="scope">
+ <el-tag>{{ getCategoryText(scope.row.category) }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="version" label="鐗堟湰" width="120" />
+ <el-table-column prop="createUserName" label="鍙戝竷浜�" width="120" />
+ <el-table-column prop="createTime" label="鍙戝竷鏃堕棿" width="180" />
+ <el-table-column prop="status" label="鐘舵��" width="100">
+ <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="readCount" label="宸茶浜烘暟" width="100" />
+ <el-table-column label="鎿嶄綔" width="320" fixed="right">
+ <template #default="scope">
+ <el-button link @click="viewRegulation(scope.row)">鏌ョ湅</el-button>
+ <el-button link type="primary" @click="handleEdit(scope.row)">缂栬緫</el-button>
+ <el-button link type="danger" @click="repealEdit(scope.row)">搴熷純</el-button>
+ <el-button link type="success" @click="viewVersionHistory(scope.row)">鐗堟湰鍘嗗彶</el-button>
+ <el-button link type="warning" @click="viewReadStatus(scope.row)">闃呰鐘舵��</el-button>
+ <el-button link type="primary" @click="openFileDialog(scope.row)">闄勪欢</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+ </el-card>
+
+ <!-- 鐢ㄥ嵃鐢宠瀵硅瘽妗嗭紙宸茬Щ闄わ級 -->
+
+ <!-- 瑙勭珷鍒跺害鍙戝竷瀵硅瘽妗� -->
+ <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '鍙戝竷鍒跺害' : '缂栬緫鍒跺害'" width="800px">
+ <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px">
+ <el-form-item label="鍒跺害缂栧彿" prop="regulationNum">
+ <el-input v-model="regulationForm.regulationNum" placeholder="璇疯緭鍏ュ埗搴︾紪鍙�" />
+ </el-form-item>
+ <el-form-item label="鍒跺害鏍囬" prop="title">
+ <el-input v-model="regulationForm.title" placeholder="璇疯緭鍏ュ埗搴︽爣棰�" />
+ </el-form-item>
+ <el-form-item label="鍒跺害鍒嗙被" prop="category">
+ <el-select v-model="regulationForm.category" placeholder="璇烽�夋嫨鍒跺害鍒嗙被" style="width: 100%">
+ <el-option label="浜轰簨鍒跺害" value="hr" />
+ <el-option label="璐㈠姟鍒跺害" value="finance" />
+ <el-option label="瀹夊叏鍒跺害" value="safety" />
+ <el-option label="鎶�鏈埗搴�" value="tech" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍒跺害鍐呭" prop="content">
+ <el-input v-model="regulationForm.content" type="textarea" :rows="10" placeholder="璇疯緭鍏ュ埗搴﹁缁嗗唴瀹�" />
+ </el-form-item>
+ <el-form-item label="鍒跺害鐗堟湰" prop="version">
+ <el-input v-model="regulationForm.version" placeholder="璇疯緭鍏ュ埗搴︾増鏈�" />
+ </el-form-item>
+ <el-form-item label="鐢熸晥鏃堕棿" prop="effectiveTime">
+ <el-date-picker v-model="regulationForm.effectiveTime" type="datetime" format="YYYY-MM-DD HH:mm:ss"
+ value-format="YYYY-MM-DD HH:mm:ss" placeholder="閫夋嫨鐢熸晥鏃堕棿" style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="閫傜敤鑼冨洿" prop="scope">
+ <el-checkbox-group v-model="regulationForm.scope">
+ <el-checkbox label="all">鍏ㄤ綋鍛樺伐</el-checkbox>
+ <el-checkbox label="manager">绠$悊灞�</el-checkbox>
+ <el-checkbox label="hr">浜轰簨閮ㄩ棬</el-checkbox>
+ <el-checkbox label="finance">璐㈠姟閮ㄩ棬</el-checkbox>
+ <el-checkbox label="tech">鎶�鏈儴闂�</el-checkbox>
+ </el-checkbox-group>
+ </el-form-item>
+ <el-form-item label="鏄惁闇�瑕佺‘璁�" prop="requireConfirm">
+ <el-switch v-model="regulationForm.requireConfirm" />
+ <span class="ml-10">寮�鍚悗鍛樺伐闇�瑕侀槄璇荤‘璁�</span>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="showRegulationDialog = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitRegulation">鍙戝竷鍒跺害</el-button>
+ </span>
+ </template>
+ </el-dialog>
+
+ <!-- 鐢ㄥ嵃璇︽儏瀵硅瘽妗嗭紙宸茬Щ闄わ級 -->
+
+ <!-- 瑙勭珷鍒跺害璇︽儏瀵硅瘽妗� -->
+ <el-dialog v-model="showRegulationDetailDialog" title="瑙勭珷鍒跺害璇︽儏" width="800px">
+ <div v-if="currentRegulationDetail">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鍒跺害缂栧彿">{{ currentRegulationDetail.id }}</el-descriptions-item>
+ <el-descriptions-item label="鍒跺害鏍囬">{{ currentRegulationDetail.title }}</el-descriptions-item>
+ <el-descriptions-item label="鍒嗙被">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item>
+ <el-descriptions-item label="鐗堟湰">{{ currentRegulationDetail.version }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戝竷浜�">{{ currentRegulationDetail.createUserName }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戝竷鏃堕棿">{{ currentRegulationDetail.createTime }}</el-descriptions-item>
+ </el-descriptions>
+ <div class="mt-20">
+ <h4>鍒跺害鍐呭</h4>
+ <div class="regulation-content">{{ currentRegulationDetail.content }}</div>
+ </div>
+ <!-- 濡傛灉tableData>0 鏄剧ず -->
+ <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" >
+ <el-button type="success" @click="resetForm(currentRegulationDetail)">纭鏌ョ湅</el-button>
+ </div>
+ </div>
+ </el-dialog>
+
+ <!-- 鐗堟湰鍘嗗彶瀵硅瘽妗� -->
+ <el-dialog v-model="showVersionHistoryDialog" title="鐗堟湰鍘嗗彶" width="800px">
+ <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px">
+ <el-table-column prop="version" label="鐗堟湰鍙�" width="100" />
+ <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" width="180" />
+ <el-table-column prop="createUserName" label="鏇存柊浜�" width="120" />
+ <el-table-column prop="changeLog" 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>
+ </el-dialog>
+
+ <!-- 闃呰鐘舵�佸璇濇 -->
+ <el-dialog v-model="showReadStatusDialog" title="闃呰鐘舵��" width="800px">
+ <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px">
+ <el-table-column prop="employee" label="鍛樺伐濮撳悕" width="120" />
+ <el-table-column prop="department" label="鎵�灞為儴闂�" width="150" />
+ <el-table-column prop="createTime" label="闃呰鏃堕棿" width="180" />
+ <el-table-column prop="confirmTime" label="纭鏃堕棿" width="180" />
+ <el-table-column prop="status" label="鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'">
+ {{ scope.row.status === 'confirmed' ? '宸茬‘璁�' : '鏈‘璁�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-dialog>
+
+ <FileListDialog
+ ref="fileListDialogRef"
+ v-model="fileDialogVisible"
+ :show-upload-button="true"
+ :show-delete-button="true"
+ :delete-method="handleAttachmentDelete"
+ :rules-regulations-management-id="currentFileRuleId"
+ :name-column-label="'闄勪欢鍚嶇О'"
+ @upload="handleAttachmentUpload"
+ />
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,addReadingStatus,updateReadingStatus } from '@/api/collaborativeApproval/sealManagement.js'
+import FileListDialog from '@/components/Dialog/FileListDialog.vue'
+import { listRuleFiles, delRuleFile, addRuleFile } from '@/api/collaborativeApproval/rulesRegulationsManagementFile.js'
+
+// 鍝嶅簲寮忔暟鎹�
+const operationType = ref('add')
+const tableData = ref([])
+const tableLoading = ref(false)
+// 鍒嗛〉鍙傛暟
+const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0
+})
+// 闄勪欢寮圭獥
+const fileDialogVisible = ref(false)
+const fileListDialogRef = ref(null)
+const currentFileRuleId = ref(null)
+const filePage = reactive({
+ current: 1,
+ size: 10,
+ total: 0
+})
+// 瑙勭珷鍒跺害鐩稿叧
+const showRegulationDialog = ref(false)
+const showRegulationDetailDialog = ref(false)
+const showVersionHistoryDialog = ref(false)
+const showReadStatusDialog = ref(false)
+const currentRegulationDetail = ref(null)
+const regulationFormRef = ref()
+const regulationForm = reactive({
+ id: '',
+ regulationNum: '',
+ title: '',
+ category: '',
+ content: '',
+ version: '',
+ status: 'active',
+ readCount: 0,
+ effectiveTime: '',
+ scope: [],
+ requireConfirm: false
+})
+
+const readStatus = ref({
+ id: '',
+ ruleId: '',
+ employee: '',
+ department: '',
+ createTime: '',
+ confirmTime: '',
+ status: 'unconfirmed'
+})
+
+const regulationRules = {
+ title: [{ required: true, message: '璇疯緭鍏ュ埗搴︽爣棰�', trigger: 'blur' }],
+ category: [{ required: true, message: '璇烽�夋嫨鍒跺害鍒嗙被', trigger: 'change' }],
+ content: [{ required: true, message: '璇疯緭鍏ュ埗搴﹀唴瀹�', trigger: 'blur' }],
+ effectiveTime: [{ required: true, message: '璇烽�夋嫨鐢熸晥鏃堕棿', trigger: 'change' }],
+ scope: [{ required: true, message: '璇烽�夋嫨閫傜敤鑼冨洿', trigger: 'change' }]
+}
+
+const regulationSearchForm = reactive({
+ title: '',
+ category: ''
+})
+
+const regulations = ref([])
+
+const versionHistory = ref([])
+
+const readStatusList = ref([])
+ // { employee: '闄堝織寮�', department: '閿�鍞儴', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' },
+ // { employee: '鍒橀泤濠�', department: '鎶�鏈儴', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' },
+ // { employee: '鐜嬪缓鍥�', department: '璐㈠姟閮�', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' }
+
+// 鍒跺害鍒嗙被
+const getCategoryText = (category) => {
+ const categoryMap = {
+ hr: '浜轰簨鍒跺害',
+ finance: '璐㈠姟鍒跺害',
+ safety: '瀹夊叏鍒跺害',
+ tech: '鎶�鏈埗搴�'
+ }
+ return categoryMap[category] || '鏈煡'
+}
+// 鎼滅储鍒跺害
+const searchRegulations = () => {
+ page.current=1
+ getRegulationList()
+}
+// 閲嶇疆鍒跺害鎼滅储
+const resetRegulationSearch = () => {
+ regulationSearchForm.title = ''
+ regulationSearchForm.category = ''
+ searchRegulations()
+}
+// 鏂板
+const handleAdd = () => {
+ operationType.value = 'add'
+ resetRegulationForm()
+ showRegulationDialog.value = true
+}
+
+// 缂栬緫
+const handleEdit = (row) => {
+ operationType.value = 'edit'
+ Object.assign(regulationForm, row)
+ showRegulationDialog.value = true
+}
+// 搴熷純
+const repealEdit = (row) => {
+ operationType.value = 'edit'
+ Object.assign(regulationForm, row)
+ regulationForm.status = 'repealed'
+ ElMessageBox.confirm('纭搴熷純璇ュ埗搴︼紵', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ updateRuleManagement(regulationForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害搴熷純鎴愬姛')
+ // showRegulationDialog.value = false
+ getRegulationList()
+ resetRegulationForm()
+ }
+ })
+ }).catch(() => {
+ ElMessage({
+ type: 'info',
+ message: '宸插彇娑堝簾寮�'
+ })
+ })
+}
+// 鍙戝竷鍒跺害
+const submitRegulation = async () => {
+ try {
+ await regulationFormRef.value.validate()
+ if(operationType.value == 'add'){
+ addRuleManagement(regulationForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害鍙戝竷鎴愬姛')
+ showRegulationDialog.value = false
+ getRegulationList()
+ resetRegulationForm()
+ }
+ })
+ }else{
+ updateRuleManagement(regulationForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害缂栬緫鎴愬姛')
+ showRegulationDialog.value = false
+ resetRegulationForm()
+ getRegulationList()
+ }})}
+ }catch(err){
+ ElMessage.error(err.msg)
+ }
+}
+//閲嶇疆鍒跺害琛ㄥ崟
+const resetRegulationForm = () => {
+ Object.assign(regulationForm, {
+ id: '',
+ regulationNum: '',
+ title: '',
+ category: '',
+ content: '',
+ version: '',
+ status: 'active',
+ readCount: 0,
+ effectiveTime: '',
+ scope: [],
+ requireConfirm: false
+})
+}
+
+
+// 鏌ョ湅鍒跺害鐗堟湰鍘嗗彶
+const viewVersionHistory = (row) => {
+ showVersionHistoryDialog.value = true
+ const params = {
+
+ category: row.category
+ }
+ listRuleManagement(page,params).then(res => {
+ if(res.code == 200){
+ versionHistory.value = res.data.records
+ }
+ })
+}
+// 鏌ョ湅鍒跺害璇︽儏
+const viewRegulation = (row) => {
+ currentRegulationDetail.value = row
+ showRegulationDetailDialog.value = true
+ getReadingStatusByRuleId(row.id).then(res => {
+ if(res.code == 200){
+ readStatusList.value = res.data
+ if(readStatusList.value.length==0 && tableData.value.length>0){
+ const params = {
+ ruleId: row.id,
+ employee: tableData.value[0].staffName,
+ department: tableData.value[0].postJob,
+ status: 'unconfirmed'
+ }
+ addReadingStatus(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害闃呰鎴愬姛')
+ }
+ })
+ }
+ }
+ })
+
+}
+// 鏌ョ湅鍒跺害闃呰鐘舵��
+const viewReadStatus = (row) => {
+ showReadStatusDialog.value = true
+ //鏌ョ湅闃呰鐘舵�佸垪琛�
+ getReadingStatusByRuleId(row.id).then(res => {
+ if(res.code == 200){
+ readStatusList.value = res.data
+ }
+ })
+}
+
+//纭鏌ョ湅
+const resetForm = (row) => {
+ console.log("row",row)
+ row.readCount = row.readCount + 1
+
+ updateRuleManagement(row).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鏌ョ湅鏁伴噺淇敼鎴愬姛')
+ //淇敼闃呰鐘舵��
+ //鏍规嵁鍒跺害id鍜屽綋鍓嶇櫥褰曠殑鍛樺伐寰楀埌闃呰鐘舵��
+ // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName )
+ // if(item.length>0){
+ // item[0].status = 'confirmed',
+ // item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
+ // }
+ // 绛涢�夊綋鍓嶅憳宸ュ搴旇鍒跺害鐨勯槄璇荤姸鎬佽褰�
+ let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id);
+
+ if (statusItem) {
+ // 濡傛灉鎵惧埌璁板綍锛屾洿鏂扮姸鎬佸拰纭鏃堕棿
+ statusItem.status = 'confirmed';
+ // 鏍煎紡鍖栨椂闂翠负"YYYY-MM-DD HH:mm:ss"鏍煎紡
+ const now = new Date();
+ statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
+ // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
+
+ updateReadingStatus(statusItem).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害闃呰鐘舵�佷慨鏀规垚鍔�')
+ }
+ })
+ }
+
+ }
+ })
+}
+
+// 瀵煎嚭瑙勭珷鍒跺害
+const { proxy } = getCurrentInstance()
+const handleExport = () => {
+ proxy.download('/rulesRegulationsManagement/export', { ...regulationSearchForm }, '瑙勭珷鍒跺害.xlsx')
+}
+
+// 闄勪欢锛氭煡璇�
+const fetchRuleFiles = async (rulesRegulationsManagementId) => {
+ const params = {
+ current: filePage.current,
+ size: filePage.size,
+ rulesRegulationsManagementId
+ }
+ const res = await listRuleFiles(params)
+ const records = res?.data?.records || []
+ filePage.total = res?.data?.total || records.length
+ const mapped = records.map(item => ({
+ id: item.id,
+ name: item.fileName || item.name,
+ url: item.fileUrl || item.url,
+ raw: item
+ }))
+ fileListDialogRef.value?.setList(mapped)
+}
+
+// 鎵撳紑闄勪欢寮圭獥
+const openFileDialog = async (row) => {
+ currentFileRuleId.value = row.id
+ fileDialogVisible.value = true
+ await fetchRuleFiles(row.id)
+}
+
+// 鍒锋柊闄勪欢鍒楄〃
+const refreshFileList = async () => {
+ if (!currentFileRuleId.value) return
+ await fetchRuleFiles(currentFileRuleId.value)
+}
+
+// 涓婁紶闄勪欢锛堢敱瀛愮粍浠惰Е鍙戯級
+const handleAttachmentUpload = async (filePayload) => {
+ if (!currentFileRuleId.value) return
+ const payload = {
+ name: filePayload?.fileName || filePayload?.name,
+ url: filePayload?.fileUrl || filePayload?.url,
+ rulesRegulationsManagementId: currentFileRuleId.value
+ }
+ await addRuleFile(payload)
+ ElMessage.success('鏂囦欢涓婁紶鎴愬姛')
+ await refreshFileList()
+}
+
+// 鍒犻櫎闄勪欢
+const handleAttachmentDelete = async (row) => {
+ if (!row?.id) return false
+ try {
+ await ElMessageBox.confirm('纭鍒犻櫎璇ラ檮浠讹紵', '鎻愮ず', { type: 'warning' })
+ } catch {
+ return false
+ }
+ await delRuleFile([row.id])
+ ElMessage.success('鍒犻櫎鎴愬姛')
+ await refreshFileList()
+}
+
+// 鑾峰彇瑙勭珷鍒跺害鍒楄〃鏁版嵁
+const getRegulationList = async () => {
+ tableLoading.value = true
+ listRuleManagement(page,regulationSearchForm)
+ .then(res => {
+
+ regulations.value = res.data.records
+ // 杩囨护鎺夊凡搴熷純鐨勫埗搴�
+ // regulations.value = res.data.records.filter(item => item.status !== 'repealed')
+ page.value.total = res.data.total;
+ tableLoading.value = false;
+
+ }).catch(err => {
+ tableLoading.value = false;
+ })
+}
+
+onMounted(() => {
+ // 鍒濆鍖�
+ getRegulationList()
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.tab-content {
+ padding: 20px 0;
+}
+
+.mb-20 {
+ margin-bottom: 20px;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+.ml-10 {
+ margin-left: 10px;
+}
+
+.regulation-content {
+ background-color: #f5f5f5;
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ height: 200px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/sealManagement/index.vue b/src/views/collaborativeApproval/sealManagement/index.vue
new file mode 100644
index 0000000..8e0d054
--- /dev/null
+++ b/src/views/collaborativeApproval/sealManagement/index.vue
@@ -0,0 +1,801 @@
+<template>
+ <div class="app-container">
+ <el-card class="box-card">
+ <template #header>
+ <div class="card-header">
+ <span>鐢ㄥ嵃绠$悊鍙戝竷</span>
+ </div>
+ </template>
+
+
+ <!-- 鐢ㄥ嵃鐢宠绠$悊 -->
+ <div class="tab-content">
+ <el-row :gutter="20" class="mb-20 ">
+ <span class="ml-10">鐢ㄥ嵃鏍囬锛�</span>
+ <el-col :span="4">
+ <el-input v-model="sealSearchForm.title" placeholder="璇疯緭鍏ョ敵璇锋爣棰�" clearable />
+ </el-col>
+ <span class="ml-10">鐢ㄥ嵃缂栧彿锛�</span>
+ <el-col :span="4">
+ <el-input v-model="sealSearchForm.applicationNum" placeholder="璇疯緭鍏ョ敤鍗扮紪鍙�" clearable />
+ </el-col>
+ <span class="search_title">瀹℃壒鐘舵�侊細</span>
+ <el-col :span="4">
+ <el-select v-model="sealSearchForm.status" placeholder="瀹℃壒鐘舵��" clearable>
+ <el-option label="寰呭鎵�" value="pending" />
+ <el-option label="宸查�氳繃" value="approved" />
+ <el-option label="宸叉嫆缁�" value="rejected" />
+ </el-select>
+ </el-col>
+ <el-col :span="8">
+ <el-button type="primary" @click="searchSealApplications">鎼滅储</el-button>
+ <el-button @click="resetSealSearch">閲嶇疆</el-button>
+ <el-button @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="primary" @click="showSealApplyDialog = true">鐢宠鐢ㄥ嵃
+ </el-button>
+ </el-col>
+ </el-row>
+
+ <el-table :data="sealApplications" border v-loading="tableLoading" style="width: 100%">
+ <el-table-column prop="applicationNum" label="鐢宠缂栧彿" width="120" />
+ <el-table-column prop="title" label="鐢宠鏍囬" min-width="200" />
+ <el-table-column prop="createUserName" label="鐢宠浜�" width="120" />
+ <el-table-column prop="department" label="鎵�灞為儴闂�" width="150" />
+ <el-table-column prop="sealType" label="鐢ㄥ嵃绫诲瀷" width="120">
+ <template #default="scope">
+ {{ getSealTypeText(scope.row.sealType) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="createTime" label="鐢宠鏃堕棿" width="180" />
+ <el-table-column prop="status" label="鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="200" fixed="right">
+ <template #default="scope">
+ <el-button link @click="viewSealDetail(scope.row)">鏌ョ湅</el-button>
+ <el-button
+ v-if="scope.row.status === 'pending'"
+ link
+ type="primary"
+ @click="approveSeal(scope.row)"
+ >
+ 瀹℃壒
+ </el-button>
+ <el-button
+ v-if="scope.row.status === 'pending'"
+ link
+ type="danger"
+ @click="rejectSeal(scope.row)"
+ >
+ 鎷掔粷
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current" :limit="page.size" @pagination="paginationChange" />
+ </div>
+ </el-card>
+
+ <!-- 鐢ㄥ嵃鐢宠瀵硅瘽妗� -->
+ <el-dialog v-model="showSealApplyDialog" title="鐢宠鐢ㄥ嵃" width="600px">
+ <el-form :model="sealForm" :rules="sealRules" ref="sealFormRef" label-width="100px">
+ <el-form-item label="鐢宠缂栧彿" prop="applicationNum">
+ <el-input v-model="sealForm.applicationNum" placeholder="璇疯緭鍏ョ敵璇风紪鍙�" />
+ </el-form-item>
+ <el-form-item label="鐢宠鏍囬" prop="title">
+ <el-input v-model="sealForm.title" placeholder="璇疯緭鍏ョ敵璇锋爣棰�" />
+ </el-form-item>
+ <el-form-item label="鐢ㄥ嵃绫诲瀷" prop="sealType">
+ <el-select v-model="sealForm.sealType" placeholder="璇烽�夋嫨鐢ㄥ嵃绫诲瀷" style="width: 100%">
+ <el-option label="鍏珷" value="official" />
+ <el-option label="鍚堝悓涓撶敤绔�" value="contract" />
+ <el-option label="璐㈠姟涓撶敤绔�" value="finance" />
+ <el-option label="娉曚汉绔�" value="legal" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鐢宠鍘熷洜" prop="reason">
+ <el-input v-model="sealForm.reason" type="textarea" :rows="4" placeholder="璇疯缁嗚鏄庣敤鍗板師鍥�" />
+ </el-form-item>
+ <el-form-item label="瀹℃壒浜�" prop="approveUserId">
+ <el-select v-model="sealForm.approveUserId" placeholder="璇烽�夋嫨瀹℃壒浜�" style="width: 100%" filterable>
+ <el-option
+ v-for="user in userList"
+ :key="user.userId"
+ :label="user.nickName"
+ :value="user.userId"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="绱ф�ョ▼搴�" prop="urgency">
+ <el-radio-group v-model="sealForm.urgency">
+ <el-radio label="normal">鏅��</el-radio>
+ <el-radio label="urgent">绱ф��</el-radio>
+ <el-radio label="very-urgent">鐗规��</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="showSealApplyDialog = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitSealApplication">鎻愪氦鐢宠</el-button>
+ </span>
+ </template>
+ </el-dialog>
+
+ <!-- 瑙勭珷鍒跺害鍙戝竷瀵硅瘽妗� -->
+ <!-- <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '鍙戝竷鍒跺害' : '缂栬緫鍒跺害'" width="800px">
+ <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px">
+ <el-form-item label="鍒跺害缂栧彿" prop="regulationNum">
+ <el-input v-model="regulationForm.regulationNum" placeholder="璇疯緭鍏ュ埗搴︾紪鍙�" />
+ </el-form-item>
+ <el-form-item label="鍒跺害鏍囬" prop="title">
+ <el-input v-model="regulationForm.title" placeholder="璇疯緭鍏ュ埗搴︽爣棰�" />
+ </el-form-item>
+ <el-form-item label="鍒跺害鍒嗙被" prop="category">
+ <el-select v-model="regulationForm.category" placeholder="璇烽�夋嫨鍒跺害鍒嗙被" style="width: 100%">
+ <el-option label="浜轰簨鍒跺害" value="hr" />
+ <el-option label="璐㈠姟鍒跺害" value="finance" />
+ <el-option label="瀹夊叏鍒跺害" value="safety" />
+ <el-option label="鎶�鏈埗搴�" value="tech" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="鍒跺害鍐呭" prop="content">
+ <el-input v-model="regulationForm.content" type="textarea" :rows="10" placeholder="璇疯緭鍏ュ埗搴﹁缁嗗唴瀹�" />
+ </el-form-item>
+ <el-form-item label="鍒跺害鐗堟湰" prop="version">
+ <el-input v-model="regulationForm.version" placeholder="璇疯緭鍏ュ埗搴︾増鏈�" />
+ </el-form-item>
+ <el-form-item label="鐢熸晥鏃堕棿" prop="effectiveTime">
+ <el-date-picker v-model="regulationForm.effectiveTime" type="datetime" format="YYYY-MM-DD HH:mm:ss"
+ value-format="YYYY-MM-DD HH:mm:ss" placeholder="閫夋嫨鐢熸晥鏃堕棿" style="width: 100%" />
+ </el-form-item>
+ <el-form-item label="閫傜敤鑼冨洿" prop="scope">
+ <el-checkbox-group v-model="regulationForm.scope">
+ <el-checkbox label="all">鍏ㄤ綋鍛樺伐</el-checkbox>
+ <el-checkbox label="manager">绠$悊灞�</el-checkbox>
+ <el-checkbox label="hr">浜轰簨閮ㄩ棬</el-checkbox>
+ <el-checkbox label="finance">璐㈠姟閮ㄩ棬</el-checkbox>
+ <el-checkbox label="tech">鎶�鏈儴闂�</el-checkbox>
+ </el-checkbox-group>
+ </el-form-item>
+ <el-form-item label="鏄惁闇�瑕佺‘璁�" prop="requireConfirm">
+ <el-switch v-model="regulationForm.requireConfirm" />
+ <span class="ml-10">寮�鍚悗鍛樺伐闇�瑕侀槄璇荤‘璁�</span>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="showRegulationDialog = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submitRegulation">鍙戝竷鍒跺害</el-button>
+ </span>
+ </template>
+ </el-dialog> -->
+
+ <!-- 鐢ㄥ嵃璇︽儏瀵硅瘽妗� -->
+ <el-dialog v-model="showSealDetailDialog" title="鐢ㄥ嵃鐢宠璇︽儏" width="700px">
+ <div v-if="currentSealDetail" class="mb10">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鐢宠缂栧彿">{{ currentSealDetail.id }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏍囬">{{ currentSealDetail.title }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠浜�">{{ currentSealDetail.createUserName }}</el-descriptions-item>
+ <el-descriptions-item label="鎵�灞為儴闂�">{{ currentSealDetail.department }}</el-descriptions-item>
+ <el-descriptions-item label="鐢ㄥ嵃绫诲瀷">{{ getSealTypeText(currentSealDetail.sealType) }}</el-descriptions-item>
+ <el-descriptions-item label="鐢宠鏃堕棿">{{ currentSealDetail.createTime }}</el-descriptions-item>
+ <el-descriptions-item label="鐘舵��">
+ <el-tag :type="getStatusType(currentSealDetail.status)">
+ {{ getStatusText(currentSealDetail.status) }}
+ </el-tag>
+ </el-descriptions-item>
+ <el-descriptions-item label="鐢宠鍘熷洜" :span="2">{{ currentSealDetail.reason }}</el-descriptions-item>
+ </el-descriptions>
+ </div>
+ </el-dialog>
+
+ <!-- 瑙勭珷鍒跺害璇︽儏瀵硅瘽妗� -->
+ <el-dialog v-model="showRegulationDetailDialog" title="瑙勭珷鍒跺害璇︽儏" width="800px">
+ <div v-if="currentRegulationDetail">
+ <el-descriptions :column="2" border>
+ <el-descriptions-item label="鍒跺害缂栧彿">{{ currentRegulationDetail.id }}</el-descriptions-item>
+ <el-descriptions-item label="鍒跺害鏍囬">{{ currentRegulationDetail.title }}</el-descriptions-item>
+ <el-descriptions-item label="鍒嗙被">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item>
+ <el-descriptions-item label="鐗堟湰">{{ currentRegulationDetail.version }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戝竷浜�">{{ currentRegulationDetail.createUserName }}</el-descriptions-item>
+ <el-descriptions-item label="鍙戝竷鏃堕棿">{{ currentRegulationDetail.createTime }}</el-descriptions-item>
+ </el-descriptions>
+ <div class="mt-20">
+ <h4>鍒跺害鍐呭</h4>
+ <div class="regulation-content">{{ currentRegulationDetail.content }}</div>
+ </div>
+ <!-- 濡傛灉tableData>0 鏄剧ず -->
+ <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" >
+ <el-button type="success" @click="resetForm(currentRegulationDetail)">纭鏌ョ湅</el-button>
+ </div>
+ </div>
+ </el-dialog>
+
+ <!-- 鐗堟湰鍘嗗彶瀵硅瘽妗� -->
+ <el-dialog v-model="showVersionHistoryDialog" title="鐗堟湰鍘嗗彶" width="800px">
+ <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px">
+ <el-table-column prop="version" label="鐗堟湰鍙�" width="100" />
+ <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" width="180" />
+ <el-table-column prop="createUserName" label="鏇存柊浜�" width="120" />
+ <el-table-column prop="changeLog" 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>
+ </el-dialog>
+
+ <!-- 闃呰鐘舵�佸璇濇 -->
+ <el-dialog v-model="showReadStatusDialog" title="闃呰鐘舵��" width="800px">
+ <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px">
+ <el-table-column prop="employee" label="鍛樺伐濮撳悕" width="120" />
+ <el-table-column prop="department" label="鎵�灞為儴闂�" width="150" />
+ <el-table-column prop="createTime" label="闃呰鏃堕棿" width="180" />
+ <el-table-column prop="confirmTime" label="纭鏃堕棿" width="180" />
+ <el-table-column prop="status" label="鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'">
+ {{ scope.row.status === 'confirmed' ? '宸茬‘璁�' : '鏈‘璁�' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Plus } from '@element-plus/icons-vue'
+import { listSealApplication, addSealApplication, updateSealApplication,listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,getReadingStatusList,addReadingStatus,updateReadingStatus } from '@/api/collaborativeApproval/sealManagement.js'
+import { el } from 'element-plus/es/locales.mjs'
+import { getUserProfile, userListNoPageByTenantId } from '@/api/system/user.js'
+import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
+import useUserStore from '@/store/modules/user'
+import { userLoginFacotryList } from "@/api/system/user.js"
+
+// 鍝嶅簲寮忔暟鎹�
+const currentUser = ref(null)
+const activeTab = ref('seal')
+const operationType = ref('add')
+const tableData = ref([])
+// 鐢ㄥ嵃鐢宠鐩稿叧
+const userStore = useUserStore()
+const route = useRoute()
+const showSealApplyDialog = ref(false)
+const tableLoading = ref(false)
+const showSealDetailDialog = ref(false)
+const currentSealDetail = ref(null)
+const sealFormRef = ref()
+const userList = ref([])
+const sealForm = reactive({
+ applicationNum: '',
+ title: '',
+ sealType: '',
+ reason: '',
+ approveUserId: '',
+ urgency: 'normal',
+ status: 'pending'
+})
+
+const sealRules = {
+ applicationNum: [{ required: true, message: '璇疯緭鍏ョ敵璇风紪鍙�', trigger: 'blur' }],
+ title: [{ required: true, message: '璇疯緭鍏ョ敵璇锋爣棰�', trigger: 'blur' }],
+ sealType: [{ required: true, message: '璇烽�夋嫨鐢ㄥ嵃绫诲瀷', trigger: 'change' }],
+ reason: [{ required: true, message: '璇疯緭鍏ョ敵璇峰師鍥�', trigger: 'blur' }],
+ approveUserId: [{ required: true, message: '璇烽�夋嫨瀹℃壒浜�', trigger: 'change' }]
+}
+
+const sealSearchForm = reactive({
+ title: '',
+ status: '',
+ applicationNum: ''
+})
+// 鍒嗛〉鍙傛暟
+const page = reactive({
+ current: 1,
+ size: 100,
+ total: 0
+})
+// 瑙勭珷鍒跺害鐩稿叧
+const showRegulationDialog = ref(false)
+const showRegulationDetailDialog = ref(false)
+const showVersionHistoryDialog = ref(false)
+const showReadStatusDialog = ref(false)
+const currentRegulationDetail = ref(null)
+const regulationFormRef = ref()
+const regulationForm = reactive({
+ id: '',
+ regulationNum: '',
+ title: '',
+ category: '',
+ content: '',
+ version: '',
+ status: 'active',
+ readCount: 0,
+ effectiveTime: '',
+ scope: [],
+ requireConfirm: false
+})
+
+const readStatus = ref({
+ id: '',
+ ruleId: '',
+ employee: '',
+ department: '',
+ createTime: '',
+ confirmTime: '',
+ status: 'unconfirmed'
+})
+
+const regulationRules = {
+ title: [{ required: true, message: '璇疯緭鍏ュ埗搴︽爣棰�', trigger: 'blur' }],
+ category: [{ required: true, message: '璇烽�夋嫨鍒跺害鍒嗙被', trigger: 'change' }],
+ content: [{ required: true, message: '璇疯緭鍏ュ埗搴﹀唴瀹�', trigger: 'blur' }],
+ effectiveTime: [{ required: true, message: '璇烽�夋嫨鐢熸晥鏃堕棿', trigger: 'change' }],
+ scope: [{ required: true, message: '璇烽�夋嫨閫傜敤鑼冨洿', trigger: 'change' }]
+}
+
+const regulationSearchForm = reactive({
+ title: '',
+ category: ''
+})
+
+// 鍋囨暟鎹�
+const sealApplications = ref([])
+
+const regulations = ref([])
+
+const versionHistory = ref([])
+
+const readStatusList = ref([])
+ // { employee: '闄堝織寮�', department: '閿�鍞儴', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' },
+ // { employee: '鍒橀泤濠�', department: '鎶�鏈儴', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' },
+ // { employee: '鐜嬪缓鍥�', department: '璐㈠姟閮�', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' }
+
+// 鐢ㄥ嵃鐢宠鐘舵��
+const getStatusType = (status) => {
+ const statusMap = {
+ pending: 'warning',
+ approved: 'success',
+ rejected: 'danger'
+ }
+ return statusMap[status] || 'info'
+}
+// 鍒跺害鐘舵��
+const getStatusText = (status) => {
+ const statusMap = {
+ pending: '寰呭鎵�',
+ approved: '宸查�氳繃',
+ rejected: '宸叉嫆缁�'
+ }
+ return statusMap[status] || '鏈煡'
+}
+// 鐢ㄥ嵃绫诲瀷
+const getSealTypeText = (sealType) => {
+ const sealTypeMap = {
+ official: '鍏珷',
+ contract: '鍚堝悓涓撶敤绔�',
+ finance: '璐㈠姟涓撶敤绔�',
+ tegal: '鎶�鏈笓鐢ㄧ珷'
+ }
+ return sealTypeMap[sealType] || '鏈煡'
+}
+// 鍒跺害鍒嗙被
+const getCategoryText = (category) => {
+ const categoryMap = {
+ hr: '浜轰簨鍒跺害',
+ finance: '璐㈠姟鍒跺害',
+ safety: '瀹夊叏鍒跺害',
+ tech: '鎶�鏈埗搴�'
+ }
+ return categoryMap[category] || '鏈煡'
+}
+// 鎼滅储鍗扮珷鐢宠
+const searchSealApplications = () => {
+ page.current=1
+ getSealApplicationList()
+
+ // ElMessage.success('鎼滅储瀹屾垚')
+}
+// 閲嶇疆鍗扮珷鐢宠鎼滅储
+const resetSealSearch = () => {
+ sealSearchForm.title = ''
+ sealSearchForm.status = ''
+ sealSearchForm.applicationNum = ''
+ searchSealApplications()
+}
+// 鎼滅储鍒跺害
+const searchRegulations = () => {
+ page.current=1
+ getRegulationList()
+}
+// 閲嶇疆鍒跺害鎼滅储
+const resetRegulationSearch = () => {
+ regulationSearchForm.title = ''
+ regulationSearchForm.category = ''
+ searchRegulations()
+}
+// 鎻愪氦鐢ㄥ嵃鐢宠
+const submitSealApplication = async () => {
+ try {
+ await sealFormRef.value.validate()
+ addSealApplication(sealForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鐢宠鎻愪氦鎴愬姛')
+ showSealApplyDialog.value = false
+ getSealApplicationList()
+ Object.assign(sealForm, {
+ applicationNum: '',
+ title: '',
+ sealType: '',
+ reason: '',
+ approveUserId: '',
+ urgency: 'normal',
+ status: 'pending'
+ })
+ }
+ }).catch(err => {
+ ElMessage.error(err.msg)
+ })
+
+ } catch (error) {
+ ElMessage.error('璇峰畬鍠勭敵璇蜂俊鎭�')
+ }
+}
+// 鏂板
+const handleAdd = () => {
+ operationType.value = 'add'
+ resetRegulationForm()
+ showRegulationDialog.value = true
+}
+
+// 缂栬緫
+const handleEdit = (row) => {
+ operationType.value = 'edit'
+ Object.assign(regulationForm, row)
+ showRegulationDialog.value = true
+}
+// 搴熷純
+const repealEdit = (row) => {
+ operationType.value = 'edit'
+ Object.assign(regulationForm, row)
+ regulationForm.status = 'repealed'
+ ElMessageBox.confirm('纭搴熷純璇ュ埗搴︼紵', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ updateRuleManagement(regulationForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害搴熷純鎴愬姛')
+ // showRegulationDialog.value = false
+ getRegulationList()
+ resetRegulationForm()
+ }
+ })
+ }).catch(() => {
+ ElMessage({
+ type: 'info',
+ message: '宸插彇娑堝簾寮�'
+ })
+ })
+}
+// 鍙戝竷鍒跺害
+const submitRegulation = async () => {
+ try {
+ await regulationFormRef.value.validate()
+ if(operationType.value == 'add'){
+ addRuleManagement(regulationForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害鍙戝竷鎴愬姛')
+ showRegulationDialog.value = false
+ getRegulationList()
+ resetRegulationForm()
+ }
+ })
+ }else{
+ updateRuleManagement(regulationForm).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害缂栬緫鎴愬姛')
+ showRegulationDialog.value = false
+ resetRegulationForm()
+ getRegulationList()
+ }})}
+ }catch(err){
+ ElMessage.error(err.msg)
+ }
+}
+//閲嶇疆鍒跺害琛ㄥ崟
+const resetRegulationForm = () => {
+ Object.assign(regulationForm, {
+ id: '',
+ regulationNum: '',
+ title: '',
+ category: '',
+ content: '',
+ version: '',
+ status: 'active',
+ readCount: 0,
+ effectiveTime: '',
+ scope: [],
+ requireConfirm: false
+})
+}
+
+
+// 鏌ョ湅鐢ㄥ嵃鐢宠璇︽儏
+const viewSealDetail = (row) => {
+ currentSealDetail.value = row
+ showSealDetailDialog.value = true
+}
+// 瀹℃壒鐢ㄥ嵃鐢宠
+const approveSeal = (row) => {
+ console.log(row)
+ ElMessageBox.confirm('纭閫氳繃璇ョ敤鍗扮敵璇凤紵', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => {
+ row.status = 'approved'
+ updateSealApplication(row).then(res => {
+ if(res.code == 200){
+ ElMessage.success('瀹℃壒閫氳繃')
+ }
+ })
+ })
+}
+// 鎷掔粷鐢ㄥ嵃鐢宠
+const rejectSeal = (row) => {
+ ElMessageBox.prompt('璇疯緭鍏ユ嫆缁濆師鍥�', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ inputPattern: /\S+/,
+ inputErrorMessage: '鎷掔粷鍘熷洜涓嶈兘涓虹┖'
+ }).then(({ value }) => {
+ row.status = 'rejected'
+ updateSealApplication(row).then(res => {
+ if(res.code == 200){
+ ElMessage.success('瀹℃壒鎷掔粷')
+ }
+ })
+ ElMessage.success('宸叉嫆缁濈敵璇�')
+ })
+}
+// 鑾峰彇鍦ㄨ亴鍛樺伐鍒楄〃
+const getList = () => {
+ tableLoading.value = true;
+ //鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛淇℃伅
+ getUserProfile().then(res => {
+ if(res.code == 200){
+ console.log(res.data.userName)
+ currentUser.value = res.data.userName
+ }
+ })
+ staffJoinListPage({staffState: 1, ...page}).then(res => {
+ tableLoading.value = false;
+ // tableData.value = res.data.records
+ // //绛涢�夊嚭鍜宑urrentUser鍚屽悕鐨勪汉鍛�
+ tableData.value = res.data.records.filter(item => item.staffName === currentUser.value)
+ page.total = res.data.total;
+
+ if(tableData.value.length == 0){
+ ElMessage.error('褰撳墠鐢ㄦ埛鏈姞鍏ヤ换浣曢儴闂�')
+ }
+ }).catch(err => {
+ tableLoading.value = false;
+ })
+
+
+};
+
+// 鏌ョ湅鍒跺害鐗堟湰鍘嗗彶
+const viewVersionHistory = (row) => {
+ showVersionHistoryDialog.value = true
+ const params = {
+
+ category: row.category
+ }
+ listRuleManagement(page,params).then(res => {
+ if(res.code == 200){
+ versionHistory.value = res.data.records
+ }
+ })
+}
+// 鏌ョ湅鍒跺害璇︽儏
+const viewRegulation = (row) => {
+ getList()
+ currentRegulationDetail.value = row
+ showRegulationDetailDialog.value = true
+ getReadingStatusByRuleId(row.id).then(res => {
+ if(res.code == 200){
+ readStatusList.value = res.data
+ if(readStatusList.value.length==0 && tableData.value.length>0){
+ const params = {
+ ruleId: row.id,
+ employee: tableData.value[0].staffName,
+ department: tableData.value[0].postJob,
+ status: 'unconfirmed'
+ }
+ addReadingStatus(params).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害闃呰鎴愬姛')
+ }
+ })
+ }
+ }
+ })
+
+}
+// 鏌ョ湅鍒跺害闃呰鐘舵��
+const viewReadStatus = (row) => {
+ showReadStatusDialog.value = true
+ //鏌ョ湅闃呰鐘舵�佸垪琛�
+ getReadingStatusByRuleId(row.id).then(res => {
+ if(res.code == 200){
+ readStatusList.value = res.data
+ }
+ })
+}
+
+//纭鏌ョ湅
+const resetForm = (row) => {
+ console.log("row",row)
+ row.readCount = row.readCount + 1
+
+ updateRuleManagement(row).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鏌ョ湅鏁伴噺淇敼鎴愬姛')
+ //淇敼闃呰鐘舵��
+ //鏍规嵁鍒跺害id鍜屽綋鍓嶇櫥褰曠殑鍛樺伐寰楀埌闃呰鐘舵��
+ // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName )
+ // if(item.length>0){
+ // item[0].status = 'confirmed',
+ // item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
+ // }
+ // 绛涢�夊綋鍓嶅憳宸ュ搴旇鍒跺害鐨勯槄璇荤姸鎬佽褰�
+ let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id);
+
+ if (statusItem) {
+ // 濡傛灉鎵惧埌璁板綍锛屾洿鏂扮姸鎬佸拰纭鏃堕棿
+ statusItem.status = 'confirmed';
+ // 鏍煎紡鍖栨椂闂翠负"YYYY-MM-DD HH:mm:ss"鏍煎紡
+ const now = new Date();
+ statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
+ // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0];
+
+ updateReadingStatus(statusItem).then(res => {
+ if(res.code == 200){
+ ElMessage.success('鍒跺害闃呰鐘舵�佷慨鏀规垚鍔�')
+ }
+ })
+ }
+
+ }
+ })
+}
+
+// 瀵煎嚭鐢ㄥ嵃鐢宠
+const { proxy } = getCurrentInstance()
+const handleExport = () => {
+ proxy.download('/sealApplicationManagement/export', { ...sealSearchForm }, '鐢ㄥ嵃鐢宠.xlsx')
+}
+
+// 鑾峰彇鍗扮珷鐢宠鍒楄〃鏁版嵁
+const getSealApplicationList = async () => {
+ tableLoading.value = true
+ listSealApplication(page,sealSearchForm)
+ .then(res => {
+ //鑾峰彇褰撳墠鐧诲綍鐨勯儴闂ㄤ俊鎭�
+// 鑾峰彇褰撳墠鐧诲綍鐨勯儴闂ㄤ俊鎭苟杩囨护鏁版嵁
+ const currentFactoryName = userStore.currentFactoryName
+ if (currentFactoryName) {
+ // 鏍规嵁currentFactoryName杩囨护鍑篸epartment鐩稿悓鐨勬暟鎹�
+ sealApplications.value = res.data.records.filter(item => item.department === currentFactoryName)
+ // 鏇存柊杩囨护鍚庣殑鎬绘暟
+ page.total = sealApplications.value.length
+ } else {
+ // 濡傛灉娌℃湁currentFactoryName锛屽垯鏄剧ず鎵�鏈夋暟鎹�
+ sealApplications.value = res.data.records
+ page.total = res.data.total
+ }
+ // sealApplications.value = res.data.records
+ // page.value.total = res.data.total;
+ tableLoading.value = false;
+
+ }).catch(err => {
+ tableLoading.value = false;
+ })
+}
+// 鑾峰彇瑙勭珷鍒跺害鍒楄〃鏁版嵁
+const getRegulationList = async () => {
+ tableLoading.value = true
+ listRuleManagement(page,regulationSearchForm)
+ .then(res => {
+
+ regulations.value = res.data.records
+ // 杩囨护鎺夊凡搴熷純鐨勫埗搴�
+ // regulations.value = res.data.records.filter(item => item.status !== 'repealed')
+ page.total = res.data.total;
+ tableLoading.value = false;
+
+ }).catch(err => {
+ tableLoading.value = false;
+ })
+}
+
+// 鐩戝惉瀵硅瘽妗嗘墦寮�锛岃幏鍙栫敤鎴峰垪琛�
+watch(showSealApplyDialog, (newVal) => {
+ if (newVal) {
+ userListNoPageByTenantId().then((res) => {
+ userList.value = res.data;
+ });
+ }
+});
+
+onMounted(() => {
+ // 璺敱鎼哄甫 applicationNum 鏃讹紝棰勫~骞舵煡璇�
+ if (route.query.applicationNum) {
+ sealSearchForm.applicationNum = String(route.query.applicationNum)
+ page.current = 1
+ getSealApplicationList()
+ } else {
+ getSealApplicationList()
+ }
+ getRegulationList()
+})
+</script>
+
+<style scoped>
+.app-container {
+ padding: 20px;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.tab-content {
+ padding: 20px 0;
+}
+
+.mb-20 {
+ margin-bottom: 20px;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+.ml-10 {
+ margin-left: 10px;
+}
+
+.regulation-content {
+ background-color: #f5f5f5;
+ padding: 15px;
+ border-radius: 4px;
+ line-height: 1.6;
+ white-space: pre-wrap;
+ height: 200px;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+}
+</style>
diff --git a/src/views/collaborativeApproval/warningSystem/index.vue b/src/views/collaborativeApproval/warningSystem/index.vue
index b04c583..ddab6c0 100644
--- a/src/views/collaborativeApproval/warningSystem/index.vue
+++ b/src/views/collaborativeApproval/warningSystem/index.vue
@@ -33,7 +33,6 @@
<th>绫诲瀷</th>
<th>绛夌骇</th>
<th>鐘舵��</th>
- <th>璐d换浜�</th>
<th>鎿嶄綔</th>
</tr>
</thead>
@@ -52,7 +51,6 @@
{{ warning.statusText }}
</span>
</td>
- <td>{{ warning.responsible }}</td>
<td>
<button @click="viewDetail(warning)">鏌ョ湅璇︽儏</button>
</td>
@@ -96,7 +94,7 @@
levelText: '绾㈣壊棰勮',
status: 'pending',
statusText: '寰呭鐞�',
- responsible: '寮犵粡鐞�',
+ responsible: '闄堝織寮�',
description: 'A椤圭洰棰勭畻鎵ц鐜囧凡杈�95%锛岄璁″皢瓒呭嚭棰勭畻鑼冨洿銆�',
impact: '褰卞搷椤圭洰鏁翠綋璐㈠姟鎸囨爣锛屽彲鑳藉鑷撮」鐩簭鎹�',
suggestions: '鏆傚仠闈炲繀瑕佹敮鍑猴紝浼樺寲璧勬簮閰嶇疆锛岀敵璇烽绠楄皟鏁�'
@@ -148,7 +146,7 @@
levelText: '绾㈣壊棰勮',
status: 'pending',
statusText: '寰呭鐞�',
- responsible: '闄堟�荤洃',
+ responsible: '闄堝織寮�',
description: '浜у搧D鍦ㄥ鎴风幇鍦哄嚭鐜拌川閲忛棶棰樸��',
impact: '褰卞搷瀹㈡埛婊℃剰搴︼紝鍙兘閫犳垚缁忔祹鎹熷け',
suggestions: '绔嬪嵆鍙洖闂浜у搧锛屽垎鏋愬師鍥狅紝鍒跺畾鏀硅繘鎺柦'
diff --git a/src/views/inventoryManagement/stockWarningLedger/index.vue b/src/views/inventoryManagement/stockWarningLedger/index.vue
new file mode 100644
index 0000000..067680c
--- /dev/null
+++ b/src/views/inventoryManagement/stockWarningLedger/index.vue
@@ -0,0 +1,360 @@
+<template>
+ <div class="app-container">
+ <div class="search_form">
+ <el-form :model="searchForm" :inline="true">
+ <el-form-item label="浜у搧澶х被锛�">
+ <el-input
+ v-model="searchForm.productCategory"
+ placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
+ clearable
+ style="width: 200px"
+ />
+ </el-form-item>
+ <el-form-item label="瑙勬牸鍨嬪彿锛�">
+ <el-input
+ v-model="searchForm.specificationModel"
+ placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+ clearable
+ style="width: 200px"
+ />
+ </el-form-item>
+ <el-form-item label="棰勮绾у埆锛�">
+ <el-select
+ v-model="searchForm.warningLevel"
+ placeholder="璇烽�夋嫨棰勮绾у埆"
+ clearable
+ style="width: 150px"
+ >
+ <el-option label="绱ф��" value="绱ф��" />
+ <el-option label="閲嶈" value="閲嶈" />
+ <el-option label="涓�鑸�" value="涓�鑸�" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="棰勮鐘舵�侊細">
+ <el-select
+ v-model="searchForm.warningStatus"
+ placeholder="璇烽�夋嫨棰勮鐘舵��"
+ clearable
+ style="width: 150px"
+ >
+ <el-option label="宸查璀�" value="宸查璀�" />
+ <el-option label="姝e父" value="姝e父" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+ <el-button @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+
+ <div class="table_list">
+ <div class="actions"></div>
+ <el-table
+ :data="tableData"
+ border
+ v-loading="tableLoading"
+ style="width: 100%"
+ height="calc(100vh - 280px)"
+ >
+ <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+ <el-table-column label="鎵规鍙�" prop="code" width="130" show-overflow-tooltip />
+ <el-table-column label="浜у搧澶х被" prop="productCategory" show-overflow-tooltip />
+ <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" show-overflow-tooltip />
+ <el-table-column label="褰撳墠搴撳瓨" prop="currentStock" width="120" show-overflow-tooltip>
+ <template #default="scope">
+ <span :class="getStockClass(scope.row)">{{ scope.row.currentStock || 0 }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏈�浣庡簱瀛�" prop="warnNum" width="120" show-overflow-tooltip />
+ <el-table-column label="棰勮绾у埆" prop="warningLevel" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="getWarningLevelTag(scope.row.warningLevel)">
+ {{ scope.row.warningLevel || '-' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="棰勮鐘舵��" prop="warningStatus" width="100" show-overflow-tooltip>
+ <template #default="scope">
+ <el-tag :type="scope.row.warningStatus === '宸查璀�' ? 'danger' : 'success'">
+ {{ scope.row.warningStatus || '姝e父' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="棰勮鏃堕棿" prop="warningTime" width="150" show-overflow-tooltip />
+ <el-table-column label="棰勮缂鸿揣鏃堕棿" prop="expectedShortageTime" width="150" show-overflow-tooltip>
+ <template #default="scope">
+ <div v-if="scope.row.expectedShortageTime">
+ <div v-if="getCountdown(scope.row.expectedShortageTime).isExpired" class="countdown-expired">
+ <el-tag type="danger">宸茬己璐�</el-tag>
+ </div>
+ <div v-else class="countdown-timer">
+ <span :class="getCountdownClass(scope.row.expectedShortageTime)">
+ {{ getCountdown(scope.row.expectedShortageTime).text }}
+ </span>
+ </div>
+ </div>
+ <span v-else>-</span>
+ </template>
+ </el-table-column>
+ </el-table>
+ <pagination
+ v-show="total > 0"
+ :total="total"
+ layout="total, sizes, prev, pager, next, jumper"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationChange"
+ />
+ </div>
+
+ </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import pagination from '@/components/PIMTable/Pagination.vue'
+import {
+ getStockWarningLedgerPage
+} from '@/api/inventoryManagement/stockWarningLedger.js'
+
+// 鍝嶅簲寮忔暟鎹�
+const tableData = ref([])
+const tableLoading = ref(false)
+const total = ref(0)
+
+// 鍒嗛〉鍙傛暟
+const page = reactive({
+ current: 1,
+ size: 100
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+ productCategory: '',
+ specificationModel: '',
+ warningLevel: '',
+ warningStatus: ''
+})
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = () => {
+ tableLoading.value = true
+ const params = {
+ ...page,
+ ...searchForm
+ }
+ getStockWarningLedgerPage(params)
+ .then(res => {
+ tableLoading.value = false
+ if (res.code === 200) {
+ tableData.value = res.data.records || []
+ total.value = res.data.total || 0
+
+ // 璁$畻棰勮绾у埆鍜岀姸鎬�
+ tableData.value = tableData.value.map(item => {
+ const currentStock = parseFloat(item.inboundNum0 || item.currentStock || 0)
+ const warnNum = parseFloat(item.warnNum || 0)
+ const safetyStock = parseFloat(item.safetyStock || warnNum * 1.2)
+
+ // 璁$畻棰勮绾у埆
+ if (currentStock <= 0) {
+ item.warningLevel = '绱ф��'
+ item.warningStatus = '宸查璀�'
+ } else if (currentStock < warnNum) {
+ item.warningLevel = '閲嶈'
+ item.warningStatus = '宸查璀�'
+ } else if (currentStock < safetyStock) {
+ item.warningLevel = '涓�鑸�'
+ item.warningStatus = '宸查璀�'
+ } else {
+ item.warningLevel = ''
+ item.warningStatus = '姝e父'
+ }
+
+ // 璁$畻棰勮缂鸿揣鏃堕棿锛堝熀浜庢棩鍧囨秷鑰楅噺锛岃繖閲岀畝鍖栧鐞嗭級
+ if (item.warningStatus === '宸查璀�' && currentStock > 0 && warnNum > 0) {
+ const dailyConsumption = warnNum / 30 // 鍋囪30澶╂秷鑰楀畬鏈�浣庡簱瀛�
+ const daysRemaining = Math.floor(currentStock / dailyConsumption)
+ if (daysRemaining > 0) {
+ const date = new Date()
+ date.setDate(date.getDate() + daysRemaining)
+ item.expectedShortageTime = date.toISOString().split('T')[0]
+ }
+ }
+
+ item.currentStock = currentStock
+ item.safetyStock = safetyStock
+
+ return item
+ })
+ }
+ })
+ .catch(err => {
+ tableLoading.value = false
+ ElMessage.error(err.msg || '鑾峰彇鏁版嵁澶辫触')
+ })
+}
+
+// 鎼滅储
+const handleQuery = () => {
+ page.current = 1
+ getList()
+}
+
+// 閲嶇疆鎼滅储
+const resetQuery = () => {
+ Object.keys(searchForm).forEach(key => {
+ searchForm[key] = ''
+ })
+ handleQuery()
+}
+
+// 鍒嗛〉鍙樺寲
+const paginationChange = (obj) => {
+ page.current = obj.page
+ page.size = obj.limit
+ getList()
+}
+
+// 鑾峰彇搴撳瓨鏍峰紡绫�
+const getStockClass = (row) => {
+ const currentStock = parseFloat(row.currentStock || row.inboundNum0 || 0)
+ const warnNum = parseFloat(row.warnNum || 0)
+
+ if (currentStock <= 0) {
+ return 'text-danger'
+ } else if (currentStock < warnNum) {
+ return 'text-warning'
+ }
+ return 'text-success'
+}
+
+// 鑾峰彇棰勮绾у埆鏍囩鏍峰紡
+const getWarningLevelTag = (level) => {
+ const levelMap = {
+ '绱ф��': 'danger',
+ '閲嶈': 'warning',
+ '涓�鑸�': 'info'
+ }
+ return levelMap[level] || 'info'
+}
+
+// 鑾峰彇鍊掕鏃朵俊鎭�
+const getCountdown = (expectedTime) => {
+ if (!expectedTime) return { text: '-', isExpired: false }
+
+ const now = new Date().getTime()
+ const expected = new Date(expectedTime).getTime()
+ const diff = expected - now
+
+ if (diff <= 0) {
+ return { text: '宸茬己璐�', isExpired: true }
+ }
+
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24))
+ const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
+
+ if (days > 0) {
+ return { text: `${days}澶�${hours}灏忔椂`, isExpired: false }
+ } else if (hours > 0) {
+ return { text: `${hours}灏忔椂${minutes}鍒嗛挓`, isExpired: false }
+ } else {
+ return { text: `${minutes}鍒嗛挓`, isExpired: false }
+ }
+}
+
+// 鑾峰彇鍊掕鏃舵牱寮忕被
+const getCountdownClass = (expectedTime) => {
+ if (!expectedTime) return ''
+
+ const now = new Date().getTime()
+ const expected = new Date(expectedTime).getTime()
+ const diff = expected - now
+
+ if (diff <= 0) {
+ return 'countdown-expired'
+ } else if (diff <= 24 * 60 * 60 * 1000) { // 24灏忔椂鍐�
+ return 'countdown-urgent'
+ } else if (diff <= 7 * 24 * 60 * 60 * 1000) { // 7澶╁唴
+ return 'countdown-warning'
+ } else {
+ return 'countdown-normal'
+ }
+}
+
+// 椤甸潰鍔犺浇
+onMounted(() => {
+ getList()
+})
+</script>
+
+<style scoped lang="scss">
+.app-container {
+ padding: 20px;
+
+ .search_form {
+ background: #fff;
+ padding: 20px;
+ border-radius: 4px;
+ margin-bottom: 20px;
+ }
+
+ .table_list {
+ background: #fff;
+ border-radius: 4px;
+ padding: 20px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+
+ .actions {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 20px;
+ }
+ }
+
+ .text-danger {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ .text-warning {
+ color: #e6a23c;
+ font-weight: bold;
+ }
+
+ .text-success {
+ color: #67c23a;
+ font-weight: bold;
+ }
+
+ .countdown-timer {
+ font-weight: bold;
+ }
+
+ .countdown-normal {
+ color: #67c23a;
+ }
+
+ .countdown-warning {
+ color: #e6a23c;
+ }
+
+ .countdown-urgent {
+ color: #f56c6c;
+ animation: blink 1s infinite;
+ }
+
+ .countdown-expired {
+ color: #f56c6c;
+ font-weight: bold;
+ }
+
+ @keyframes blink {
+ 0%, 50% { opacity: 1; }
+ 51%, 100% { opacity: 0.5; }
+ }
+}
+</style>
diff --git a/src/views/personnelManagement/payrollManagement/components/formDia.vue b/src/views/personnelManagement/payrollManagement/components/formDia.vue
index 6dbc326..e4cf0b3 100644
--- a/src/views/personnelManagement/payrollManagement/components/formDia.vue
+++ b/src/views/personnelManagement/payrollManagement/components/formDia.vue
@@ -2,7 +2,7 @@
<div>
<el-dialog
v-model="dialogFormVisible"
- :title="operationType === 'add' ? '鏂板鍏ヨ亴' : '缂栬緫浜哄憳'"
+ :title="operationType === 'add' ? '鏂板钖祫' : '缂栬緫钖祫'"
width="50%"
@close="closeDia"
>
diff --git a/src/views/personnelManagement/payrollManagement/index.vue b/src/views/personnelManagement/payrollManagement/index.vue
index 1de23e4..24e3dd8 100644
--- a/src/views/personnelManagement/payrollManagement/index.vue
+++ b/src/views/personnelManagement/payrollManagement/index.vue
@@ -27,8 +27,8 @@
>
</div>
<div>
+ <el-button @click="handleExport" style="margin-right: 10px">瀵煎嚭</el-button>
<el-button type="primary" @click="openForm('add')">鏂板钖祫</el-button>
-<!-- <el-button @click="handleOut">瀵煎嚭</el-button>-->
<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
</div>
</div>
@@ -51,7 +51,7 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
-import {onMounted, ref} from "vue";
+import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import FormDia from "@/views/personnelManagement/payrollManagement/components/formDia.vue";
import {staffJoinDel} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
@@ -283,6 +283,22 @@
proxy.$modal.msg("宸插彇娑�");
});
};
+
+// 瀵煎嚭钖祫绠$悊
+const handleExport = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ proxy.download("/compensationPerformance/export", { ...searchForm.value, ...page }, "钖祫绠$悊.xlsx");
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+};
+
onMounted(() => {
getList();
});
--
Gitblit v1.9.3