From b12b55a5ee1b34b5a3f9d21533fa9fc909207285 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期四, 05 二月 2026 09:40:13 +0800
Subject: [PATCH] Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-management into dev_New
---
src/views/index.vue | 10
src/views/productionManagement/productionProcess/index.vue | 8
src/api/basicData/enum.js | 28
src/views/collaborativeApproval/rulesRegulationsManagement/index.vue | 122
src/views/basicData/product/index.vue | 54
src/api/productionManagement/productionOrder.js | 16
src/views/qualityManagement/rawMaterialInspection/index.vue | 5
src/views/qualityManagement/finalInspection/index.vue | 5
src/views/productionManagement/productionProcess/Edit.vue | 6
src/views/fileManagement/document/attachmentManager.vue | 3
src/views/qualityManagement/processInspection/index.vue | 5
src/views/collaborativeApproval/sealManagement/index.vue | 589 -----
src/views/reportAnalysis/productionAnalysis/components/right-top.vue | 2
src/views/reportAnalysis/qualityAnalysis/components/right-top.vue | 132 +
src/views/safeProduction/safetyTrainingAssessment/index.vue | 47
src/views/inventoryManagement/receiptManagement/Record.vue | 7
src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue | 94 +
src/views/qualityManagement/processInspection/components/formDia.vue | 42
src/views/reportAnalysis/qualityAnalysis/components/left-top.vue | 448 ++++
src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue | 306 +++
src/views/reportAnalysis/qualityAnalysis/components/left-bottom.vue | 170 +
src/views/salesManagement/salesQuotation/index.vue | 93
src/views/safeProduction/hazardSourceLedger/index.vue | 4
src/views/reportAnalysis/qualityAnalysis/index.vue | 289 +++
src/views/procurementManagement/procurementLedger/index.vue | 10
src/views/reportAnalysis/reportManagement/index.vue | 8
src/views/qualityManagement/rawMaterialInspection/components/formDia.vue | 44
src/api/viewIndex.js | 84
src/views/productionManagement/productionOrder/index.vue | 47
src/api/reportAnalysis/qualityReport.js | 4
src/views/inventoryManagement/stockReport/index.vue | 10
src/views/productionManagement/productionProcess/New.vue | 4
src/views/reportAnalysis/qualityAnalysis/components/center-center.vue | 355 +++
src/views/productionManagement/processRoute/processRouteItem/index.vue | 28
src/views/safeProduction/emergencyPlanReview/index.vue | 6
src/views/salesManagement/salesLedger/index.vue | 40
src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue | 33
src/views/safeProduction/accidentReportingRecord/index.vue | 6
src/views/inventoryManagement/dispatchLog/Record.vue | 8
src/views/productionManagement/productionOrder/New.vue | 192 ++
src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue | 189 ++
src/components/Dialog/FileListDialog.vue | 543 +++--
src/views/collaborativeApproval/knowledgeBase/index.vue | 16
src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue | 1
src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue | 254 ++
src/views/safeProduction/safeQualifications/index.vue | 41
src/views/salesManagement/receiptPayment/index.vue | 2
src/views/qualityManagement/finalInspection/components/formDia.vue | 123
src/views/equipmentManagement/inspectionManagement/index.vue | 600 +++---
src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue | 85
src/views/personnelManagement/contractManagement/filesDia.vue | 3
src/views/reportAnalysis/qualityAnalysis/components/center-top.vue | 147 +
src/views/safeProduction/hazardousMaterialsControl/index.vue | 68
src/views/safeProduction/dangerInvestigation/index.vue | 66
54 files changed, 4,195 insertions(+), 1,307 deletions(-)
diff --git a/src/api/basicData/enum.js b/src/api/basicData/enum.js
index 0d35692..350df17 100644
--- a/src/api/basicData/enum.js
+++ b/src/api/basicData/enum.js
@@ -7,17 +7,35 @@
})
}
-
-export function findAllQualifiedStockRecordTypeOptions() {
+// 鍚堟牸鍏ュ簱鏉ユ簮绫诲瀷
+export function findAllQualifiedStockInRecordTypeOptions() {
return request({
- url: '/basic/enum/StockQualifiedRecordTypeEnum',
+ url: '/basic/enum/StockInQualifiedRecordTypeEnum',
method: 'get'
})
}
-export function findAllUnqualifiedStockRecordTypeOptions() {
+// 涓嶅悎鏍煎叆搴撴潵婧愮被鍨�
+export function findAllUnQualifiedStockInRecordTypeOptions() {
return request({
- url: '/basic/enum/StockUnQualifiedRecordTypeEnum',
+ url: '/basic/enum/StockInUnQualifiedRecordTypeEnum',
method: 'get'
})
}
+
+// 鍚堟牸鍑哄簱鏉ユ簮绫诲瀷
+export function findAllQualifiedStockOutRecordTypeOptions() {
+ return request({
+ url: '/basic/enum/StockOutQualifiedRecordTypeEnum',
+ method: 'get'
+ })
+}
+
+// 涓嶅悎鏍煎嚭搴撴潵婧愮被鍨�
+export function findAllUnQualifiedStockOutRecordTypeOptions() {
+ return request({
+ url: '/basic/enum/StockOutUnQualifiedRecordTypeEnum',
+ method: 'get'
+ })
+}
+
diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index 9f110a7..6c8dbe2 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -36,6 +36,22 @@
});
}
+// 鐢熶骇璁㈠崟-鏂板
+export function addProductOrder(data) {
+ return request({
+ url: "/productOrder/addProductOrder",
+ method: "post",
+ data: data,
+ });
+}
+
+export function delProductOrder(ids) {
+ return request({
+ url: `/productOrder/${ids}`,
+ method: "delete",
+ });
+}
+
// 鐢熶骇璁㈠崟-鏌ヨ浜у搧缁撴瀯鍒楄〃
export function listProcessBom(query) {
return request({
diff --git a/src/api/reportAnalysis/qualityReport.js b/src/api/reportAnalysis/qualityReport.js
index a179a5f..66a7540 100644
--- a/src/api/reportAnalysis/qualityReport.js
+++ b/src/api/reportAnalysis/qualityReport.js
@@ -43,10 +43,10 @@
}
// 鑾峰彇鐑偣妫�娴嬫寚鏍囩粺璁�
-export function getTopParameters(inspectType) {
+export function getTopParameters(modelType) {
return request({
url: '/qualityReport/getTopParameters',
method: 'get',
- params: { inspectType }
+ params: { modelType }
})
}
diff --git a/src/api/viewIndex.js b/src/api/viewIndex.js
index a14ed2a..3caced7 100644
--- a/src/api/viewIndex.js
+++ b/src/api/viewIndex.js
@@ -1,6 +1,82 @@
// 棣栭〉鎺ュ彛
import request from "@/utils/request";
+// 宸ュ崟鎵ц鏁堢巼鍒嗘瀽
+export const workOrderEfficiencyAnalysis = (query) => {
+ return request({
+ url: "/home/workOrderEfficiencyAnalysis",
+ method: "get",
+ params: query,
+ });
+};
+
+// 鍘熸潗鏂欐娴�
+export const rawMaterialDetection = (query) => {
+ return request({
+ url: "/home/rawMaterialDetection",
+ method: "get",
+ params: query,
+ });
+};
+
+// 杩囩▼妫�娴�
+export const processDetection = (query) => {
+ return request({
+ url: "/home/processDetection",
+ method: "get",
+ params: query,
+ });
+};
+
+// 鎴愬搧鍑哄巶妫�娴�
+export const factoryDetection = (query) => {
+ return request({
+ url: "/home/factoryDetection",
+ method: "get",
+ params: query,
+ });
+};
+
+// 妫�楠屾暟閲�
+export const qualityInspectionCount = () => {
+ return request({
+ url: "/home/qualityInspectionCount",
+ method: "get",
+ });
+};
+
+// 涓嶅悎鏍奸璀�
+export const nonComplianceWarning = () => {
+ return request({
+ url: "/home/nonComplianceWarning",
+ method: "get",
+ });
+};
+
+// 瀹屾垚妫�楠屾暟
+export const completedInspectionCount = () => {
+ return request({
+ url: "/home/completedInspectionCount",
+ method: "get",
+ });
+};
+
+// 涓嶅悎鏍间骇鍝佹帓鍚�
+export const unqualifiedProductRanking = () => {
+ return request({
+ url: "/home/unqualifiedProductRanking",
+ method: "get",
+ });
+};
+
+// 涓嶅悎鏍兼鍝佸鐞嗗垎鏋�
+export const unqualifiedProductProcessingAnalysis = () => {
+ return request({
+ url: "/home/unqualifiedProductProcessingAnalysis",
+ method: "get",
+ });
+};
+
// 閿�鍞�-閲囪喘-搴撳瓨鏁版嵁
export const getBusiness = () => {
return request({
@@ -41,6 +117,14 @@
params,
});
};
+// 鐢熶骇鏍哥畻鍒嗘瀽
+export const productionAccountingAnalysis = (query) => {
+ return request({
+ url: "/home/productionAccountingAnalysis",
+ method: "get",
+ params: query,
+ });
+};
// 搴旀敹搴斾粯缁熻
export const statisticsReceivablePayable = (query) => {
return request({
diff --git a/src/components/Dialog/FileListDialog.vue b/src/components/Dialog/FileListDialog.vue
index 0721a55..fc82411 100644
--- a/src/components/Dialog/FileListDialog.vue
+++ b/src/components/Dialog/FileListDialog.vue
@@ -1,309 +1,328 @@
<template>
- <el-dialog
- v-model="dialogVisible"
- :title="title"
- :width="width"
- :before-close="handleClose"
- >
- <div class="file-list-toolbar" v-if="showToolbar">
+ <el-dialog v-model="dialogVisible"
+ :title="title"
+ :width="width"
+ :before-close="handleClose">
+ <div class="file-list-toolbar"
+ v-if="showToolbar">
<template v-if="useBuiltInUpload">
- <el-upload
- v-model:file-list="uploadFileList"
- class="upload-demo"
- :action="uploadAction"
- :headers="uploadHeaders"
- :show-file-list="false"
- :on-success="handleDefaultUploadSuccess"
- :on-error="handleDefaultUploadError"
- >
- <el-button
- v-if="showUploadButton"
- type="primary"
- size="small"
- >
+ <el-upload v-model:file-list="uploadFileList"
+ class="upload-demo"
+ :action="uploadAction"
+ :headers="uploadHeaders"
+ :show-file-list="false"
+ :on-success="handleDefaultUploadSuccess"
+ :on-error="handleDefaultUploadError">
+ <el-button v-if="showUploadButton"
+ type="primary"
+ size="small">
涓婁紶闄勪欢
</el-button>
</el-upload>
</template>
<template v-else>
- <el-button
- v-if="showUploadButton"
- type="primary"
- size="small"
- @click="handleUpload"
- >
+ <el-button v-if="showUploadButton"
+ type="primary"
+ size="small"
+ @click="handleUpload">
鏂板闄勪欢
</el-button>
</template>
</div>
- <el-table :data="tableData" border :height="tableHeight">
- <el-table-column
- :label="nameColumnLabel"
- :prop="nameColumnProp"
- :min-width="nameColumnMinWidth"
- show-overflow-tooltip
- />
- <el-table-column
- v-if="showActions"
- fixed="right"
- label="鎿嶄綔"
- :width="actionColumnWidth"
- align="center"
- >
+ <el-table :data="tableData"
+ border
+ :height="tableHeight">
+ <el-table-column :label="nameColumnLabel"
+ :prop="nameColumnProp"
+ :min-width="nameColumnMinWidth"
+ show-overflow-tooltip />
+ <el-table-column v-if="showActions"
+ fixed="right"
+ label="鎿嶄綔"
+ :width="actionColumnWidth"
+ align="center">
<template #default="scope">
- <el-button
- v-if="showDownload"
- link
- type="primary"
- size="small"
- @click="handleDownload(scope.row)"
- >
+ <el-button v-if="showDownload"
+ link
+ type="primary"
+ size="small"
+ @click="handleDownload(scope.row)">
涓嬭浇
</el-button>
- <el-button
- v-if="showPreview"
- link
- type="primary"
- size="small"
- @click="handlePreview(scope.row)"
- >
+ <el-button v-if="showPreview"
+ link
+ type="primary"
+ size="small"
+ @click="handlePreview(scope.row)">
棰勮
</el-button>
- <el-button
- v-if="showDeleteButton"
- link
- type="danger"
- size="small"
- @click="handleDelete(scope.row, scope.$index)"
- >
+ <el-button v-if="showDeleteButton"
+ link
+ type="danger"
+ size="small"
+ @click="handleDelete(scope.row, scope.$index)">
鍒犻櫎
</el-button>
- <slot name="actions" :row="scope.row"></slot>
+ <slot name="actions"
+ :row="scope.row"></slot>
</template>
</el-table-column>
<slot name="columns"></slot>
</el-table>
+ <pagination v-if="isShowPagination"
+ style="margin-bottom: 20px;"
+ :total="page.total"
+ :page="page.current"
+ :limit="page.size"
+ @pagination="paginationSearch"
+ @change="handleChange" />
</el-dialog>
- <filePreview v-if="showPreview" ref="filePreviewRef" />
+ <filePreview v-if="showPreview"
+ ref="filePreviewRef" />
</template>
<script setup>
-import { ref, computed, getCurrentInstance } from 'vue'
-import { ElMessage } from 'element-plus'
-import filePreview from '@/components/filePreview/index.vue'
-import { getToken } from '@/utils/auth'
+ import { ref, computed, getCurrentInstance } from "vue";
+ import pagination from "@/components/Pagination/index.vue";
+ import { ElMessage } from "element-plus";
+ import filePreview from "@/components/filePreview/index.vue";
+ import { getToken } from "@/utils/auth";
-const props = defineProps({
- modelValue: {
- type: Boolean,
- default: false
- },
- title: {
- type: String,
- default: '闄勪欢'
- },
- width: {
- type: String,
- default: '40%'
- },
- tableHeight: {
- type: String,
- default: '40vh'
- },
- nameColumnLabel: {
- type: String,
- default: '闄勪欢鍚嶇О'
- },
- nameColumnProp: {
- type: String,
- default: 'name'
- },
- nameColumnMinWidth: {
- type: [String, Number],
- default: 400
- },
- actionColumnWidth: {
- type: [String, Number],
- default: 160
- },
- showActions: {
- type: Boolean,
- default: true
- },
- showDownload: {
- type: Boolean,
- default: true
- },
- showPreview: {
- type: Boolean,
- default: true
- },
- showUploadButton: {
- type: Boolean,
- default: false
- },
- showDeleteButton: {
- type: Boolean,
- default: false
- },
- urlField: {
- type: String,
- default: 'url'
- },
- downloadMethod: {
- type: Function,
- default: null
- },
- previewMethod: {
- type: Function,
- default: null
- },
- uploadMethod: {
- type: Function,
- default: null
- },
- deleteMethod: {
- type: Function,
- default: null
- },
- rulesRegulationsManagementId: {
- type: [String, Number],
- default: ''
- },
- uploadUrl: {
- type: String,
- default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`
- }
-})
+ const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false,
+ },
+ title: {
+ type: String,
+ default: "闄勪欢",
+ },
+ width: {
+ type: String,
+ default: "40%",
+ },
+ tableHeight: {
+ type: String,
+ default: "40vh",
+ },
+ nameColumnLabel: {
+ type: String,
+ default: "闄勪欢鍚嶇О",
+ },
+ nameColumnProp: {
+ type: String,
+ default: "name",
+ },
+ nameColumnMinWidth: {
+ type: [String, Number],
+ default: 400,
+ },
+ actionColumnWidth: {
+ type: [String, Number],
+ default: 160,
+ },
+ showActions: {
+ type: Boolean,
+ default: true,
+ },
+ showDownload: {
+ type: Boolean,
+ default: true,
+ },
+ showPreview: {
+ type: Boolean,
+ default: true,
+ },
+ showUploadButton: {
+ type: Boolean,
+ default: false,
+ },
+ showDeleteButton: {
+ type: Boolean,
+ default: false,
+ },
+ urlField: {
+ type: String,
+ default: "url",
+ },
+ downloadMethod: {
+ type: Function,
+ default: null,
+ },
+ previewMethod: {
+ type: Function,
+ default: null,
+ },
+ uploadMethod: {
+ type: Function,
+ default: null,
+ },
+ deleteMethod: {
+ type: Function,
+ default: null,
+ },
+ rulesRegulationsManagementId: {
+ type: [String, Number],
+ default: "",
+ },
+ uploadUrl: {
+ type: String,
+ default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`,
+ },
+ isShowPagination: {
+ type: Boolean,
+ default: false,
+ },
+ page: {
+ type: Object,
+ default: () => ({
+ current: 1,
+ size: 10,
+ total: 0,
+ }),
+ },
+ });
-const emit = defineEmits(['update:modelValue', 'close', 'download', 'preview', 'upload', 'delete'])
+ const emit = defineEmits([
+ "update:modelValue",
+ "close",
+ "download",
+ "preview",
+ "upload",
+ "delete",
+ ]);
-const { proxy } = getCurrentInstance()
-const filePreviewRef = ref(null)
-const uploadFileList = ref([])
+ const { proxy } = getCurrentInstance();
+ const filePreviewRef = ref(null);
+ const uploadFileList = ref([]);
-const dialogVisible = computed({
- get: () => props.modelValue,
- set: (val) => emit('update:modelValue', val)
-})
+ const dialogVisible = computed({
+ get: () => props.modelValue,
+ set: val => emit("update:modelValue", val),
+ });
-const tableData = ref([])
-const showToolbar = computed(() => props.showUploadButton)
-const useBuiltInUpload = computed(() => !props.uploadMethod)
-const uploadAction = computed(() => props.uploadUrl)
-const uploadHeaders = computed(() => ({
- Authorization: `Bearer ${getToken()}`
-}))
+ const tableData = ref([]);
+ const showToolbar = computed(() => props.showUploadButton);
+ const useBuiltInUpload = computed(() => !props.uploadMethod);
+ const uploadAction = computed(() => props.uploadUrl);
+ const uploadHeaders = computed(() => ({
+ Authorization: `Bearer ${getToken()}`,
+ }));
-const handleClose = () => {
- emit('close')
- dialogVisible.value = false
-}
+ const handleClose = () => {
+ emit("close");
+ dialogVisible.value = false;
+ };
-const handleDownload = (row) => {
- if (props.downloadMethod) {
- props.downloadMethod(row)
- } else {
- // 榛樿涓嬭浇鏂规硶
- proxy.$download.name(row[props.urlField])
- }
- emit('download', row)
-}
-
-const handlePreview = (row) => {
- if (props.previewMethod) {
- props.previewMethod(row)
- } else {
- // 榛樿棰勮鏂规硶
- if (filePreviewRef.value) {
- filePreviewRef.value.open(row[props.urlField])
+ const handleDownload = row => {
+ if (props.downloadMethod) {
+ props.downloadMethod(row);
+ } else {
+ // 榛樿涓嬭浇鏂规硶
+ proxy.$download.name(row[props.urlField]);
}
- }
- emit('preview', row)
-}
+ emit("download", row);
+ };
-const open = (list) => {
- dialogVisible.value = true
- tableData.value = list || []
-}
-
-const handleUpload = async () => {
- if (props.uploadMethod) {
- // 濡傛灉鎻愪緵浜嗚嚜瀹氫箟涓婁紶鏂规硶锛岀敱鐖剁粍浠惰礋璐f洿鏂板垪琛紙閫氳繃 setList锛�
- // 杩欓噷涓嶅啀鑷姩娣诲姞锛岄伩鍏嶄笌鐖剁粍浠剁殑 setList 閲嶅
- await props.uploadMethod()
- }
- emit('upload')
-}
-
-const handleDelete = async (row, index) => {
- if (props.deleteMethod) {
- const result = await props.deleteMethod(row, index)
- if (result === false) {
- return
+ const handlePreview = row => {
+ if (props.previewMethod) {
+ props.previewMethod(row);
+ } else {
+ // 榛樿棰勮鏂规硶
+ if (filePreviewRef.value) {
+ filePreviewRef.value.open(row[props.urlField]);
+ }
}
- // 濡傛灉鎻愪緵浜� deleteMethod锛岀敱鐖剁粍浠惰礋璐e埛鏂板垪琛紝涓嶅湪杩欓噷鍒犻櫎
- } else {
- // 濡傛灉娌℃湁鎻愪緵 deleteMethod锛屾墠鍦ㄧ粍浠跺唴閮ㄥ垹闄�
- removeAttachment(index)
- }
- emit('delete', row)
-}
+ emit("preview", row);
+ };
+ const paginationSearch = page => {
+ props.page.current = page.page;
+ props.page.size = page.limit;
+ emit("pagination", page.page, page.limit);
+ };
-const addAttachment = (item) => {
- tableData.value = [...tableData.value, item]
-}
+ const open = list => {
+ dialogVisible.value = true;
+ tableData.value = list || [];
+ };
-const handleDefaultUploadSuccess = async (res, file) => {
- if (res?.code !== 200) {
- ElMessage.error(res?.msg || '鏂囦欢涓婁紶澶辫触')
- return
- }
- if (!props.rulesRegulationsManagementId) {
- ElMessage.error('缂哄皯瑙勭珷鍒跺害ID锛屾棤娉曚繚瀛橀檮浠�')
- return
- }
- const fileName = res?.data?.originalName || file?.name
- const fileUrl = res?.data?.tempPath || res?.data?.url
- const payload = {
- fileName,
- fileUrl,
- rulesRegulationsManagementId: props.rulesRegulationsManagementId,
- raw: res?.data || {}
- }
- emit('upload', payload)
-}
+ const handleUpload = async () => {
+ if (props.uploadMethod) {
+ // 濡傛灉鎻愪緵浜嗚嚜瀹氫箟涓婁紶鏂规硶锛岀敱鐖剁粍浠惰礋璐f洿鏂板垪琛紙閫氳繃 setList锛�
+ // 杩欓噷涓嶅啀鑷姩娣诲姞锛岄伩鍏嶄笌鐖剁粍浠剁殑 setList 閲嶅
+ await props.uploadMethod();
+ }
+ emit("upload");
+ };
-const handleDefaultUploadError = () => {
- ElMessage.error('鏂囦欢涓婁紶澶辫触')
-}
+ const handleDelete = async (row, index) => {
+ if (props.deleteMethod) {
+ const result = await props.deleteMethod(row, index);
+ if (result === false) {
+ return;
+ }
+ // 濡傛灉鎻愪緵浜� deleteMethod锛岀敱鐖剁粍浠惰礋璐e埛鏂板垪琛紝涓嶅湪杩欓噷鍒犻櫎
+ } else {
+ // 濡傛灉娌℃湁鎻愪緵 deleteMethod锛屾墠鍦ㄧ粍浠跺唴閮ㄥ垹闄�
+ removeAttachment(index);
+ }
+ emit("delete", row);
+ };
-const removeAttachment = (index) => {
- if (index > -1 && index < tableData.value.length) {
- const newList = [...tableData.value]
- newList.splice(index, 1)
- tableData.value = newList
- }
-}
+ const addAttachment = item => {
+ tableData.value = [...tableData.value, item];
+ };
-const setList = (list) => {
- tableData.value = list || []
-}
+ const handleDefaultUploadSuccess = async (res, file) => {
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || "鏂囦欢涓婁紶澶辫触");
+ return;
+ }
+ if (!props.rulesRegulationsManagementId) {
+ ElMessage.error("缂哄皯瑙勭珷鍒跺害ID锛屾棤娉曚繚瀛橀檮浠�");
+ return;
+ }
+ const fileName = res?.data?.originalName || file?.name;
+ const fileUrl = res?.data?.tempPath || res?.data?.url;
+ const payload = {
+ fileName,
+ fileUrl,
+ rulesRegulationsManagementId: props.rulesRegulationsManagementId,
+ raw: res?.data || {},
+ };
+ emit("upload", payload);
+ };
-defineExpose({
- open,
- addAttachment,
- removeAttachment,
- setList,
- handleUpload,
- handleDelete
-})
+ const handleDefaultUploadError = () => {
+ ElMessage.error("鏂囦欢涓婁紶澶辫触");
+ };
+
+ const removeAttachment = index => {
+ if (index > -1 && index < tableData.value.length) {
+ const newList = [...tableData.value];
+ newList.splice(index, 1);
+ tableData.value = newList;
+ }
+ };
+
+ const setList = list => {
+ tableData.value = list || [];
+ };
+
+ defineExpose({
+ open,
+ addAttachment,
+ removeAttachment,
+ setList,
+ handleUpload,
+ handleDelete,
+ });
</script>
<style scoped>
-.file-list-toolbar {
- margin-bottom: 8px;
- text-align: right;
-}
+ .file-list-toolbar {
+ margin-bottom: 8px;
+ text-align: right;
+ }
</style>
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 3f0921a..c9058aa 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -30,11 +30,8 @@
:props="{ children: 'children', label: 'label' }"
highlight-current
node-key="id"
- style="
- height: calc(100vh - 190px);
- overflow-y: scroll;
- scrollbar-width: none;
- "
+ class="product-tree-scroll"
+ style="height: calc(100vh - 190px); overflow-y: auto"
>
<template #default="{ node, data }">
<div class="custom-tree-node">
@@ -43,7 +40,7 @@
<component :is="data.children && data.children.length > 0
? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" />
</el-icon>
- {{ data.label }}
+ <span class="tree-node-label">{{ data.label }}</span>
</span>
<div>
<el-button
@@ -111,6 +108,8 @@
<el-input
v-model="form.productName"
placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+ maxlength="20"
+ show-word-limit
clearable
@keydown.enter.prevent
/>
@@ -239,7 +238,10 @@
productName: "",
},
rules: {
- productName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ productName: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+ ],
},
modelForm: {
model: "",
@@ -467,18 +469,21 @@
display: flex;
}
.left {
- width: 380px;
+ width: 450px;
+ min-width: 450px;
padding: 16px;
background: #ffffff;
}
.right {
- width: calc(100% - 380px);
+ flex: 1;
+ min-width: 0;
padding: 16px;
margin-left: 20px;
background: #ffffff;
}
.custom-tree-node {
flex: 1;
+ min-width: 0;
display: flex;
align-items: center;
justify-content: space-between;
@@ -486,13 +491,42 @@
padding-right: 8px;
}
.tree-node-content {
+ flex: 1;
+ min-width: 0;
display: flex;
- align-items: center; /* 鍨傜洿灞呬腑 */
+ align-items: center;
height: 100%;
+ overflow: hidden;
+}
+.tree-node-content .orange-icon {
+ flex-shrink: 0;
+}
+.tree-node-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.orange-icon {
color: orange;
font-size: 18px;
margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
}
+.product-tree-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: #c0c4cc #f5f7fa;
+}
+.product-tree-scroll::-webkit-scrollbar {
+ width: 8px;
+}
+.product-tree-scroll::-webkit-scrollbar-track {
+ background: #f5f7fa;
+ border-radius: 4px;
+}
+.product-tree-scroll::-webkit-scrollbar-thumb {
+ background: #c0c4cc;
+ border-radius: 4px;
+}
+.product-tree-scroll::-webkit-scrollbar-thumb:hover {
+ background: #909399;
+}
</style>
diff --git a/src/views/collaborativeApproval/knowledgeBase/index.vue b/src/views/collaborativeApproval/knowledgeBase/index.vue
index f7a1ef3..bdaf0b4 100644
--- a/src/views/collaborativeApproval/knowledgeBase/index.vue
+++ b/src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -425,8 +425,15 @@
listKnowledgeBase({...page.value, ...searchForm.value})
.then(res => {
tableLoading.value = false;
- tableData.value = res.data.records
page.value.total = res.data.total;
+ // 濡傛灉褰撳墠椤垫暟瓒呰繃鎬婚〉鏁帮紝閲嶇疆鍒扮1椤靛苟閲嶆柊鏌ヨ
+ const maxPage = Math.ceil(res.data.total / page.value.size) || 1;
+ if (page.value.current > maxPage && maxPage > 0) {
+ page.value.current = 1;
+ // 閲嶆柊鏌ヨ绗�1椤垫暟鎹�
+ return getList();
+ }
+ tableData.value = res.data.records;
}).catch(err => {
tableLoading.value = false;
})
@@ -434,9 +441,14 @@
// 鍒嗛〉澶勭悊
const pagination = (obj) => {
+ const oldSize = page.value.size;
page.value.current = obj.page;
page.value.size = obj.limit;
- handleQuery();
+ // 濡傛灉 size 鏀瑰彉浜嗭紝閲嶇疆鍒扮1椤碉紝閬垮厤褰撳墠椤佃秴鍑鸿寖鍥�
+ if (oldSize !== obj.limit) {
+ page.value.current = 1;
+ }
+ getList();
};
// 閫夋嫨鍙樺寲澶勭悊
diff --git a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
index 1c71cdc..ba8e46d 100644
--- a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
+++ b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
@@ -42,66 +42,15 @@
</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>
+ <PIMTable
+ rowKey="id"
+ :column="regulationTableColumn"
+ :tableData="regulations"
+ :tableLoading="tableLoading"
+ :page="page"
+ :isShowPagination="true"
+ @pagination="paginationChange"
+ />
</div>
</el-card>
<!-- 鐢ㄥ嵃鐢宠瀵硅瘽妗嗭紙宸茬Щ闄わ級 -->
@@ -271,7 +220,7 @@
:delete-method="handleAttachmentDelete"
:rules-regulations-management-id="currentFileRuleId"
:name-column-label="'闄勪欢鍚嶇О'"
- @upload="handleAttachmentUpload" />
+ @upload="handleAttachmentUpload"/>
</div>
</template>
@@ -293,6 +242,7 @@
delRuleFile,
addRuleFile,
} from "@/api/collaborativeApproval/rulesRegulationsManagementFile.js";
+ import PIMTable from "@/components/PIMTable/PIMTable.vue";
// 鍝嶅簲寮忔暟鎹�
const operationType = ref("add");
@@ -310,7 +260,7 @@
const currentFileRuleId = ref(null);
const filePage = reactive({
current: 1,
- size: 10,
+ size: 1000,
total: 0,
});
// 瑙勭珷鍒跺害鐩稿叧
@@ -360,6 +310,45 @@
});
const regulations = ref([]);
+
+ // 琛ㄦ牸鍒楅厤缃�
+ const regulationTableColumn = ref([
+ { label: "鍒跺害缂栧彿", prop: "regulationNum"},
+ { label: "鍒跺害鏍囬", prop: "title" },
+ {
+ label: "鍒嗙被",
+ prop: "category",
+ dataType: "tag",
+ formatData: (v) => getCategoryText(v),
+ formatType: () => "info",
+ },
+ { label: "鐗堟湰", prop: "version", width: 120 },
+ { label: "鍙戝竷浜�", prop: "createUserName", width: 120 },
+ { label: "鍙戝竷鏃堕棿", prop: "createTime", width: 180 },
+ {
+ label: "鐘舵��",
+ prop: "status",
+ width: 100,
+ dataType: "tag",
+ formatData: (v) => (v === "active" ? "鐢熸晥涓�" : "宸插簾姝�"),
+ formatType: (v) => (v === "active" ? "success" : "info"),
+ },
+ { label: "宸茶浜烘暟", prop: "readCount", width: 100 },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ width: 320,
+ fixed: "right",
+ align: "center",
+ operation: [
+ { name: "鏌ョ湅", clickFun: (row) => viewRegulation(row) },
+ { name: "缂栬緫", clickFun: (row) => handleEdit(row) },
+ { name: "搴熷純", clickFun: (row) => repealEdit(row) },
+ { name: "鐗堟湰鍘嗗彶", clickFun: (row) => viewVersionHistory(row) },
+ { name: "闄勪欢", clickFun: (row) => openFileDialog(row) },
+ ],
+ },
+ ]);
const versionHistory = ref([]);
@@ -642,14 +631,19 @@
regulations.value = res.data.records;
// 杩囨护鎺夊凡搴熷純鐨勫埗搴�
// regulations.value = res.data.records.filter(item => item.status !== 'repealed')
- page.value.total = res.data.total;
+ page.total = res.data.total;
tableLoading.value = false;
})
.catch(err => {
tableLoading.value = false;
});
};
-
+ // 鍒嗛〉鍙樺寲澶勭悊
+ const paginationChange = (obj) => {
+ page.current = obj.page;
+ page.size = obj.limit;
+ getRegulationList();
+ };
onMounted(() => {
// 鍒濆鍖�
getRegulationList();
diff --git a/src/views/collaborativeApproval/sealManagement/index.vue b/src/views/collaborativeApproval/sealManagement/index.vue
index 5193aa9..c0d13f2 100644
--- a/src/views/collaborativeApproval/sealManagement/index.vue
+++ b/src/views/collaborativeApproval/sealManagement/index.vue
@@ -36,48 +36,15 @@
</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" />
+ <PIMTable
+ rowKey="id"
+ :column="sealTableColumn"
+ :tableData="sealApplications"
+ :tableLoading="tableLoading"
+ :page="page"
+ :isShowPagination="true"
+ @pagination="paginationChange"
+ />
</div>
</el-card>
@@ -128,55 +95,6 @@
</el-form>
</FormDialog>
- <!-- 瑙勭珷鍒跺害鍙戝竷瀵硅瘽妗� -->
- <!-- <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> -->
-
<!-- 鐢ㄥ嵃璇︽儏瀵硅瘽妗� -->
<FormDialog
v-model="showSealDetailDialog"
@@ -204,81 +122,6 @@
</div>
</FormDialog>
- <!-- 瑙勭珷鍒跺害璇︽儏瀵硅瘽妗� -->
- <FormDialog
- v-model="showRegulationDetailDialog"
- title="瑙勭珷鍒跺害璇︽儏"
- :width="'800px'"
- @close="closeRegulationDetailDialog"
- @confirm="handleRegulationDetailConfirm"
- @cancel="closeRegulationDetailDialog"
- >
- <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>
- </FormDialog>
-
- <!-- 鐗堟湰鍘嗗彶瀵硅瘽妗� -->
- <FormDialog
- v-model="showVersionHistoryDialog"
- title="鐗堟湰鍘嗗彶"
- :width="'800px'"
- @close="closeVersionHistoryDialog"
- @confirm="closeVersionHistoryDialog"
- @cancel="closeVersionHistoryDialog"
- >
- <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>
- </FormDialog>
-
- <!-- 闃呰鐘舵�佸璇濇 -->
- <FormDialog
- v-model="showReadStatusDialog"
- title="闃呰鐘舵��"
- :width="'800px'"
- @close="closeReadStatusDialog"
- @confirm="closeReadStatusDialog"
- @cancel="closeReadStatusDialog"
- >
- <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>
- </FormDialog>
</div>
</template>
@@ -286,20 +129,13 @@
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 { listSealApplication, addSealApplication, updateSealApplication } from '@/api/collaborativeApproval/sealManagement.js'
+import { userListNoPageByTenantId } from '@/api/system/user.js'
import useUserStore from '@/store/modules/user'
-import { userLoginFacotryList } from "@/api/system/user.js"
-import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"
import FormDialog from '@/components/Dialog/FormDialog.vue'
+import PIMTable from '@/components/PIMTable/PIMTable.vue'
// 鍝嶅簲寮忔暟鎹�
-const currentUser = ref(null)
-const activeTab = ref('seal')
-const operationType = ref('add')
-const tableData = ref([])
// 鐢ㄥ嵃鐢宠鐩稿叧
const userStore = useUserStore()
const route = useRoute()
@@ -335,64 +171,11 @@
// 鍒嗛〉鍙傛暟
const page = reactive({
current: 1,
- size: 100,
+ 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 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) => {
@@ -403,7 +186,7 @@
}
return statusMap[status] || 'info'
}
-// 鍒跺害鐘舵��
+// 鐢ㄥ嵃鐢宠鐘舵�佹枃鏈�
const getStatusText = (status) => {
const statusMap = {
pending: '寰呭鎵�',
@@ -418,20 +201,56 @@
official: '鍏珷',
contract: '鍚堝悓涓撶敤绔�',
finance: '璐㈠姟涓撶敤绔�',
+ legal: '娉曚汉绔�',
tegal: '鎶�鏈笓鐢ㄧ珷'
}
return sealTypeMap[sealType] || '鏈煡'
}
-// 鍒跺害鍒嗙被
-const getCategoryText = (category) => {
- const categoryMap = {
- hr: '浜轰簨鍒跺害',
- finance: '璐㈠姟鍒跺害',
- safety: '瀹夊叏鍒跺害',
- tech: '鎶�鏈埗搴�'
+
+// 鐢ㄥ嵃鐢宠琛ㄦ牸鍒楅厤缃紙闇�鍦� getStatusText/getSealTypeText 绛変箣鍚庡畾涔夛級
+const sealTableColumn = ref([
+ { label: '鐢宠缂栧彿', prop: 'applicationNum',},
+ { label: '鐢宠鏍囬', prop: 'title', showOverflowTooltip: true },
+ { label: '鐢宠浜�', prop: 'createUserName', },
+ { label: '鎵�灞為儴闂�', prop: 'department', width: 150 },
+ {
+ label: '鐢ㄥ嵃绫诲瀷',
+ prop: 'sealType',
+ dataType: 'tag',
+ formatData: (v) => getSealTypeText(v),
+ formatType: () => 'info'
+ },
+ { label: '鐢宠鏃堕棿', prop: 'createTime', width: 180 },
+ {
+ label: '鐘舵��',
+ prop: 'status',
+ width: 100,
+ dataType: 'tag',
+ formatData: (v) => getStatusText(v),
+ formatType: (v) => getStatusType(v)
+ },
+ {
+ dataType: 'action',
+ label: '鎿嶄綔',
+ width: 200,
+ fixed: 'right',
+ align: 'center',
+ operation: [
+ { name: '鏌ョ湅', clickFun: (row) => viewSealDetail(row) },
+ {
+ name: '瀹℃壒',
+ clickFun: (row) => approveSeal(row),
+ showHide: (row) => row.status === 'pending'
+ },
+ {
+ name: '鎷掔粷',
+ clickFun: (row) => rejectSeal(row),
+ showHide: (row) => row.status === 'pending'
+ }
+ ]
}
- return categoryMap[category] || '鏈煡'
-}
+])
+
// 鎼滅储鍗扮珷鐢宠
const searchSealApplications = () => {
page.current=1
@@ -445,17 +264,6 @@
sealSearchForm.status = ''
sealSearchForm.applicationNum = ''
searchSealApplications()
-}
-// 鎼滅储鍒跺害
-const searchRegulations = () => {
- page.current=1
- getRegulationList()
-}
-// 閲嶇疆鍒跺害鎼滅储
-const resetRegulationSearch = () => {
- regulationSearchForm.title = ''
- regulationSearchForm.category = ''
- searchRegulations()
}
// 鎻愪氦鐢ㄥ嵃鐢宠
const submitSealApplication = async () => {
@@ -505,106 +313,6 @@
const closeSealDetailDialog = () => {
showSealDetailDialog.value = false
}
-// 鍏抽棴瑙勭珷鍒跺害璇︽儏瀵硅瘽妗�
-const closeRegulationDetailDialog = () => {
- showRegulationDetailDialog.value = false
-}
-// 澶勭悊瑙勭珷鍒跺害璇︽儏纭
-const handleRegulationDetailConfirm = () => {
- // 濡傛灉tableData>0锛屾墽琛岀‘璁ゆ煡鐪嬫搷浣�
- if (currentRegulationDetail.value && tableData.value && tableData.value.length > 0) {
- resetForm(currentRegulationDetail.value)
- }
- closeRegulationDetailDialog()
-}
-// 鍏抽棴鐗堟湰鍘嗗彶瀵硅瘽妗�
-const closeVersionHistoryDialog = () => {
- showVersionHistoryDialog.value = false
-}
-// 鍏抽棴闃呰鐘舵�佸璇濇
-const closeReadStatusDialog = () => {
- showReadStatusDialog.value = false
-}
-// 鏂板
-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) => {
@@ -613,7 +321,6 @@
}
// 瀹℃壒鐢ㄥ嵃鐢宠
const approveSeal = (row) => {
- console.log(row)
ElMessageBox.confirm('纭閫氳繃璇ョ敤鍗扮敵璇凤紵', '鎻愮ず', {
confirmButtonText: '纭畾',
cancelButtonText: '鍙栨秷',
@@ -623,6 +330,7 @@
updateSealApplication(row).then(res => {
if(res.code == 200){
ElMessage.success('瀹℃壒閫氳繃')
+ getSealApplicationList()
}
})
})
@@ -638,122 +346,10 @@
row.status = 'rejected'
updateSealApplication(row).then(res => {
if(res.code == 200){
- ElMessage.success('瀹℃壒鎷掔粷')
+ ElMessage.success('宸叉嫆缁濈敵璇�')
+ getSealApplicationList()
}
})
- ElMessage.success('宸叉嫆缁濈敵璇�')
- })
-}
-// 鑾峰彇鍦ㄨ亴鍛樺伐鍒楄〃
-const getList = () => {
- tableLoading.value = true;
- //鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛淇℃伅
- getUserProfile().then(res => {
- if(res.code == 200){
- console.log(res.data.userName)
- currentUser.value = res.data.userName
- }
- })
- staffOnJobListPage({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('鍒跺害闃呰鐘舵�佷慨鏀规垚鍔�')
- }
- })
- }
-
- }
})
}
@@ -766,46 +362,15 @@
// 鑾峰彇鍗扮珷鐢宠鍒楄〃鏁版嵁
const getSealApplicationList = async () => {
tableLoading.value = true
- listSealApplication(page,sealSearchForm)
+ 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;
-
+ sealApplications.value = res.data.records
+ page.total = res.data.total
+ tableLoading.value = false
}).catch(err => {
- tableLoading.value = false;
+ 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;
- })
-}
-
// 鍒嗛〉鍙樺寲澶勭悊
const paginationChange = (obj) => {
page.current = obj.page;
@@ -831,7 +396,6 @@
} else {
getSealApplicationList()
}
- getRegulationList()
})
</script>
@@ -854,26 +418,7 @@
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/equipmentManagement/inspectionManagement/index.vue b/src/views/equipmentManagement/inspectionManagement/index.vue
index f4975e6..35f82d5 100644
--- a/src/views/equipmentManagement/inspectionManagement/index.vue
+++ b/src/views/equipmentManagement/inspectionManagement/index.vue
@@ -1,31 +1,37 @@
<template>
<div class="app-container">
- <el-form :inline="true" :model="queryParams" class="search-form">
+ <el-form :inline="true"
+ :model="queryParams"
+ class="search-form">
<el-form-item label="宸℃浠诲姟鍚嶇О">
- <el-input
- v-model="queryParams.taskName"
- placeholder="璇疯緭鍏ュ贰妫�浠诲姟鍚嶇О"
- clearable
- style="width: 200px "
- />
+ <el-input v-model="queryParams.taskName"
+ placeholder="璇疯緭鍏ュ贰妫�浠诲姟鍚嶇О"
+ clearable
+ style="width: 200px " />
</el-form-item>
<el-form-item>
- <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+ <el-button type="primary"
+ @click="handleQuery">鏌ヨ</el-button>
<el-button @click="resetQuery">閲嶇疆</el-button>
</el-form-item>
</el-form>
<el-card>
<div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
- <el-radio-group v-model="activeRadio" @change="radioChange">
+ <el-radio-group v-model="activeRadio"
+ @change="radioChange">
<el-radio-button v-for="tab in radios"
:key="tab.name"
:label="tab.label"
- :value="tab.name"/>
+ :value="tab.name" />
</el-radio-group>
<!-- 鎿嶄綔鎸夐挳鍖� -->
<el-space v-if="activeRadio !== 'task'">
- <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">鏂板缓</el-button>
- <el-button type="danger" :icon="Delete" @click="handleDelete">鍒犻櫎</el-button>
+ <el-button type="primary"
+ :icon="Plus"
+ @click="handleAdd(undefined)">鏂板缓</el-button>
+ <el-button type="danger"
+ :icon="Delete"
+ @click="handleDelete">鍒犻櫎</el-button>
<el-button @click="handleOut">瀵煎嚭</el-button>
</el-space>
<el-space v-else>
@@ -34,320 +40,354 @@
</div>
<div>
<PIMTable :table-loading="tableLoading"
- :table-data="tableData"
- :column="tableColumns"
- @selection-change="handleSelectionChange"
- @pagination="handlePagination"
- :is-selection="true"
- :border="true"
- :page="{
+ :table-data="tableData"
+ :column="tableColumns"
+ @selection-change="handleSelectionChange"
+ @pagination="handlePagination"
+ :is-selection="true"
+ :border="true"
+ :page="{
current: pageNum,
size: pageSize,
total: total,
layout: 'total, sizes, prev, pager, next, jumper'
}"
- :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
- >
+ :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }">
<template #inspector="{ row }">
<div class="person-tags">
<!-- 璋冭瘯淇℃伅锛屼笂绾挎椂鍒犻櫎 -->
<!-- {{ console.log('inspector data:', row.inspector) }} -->
<template v-if="row.inspector && row.inspector.length > 0">
- <el-tag
- v-for="(person, index) in row.inspector"
- :key="index"
- size="small"
- type="primary"
- class="person-tag"
- >
+ <el-tag v-for="(person, index) in row.inspector"
+ :key="index"
+ size="small"
+ type="primary"
+ class="person-tag">
{{ person }}
</el-tag>
</template>
- <span v-else class="no-data">--</span>
+ <span v-else
+ class="no-data">--</span>
</div>
</template>
</PIMTable>
</div>
</el-card>
- <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
+ <form-dia ref="formDia"
+ @closeDia="handleQuery"></form-dia>
<view-files ref="viewFiles"></view-files>
</div>
</template>
<script setup>
-import { Delete, Plus } from "@element-plus/icons-vue";
-import { onMounted, ref, reactive, getCurrentInstance, nextTick } from "vue";
-import { ElMessageBox } from "element-plus";
+ import { Delete, Plus } from "@element-plus/icons-vue";
+ import { onMounted, ref, reactive, getCurrentInstance, nextTick } from "vue";
+ import { ElMessageBox } from "element-plus";
-// 缁勪欢寮曞叆
-import PIMTable from "@/components/PIMTable/PIMTable.vue";
-import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
-import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
+ // 缁勪欢寮曞叆
+ import PIMTable from "@/components/PIMTable/PIMTable.vue";
+ import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
+ import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
-// 鎺ュ彛寮曞叆
-import {
- delTimingTask,
- inspectionTaskList,
- timingTaskList
-} from "@/api/inspectionManagement/index.js";
+ // 鎺ュ彛寮曞叆
+ import {
+ delTimingTask,
+ inspectionTaskList,
+ timingTaskList,
+ } from "@/api/inspectionManagement/index.js";
-// 鍏ㄥ眬鍙橀噺
-const { proxy } = getCurrentInstance();
-const formDia = ref();
-const viewFiles = ref();
+ // 鍏ㄥ眬鍙橀噺
+ const { proxy } = getCurrentInstance();
+ const formDia = ref();
+ const viewFiles = ref();
-// 鏌ヨ鍙傛暟
-const queryParams = reactive({
- taskName: "",
-});
+ // 鏌ヨ鍙傛暟
+ const queryParams = reactive({
+ taskName: "",
+ });
-// 鍗曢�夋閰嶇疆
-const activeRadio = ref("taskManage");
-const radios = reactive([
- { name: "taskManage", label: "瀹氭椂浠诲姟绠$悊" },
- { name: "task", label: "瀹氭椂浠诲姟璁板綍" },
-]);
+ // 鍗曢�夋閰嶇疆
+ const activeRadio = ref("taskManage");
+ const radios = reactive([
+ { name: "taskManage", label: "瀹氭椂浠诲姟绠$悊" },
+ { name: "task", label: "瀹氭椂浠诲姟璁板綍" },
+ ]);
-// 琛ㄦ牸鏁版嵁
-const selectedRows = ref([]);
-const tableData = ref([]);
-const operationsArr = ref([]);
-const tableColumns = ref([]);
-const tableLoading = ref(false);
-const total = ref(0);
-const pageNum = ref(1);
-const pageSize = ref(10);
+ // 琛ㄦ牸鏁版嵁
+ const selectedRows = ref([]);
+ const tableData = ref([]);
+ const operationsArr = ref([]);
+ const tableColumns = ref([]);
+ const tableLoading = ref(false);
+ const total = ref(0);
+ const pageNum = ref(1);
+ const pageSize = ref(10);
-// 鍒楅厤缃�
-const columns = ref([
- { prop: "taskName", label: "宸℃浠诲姟鍚嶇О", minWidth: 160 },
- { prop: "remarks", label: "澶囨敞", minWidth: 150 },
- { prop: "inspector", label: "鎵ц宸℃浜�", minWidth: 150, slot: "inspector" },
- {
- prop: "frequencyType",
- label: "棰戞",
- minWidth: 150,
- formatter: (_, __, val) => ({
- DAILY: "姣忔棩",
- WEEKLY: "姣忓懆",
- MONTHLY: "姣忔湀",
- QUARTERLY: "瀛e害"
- }[val] || "")
- },
- {
- prop: "frequencyDetail",
- label: "寮�濮嬫棩鏈熶笌鏃堕棿",
- minWidth: 150,
- formatter: (row, column, cellValue) => {
- // 鍏堝垽鏂槸鍚︽槸瀛楃涓�
- if (typeof cellValue !== 'string') return '';
- let val = cellValue;
- const replacements = {
- MON: '鍛ㄤ竴',
- TUE: '鍛ㄤ簩',
- WED: '鍛ㄤ笁',
- THU: '鍛ㄥ洓',
- FRI: '鍛ㄤ簲',
- SAT: '鍛ㄥ叚',
- SUN: '鍛ㄦ棩'
- };
- // 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
- return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
- }
- },
- { prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
- { prop: "createTime", label: "鐧昏鏃ユ湡", minWidth: 100 },
-]);
+ // 鍒楅厤缃�
+ const columns = ref([
+ { prop: "taskName", label: "宸℃浠诲姟鍚嶇О", minWidth: 160 },
+ { prop: "remarks", label: "澶囨敞", minWidth: 150 },
+ { prop: "inspector", label: "鎵ц宸℃浜�", minWidth: 150, slot: "inspector" },
+ {
+ prop: "frequencyType",
+ label: "棰戞",
+ minWidth: 150,
+ // formatter: (_, __, val) => ({
+ // DAILY: "姣忔棩",
+ // WEEKLY: "姣忓懆",
+ // MONTHLY: "姣忔湀",
+ // QUARTERLY: "瀛e害"
+ // }[val] || "")
+ formatData: params => {
+ return params === "DAILY"
+ ? "姣忔棩"
+ : params === "WEEKLY"
+ ? "姣忓懆"
+ : params === "MONTHLY"
+ ? "姣忔湀"
+ : params === "QUARTERLY"
+ ? "瀛e害"
+ : "";
+ },
+ },
+ {
+ prop: "frequencyDetail",
+ label: "寮�濮嬫棩鏈熶笌鏃堕棿",
+ minWidth: 150,
+ formatter: (row, column, cellValue) => {
+ // 鍏堝垽鏂槸鍚︽槸瀛楃涓�
+ if (typeof cellValue !== "string") return "";
+ let val = cellValue;
+ const replacements = {
+ MON: "鍛ㄤ竴",
+ TUE: "鍛ㄤ簩",
+ WED: "鍛ㄤ笁",
+ THU: "鍛ㄥ洓",
+ FRI: "鍛ㄤ簲",
+ SAT: "鍛ㄥ叚",
+ SUN: "鍛ㄦ棩",
+ };
+ // 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
+ return val.replace(
+ /MON|TUE|WED|THU|FRI|SAT|SUN/g,
+ match => replacements[match]
+ );
+ },
+ },
+ { prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
+ { prop: "createTime", label: "鐧昏鏃ユ湡", minWidth: 100 },
+ ]);
-// 鎿嶄綔鍒楅厤缃�
-const getOperationColumn = (operations) => {
- if (!operations || operations.length === 0) return null;
-
- const operationConfig = {
- label: "鎿嶄綔",
- width: 130,
- fixed: "right",
- dataType: "action",
- operation: operations.map(op => {
- switch (op) {
- case 'edit':
- return {
- name: "缂栬緫",
- clickFun: handleAdd,
- color: "#409EFF"
- };
- case 'viewFile':
- return {
- name: "鏌ョ湅闄勪欢",
- clickFun: viewFile,
- color: "#67C23A"
- };
- default:
- return null;
- }
- }).filter(Boolean)
+ // 鎿嶄綔鍒楅厤缃�
+ const getOperationColumn = operations => {
+ if (!operations || operations.length === 0) return null;
+
+ const operationConfig = {
+ label: "鎿嶄綔",
+ width: 130,
+ fixed: "right",
+ dataType: "action",
+ operation: operations
+ .map(op => {
+ switch (op) {
+ case "edit":
+ return {
+ name: "缂栬緫",
+ clickFun: handleAdd,
+ color: "#409EFF",
+ };
+ case "viewFile":
+ return {
+ name: "鏌ョ湅闄勪欢",
+ clickFun: viewFile,
+ color: "#67C23A",
+ };
+ default:
+ return null;
+ }
+ })
+ .filter(Boolean),
+ };
+
+ return operationConfig;
};
-
- return operationConfig;
-};
-onMounted(() => {
- radioChange('taskManage');
-});
-
-// 鍗曢�夊彉鍖�
-const radioChange = (value) => {
- if (value === "taskManage") {
- const operationColumn = getOperationColumn(['edit']);
- tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
- operationsArr.value = ['edit'];
- } else if (value === "task") {
- const operationColumn = getOperationColumn(['viewFile']);
- tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
- operationsArr.value = ['viewFile'];
- }
- pageNum.value = 1;
- pageSize.value = 10;
- getList();
-};
-
-// 鏌ヨ鎿嶄綔
-const handleQuery = () => {
- pageNum.value = 1;
- pageSize.value = 10;
- getList();
-};
-// 鍒嗛〉澶勭悊
-const handlePagination = (val) => {
- pageNum.value = val.page;
- pageSize.value = val.limit;
- getList();
-};
-// 鑾峰彇鍒楄〃鏁版嵁
-const getList = () => {
- tableLoading.value = true;
-
- const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
-
- let apiCall;
- if (activeRadio.value === "task") {
- apiCall = inspectionTaskList(params);
- } else {
- apiCall = timingTaskList(params);
- }
-
- apiCall.then(res => {
- const rawData = res.data.records || [];
- // 澶勭悊 inspector 瀛楁锛屽皢瀛楃涓茶浆鎹负鏁扮粍锛堥�傜敤浜庢墍鏈夋儏鍐碉級
- tableData.value = rawData.map(item => {
- const processedItem = { ...item };
-
- // 澶勭悊 inspector 瀛楁
- if (processedItem.inspector) {
- if (typeof processedItem.inspector === 'string') {
- // 瀛楃涓叉寜閫楀彿鍒嗗壊
- processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
- } else if (!Array.isArray(processedItem.inspector)) {
- // 闈炴暟缁勮浆涓烘暟缁�
- processedItem.inspector = [processedItem.inspector];
- }
- } else {
- // 绌哄�艰涓虹┖鏁扮粍
- processedItem.inspector = [];
- }
-
- return processedItem;
- });
- total.value = res.data.total || 0;
- }).finally(() => {
- tableLoading.value = false;
+ onMounted(() => {
+ radioChange("taskManage");
});
-};
-// 閲嶇疆鏌ヨ
-const resetQuery = () => {
- for (const key in queryParams) {
- if (!["pageNum", "pageSize"].includes(key)) {
- queryParams[key] = "";
+ // 鍗曢�夊彉鍖�
+ const radioChange = value => {
+ if (value === "taskManage") {
+ const operationColumn = getOperationColumn(["edit"]);
+ tableColumns.value = [
+ ...columns.value,
+ ...(operationColumn ? [operationColumn] : []),
+ ];
+ operationsArr.value = ["edit"];
+ } else if (value === "task") {
+ const operationColumn = getOperationColumn(["viewFile"]);
+ tableColumns.value = [
+ ...columns.value,
+ ...(operationColumn ? [operationColumn] : []),
+ ];
+ operationsArr.value = ["viewFile"];
}
- }
- handleQuery();
-};
+ pageNum.value = 1;
+ pageSize.value = 10;
+ getList();
+ };
-// 鏂板 / 缂栬緫
-const handleAdd = (row) => {
- const type = row ? 'edit' : 'add';
- nextTick(() => {
- formDia.value?.openDialog(type, row);
- });
-};
+ // 鏌ヨ鎿嶄綔
+ const handleQuery = () => {
+ pageNum.value = 1;
+ pageSize.value = 10;
+ getList();
+ };
+ // 鍒嗛〉澶勭悊
+ const handlePagination = val => {
+ pageNum.value = val.page;
+ pageSize.value = val.limit;
+ getList();
+ };
+ // 鑾峰彇鍒楄〃鏁版嵁
+ const getList = () => {
+ tableLoading.value = true;
-// 鏌ョ湅闄勪欢
-const viewFile = (row) => {
- nextTick(() => {
- viewFiles.value?.openDialog(row);
- });
-};
+ const params = {
+ ...queryParams,
+ size: pageSize.value,
+ current: pageNum.value,
+ };
-// 鍒犻櫎鎿嶄綔
-const handleDelete = () => {
- if (!selectedRows.value.length) {
- proxy.$modal.msgWarning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
- return;
- }
-
- const deleteIds = selectedRows.value.map(item => item.id);
-
- proxy.$modal.confirm('鏄惁纭鍒犻櫎鎵�閫夋暟鎹」锛�').then(() => {
- return delTimingTask(deleteIds);
- }).then(() => {
- proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
- handleQuery();
- }).catch(() => {});
-};
+ let apiCall;
+ if (activeRadio.value === "task") {
+ apiCall = inspectionTaskList(params);
+ } else {
+ apiCall = timingTaskList(params);
+ }
-// 澶氶�夊彉鏇�
-const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
-};
+ apiCall
+ .then(res => {
+ const rawData = res.data.records || [];
+ // 澶勭悊 inspector 瀛楁锛屽皢瀛楃涓茶浆鎹负鏁扮粍锛堥�傜敤浜庢墍鏈夋儏鍐碉級
+ tableData.value = rawData.map(item => {
+ const processedItem = { ...item };
-// 瀵煎嚭
-const handleOut = () => {
- ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
- confirmButtonText: "纭",
- cancelButtonText: "鍙栨秷",
- type: "warning",
- })
- .then(() => {
- // 鏍规嵁褰撳墠閫変腑鐨勬爣绛鹃〉璋冪敤涓嶅悓鐨勫鍑烘帴鍙�
- if (activeRadio.value === "taskManage") {
- // 瀹氭椂浠诲姟绠$悊
- proxy.download("/timingTask/export", {}, "瀹氭椂浠诲姟绠$悊.xlsx");
- } else if (activeRadio.value === "task") {
- // 瀹氭椂浠诲姟璁板綍
- proxy.download("/inspectionTask/export", {}, "瀹氭椂浠诲姟璁板綍.xlsx");
+ // 澶勭悊 inspector 瀛楁
+ if (processedItem.inspector) {
+ if (typeof processedItem.inspector === "string") {
+ // 瀛楃涓叉寜閫楀彿鍒嗗壊
+ processedItem.inspector = processedItem.inspector
+ .split(",")
+ .map(s => s.trim())
+ .filter(s => s);
+ } else if (!Array.isArray(processedItem.inspector)) {
+ // 闈炴暟缁勮浆涓烘暟缁�
+ processedItem.inspector = [processedItem.inspector];
+ }
+ } else {
+ // 绌哄�艰涓虹┖鏁扮粍
+ processedItem.inspector = [];
+ }
+
+ return processedItem;
+ });
+ total.value = res.data.total || 0;
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ };
+
+ // 閲嶇疆鏌ヨ
+ const resetQuery = () => {
+ for (const key in queryParams) {
+ if (!["pageNum", "pageSize"].includes(key)) {
+ queryParams[key] = "";
}
- })
- .catch(() => {
- proxy.$modal.msg("宸插彇娑�");
+ }
+ handleQuery();
+ };
+
+ // 鏂板 / 缂栬緫
+ const handleAdd = row => {
+ const type = row ? "edit" : "add";
+ nextTick(() => {
+ formDia.value?.openDialog(type, row);
});
-};
+ };
+
+ // 鏌ョ湅闄勪欢
+ const viewFile = row => {
+ nextTick(() => {
+ viewFiles.value?.openDialog(row);
+ });
+ };
+
+ // 鍒犻櫎鎿嶄綔
+ const handleDelete = () => {
+ if (!selectedRows.value.length) {
+ proxy.$modal.msgWarning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
+ return;
+ }
+
+ const deleteIds = selectedRows.value.map(item => item.id);
+
+ proxy.$modal
+ .confirm("鏄惁纭鍒犻櫎鎵�閫夋暟鎹」锛�")
+ .then(() => {
+ return delTimingTask(deleteIds);
+ })
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ handleQuery();
+ })
+ .catch(() => {});
+ };
+
+ // 澶氶�夊彉鏇�
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
+
+ // 瀵煎嚭
+ const handleOut = () => {
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ // 鏍规嵁褰撳墠閫変腑鐨勬爣绛鹃〉璋冪敤涓嶅悓鐨勫鍑烘帴鍙�
+ if (activeRadio.value === "taskManage") {
+ // 瀹氭椂浠诲姟绠$悊
+ proxy.download("/timingTask/export", {}, "瀹氭椂浠诲姟绠$悊.xlsx");
+ } else if (activeRadio.value === "task") {
+ // 瀹氭椂浠诲姟璁板綍
+ proxy.download("/inspectionTask/export", {}, "瀹氭椂浠诲姟璁板綍.xlsx");
+ }
+ })
+ .catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
</script>
<style scoped>
-.person-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
-}
+ .person-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ }
-.person-tag {
- margin-right: 4px;
- margin-bottom: 2px;
-}
+ .person-tag {
+ margin-right: 4px;
+ margin-bottom: 2px;
+ }
-.no-data {
- color: #909399;
- font-size: 14px;
-}
+ .no-data {
+ color: #909399;
+ font-size: 14px;
+ }
</style>
\ No newline at end of file
diff --git a/src/views/fileManagement/document/attachmentManager.vue b/src/views/fileManagement/document/attachmentManager.vue
index a4e1d43..f3c0504 100644
--- a/src/views/fileManagement/document/attachmentManager.vue
+++ b/src/views/fileManagement/document/attachmentManager.vue
@@ -13,7 +13,6 @@
:on-remove="handleRemove"
:file-list="fileList"
multiple
- :limit="10"
:show-file-list="false"
:data="{documentId: currentDocumentId}"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt,.xml,.jpg,.jpeg,.png,.gif,.bmp,.rar,.zip,.7z"
@@ -30,7 +29,7 @@
<!-- 闄勪欢鍒楄〃 -->
<div class="attachment-list">
- <el-table :data="fileList" border height="400px" v-loading="loading">
+ <el-table :data="fileList" border max-height="400px" v-loading="loading">
<el-table-column label="搴忓彿" type="index" width="60" align="center" />
<el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="200" show-overflow-tooltip />
<el-table-column label="鏂囦欢澶у皬" prop="size" width="100" align="center">
diff --git a/src/views/index.vue b/src/views/index.vue
index 2888b16..401f30a 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -118,11 +118,11 @@
<div class="main-panel">
<div style="display: flex;justify-content: space-between;">
<div class="section-title">搴旀敹搴斾粯缁熻</div>
- <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">
- <el-radio-button label="鎸夊懆" :value="1" />
- <el-radio-button label="鎸夋湀" :value="2" />
- <el-radio-button label="鎸夊搴�" :value="3" />
- </el-radio-group>
+<!-- <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">-->
+<!-- <el-radio-button label="鎸夊懆" :value="1" />-->
+<!-- <el-radio-button label="鎸夋湀" :value="2" />-->
+<!-- <el-radio-button label="鎸夊搴�" :value="3" />-->
+<!-- </el-radio-group>-->
</div>
<Echarts ref="chart"
:color="barColors2"
diff --git a/src/views/inventoryManagement/dispatchLog/Record.vue b/src/views/inventoryManagement/dispatchLog/Record.vue
index 6fa29c8..1637226 100644
--- a/src/views/inventoryManagement/dispatchLog/Record.vue
+++ b/src/views/inventoryManagement/dispatchLog/Record.vue
@@ -112,9 +112,7 @@
delStockOut,
} from "@/api/inventoryManagement/stockOut.js";
import {
- findAllQualifiedStockRecordTypeOptions,
- findAllStockRecordTypeOptions,
- findAllUnqualifiedStockRecordTypeOptions
+ findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions,
} from "@/api/basicData/enum.js";
const userStore = useUserStore();
@@ -186,13 +184,13 @@
// 鑾峰彇鏉ユ簮绫诲瀷閫夐」
const fetchStockRecordTypeOptions = () => {
if (props.type === '0') {
- findAllQualifiedStockRecordTypeOptions()
+ findAllQualifiedStockOutRecordTypeOptions()
.then(res => {
stockRecordTypeOptions.value = res.data;
})
return
}
- findAllUnqualifiedStockRecordTypeOptions()
+ findAllUnQualifiedStockOutRecordTypeOptions()
.then(res => {
stockRecordTypeOptions.value = res.data;
})
diff --git a/src/views/inventoryManagement/receiptManagement/Record.vue b/src/views/inventoryManagement/receiptManagement/Record.vue
index b74ffc4..cd103ea 100644
--- a/src/views/inventoryManagement/receiptManagement/Record.vue
+++ b/src/views/inventoryManagement/receiptManagement/Record.vue
@@ -109,8 +109,7 @@
batchDeleteStockInRecords,
} from "@/api/inventoryManagement/stockInRecord.js";
import {
- findAllQualifiedStockRecordTypeOptions,
- findAllUnqualifiedStockRecordTypeOptions
+ findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions,
} from "@/api/basicData/enum.js";
const {proxy} = getCurrentInstance();
@@ -176,13 +175,13 @@
// 鑾峰彇鏉ユ簮绫诲瀷閫夐」
const fetchStockRecordTypeOptions = () => {
if (props.type === '0') {
- findAllQualifiedStockRecordTypeOptions()
+ findAllQualifiedStockInRecordTypeOptions()
.then(res => {
stockRecordTypeOptions.value = res.data;
})
return
}
- findAllUnqualifiedStockRecordTypeOptions()
+ findAllUnQualifiedStockInRecordTypeOptions()
.then(res => {
stockRecordTypeOptions.value = res.data;
})
diff --git a/src/views/inventoryManagement/stockReport/index.vue b/src/views/inventoryManagement/stockReport/index.vue
index a0a1f67..ff1d901 100644
--- a/src/views/inventoryManagement/stockReport/index.vue
+++ b/src/views/inventoryManagement/stockReport/index.vue
@@ -240,14 +240,12 @@
import { ElMessage } from 'element-plus'
import * as echarts from 'echarts'
import {
- getStockMonthlyReport,
- getStockInOutReport,
-} from '@/api/inventoryManagement/stockReport'
-import {
getStockInventoryInAndOutReportList,
getStockInventoryReportList
} from "@/api/inventoryManagement/stockInventory.js";
-import {findAllQualifiedStockRecordTypeOptions} from "@/api/basicData/enum.js";
+import {
+ findAllQualifiedStockInRecordTypeOptions,
+} from "@/api/basicData/enum.js";
const { proxy } = getCurrentInstance()
@@ -277,7 +275,7 @@
// 鑾峰彇鏉ユ簮绫诲瀷閫夐」
const fetchStockRecordTypeOptions = () => {
- findAllQualifiedStockRecordTypeOptions()
+ findAllQualifiedStockInRecordTypeOptions()
.then(res => {
stockRecordTypeOptions.value = res.data;
})
diff --git a/src/views/personnelManagement/contractManagement/filesDia.vue b/src/views/personnelManagement/contractManagement/filesDia.vue
index c0c5ee9..4bcd812 100644
--- a/src/views/personnelManagement/contractManagement/filesDia.vue
+++ b/src/views/personnelManagement/contractManagement/filesDia.vue
@@ -28,6 +28,7 @@
:tableData="tableData"
:tableLoading="tableLoading"
:isSelection="true"
+ :page="page"
@selection-change="handleSelectionChange"
height="500"
@pagination="paginationSearch"
@@ -118,7 +119,7 @@
const getList = () => {
fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => {
tableData.value = res.data.records;
- total.value = res.data.total;
+ page.total = res.data.total;
})
}
// 琛ㄦ牸閫夋嫨鏁版嵁
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 858b633..5c83ab2 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -157,6 +157,10 @@
prop="entryDate"
width="100"
show-overflow-tooltip />
+ <el-table-column label="澶囨敞"
+ prop="remarks"
+ width="200"
+ show-overflow-tooltip />
<el-table-column fixed="right"
label="鎿嶄綔"
width="120"
@@ -450,8 +454,8 @@
<el-row :gutter="30">
<el-col :span="24">
<el-form-item label="澶囨敞路锛�"
- prop="remark">
- <el-input v-model="form.remark"
+ prop="remarks">
+ <el-input v-model="form.remarks"
placeholder="璇疯緭鍏�"
clearable
type="textarea"
@@ -462,7 +466,7 @@
<el-row :gutter="30">
<el-col :span="24">
<el-form-item label="闄勪欢鏉愭枡锛�"
- prop="remark">
+ prop="purchaseLedgerFiles">
<el-upload v-model:file-list="fileList"
:action="upload.url"
multiple
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index 18e21e8..3aecfa0 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -82,10 +82,15 @@
<el-table-column label="浜у搧鍚嶇О" prop="productName" min-width="160" />
<el-table-column label="瑙勬牸鍚嶇О" prop="model" min-width="140" />
<el-table-column label="鍗曚綅" prop="unit" width="100" />
+ <el-table-column label="鏄惁璐ㄦ" prop="isQuality" width="100">
+ <template #default="scope">
+ {{scope.row.isQuality ? "鏄�" : "鍚�"}}
+ </template>
+ </el-table-column>
<el-table-column label="鎿嶄綔" align="center" fixed="right" width="150">
<template #default="scope">
- <el-button type="primary" link size="small" @click="handleEdit(scope.row)">缂栬緫</el-button>
- <el-button type="danger" link size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+ <el-button type="primary" link size="small" @click="handleEdit(scope.row)" :disabled="scope.row.isComplete">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(scope.row)" :disabled="scope.row.isComplete">鍒犻櫎</el-button>
</template>
</el-table-column>
</el-table>
@@ -130,14 +135,15 @@
{{ item.model }}
<!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
</div>
+ <el-tag type="primary" class="product-tag" v-if="item.isQuality">璐ㄦ</el-tag>
</div>
<div v-else class="product-info empty">鏆傛棤浜у搧淇℃伅</div>
</div>
<!-- 鎿嶄綔鎸夐挳 -->
<div class="card-footer">
- <el-button type="primary" link size="small" @click="handleEdit(item)">缂栬緫</el-button>
- <el-button type="danger" link size="small" @click="handleDelete(item)">鍒犻櫎</el-button>
+ <el-button type="primary" link size="small" @click="handleEdit(item)" :disabled="item.isComplete">缂栬緫</el-button>
+ <el-button type="danger" link size="small" @click="handleDelete(item)" :disabled="item.isComplete">鍒犻櫎</el-button>
</div>
</div>
</div>
@@ -188,6 +194,10 @@
clearable
:disabled="true"
/>
+ </el-form-item>
+
+ <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
+ <el-switch v-model="form.isQuality" :active-value="true" inactive-value="false"/>
</el-form-item>
</el-form>
@@ -262,6 +272,7 @@
productName: "",
model: "",
unit: "",
+ isQuality: false,
});
const rules = {
@@ -340,6 +351,7 @@
productName: row.productName || "",
model: row.model || "",
unit: row.unit || "",
+ isQuality: row.isQuality,
};
dialogVisible.value = true;
};
@@ -402,12 +414,14 @@
productRouteId: routeId.value,
processId: form.value.processId,
productModelId: form.value.productModelId,
+ isQuality: form.value.isQuality,
dragSort,
})
: addOrUpdateProcessRouteItem({
routeId: routeId.value,
processId: form.value.processId,
productModelId: form.value.productModelId,
+ isQuality: form.value.isQuality,
dragSort,
});
@@ -432,12 +446,14 @@
id: form.value.id,
processId: form.value.processId,
productModelId: form.value.productModelId,
+ isQuality: form.value.isQuality,
})
: addOrUpdateProcessRouteItem({
routeId: routeId.value,
processId: form.value.processId,
productModelId: form.value.productModelId,
id: form.value.id,
+ isQuality: form.value.isQuality,
});
updatePromise
@@ -733,6 +749,10 @@
color: #409eff;
}
+.product-tag {
+ margin: 10px 0;
+}
+
.card-footer {
display: flex;
justify-content: space-around;
diff --git a/src/views/productionManagement/productionOrder/New.vue b/src/views/productionManagement/productionOrder/New.vue
new file mode 100644
index 0000000..c9c478b
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/New.vue
@@ -0,0 +1,192 @@
+<template>
+ <div>
+ <el-dialog
+ v-model="isShow"
+ title="鏂板鐢熶骇璁㈠崟"
+ width="800"
+ @close="closeModal"
+ >
+ <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+ <el-form-item
+ label="浜у搧鍚嶇О"
+ prop="productModelId"
+ :rules="[
+ {
+ required: true,
+ message: '璇烽�夋嫨浜у搧',
+ trigger: 'change',
+ }
+ ]"
+ >
+ <el-button type="primary" @click="showProductSelectDialog = true">
+ {{ formState.productName ? formState.productName : '閫夋嫨浜у搧' }}
+ </el-button>
+ </el-form-item>
+
+ <el-form-item
+ label="瑙勬牸"
+ prop="productModelName"
+ >
+ <el-input v-model="formState.productModelName" disabled />
+ </el-form-item>
+
+ <el-form-item
+ label="鍗曚綅"
+ prop="unit"
+ >
+ <el-input v-model="formState.unit" disabled />
+ </el-form-item>
+
+ <el-form-item label="宸ヨ壓璺嚎">
+ <el-select v-model="formState.routeId"
+ placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
+ style="width: 100%;"
+ :loading="bindRouteLoading">
+ <el-option v-for="item in routeOptions"
+ :key="item.id"
+ :label="`${item.processRouteCode || ''}`"
+ :value="item.id" />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item
+ label="闇�姹傛暟閲�"
+ prop="quantity"
+ >
+ <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
+ </el-form-item>
+ </el-form>
+
+ <!-- 浜у搧閫夋嫨寮圭獥 -->
+ <ProductSelectDialog
+ v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single
+ />
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleSubmit">纭</el-button>
+ <el-button @click="closeModal">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import {ref, computed, getCurrentInstance} from "vue";
+import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
+
+const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+
+ type: {
+ type: String,
+ required: true,
+ default: 'qualified',
+ },
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+const formState = ref({
+ productId: undefined,
+ productModelId: undefined,
+ routeId: undefined,
+ productName: "",
+ productModelName: "",
+ unit: "",
+ quantity: 0,
+});
+
+const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit('update:visible', val);
+ },
+});
+
+const showProductSelectDialog = ref(false);
+
+let { proxy } = getCurrentInstance()
+
+const closeModal = () => {
+ // 閲嶇疆琛ㄥ崟鏁版嵁
+ formState.value = {
+ productId: undefined,
+ productModelId: undefined,
+ routeId: undefined,
+ productName: "",
+ productModelName: "",
+ quantity: '',
+ };
+ isShow.value = false;
+};
+
+// 浜у搧閫夋嫨澶勭悊
+const handleProductSelect = async (products) => {
+ if (products && products.length > 0) {
+ const product = products[0];
+ formState.value.productId = product.productId;
+ formState.value.productName = product.productName;
+ formState.value.productModelName = product.model;
+ formState.value.productModelId = product.id;
+ formState.value.unit = product.unit;
+ showProductSelectDialog.value = false;
+ fetchRouteOptions( product.id);
+ // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+ proxy.$refs["formRef"]?.validateField('productModelId');
+ }
+};
+
+const routeOptions = ref([]);
+const bindRouteLoading = ref(false);
+const fetchRouteOptions = (productModelId) => {
+ formState.value.routeId = undefined;
+ routeOptions.value = []
+ bindRouteLoading.value = true;
+ listProcessRoute({ productModelId: productModelId }).then(res => {
+ routeOptions.value = res.data || [];
+ }).finally(() => {
+ bindRouteLoading.value = false;
+ })
+}
+
+const handleSubmit = () => {
+ proxy.$refs["formRef"].validate(valid => {
+ if (valid) {
+ // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
+ if (!formState.value.productModelId) {
+ proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+ return;
+ }
+ if (!formState.value.productModelId) {
+ proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
+ return;
+ }
+
+ addProductOrder(formState.value).then(res => {
+ // 鍏抽棴妯℃�佹
+ isShow.value = false;
+ // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+ emit('completed');
+ proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+ })
+ }
+ })
+};
+
+
+defineExpose({
+ closeModal,
+ handleSubmit,
+ isShow,
+});
+</script>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index ece5ca6..761139e 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -41,6 +41,8 @@
</el-form-item>
</el-form>
<div>
+ <el-button type="primary" @click="isShowNewModal = true">鏂板</el-button>
+ <el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
<el-button @click="handleOut">瀵煎嚭</el-button>
</div>
</div>
@@ -51,6 +53,8 @@
:page="page"
:tableLoading="tableLoading"
:row-class-name="tableRowClassName"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
@pagination="pagination">
<template #completionStatus="{ row }">
<el-progress
@@ -86,6 +90,10 @@
</span>
</template>
</el-dialog>
+
+ <new-product-order v-if="isShowNewModal"
+ v-model:visible="isShowNewModal"
+ @completed="handleQuery" />
</div>
</template>
@@ -98,12 +106,17 @@
productOrderListPage,
listProcessRoute,
bindingRoute,
- listProcessBom,
+ listProcessBom, delProductOrder,
} from "@/api/productionManagement/productionOrder.js";
import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
+ import {fileDel} from "@/api/financialManagement/revenueManagement.js";
+ import PIMTable from "@/components/PIMTable/PIMTable.vue";
+ const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
+
const { proxy } = getCurrentInstance();
const router = useRouter();
+ const isShowNewModal = ref(false);
const tableColumn = ref([
{
@@ -208,6 +221,7 @@
size: 100,
total: 0,
});
+ const selectedRows = ref([]);
const data = reactive({
searchForm: {
@@ -239,8 +253,10 @@
// 娣诲姞琛ㄨ绫诲悕鏂规硶
const tableRowClassName = ({ row }) => {
- const diff = row.deliveryDaysDiff;
+ if (!row.deliveryDate) return '';
+ if (row.isFh) return '';
+ const diff = row.deliveryDaysDiff;
if (diff === 15) {
return 'yellow';
} else if (diff === 10) {
@@ -385,6 +401,33 @@
});
};
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = (selection) => {
+ selectedRows.value = selection;
+ };
+
+ const handleDelete = () => {
+ let ids = [];
+ if (selectedRows.value.length > 0) {
+ ids = selectedRows.value.map((item) => item.id);
+ } else {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ }).then(() => {
+ delProductOrder(ids).then((res) => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ });
+ }).catch(() => {
+ proxy.$modal.msg("宸插彇娑�");
+ });
+ };
+
// 瀵煎嚭
const handleOut = () => {
ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
index f979d51..8e92403 100644
--- a/src/views/productionManagement/productionProcess/Edit.vue
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -28,6 +28,9 @@
<el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
<el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
</el-form-item>
+ <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
+ <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
+ </el-form-item>
<el-form-item label="澶囨敞" prop="remark">
<el-input v-model="formState.remark" type="textarea" />
</el-form-item>
@@ -67,6 +70,7 @@
no: props.record.no,
remark: props.record.remark,
salaryQuota: props.record.salaryQuota,
+ isQuality: props.record.isQuality,
});
const isShow = computed({
@@ -87,6 +91,7 @@
no: newRecord.no || '',
remark: newRecord.remark || '',
salaryQuota: newRecord.salaryQuota || '',
+ isQuality: props.record.isQuality,
};
}
}, { immediate: true, deep: true });
@@ -100,6 +105,7 @@
no: props.record.no || '',
remark: props.record.remark || '',
salaryQuota: props.record.salaryQuota || '',
+ isQuality: props.record.isQuality,
};
}
});
diff --git a/src/views/productionManagement/productionProcess/New.vue b/src/views/productionManagement/productionProcess/New.vue
index 7558ba7..5443e8d 100644
--- a/src/views/productionManagement/productionProcess/New.vue
+++ b/src/views/productionManagement/productionProcess/New.vue
@@ -28,6 +28,9 @@
<el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
<el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
</el-form-item>
+ <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
+ <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
+ </el-form-item>
<el-form-item label="澶囨敞" prop="remark">
<el-input v-model="formState.remark" type="textarea" />
</el-form-item>
@@ -60,6 +63,7 @@
name: '',
remark: '',
salaryQuota: '',
+ isQuality: false,
});
const isShow = computed({
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
index 67430cb..4f3f3ef 100644
--- a/src/views/productionManagement/productionProcess/index.vue
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -98,12 +98,18 @@
label: "宸ュ簭鍚嶇О",
prop: "name",
},
-
{
label: "宸ヨ祫瀹氶",
prop: "salaryQuota",
},
{
+ label: "鏄惁璐ㄦ",
+ prop: "isQuality",
+ formatData: (params) => {
+ return params ? "鏄�" : "鍚�";
+ },
+ },
+ {
label: "澶囨敞",
prop: "remark",
},
diff --git a/src/views/qualityManagement/finalInspection/components/formDia.vue b/src/views/qualityManagement/finalInspection/components/formDia.vue
index 8dfd3ba..6a3e774 100644
--- a/src/views/qualityManagement/finalInspection/components/formDia.vue
+++ b/src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -201,64 +201,91 @@
const openDialog = async (type, row) => {
operationType.value = type;
dialogFormVisible.value = true;
- getOptions().then((res) => {
- supplierList.value = res.data;
- });
- let userLists = await userListNoPage();
- userList.value = userLists.data;
- form.value = {}
+ // 鍏堟竻绌鸿〃鍗曢獙璇佺姸鎬侊紝閬垮厤闂儊
+ await nextTick();
+ proxy.$refs.formRef?.clearValidate();
+
+ // 骞惰鍔犺浇鍩虹鏁版嵁
+ const [userListsRes] = await Promise.all([
+ userListNoPage(),
+ getProductOptions(),
+ getOptions().then((res) => {
+ supplierList.value = res.data;
+ })
+ ]);
+ userList.value = userListsRes.data;
+
+ form.value = {}
testStandardOptions.value = [];
tableData.value = [];
- getProductOptions();
+
if (operationType.value === 'edit') {
// 鍏堜繚瀛� testStandardId锛岄伩鍏嶈娓呯┖
const savedTestStandardId = row.testStandardId;
// 鍏堣缃〃鍗曟暟鎹紝浣嗘殏鏃舵竻绌� testStandardId锛岀瓑閫夐」鍔犺浇瀹屾垚鍚庡啀璁剧疆
form.value = {...row, testStandardId: ''}
- currentProductId.value = row.productId || 0
- // 缂栬緫妯″紡涓嬶紝鍏堝姞杞芥寚鏍囬�夐」锛岀劧鍚庡姞杞藉弬鏁板垪琛�
- if (currentProductId.value) {
- // 鍏堝姞杞芥寚鏍囬�夐」
- let params = {
- productId: currentProductId.value,
- inspectType: 2
- }
- qualityInspectDetailByProductId(params).then(res => {
- testStandardOptions.value = res.data || [];
- // 浣跨敤 nextTick 鍜� setTimeout 纭繚閫夐」宸茬粡娓叉煋鍒� DOM
- nextTick(() => {
- setTimeout(() => {
- // 濡傛灉缂栬緫鏁版嵁涓湁 testStandardId锛屽垯璁剧疆骞跺姞杞藉搴旂殑鍙傛暟
- if (savedTestStandardId) {
- // 纭繚绫诲瀷鍖归厤锛坕tem.id 鍙兘鏄暟瀛楁垨瀛楃涓诧級
- const matchedOption = testStandardOptions.value.find(item =>
- item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
- );
- if (matchedOption) {
- // 纭繚浣跨敤鍖归厤椤圭殑 id锛堜繚鎸佺被鍨嬩竴鑷达級
- form.value.testStandardId = matchedOption.id;
- // 缂栬緫鍦烘櫙淇濈暀宸叉湁妫�楠屽�硷紝鐩存帴鎷夊彇鍘熷弬鏁版暟鎹�
- getQualityInspectParamList(row.id);
- } else {
- // 濡傛灉鎵句笉鍒板尮閰嶉」锛屽皾璇曠洿鎺ヤ娇鐢ㄥ師鍊�
- console.warn('鏈壘鍒板尮閰嶇殑鎸囨爣閫夐」锛宼estStandardId:', savedTestStandardId, '鍙敤閫夐」:', testStandardOptions.value);
- form.value.testStandardId = savedTestStandardId;
- getQualityInspectParamList(row.id);
- }
- } else {
- // 鍚﹀垯浣跨敤鏃х殑閫昏緫
- getQualityInspectParamList(row.id);
- }
- }, 100);
- });
- });
- } else {
- getQualityInspectParamList(row.id);
- }
+ currentProductId.value = row.productId || 0
+ // 娓呯┖楠岃瘉鐘舵�侊紝閬垮厤鏁版嵁鍔犺浇杩囩▼涓殑鏍¢獙闂儊
+ nextTick(() => {
+ proxy.$refs.formRef?.clearValidate();
+ });
+
+ // 缂栬緫妯″紡涓嬶紝骞惰鍔犺浇瑙勬牸鍨嬪彿鍜屾寚鏍囬�夐」
+ if (currentProductId.value) {
+ // 璁剧疆浜у搧鍚嶇О
+ form.value.productName = findNodeById(productOptions.value, currentProductId.value);
+
+ // 骞惰鍔犺浇瑙勬牸鍨嬪彿鍜屾寚鏍囬�夐」
+ const params = {
+ productId: currentProductId.value,
+ inspectType: 2
+ };
+
+ Promise.all([
+ modelList({ id: currentProductId.value }),
+ qualityInspectDetailByProductId(params)
+ ]).then(([modelRes, testStandardRes]) => {
+ // 璁剧疆瑙勬牸鍨嬪彿閫夐」
+ modelOptions.value = modelRes || [];
+ // 濡傛灉琛ㄥ崟涓凡鏈� productModelId锛岃缃搴旂殑 model 鍜� unit
+ if (form.value.productModelId && modelOptions.value.length > 0) {
+ const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId);
+ if (selectedModel) {
+ form.value.model = selectedModel.model || '';
+ form.value.unit = selectedModel.unit || '';
+ }
+ }
+
+ // 璁剧疆鎸囨爣閫夐」
+ testStandardOptions.value = testStandardRes.data || [];
+
+ // 璁剧疆 testStandardId 骞跺姞杞藉弬鏁板垪琛�
+ nextTick(() => {
+ if (savedTestStandardId) {
+ // 纭繚绫诲瀷鍖归厤锛坕tem.id 鍙兘鏄暟瀛楁垨瀛楃涓诧級
+ const matchedOption = testStandardOptions.value.find(item =>
+ item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId)
+ );
+ if (matchedOption) {
+ // 纭繚浣跨敤鍖归厤椤圭殑 id锛堜繚鎸佺被鍨嬩竴鑷达級
+ form.value.testStandardId = matchedOption.id;
+ } else {
+ // 濡傛灉鎵句笉鍒板尮閰嶉」锛屽皾璇曠洿鎺ヤ娇鐢ㄥ師鍊�
+ console.warn('鏈壘鍒板尮閰嶇殑鎸囨爣閫夐」锛宼estStandardId:', savedTestStandardId, '鍙敤閫夐」:', testStandardOptions.value);
+ form.value.testStandardId = savedTestStandardId;
+ }
+ }
+ // 缂栬緫鍦烘櫙淇濈暀宸叉湁妫�楠屽�硷紝鐩存帴鎷夊彇鍘熷弬鏁版暟鎹�
+ getQualityInspectParamList(row.id);
+ });
+ });
+ } else {
+ getQualityInspectParamList(row.id);
+ }
}
}
const getProductOptions = () => {
- productTreeList().then((res) => {
+ return productTreeList().then((res) => {
productOptions.value = convertIdToValue(res);
});
};
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index 764e999..db44222 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/src/views/qualityManagement/finalInspection/index.vue
@@ -96,6 +96,11 @@
width: 120
},
{
+ label: "鐢熶骇宸ュ崟鍙�",
+ prop: "workOrderNo",
+ width: 120
+ },
+ {
label: "妫�楠屽憳",
prop: "checkName",
},
diff --git a/src/views/qualityManagement/processInspection/components/formDia.vue b/src/views/qualityManagement/processInspection/components/formDia.vue
index e0aea52..1b943e2 100644
--- a/src/views/qualityManagement/processInspection/components/formDia.vue
+++ b/src/views/qualityManagement/processInspection/components/formDia.vue
@@ -207,22 +207,50 @@
// 鎵撳紑寮规
const openDialog = async (type, row) => {
operationType.value = type;
- dialogFormVisible.value = true;
getOptions().then((res) => {
supplierList.value = res.data;
});
let userLists = await userListNoPage();
userList.value = userLists.data;
- form.value = {}
+ // 鍏堥噸缃〃鍗曟暟鎹紙淇濇寔瀛楁瀹屾暣锛岄伩鍏嶅脊绐楅娆℃覆鏌撴椂瑙﹀彂蹇呭~绾㈡鈥滈棯涓�涓嬧�濓級
+ form.value = {
+ checkTime: "",
+ process: "",
+ checkName: "",
+ productName: "",
+ productId: "",
+ productModelId: "",
+ model: "",
+ testStandardId: "",
+ unit: "",
+ quantity: "",
+ checkCompany: "",
+ checkResult: "",
+ }
testStandardOptions.value = [];
tableData.value = [];
- getProductOptions();
+ // 鍏堢‘淇濅骇鍝佹爲宸插姞杞斤紝鍚﹀垯缂栬緫鏃朵骇鍝�/瑙勬牸鍨嬪彿鏃犳硶鍙嶆樉
+ await getProductOptions();
if (operationType.value === 'edit') {
// 鍏堜繚瀛� testStandardId锛岄伩鍏嶈娓呯┖
const savedTestStandardId = row.testStandardId;
// 鍏堣缃〃鍗曟暟鎹紝浣嗘殏鏃舵竻绌� testStandardId锛岀瓑閫夐」鍔犺浇瀹屾垚鍚庡啀璁剧疆
form.value = {...row, testStandardId: ''}
currentProductId.value = row.productId || 0
+ // 鍏抽敭锛氱紪杈戞椂鍔犺浇瑙勬牸鍨嬪彿涓嬫媺閫夐」锛屾墠鑳藉弽鏄� productModelId
+ if (currentProductId.value) {
+ try {
+ const res = await modelList({ id: currentProductId.value });
+ modelOptions.value = res || [];
+ // 鍚屾鍥炲~ model / unit锛堟湁浜涙帴鍙h繑鍥炵殑 row 閲屽彲鑳芥病甯﹀叏锛�
+ if (form.value.productModelId) {
+ handleChangeModel(form.value.productModelId);
+ }
+ } catch (e) {
+ console.error("鍔犺浇瑙勬牸鍨嬪彿澶辫触", e);
+ modelOptions.value = [];
+ }
+ }
// 缂栬緫妯″紡涓嬶紝鍏堝姞杞芥寚鏍囬�夐」锛岀劧鍚庡姞杞藉弬鏁板垪琛�
if (currentProductId.value) {
// 鍏堝姞杞芥寚鏍囬�夐」
@@ -264,10 +292,16 @@
getQualityInspectParamList(row.id);
}
}
+ // 鏈�鍚庡啀鎵撳紑寮圭獥锛屽苟娓呯悊鏍¢獙鎬侊紝閬垮厤蹇呭~鎻愮ず闂儊
+ dialogFormVisible.value = true;
+ nextTick(() => {
+ proxy.$refs?.formRef?.clearValidate?.();
+ });
}
const getProductOptions = () => {
- productTreeList().then((res) => {
+ return productTreeList().then((res) => {
productOptions.value = convertIdToValue(res);
+ return productOptions.value;
});
};
const getModels = (value) => {
diff --git a/src/views/qualityManagement/processInspection/index.vue b/src/views/qualityManagement/processInspection/index.vue
index cad87aa..cbeab71 100644
--- a/src/views/qualityManagement/processInspection/index.vue
+++ b/src/views/qualityManagement/processInspection/index.vue
@@ -96,6 +96,11 @@
width: 120
},
{
+ label: "鐢熶骇宸ュ崟鍙�",
+ prop: "workOrderNo",
+ width: 120
+ },
+ {
label: "宸ュ簭",
prop: "process",
width: 230
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
index 3323986..21c4323 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -218,21 +218,49 @@
const modelOptions = ref([]);
// 鎵撳紑寮规
-const openDialog = (type, row) => {
+const openDialog = async (type, row) => {
operationType.value = type;
- dialogFormVisible.value = true;
getOptions().then((res) => {
supplierList.value = res.data;
});
- form.value = {}
+ // 鍏堥噸缃〃鍗曟暟鎹紙淇濇寔瀛楁瀹屾暣锛岄伩鍏嶅脊绐楅娆℃覆鏌撴椂瑙﹀彂蹇呭~绾㈡鈥滈棯涓�涓嬧�濓級
+ form.value = {
+ checkTime: "",
+ supplier: "",
+ checkName: "",
+ productName: "",
+ productId: "",
+ productModelId: "",
+ model: "",
+ testStandardId: "",
+ unit: "",
+ quantity: "",
+ checkCompany: "",
+ checkResult: "",
+ }
testStandardOptions.value = [];
tableData.value = [];
- getProductOptions();
+ // 鍏堢‘淇濅骇鍝佹爲宸插姞杞斤紝鍚﹀垯缂栬緫鏃朵骇鍝�/瑙勬牸鍨嬪彿鏃犳硶鍙嶆樉
+ await getProductOptions();
if (operationType.value === 'edit') {
// 鍏堜繚瀛� testStandardId锛岄伩鍏嶈娓呯┖
const savedTestStandardId = row.testStandardId;
form.value = {...row}
currentProductId.value = row.productId || 0
+ // 鍏抽敭锛氱紪杈戞椂鍔犺浇瑙勬牸鍨嬪彿涓嬫媺閫夐」锛屾墠鑳藉弽鏄� productModelId
+ if (currentProductId.value) {
+ try {
+ const res = await modelList({ id: currentProductId.value });
+ modelOptions.value = res || [];
+ // 鍚屾鍥炲~ model / unit锛堟湁浜涙帴鍙h繑鍥炵殑 row 閲屽彲鑳芥病甯﹀叏锛�
+ if (form.value.productModelId) {
+ handleChangeModel(form.value.productModelId);
+ }
+ } catch (e) {
+ console.error("鍔犺浇瑙勬牸鍨嬪彿澶辫触", e);
+ modelOptions.value = [];
+ }
+ }
// 缂栬緫妯″紡涓嬶紝鍏堝姞杞芥寚鏍囬�夐」锛岀劧鍚庡姞杞藉弬鏁板垪琛�
if (currentProductId.value) {
// 鍏堝姞杞芥寚鏍囬�夐」
@@ -273,10 +301,16 @@
getQualityInspectParamList(row.id);
}
}
+ // 鏈�鍚庡啀鎵撳紑寮圭獥锛屽苟娓呯悊鏍¢獙鎬侊紝閬垮厤蹇呭~鎻愮ず闂儊
+ dialogFormVisible.value = true;
+ nextTick(() => {
+ proxy.$refs?.formRef?.clearValidate?.();
+ });
}
const getProductOptions = () => {
- productTreeList().then((res) => {
+ return productTreeList().then((res) => {
productOptions.value = convertIdToValue(res);
+ return productOptions.value;
});
};
const getModels = (value) => {
diff --git a/src/views/qualityManagement/rawMaterialInspection/index.vue b/src/views/qualityManagement/rawMaterialInspection/index.vue
index f50ddb8..26504b0 100644
--- a/src/views/qualityManagement/rawMaterialInspection/index.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -98,6 +98,11 @@
width: 120
},
{
+ label: "閲囪喘璁㈠崟鍙�",
+ prop: "purchaseContractNo",
+ width: 120
+ },
+ {
label: "渚涘簲鍟�",
prop: "supplier",
width: 230
diff --git a/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
index 57b87d0..8ecd507 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
@@ -103,6 +103,7 @@
})
}
+
onMounted(() => {
fetchData()
})
diff --git a/src/views/reportAnalysis/productionAnalysis/components/right-top.vue b/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
index 33ef67b..d3a9eb9 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
@@ -43,7 +43,6 @@
data: ['寮�宸�', '瀹屾垚', '鑹搧鐜�'],
}
-// 鏌辩姸鍥撅細寮�宸ャ�佸畬鎴愶紱鎶樼嚎鍥撅細鑹搧鐜囷紙棰滆壊 rgba(90, 216, 166, 1)锛�
const chartSeries = ref([
{
name: '寮�宸�',
@@ -117,6 +116,7 @@
const xAxis1 = ref([
{ type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] },
])
+
const yAxis1 = [
{ type: 'value', name: '浠�', axisLabel: { color: '#B8C8E0' }, nameTextStyle: { color: '#B8C8E0' } },
{
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
new file mode 100644
index 0000000..0498824
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
@@ -0,0 +1,306 @@
+<template>
+ <div class="carousel-cards">
+ <button
+ v-if="canScrollLeft"
+ class="nav-button nav-button-left"
+ @click="scrollLeftFn"
+ >
+ <img src="@/assets/BI/jiantou.png" alt="宸︾澶�" />
+ </button>
+ <div
+ class="cards-container"
+ :style="{ '--visible-count': visibleCount }"
+ ref="cardsContainerRef"
+ >
+ <div
+ v-for="(item, index) in items"
+ :key="index"
+ class="card-item"
+ >
+ <div v-if="item.icon" class="card-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
+ <div class="card-title">
+ <div class="card-label">{{ item.label }}</div>
+ <div class="card-value">
+ <span class="value-number">{{ item.value }}</span>
+ <span class="value-unit">{{ item.unit }}</span>
+ </div>
+ <div v-if="item.rate ?? item.ratio ?? item.percent" class="card-rate">
+ <span class="rate-value">{{ item.rate ?? item.ratio ?? item.percent }}%</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <button
+ v-if="canScrollRight"
+ class="nav-button nav-button-right"
+ @click="scrollRightFn"
+ >
+ <img src="@/assets/BI/jiantou.png" alt="鍙崇澶�" />
+ </button>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
+
+const props = defineProps({
+ items: {
+ type: Array,
+ default: () => [],
+ validator: (value) => {
+ return value.every(item =>
+ item && typeof item.label !== 'undefined' &&
+ typeof item.value !== 'undefined' &&
+ typeof item.unit !== 'undefined'
+ )
+ }
+ },
+ visibleCount: {
+ type: Number,
+ default: 3
+ }
+})
+
+const cardsContainerRef = ref(null)
+const currentScrollLeft = ref(0)
+const maxScrollLeft = ref(0)
+
+// 妫�鏌ユ槸鍚﹀彲浠ュ悜宸︽粴鍔�
+const canScrollLeft = computed(() => {
+ return currentScrollLeft.value > 0
+})
+
+// 妫�鏌ユ槸鍚﹀彲浠ュ悜鍙虫粴鍔�
+const canScrollRight = computed(() => {
+ return currentScrollLeft.value < maxScrollLeft.value
+})
+
+// 鏇存柊婊氬姩鐘舵��
+const updateScrollState = () => {
+ const container = cardsContainerRef.value
+ if (!container) return
+
+ currentScrollLeft.value = container.scrollLeft
+ maxScrollLeft.value = container.scrollWidth - container.clientWidth
+}
+
+// 鍚戝乏婊氬姩
+const scrollLeftFn = () => {
+ const container = cardsContainerRef.value
+ if (!container) return
+
+ const scrollItems = Array.from(container.querySelectorAll('.card-item'))
+ if (scrollItems.length === 0) return
+
+ const itemWidth = scrollItems[0]?.offsetWidth || 0
+ const gap = 12
+ const scrollDistance = itemWidth + gap
+
+ container.scrollBy({
+ left: -scrollDistance,
+ behavior: 'smooth'
+ })
+
+ // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
+ setTimeout(() => {
+ updateScrollState()
+ }, 300)
+}
+
+// 鍚戝彸婊氬姩
+const scrollRightFn = () => {
+ const container = cardsContainerRef.value
+ if (!container) return
+
+ const scrollItems = Array.from(container.querySelectorAll('.card-item'))
+ if (scrollItems.length === 0) return
+
+ const itemWidth = scrollItems[0]?.offsetWidth || 0
+ const gap = 12
+ const scrollDistance = itemWidth + gap
+
+ container.scrollBy({
+ left: scrollDistance,
+ behavior: 'smooth'
+ })
+
+ // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
+ setTimeout(() => {
+ updateScrollState()
+ }, 300)
+}
+
+// 鐩戝惉 items 鍙樺寲锛屾洿鏂版粴鍔ㄧ姸鎬�
+watch(() => props.items, () => {
+ nextTick(() => {
+ updateScrollState()
+ })
+}, { deep: true })
+
+onMounted(() => {
+ nextTick(() => {
+ updateScrollState()
+ // 鐩戝惉婊氬姩浜嬩欢
+ const container = cardsContainerRef.value
+ if (container) {
+ container.addEventListener('scroll', updateScrollState)
+ }
+ })
+})
+
+onBeforeUnmount(() => {
+ // 娓呯悊婊氬姩浜嬩欢鐩戝惉鍣�
+ const container = cardsContainerRef.value
+ if (container) {
+ container.removeEventListener('scroll', updateScrollState)
+ }
+})
+</script>
+
+<style scoped>
+.carousel-cards {
+ width: 100%;
+ overflow: hidden;
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.cards-container {
+ display: flex;
+ gap: 12px;
+ width: 100%;
+ overflow-x: auto;
+ overflow-y: hidden;
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
+ padding-bottom: 4px;
+ scroll-behavior: smooth;
+}
+
+.cards-container::-webkit-scrollbar {
+ display: none; /* Chrome, Safari, Opera */
+}
+
+.nav-button {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 32px;
+ height: 32px;
+ background: rgba(26, 88, 176, 0.6);
+ border: 1px solid rgba(26, 88, 176, 0.8);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ z-index: 10;
+ transition: all 0.3s ease;
+ padding: 0;
+}
+
+.nav-button:hover {
+ background: rgba(26, 88, 176, 0.8);
+ transform: translateY(-50%) scale(1.1);
+}
+
+.nav-button-left {
+ left: -16px;
+}
+
+.nav-button-left img {
+ width: 16px;
+ height: 16px;
+ transform: rotate(180deg);
+}
+
+.nav-button-right {
+ right: -16px;
+}
+
+.nav-button-right img {
+ width: 16px;
+ height: 16px;
+}
+
+.card-item {
+ flex: 0 0 calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
+ min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
+ display: flex;
+ align-items: center;
+ background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
+ border-radius: 8px 8px 8px 8px;
+ padding: 12px 16px;
+ transition: all 0.3s ease;
+}
+
+.card-item:hover {
+ transform: translateY(-2px);
+}
+
+.card-icon {
+ width: 80px;
+ height: 60px;
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ flex-shrink: 0;
+ margin-right: 12px;
+}
+
+.card-title {
+ display: flex;
+ align-items: flex-start;
+ flex-direction: column;
+ flex: 1;
+}
+
+.card-label {
+ font-weight: 400;
+ font-size: 14px;
+ color: #FFFFFF;
+ margin-bottom: 4px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+}
+
+.card-value {
+ display: flex;
+ align-items: baseline;
+ gap: 4px;
+}
+
+.card-rate {
+ margin-top: 4px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-weight: 400;
+ font-size: 12px;
+ color: rgba(255, 255, 255, 0.85);
+}
+
+.rate-label {
+ opacity: 0.85;
+}
+
+.rate-value {
+ font-weight: 500;
+}
+
+.value-number {
+ font-weight: 400;
+ font-size: 14px;
+ color: #FFFFFF;
+ line-height: 1;
+}
+
+.value-unit {
+ font-size: 14px;
+ color: #FFFFFF;
+ font-weight: 400;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue b/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue
new file mode 100644
index 0000000..0c57b25
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue
@@ -0,0 +1,94 @@
+<template>
+ <el-radio-group
+ v-model="currentValue"
+ class="date-type-switch"
+ @change="handleChange"
+ >
+ <el-radio-button :label="1">鍛�</el-radio-button>
+ <el-radio-button :label="2">鏈�</el-radio-button>
+ <el-radio-button :label="3">瀛e害</el-radio-button>
+ </el-radio-group>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+ modelValue: {
+ type: Number,
+ default: 1, // 榛樿閫変腑"鍛�"
+ },
+})
+
+const emit = defineEmits(['update:modelValue', 'change'])
+
+const currentValue = ref(props.modelValue)
+
+// 鐩戝惉澶栭儴鍊煎彉鍖�
+watch(
+ () => props.modelValue,
+ (newVal) => {
+ currentValue.value = newVal
+ }
+)
+
+// 澶勭悊鍊煎彉鍖�
+const handleChange = (value) => {
+ emit('update:modelValue', value)
+ emit('change', value)
+}
+</script>
+
+<style scoped>
+.date-type-switch {
+ display: inline-flex;
+}
+
+/* 鏈�変腑鐘舵�佺殑鏍峰紡 */
+.date-type-switch :deep(.el-radio-button__inner) {
+ background-color: rgba(26, 88, 176, 0.3);
+ color: rgba(184, 200, 224, 0.8);
+ border-color: rgba(255, 255, 255, 0.2);
+ border-radius: 0;
+ padding: 6px 20px;
+ font-size: 14px;
+ transition: all 0.3s;
+}
+
+/* 绗竴涓寜閽乏渚у渾瑙� */
+.date-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+/* 鏈�鍚庝竴涓寜閽彸渚у渾瑙� */
+.date-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+/* 鎸夐挳涔嬮棿鐨勫垎闅旂嚎 */
+.date-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
+ border-right: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+/* 閫変腑鐘舵�佺殑鏍峰紡 */
+.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
+ background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ color: #ffffff;
+ border-color: rgba(51, 120, 255, 0.8);
+ box-shadow: none;
+}
+
+/* 鎮仠鏁堟灉 */
+.date-type-switch :deep(.el-radio-button__inner:hover) {
+ color: rgba(184, 200, 224, 1);
+ border-color: rgba(255, 255, 255, 0.3);
+}
+
+/* 閫変腑鐘舵�佹偓鍋� */
+.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
+ background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ color: #ffffff;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue b/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue
new file mode 100644
index 0000000..313f1df
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue
@@ -0,0 +1,33 @@
+<template>
+ <div class="panel-header">
+ <span class="panel-title">{{ title }}</span>
+ </div>
+</template>
+
+<script setup>
+defineProps({
+ title: {
+ type: String,
+ required: true,
+ default: ''
+ }
+})
+</script>
+
+<style scoped>
+.panel-header {
+ background-image: url("@/assets/BI/kehuhetongback@2x.png");
+ background-size: 100% 100%;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+.panel-title {
+ width: 100%;
+ font-weight: 500;
+ font-size: 16px;
+ color: #D9ECFF;
+ padding-left: 46px;
+ line-height: 36px;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue b/src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue
new file mode 100644
index 0000000..87cde44
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue
@@ -0,0 +1,85 @@
+<template>
+ <el-radio-group
+ v-model="currentValue"
+ class="product-type-switch"
+ @change="handleChange"
+ >
+ <el-radio-button :label="1">鍘熸潗鏂�</el-radio-button>
+ <el-radio-button :label="3">鍗婃垚鍝�</el-radio-button>
+ <el-radio-button :label="2">鎴愬搧</el-radio-button>
+ </el-radio-group>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+ modelValue: {
+ type: Number,
+ default: 1, // 榛樿閫変腑"鍘熸潗鏂�"
+ },
+})
+
+const emit = defineEmits(['update:modelValue', 'change'])
+
+const currentValue = ref(props.modelValue)
+
+watch(
+ () => props.modelValue,
+ (newVal) => {
+ currentValue.value = newVal
+ }
+)
+
+const handleChange = (value) => {
+ emit('update:modelValue', value)
+ emit('change', value)
+}
+</script>
+
+<style scoped>
+.product-type-switch {
+ display: inline-flex;
+}
+
+.product-type-switch :deep(.el-radio-button__inner) {
+ background-color: rgba(26, 88, 176, 0.3);
+ color: rgba(184, 200, 224, 0.8);
+ border-color: rgba(255, 255, 255, 0.2);
+ border-radius: 0;
+ padding: 6px 20px;
+ font-size: 14px;
+ transition: all 0.3s;
+}
+
+.product-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+.product-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+.product-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
+ border-right: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
+ background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ color: #ffffff;
+ border-color: rgba(51, 120, 255, 0.8);
+ box-shadow: none;
+}
+
+.product-type-switch :deep(.el-radio-button__inner:hover) {
+ color: rgba(184, 200, 224, 1);
+ border-color: rgba(255, 255, 255, 0.3);
+}
+
+.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
+ background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ color: #ffffff;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue
new file mode 100644
index 0000000..24d9552
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue
@@ -0,0 +1,254 @@
+<template>
+ <div>
+ <div class="chart-header">
+ <PanelHeader title="瀹屾垚妫�楠屾暟" />
+ <div class="warn-range" @click="handleRangeClick">杩�7澶�</div>
+ </div>
+ <div class="main-panel panel-item-customers">
+ <Echarts
+ ref="chart"
+ :chartStyle="chartStyle"
+ :grid="grid"
+ :legend="barLegend"
+ :series="chartSeries"
+ :tooltip="tooltip"
+ :xAxis="xAxis1"
+ :yAxis="yAxis1"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ style="height: 260px"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { completedInspectionCount } from '@/api/viewIndex.js'
+import PanelHeader from './PanelHeader.vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+
+const chartStyle = {
+ width: '100%',
+ height: '135%',
+}
+
+const grid = { left: '8%', right: '8%', bottom: '8%', top: '15%', containLabel: true }
+
+const barLegend = {
+ show: true,
+ top: '5%',
+ left: 'center',
+ textStyle: { color: '#B8C8E0', fontSize: 14 },
+ itemGap: 30,
+ data: ['鍚堟牸', '涓嶅悎鏍�', '鍚堟牸鐜�'],
+}
+
+// 鏌辩姸鍥撅細鍚堟牸锛堥粍鑹诧級銆佷笉鍚堟牸锛堢传鑹诧級锛涙姌绾垮浘锛氬悎鏍肩巼锛堣摑鑹诧級
+const chartSeries = ref([
+ {
+ name: '鍚堟牸',
+ type: 'bar',
+ barWidth: 20,
+ barGap: '20%',
+ yAxisIndex: 0,
+ emphasis: { focus: 'series' },
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 0, color: 'rgba(255, 215, 0, 1)' }, // 閲戦粍鑹查《閮�
+ { offset: 1, color: 'rgba(255, 215, 0, 0.5)' }, // 鍗婇�忔槑搴曢儴
+ ],
+ },
+ },
+ data: [],
+ },
+ {
+ name: '涓嶅悎鏍�',
+ type: 'bar',
+ barGap: '20%',
+ barWidth: 20,
+ yAxisIndex: 0,
+ emphasis: { focus: 'series' },
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 0, color: 'rgba(144, 97, 248, 1)' }, // 绱壊椤堕儴
+ { offset: 1, color: 'rgba(144, 97, 248, 0.6)' }, // 鍗婇�忔槑搴曢儴
+ ],
+ },
+ },
+ data: [],
+ },
+ {
+ name: '鍚堟牸鐜�',
+ type: 'line',
+ yAxisIndex: 1,
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ lineStyle: {
+ color: 'rgba(78, 228, 255, 1)', // 闈掕壊
+ width: 2,
+ },
+ itemStyle: {
+ color: 'rgba(78, 228, 255, 1)',
+ borderWidth: 2,
+ borderColor: '#fff',
+ },
+ emphasis: {
+ focus: 'series',
+ itemStyle: {
+ shadowBlur: 10,
+ shadowColor: 'rgba(78, 228, 255, 0.8)',
+ },
+ },
+ data: [],
+ },
+])
+
+const tooltip = {
+ trigger: 'axis',
+ axisPointer: { type: 'cross' },
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
+ borderColor: 'rgba(78, 228, 255, 0.5)',
+ borderWidth: 1,
+ textStyle: { color: '#B8C8E0' },
+ formatter(params) {
+ let result = params[0].axisValueLabel + '<br/>'
+ params.forEach((item) => {
+ let unit = ''
+ if (item.seriesName === '鍚堟牸鐜�') {
+ unit = '%'
+ } else {
+ unit = '浠�'
+ }
+ result += `<div style="margin: 4px 0;">${item.marker} ${item.seriesName}: ${item.value}${unit}</div>`
+ })
+ return result
+ },
+}
+
+const xAxis1 = ref([
+ {
+ type: 'category',
+ axisTick: { show: false },
+ axisLabel: { color: '#B8C8E0', fontSize: 12 },
+ axisLine: { lineStyle: { color: 'rgba(184, 200, 224, 0.3)' } },
+ data: [],
+ },
+])
+
+const yAxis1 = [
+ {
+ type: 'value',
+ name: '鍗曚綅: 浠�',
+ nameLocation: 'start',
+ nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 10] },
+ axisLabel: { color: '#B8C8E0', fontSize: 12 },
+ axisLine: { show: false },
+ splitLine: {
+ show: true,
+ lineStyle: { color: 'rgba(184, 200, 224, 0.2)', type: 'dashed' },
+ },
+ },
+ {
+ type: 'value',
+ name: '鍗曚綅: %',
+ nameLocation: 'end',
+ nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 10] },
+ min: 0,
+ max: 100,
+ axisLabel: { color: '#B8C8E0', fontSize: 12, formatter: '{value}' },
+ axisLine: { show: false },
+ splitLine: {
+ show: true,
+ lineStyle: { color: 'rgba(184, 200, 224, 0.2)', type: 'dashed' },
+ },
+ },
+]
+
+const fetchData = () => {
+ completedInspectionCount()
+ .then((res) => {
+ if (res?.code === 200 && Array.isArray(res?.data)) {
+ const items = res.data
+ // 鏇存柊X杞存棩鏈熸暟鎹�
+ xAxis1.value[0].data = items.map((d) => d.dateStr || '')
+ // 鏇存柊鍚堟牸鏁帮紙榛勮壊鏌辩姸鍥撅級
+ chartSeries.value[0].data = items.map((d) => Number(d.qualifiedCount) || 0)
+ // 鏇存柊涓嶅悎鏍兼暟锛堢传鑹叉煴鐘跺浘锛�
+ chartSeries.value[1].data = items.map((d) => Number(d.unqualifiedCount) || 0)
+ // 鏇存柊鍚堟牸鐜囷紙钃濊壊鎶樼嚎鍥撅級
+ chartSeries.value[2].data = items.map((d) => Number(d.passRate) || 0)
+ }
+ })
+ .catch((err) => {
+ console.error('鑾峰彇瀹屾垚妫�楠屾暟鏁版嵁澶辫触:', err)
+ })
+}
+
+const handleRangeClick = () => {
+ // 鍏堟寜鎴浘鍋氶潤鎬�"杩�7澶�"锛屽悗缁湁鐪熷疄绛涢�夐渶姹傚啀鎺ュ叆
+ fetchData()
+}
+
+onMounted(() => {
+ fetchData()
+})
+</script>
+
+<style scoped>
+.chart-header {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.warn-range {
+ position: absolute;
+ right: 0;
+ top: 0;
+ height: 32px;
+ padding: 0 14px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 4px;
+ color: #ffffff;
+ font-weight: 600;
+ font-size: 14px;
+ background: linear-gradient(180deg, rgba(51, 120, 255, 1) 0%, rgba(0, 164, 237, 1) 100%);
+ border: 1px solid rgba(78, 228, 255, 0.25);
+ cursor: pointer;
+ z-index: 10;
+}
+
+.warn-range:hover {
+ background: linear-gradient(180deg, rgba(51, 140, 255, 1) 0%, rgba(0, 184, 237, 1) 100%);
+}
+
+.main-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.panel-item-customers {
+ border: 1px solid #1a58b0;
+ padding: 18px;
+ width: 100%;
+ height: 449px;
+ position: relative;
+ background: radial-gradient(circle at 50% 50%, rgba(78, 228, 255, 0.05) 0%, rgba(0, 0, 0, 0) 70%);
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue b/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue
new file mode 100644
index 0000000..8d28f7a
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue
@@ -0,0 +1,355 @@
+<template>
+ <div>
+ <div class="warn-panel">
+ <div class="warn-header">
+ <div class="warn-header-left">
+ <div class="warn-badge"></div>
+ <span class="warn-title">涓嶅悎鏍奸璀�</span>
+ </div>
+ <div class="warn-range" @click="handleRangeClick">杩�7澶�</div>
+ </div>
+
+ <div class="warn-body">
+ <div class="warn-list" role="list">
+ <div v-for="item in warnings" :key="item.id" class="warn-item" role="listitem" @click="openWarning(item)">
+ <div class="warn-tag" :class="tagClass(item.type)">{{ item.parentProductTitle }}-{{ item.productTitle }}
+ </div>
+ <div class="warn-text" :title="item.title">{{ item.title }}</div>
+ <div class="warn-action" @click.stop="openWarning(item)">鏌ョ湅</div>
+ <div class="warn-date">{{ item.date }}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { computed, getCurrentInstance, ref, onMounted } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import { nonComplianceWarning } from '@/api/viewIndex.js'
+
+const { proxy } = getCurrentInstance() || {}
+
+const warnings = ref([])
+
+// 鍗犳瘮鏁版嵁
+const ratios = ref({
+ rawMaterialRatio: 0,
+ semiFinishedProductRatio: 0,
+ finishedProductRatio: 0,
+})
+
+const TAG_COLORS = {
+ raw: '#7C4DFF',
+ final: '#F5A000',
+ semi: '#FF66CC',
+}
+
+const tagClass = (type) => {
+ if (type === 'raw') return 'tag-raw'
+ if (type === 'final') return 'tag-final'
+ return 'tag-semi'
+}
+
+// 鏍规嵁productTitle鏄犲皠绫诲瀷
+const mapProductTitleToType = (productTitle) => {
+ if (productTitle === '鍘熸潗鏂�') return 'raw'
+ if (productTitle === '鍗婃垚鍝�') return 'semi'
+ if (productTitle === '鎴愬搧') return 'final'
+ return 'raw' // 榛樿鍊�
+}
+
+const pieChartStyle = { width: '100%', height: '100%' }
+
+const pieOptions = {
+ backgroundColor: 'transparent',
+ textStyle: { color: '#B8C8E0' },
+}
+
+const pieTooltip = {
+ trigger: 'item',
+ formatter: (p) => `${p.name}锛�${p.value}%`,
+}
+
+const pieData = computed(() => {
+ return [
+ { name: '鍘熸潗鏂�', value: ratios.value.rawMaterialRatio, itemStyle: { color: TAG_COLORS.raw } },
+ { name: '鍗婃垚鍝�', value: ratios.value.semiFinishedProductRatio, itemStyle: { color: TAG_COLORS.semi } },
+ { name: '鎴愬搧', value: ratios.value.finishedProductRatio, itemStyle: { color: TAG_COLORS.final } },
+ ]
+})
+
+const pieSeries = computed(() => {
+ return [
+ {
+ type: 'pie',
+ radius: ['0%', '68%'],
+ center: ['50%', '50%'],
+ startAngle: 90,
+ clockwise: true,
+ avoidLabelOverlap: true,
+ label: { show: false },
+ labelLine: { show: false },
+ itemStyle: {
+ borderColor: '#071a3a',
+ borderWidth: 4,
+ shadowBlur: 14,
+ shadowColor: 'rgba(0, 0, 0, 0.35)',
+ },
+ data: pieData.value,
+ },
+ {
+ // 鍐呭湀鏆楃幆锛屽寮哄眰娆�
+ type: 'pie',
+ radius: ['70%', '74%'],
+ center: ['50%', '50%'],
+ silent: true,
+ label: { show: false },
+ labelLine: { show: false },
+ itemStyle: { color: 'rgba(78, 228, 255, 0.12)' },
+ data: [1],
+ },
+ ]
+})
+
+const fetchWarnings = async () => {
+ try {
+ const res = await nonComplianceWarning()
+ if (res?.code === 200 && res?.data) {
+ const data = res.data
+
+ // 鏇存柊鍗犳瘮鏁版嵁
+ ratios.value = {
+ rawMaterialRatio: data.rawMaterialRatio ?? 0,
+ semiFinishedProductRatio: data.semiFinishedProductRatio ?? 0,
+ finishedProductRatio: data.finishedProductRatio ?? 0,
+ }
+
+ // 鏇存柊璀﹀憡鍒楄〃
+ const children = data.children || []
+ warnings.value = children.map((item, idx) => {
+ const type = mapProductTitleToType(item.parentProductTitle)
+ const date = item.date ? item.date.replace(/-/g, '.') : ''
+ return {
+ id: item.id ?? `warning-${idx}`,
+ type,
+ parentProductTitle: item.parentProductTitle || '鍘熸潗鏂�',
+ productTitle: item.productTitle || '鍘熸潗鏂�',
+ title: item.description || '涓嶅悎鏍奸璀�',
+ date,
+ }
+ })
+ }
+ } catch (e) {
+ // 鎺ュ彛澶辫触鍒欎繚鎸佺┖鏁版嵁
+ console.error('鑾峰彇涓嶅悎鏍奸璀﹀け璐�:', e)
+ }
+}
+
+const openWarning = (item) => {
+ const title = `銆�${item.parentProductTitle}-${item.productTitle}銆�${item.title}`
+ if (proxy?.$modal?.alert) {
+ proxy.$modal.alert(title)
+ return
+ }
+ // 鍏滃簳锛氭病鏈夊叏灞� modal 鏃剁敤 console
+ console.log('warning:', { ...item })
+}
+
+const handleRangeClick = () => {
+ // 鍏堟寜鎴浘鍋氶潤鎬佲�滆繎7澶┾�濓紝鍚庣画鏈夌湡瀹炵瓫閫夐渶姹傚啀鎺ュ叆
+}
+
+onMounted(() => {
+ fetchWarnings()
+})
+</script>
+
+<style scoped>
+.warn-panel {
+ border: 1px solid #1a58b0;
+ padding: 0 18px 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ height: 100%;
+ box-sizing: border-box;
+}
+
+.warn-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid;
+ border-image: linear-gradient(270deg,
+ rgba(0, 126, 255, 0) 0%,
+ rgba(0, 126, 255, 0.4549) 35%,
+ #007eff 78%,
+ #007eff 100%) 1;
+ padding: 10px 0 6px;
+}
+
+.warn-header-left {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.warn-badge {
+ width: 18px;
+ height: 18px;
+ background: linear-gradient(180deg, #2aa8ff 0%, #4ee4ff 100%);
+ clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
+ box-shadow: 0 0 12px rgba(78, 228, 255, 0.25);
+}
+
+.warn-title {
+ font-weight: 600;
+ font-size: 18px;
+ background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ line-height: 24px;
+}
+
+.warn-range {
+ height: 32px;
+ padding: 0 14px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 4px;
+ color: #ffffff;
+ font-weight: 600;
+ background: linear-gradient(180deg, rgba(51, 120, 255, 1) 0%, rgba(0, 164, 237, 1) 100%);
+ border: 1px solid rgba(78, 228, 255, 0.25);
+ cursor: pointer;
+}
+
+.warn-body {
+ display: grid;
+ gap: 18px;
+ align-items: stretch;
+ min-height: 260px;
+}
+
+.warn-list {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding-top: 6px;
+}
+
+.warn-item {
+ display: grid;
+ grid-template-columns: 130px 1fr auto 110px;
+ align-items: center;
+ gap: 12px;
+ color: #b8c8e0;
+ font-size: 14px;
+ line-height: 1.2;
+ padding: 8px 0;
+ border-radius: 4px;
+ transition: background-color 0.2s, color 0.2s;
+}
+
+.warn-item:hover {
+ color: #ff4d4f;
+ background-color: rgba(255, 77, 79, 0.06);
+}
+
+.warn-item:hover .warn-text {
+ color: #ff4d4f;
+}
+
+.warn-tag {
+ height: 28px;
+ padding: 0 10px;
+ border-radius: 4px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: 700;
+ color: #ffffff;
+}
+
+.tag-raw {
+ background: #7c4dff;
+}
+
+.tag-final {
+ background: #f5a000;
+}
+
+.tag-semi {
+ background: #ff66cc;
+}
+
+.warn-text {
+ color: #e8f1ff;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.warn-action {
+ color: #ff4d4f;
+ font-weight: 700;
+ white-space: nowrap;
+ cursor: pointer;
+}
+
+.warn-date {
+ color: rgba(184, 200, 224, 0.75);
+ white-space: nowrap;
+}
+
+.warn-chart {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.chart-frame {
+ width: 100%;
+ height: 260px;
+ border: 2px dashed rgba(184, 200, 224, 0.35);
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: radial-gradient(circle at 50% 50%, rgba(78, 228, 255, 0.08) 0%, rgba(0, 0, 0, 0) 65%);
+}
+
+/* 澶栧湀鍒诲害鐜� */
+.chart-frame::before {
+ content: '';
+ position: absolute;
+ width: 220px;
+ height: 220px;
+ border-radius: 50%;
+ background: repeating-conic-gradient(from 0deg, rgba(78, 228, 255, 0.75) 0 1deg, rgba(78, 228, 255, 0) 1deg 9deg);
+ -webkit-mask: radial-gradient(circle, transparent 62%, #000 63%);
+ mask: radial-gradient(circle, transparent 62%, #000 63%);
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+/* 鍗佸瓧杈呭姪绾� */
+.chart-frame::after {
+ content: '';
+ position: absolute;
+ width: 240px;
+ height: 240px;
+ background:
+ linear-gradient(to right, rgba(78, 228, 255, 0) 0%, rgba(78, 228, 255, 0.55) 50%, rgba(78, 228, 255, 0) 100%),
+ linear-gradient(to bottom, rgba(78, 228, 255, 0) 0%, rgba(78, 228, 255, 0.55) 50%, rgba(78, 228, 255, 0) 100%);
+ background-size: 100% 1px, 1px 100%;
+ background-position: center, center;
+ background-repeat: no-repeat;
+ opacity: 0.35;
+ pointer-events: none;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue b/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue
new file mode 100644
index 0000000..3ecf799
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue
@@ -0,0 +1,147 @@
+<template>
+ <div>
+ <!-- 椤堕儴缁熻鍗$墖 -->
+ <div class="stats-cards">
+ <div v-for="item in statItems" :key="item.name" class="stat-card">
+ <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+ <div class="card-content">
+ <span class="card-label">{{ item.name }}</span>
+ <span class="card-value">{{ item.value }}</span>
+ <div class="card-compare" :class="compareClass(Number(item.rate))">
+ <span>鍚屾瘮</span>
+ <span class="compare-value">{{ formatPercent(item.rate) }}</span>
+ <span class="compare-icon">{{ Number(item.rate) >= 0 ? '鈫�' : '鈫�' }}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { qualityInspectionCount } from '@/api/viewIndex.js'
+
+const statItems = ref([])
+
+const formatPercent = (val) => {
+ const num = Number(val) || 0
+ return `${num.toFixed(2)}%`
+}
+
+const compareClass = (val) => (val >= 0 ? 'compare-up' : 'compare-down')
+
+const fetchData = () => {
+ qualityInspectionCount()
+ .then((res) => {
+ if (res.code === 200 && res.data) {
+ const data = res.data
+
+ statItems.value = [
+ {
+ name: '鎬绘楠屾暟',
+ value: data.totalCount ?? 0,
+ rate: data.totalCountGrowthRate ?? 0,
+ },
+ {
+ name: '浠婃棩寰呭畬鎴愭暟',
+ value: data.todayPendingCount ?? 0,
+ rate: data.todayPendingCountGrowthRate ?? 0,
+ },
+ {
+ name: '浠婃棩宸插畬鎴愭暟',
+ value: data.todayCompletedCount ?? 0,
+ rate: data.todayCompletedCountGrowthRate ?? 0,
+ },
+ ]
+ }
+ })
+ .catch((err) => {
+ console.error('鑾峰彇璐ㄩ噺妫�楠岀粺璁″け璐�:', err)
+ })
+}
+
+onMounted(() => {
+ fetchData()
+})
+</script>
+
+<style scoped>
+.stats-cards {
+ display: flex;
+ gap: 30px;
+}
+
+.stat-card {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ background-image: url('@/assets/BI/border@2x.png');
+ background-size: 100% 100%;
+ background-position: center;
+ background-repeat: no-repeat;
+ height: 142px;
+}
+
+.card-icon {
+ width: 100px;
+ height: 100px;
+ margin: 20px 20px 0 10px;
+}
+
+.card-content {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.card-value {
+ font-weight: 500;
+ font-size: 40px;
+ background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.card-label {
+ font-weight: 400;
+ font-size: 16px;
+ color: rgba(208, 231, 255, 0.7);
+}
+
+.card-compare {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 15px;
+ color: #d0e7ff;
+}
+
+.card-compare>span:first-child {
+ font-size: 13px;
+ opacity: 0.8;
+}
+
+.compare-value {
+ font-weight: 600;
+}
+
+.compare-icon {
+ font-size: 14px;
+ position: relative;
+ top: -1px;
+ /* 杞诲井涓婄Щ锛岃绠ご涓庢枃瀛楀瀭鐩村眳涓榻� */
+}
+
+.compare-up .compare-value,
+.compare-up .compare-icon {
+ color: #00c853;
+}
+
+.compare-down .compare-value,
+.compare-down .compare-icon {
+ color: #ff5252;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/qualityAnalysis/components/left-bottom.vue
new file mode 100644
index 0000000..33f431d
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/left-bottom.vue
@@ -0,0 +1,170 @@
+<template>
+ <div>
+ <PanelHeader title="鍦ㄥ埗鍝佺粺璁″垎鏋�" />
+ <div class="main-panel panel-item-customers">
+ <CarouselCards :items="cardItems" :visible-count="3" />
+ <div class="chart-wrapper">
+ <Echarts
+ ref="chart"
+ :chartStyle="chartStyle"
+ :grid="grid"
+ :legend="workInProcessBarLegend"
+ :series="workInProcessBarSeries"
+ :tooltip="tooltip"
+ :xAxis="workInProcessXAxis"
+ :yAxis="workInProcessYAxis"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ style="height: 100%"
+ />
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
+import CarouselCards from './CarouselCards.vue'
+import { getWorkInProcessTurnover } from '@/api/viewIndex.js'
+
+// 鍦ㄥ埗鍝佸懆杞粺璁″璞�
+const workInProcessStatistics = ref({
+ totalQuantity: 0,
+ avgTurnoverDays: 0,
+ turnoverEfficiency: 0,
+})
+
+// 杞挱鍗$墖鏁版嵁锛堢敱 workInProcessStatistics 鍚屾锛�
+const cardItems = ref([])
+
+const chartStyle = {
+ width: '100%',
+ height: '100%',
+}
+
+const grid = {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true,
+}
+
+const tooltip = {
+ trigger: 'axis',
+ axisPointer: { type: 'shadow' },
+ formatter: function (params) {
+ let result = params[0].axisValueLabel + '<br/>'
+ params.forEach((item) => {
+ result += `<div style="color: #B8C8E0">${item.marker} ${item.seriesName}: ${item.value}</div>`
+ })
+ return result
+ },
+}
+
+// 鍦ㄥ埗鍝佸伐搴忔煴鐘跺浘閰嶇疆
+const workInProcessXAxis = ref([
+ {
+ type: 'category',
+ axisTick: { show: false },
+ axisLabel: { color: '#B8C8E0' },
+ data: [],
+ },
+])
+const workInProcessYAxis = [
+ {
+ type: 'value',
+ axisLabel: { color: '#B8C8E0' },
+ name: '',
+ },
+]
+const workInProcessBarLegend = {
+ show: false,
+ textStyle: { color: '#B8C8E0' },
+ data: [],
+}
+const workInProcessBarSeries = ref([
+ {
+ name: '鍦ㄥ埗鍝佹暟閲�',
+ type: 'bar',
+ barWidth: 25,
+ barGap: 0,
+ emphasis: { focus: 'series' },
+ itemStyle: {
+ color: {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 1, color: 'rgba(0,164,237,0)' },
+ { offset: 0, color: '#4EE4FF' },
+ ],
+ },
+ },
+ label: {
+ show: true,
+ position: 'top',
+ color: '#B8C8E0',
+ },
+ data: [],
+ },
+])
+
+const workInProcessTurnoverInfo = () => {
+ getWorkInProcessTurnover()
+ .then((res) => {
+ if (!res || !res.data) return
+ const stats = {
+ totalQuantity: res.data.totalOrderCount || 0,
+ avgTurnoverDays: res.data.averageTurnoverDays || 0,
+ turnoverEfficiency: res.data.turnoverEfficiency || 0,
+ }
+ workInProcessStatistics.value = stats
+ cardItems.value = [
+ { label: '鎬诲湪鍒舵暟閲�', value: stats.totalQuantity, unit: '浠�' },
+ { label: '骞冲潎鍛ㄨ浆澶╂暟', value: stats.avgTurnoverDays, unit: '澶�' },
+ { label: '鍛ㄨ浆鏁堢巼', value: stats.turnoverEfficiency, unit: '%' },
+ ]
+ if (res.data.processDetails && Array.isArray(res.data.processDetails)) {
+ workInProcessXAxis.value[0].data = res.data.processDetails
+ } else {
+ workInProcessXAxis.value[0].data = []
+ }
+ if (res.data.processQuantityDetails && Array.isArray(res.data.processQuantityDetails)) {
+ workInProcessBarSeries.value[0].data = res.data.processQuantityDetails
+ } else {
+ workInProcessBarSeries.value[0].data = []
+ }
+ })
+ .catch((err) => {
+ console.error('鑾峰彇鍦ㄥ埗鍝佸懆杞粺璁″け璐�:', err)
+ })
+}
+
+onMounted(() => {
+ workInProcessTurnoverInfo()
+})
+</script>
+
+<style scoped>
+.main-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.panel-item-customers {
+ border: 1px solid #1a58b0;
+ padding: 18px;
+ width: 100%;
+ height: 449px;
+}
+
+.chart-wrapper {
+ height: 70%;
+ flex: 1;
+ min-height: 200px;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue b/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue
new file mode 100644
index 0000000..8237a3f
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue
@@ -0,0 +1,448 @@
+<template>
+ <div>
+ <PanelHeader title="璐ㄩ噺鎸囨爣鍚堟牸鍒嗘瀽" />
+ <div class="main-panel panel-item-customers">
+ <div v-for="section in sections" :key="section.key" class="inspect-block">
+ <div class="filters-row">
+ <div class="filters-row-left">
+ <span></span>
+ <p>{{ section.title }}</p>
+ </div>
+ <DateTypeSwitch v-model="section.dateType" @change="(v) => handleDateTypeChange(section.key, v)" />
+ </div>
+
+ <div class="inspect-body">
+ <div class="ring">
+ <Echarts :chartStyle="ringChartStyle" :series="buildRingSeries(section)" :tooltip="ringTooltip"
+ :legend="{ show: false }" :options="ringOptions" />
+ </div>
+
+ <div class="stats">
+ <div class="stat-row">
+ <div class="stat-left">
+ <span class="dot dot-qualified"></span>
+ <span class="stat-label">鍚堟牸鏁�</span>
+ </div>
+ <div class="stat-right">
+ <span class="stat-value">{{ section.qualifiedCount }}</span>
+ <span class="stat-percent">{{ formatPercent(section.qualifiedRate) }}</span>
+ </div>
+ </div>
+ <div class="stat-row">
+ <div class="stat-left">
+ <span class="dot dot-unqualified"></span>
+ <span class="stat-label">涓嶅悎鏍兼暟</span>
+ </div>
+ <div class="stat-right">
+ <span class="stat-value">{{ section.unqualifiedCount }}</span>
+ <span class="stat-percent">{{ formatPercent(section.unqualifiedRate) }}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { reactive, onMounted } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
+import DateTypeSwitch from './DateTypeSwitch.vue'
+import { rawMaterialDetection, processDetection, factoryDetection } from '@/api/viewIndex.js'
+
+const QUALIFIED_COLOR = '#4EE4FF'
+const UNQUALIFIED_COLOR = '#3378FF'
+const TRACK_COLOR = 'rgba(78, 228, 255, 0.12)'
+
+const apiMap = {
+ raw: rawMaterialDetection,
+ process: processDetection,
+ final: factoryDetection,
+}
+
+
+const fetchSectionData = async (section) => {
+ const api = apiMap[section.key]
+ if (!api) return
+
+ try {
+ const res = await api({
+ type: section.dateType,
+ })
+
+ if (res?.code === 200 && res?.data) {
+ const data = res.data
+ section.qualifiedCount = Number(data.qualifiedCount || 0)
+ section.unqualifiedCount = Number(data.unqualifiedCount || 0)
+ section.qualifiedRate = Number(data.qualifiedRate || 0)
+ section.unqualifiedRate = Number(data.unqualifiedRate || 0)
+ }
+ } catch (err) {
+ console.error(`${section.key} 鎺ュ彛璇锋眰澶辫触`, err)
+ }
+}
+
+
+const sections = reactive([
+ {
+ key: 'raw',
+ title: '鍘熸潗鏂欐娴�',
+ dateType: 1,
+ qualifiedCount: 0,
+ unqualifiedCount: 0,
+ qualifiedRate: 0,
+ unqualifiedRate: 0,
+ },
+ {
+ key: 'process',
+ title: '杩囩▼妫�娴�',
+ dateType: 1,
+ qualifiedCount: 0,
+ unqualifiedCount: 0,
+ qualifiedRate: 0,
+ unqualifiedRate: 0,
+ },
+ {
+ key: 'final',
+ title: '鎴愬搧鍑哄巶妫�娴�',
+ dateType: 1,
+ qualifiedCount: 0,
+ unqualifiedCount: 0,
+ qualifiedRate: 0,
+ unqualifiedRate: 0,
+ },
+])
+
+const ringChartStyle = {
+ width: '110px',
+ height: '110px',
+}
+
+const ringOptions = {
+ backgroundColor: 'transparent',
+ textStyle: { color: '#B8C8E0' },
+}
+
+const ringTooltip = {
+ show: false,
+}
+
+const calcRates = (qualifiedCount, unqualifiedCount) => {
+ const total = Number(qualifiedCount || 0) + Number(unqualifiedCount || 0)
+ if (total <= 0) return { qualifiedRate: 0, unqualifiedRate: 0 }
+ const qualifiedRate = Math.round((Number(qualifiedCount || 0) / total) * 100)
+ const unqualifiedRate = Math.max(0, 100 - qualifiedRate)
+ return { qualifiedRate, unqualifiedRate }
+}
+
+const formatPercent = (v) => `${Number(v || 0)}%`
+
+const buildRingSeries = (section) => {
+ const qualified = Number(section.qualifiedCount || 0)
+ const unqualified = Number(section.unqualifiedCount || 0)
+ const total = qualified + unqualified
+
+ return [
+ {
+ type: 'pie',
+ radius: ['68%', '82%'],
+ center: ['50%', '50%'],
+ silent: true,
+ label: { show: false },
+ labelLine: { show: false },
+ itemStyle: { color: TRACK_COLOR },
+ data: [1],
+ },
+ {
+ name: section.title,
+ type: 'pie',
+ radius: ['68%', '82%'],
+ center: ['50%', '50%'],
+ silent: true,
+ label: { show: false },
+ labelLine: { show: false },
+ startAngle: 90,
+ clockwise: true,
+ minAngle: total > 0 ? 8 : 0,
+ itemStyle: {
+ borderColor: 'rgba(10, 28, 58, 0.95)',
+ borderWidth: 2,
+ },
+ data: [
+ {
+ value: qualified,
+ name: '鍚堟牸鏁�',
+ itemStyle: {
+ color: QUALIFIED_COLOR,
+ shadowBlur: 16,
+ shadowColor: 'rgba(78, 228, 255, 0.45)',
+ },
+ },
+ {
+ value: unqualified,
+ name: '涓嶅悎鏍兼暟',
+ itemStyle: {
+ color: UNQUALIFIED_COLOR,
+ shadowBlur: 10,
+ shadowColor: 'rgba(51, 120, 255, 0.35)',
+ },
+ },
+ ],
+ },
+ {
+ type: 'pie',
+ radius: ['52%', '56%'],
+ center: ['50%', '50%'],
+ silent: true,
+ label: { show: false },
+ labelLine: { show: false },
+ itemStyle: { color: 'rgba(0, 127, 255, 0.22)' },
+ data: [1],
+ },
+ ]
+}
+
+const handleDateTypeChange = (key, dateType) => {
+ const section = sections.find((s) => s.key === key)
+ if (!section) return
+ section.dateType = dateType
+ // 鍒囨崲鏃ユ湡绫诲瀷鏃堕噸鏂拌幏鍙栨暟鎹�
+ fetchSectionData(section)
+}
+
+// 缁勪欢鎸傝浇鏃惰幏鍙栨墍鏈塻ection鐨勬暟鎹�
+onMounted(() => {
+ sections.forEach((section) => {
+ fetchSectionData(section)
+ })
+})
+</script>
+
+<style scoped>
+.main-panel {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ gap: 0;
+}
+
+.filters-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 10px;
+
+ .filters-row-left {
+ width: 50%;
+ color: white;
+ /* 鐢╢lex鏇夸唬float锛岃瀛愬厓绱犲榻愭洿绋冲畾 */
+ display: flex;
+ align-items: center;
+
+ span {
+ /* 鏍稿績锛氱埗绾х浉瀵瑰畾浣嶏紝浣滀负浼厓绱犲熀鍑� */
+ position: relative;
+ display: inline-block;
+ /* 缁欎吉鍏冪礌鍜屾枃瀛楃暀绌洪棿 */
+ padding-left: 22px;
+ /* 鏂囧瓧鍨傜洿灞呬腑 */
+ line-height: 23px;
+ margin-right: 8px;
+
+ &::after {
+ content: '';
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
+ background: #217AFF;
+ position: absolute;
+ top: 50%;
+ left: 0;
+ transform: translateY(-50%);
+ /* 纭繚鑿卞舰鍦ㄦ笎鍙樺潡涓婃柟 */
+ z-index: 1;
+ }
+
+ &::before {
+ content: '';
+ display: inline-block;
+ width: 18px;
+ height: 7px;
+ border-radius: 8px;
+ background: linear-gradient(360deg, rgba(33, 133, 255, 0.4) 0%, rgba(33, 221, 255, 0) 100%);
+ position: absolute;
+ top: 50%;
+ left: -1px;
+ /* 绮惧噯璐村湪鑿卞舰姝d笅鏂� */
+ transform: translateY(calc(0% + 8px));
+ z-index: 0;
+ }
+ }
+
+ p {
+ width: 100px;
+ height: 23px;
+ /* 娓愬彉璧峰鑹插拰鑿卞舰缁熶竴锛屾洿鍗忚皟 */
+ background: linear-gradient(90deg, #217AFF 0%, rgba(33, 221, 255, 0) 100%);
+ /* 绮惧噯鍨傜洿灞呬腑 */
+ line-height: 26px;
+ text-align: center;
+ color: white;
+ /* 鐢ㄩ珮搴︾殑涓�鍗婂仛鍦嗚锛岀‘淇濆乏杈规槸瀹岀編鍗婂渾 */
+ border-radius: 12px 0 0 12px;
+ /* 鍙�夛細鍔犱竴鐐瑰乏鍐呰竟璺濓紝璁╂枃瀛椾笉璐磋竟 */
+ padding-left: 4px;
+ }
+ }
+}
+
+.panel-item-customers {
+ border: 1px solid #1a58b0;
+ padding: 14px 18px;
+ width: 100%;
+ height: 960px;
+ box-sizing: border-box;
+}
+
+.inspect-block {
+ flex: 1 1 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ padding: 8px 0;
+ gap: 6px;
+ position: relative;
+}
+
+.inspect-block:not(:last-child)::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 1px;
+ background: linear-gradient(90deg, rgba(33, 122, 255, 0) 0%, rgba(33, 122, 255, 0.55) 50%, rgba(33, 122, 255, 0) 100%);
+ pointer-events: none;
+}
+
+.inspect-body {
+ flex: 1 1 auto;
+ min-height: 0;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ gap: 18px;
+}
+
+.ring {
+ width: 120px;
+ height: 120px;
+ flex: 0 0 120px;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* 澶栧湀鍒诲害锛堢偣鐘剁幆锛� */
+.ring::before {
+ content: '';
+ position: absolute;
+ inset: -8px;
+ border-radius: 50%;
+ background: repeating-conic-gradient(from 0deg,
+ rgba(78, 228, 255, 0.75) 0 1deg,
+ rgba(78, 228, 255, 0) 1deg 9deg);
+ -webkit-mask: radial-gradient(circle, transparent 62%, #000 63%);
+ mask: radial-gradient(circle, transparent 62%, #000 63%);
+ opacity: 0.35;
+ pointer-events: none;
+}
+
+/* 鏌斿拰鍙戝厜鑳屾櫙 */
+.ring::after {
+ content: '';
+ position: absolute;
+ inset: -20px;
+ border-radius: 50%;
+ background: radial-gradient(circle, rgba(78, 228, 255, 0.18) 0%, rgba(78, 228, 255, 0.06) 40%, rgba(0, 0, 0, 0) 70%);
+ filter: blur(0.2px);
+ pointer-events: none;
+}
+
+.stats {
+ width: 240px;
+ flex: 0 0 240px;
+ display: grid;
+ grid-template-rows: 1fr 1fr;
+ gap: 10px;
+}
+
+.stat-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 100%;
+ padding: 10px 14px;
+ border-radius: 4px;
+ border: 1px solid rgba(78, 228, 255, 0.22);
+ background: linear-gradient(90deg, rgba(33, 122, 255, 0.28) 0%, rgba(10, 28, 58, 0.35) 55%, rgba(10, 28, 58, 0.2) 100%);
+ box-shadow: inset 0 0 18px rgba(16, 45, 95, 0.25);
+}
+
+.stat-left {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ color: #b8c8e0;
+ font-size: 12px;
+}
+
+.dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 2px;
+ display: inline-block;
+ box-shadow: 0 0 10px rgba(78, 228, 255, 0.25);
+}
+
+.dot-qualified {
+ background: rgba(184, 200, 224, 0.85);
+}
+
+.dot-unqualified {
+ background: #4ee4ff;
+}
+
+.stat-right {
+ display: inline-flex;
+ align-items: baseline;
+ gap: 14px;
+}
+
+.stat-value {
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: 600;
+ min-width: 40px;
+ text-align: right;
+ text-shadow: 0 0 10px rgba(78, 228, 255, 0.15);
+}
+
+.stat-percent {
+ color: rgba(184, 200, 224, 0.95);
+ font-size: 12px;
+ min-width: 40px;
+ text-align: right;
+}
+
+/* 璁╁垏鎹㈡寜閽洿璐磋繎鎴浘锛堟洿绱у噾锛� */
+:deep(.date-type-switch .el-radio-button__inner) {
+ padding: 4px 16px;
+ font-size: 12px;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue b/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue
new file mode 100644
index 0000000..49621f3
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue
@@ -0,0 +1,189 @@
+<template>
+ <div>
+ <PanelHeader title="涓嶅悎鏍兼鍝佸鐞嗗垎鏋�" />
+ <div class="panel-item-customers">
+ <div class="pie-chart-wrapper" ref="pieWrapperRef">
+ <div class="pie-background" ref="pieBackgroundRef"></div>
+ <Echarts ref="chart" :chartStyle="chartStyle" :legend="landLegend" :series="computedSeries"
+ :tooltip="landTooltip" :color="landColors" :options="pieOptions" style="height: 100%" class="land-chart" />
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
+import { unqualifiedProductProcessingAnalysis } from '@/api/viewIndex.js'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
+const chart = ref(null)
+
+// 鏁版嵁鍒楄〃
+const dataList = ref([])
+
+// 棰滆壊鍒楄〃
+const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF']
+
+// label 瀵屾枃鏈牱寮�
+const dotRich = landColors.reduce((acc, color, idx) => {
+ acc[`dot${idx}`] = {
+ width: 8,
+ height: 8,
+ borderRadius: 8,
+ backgroundColor: color,
+ align: 'center',
+ }
+ return acc
+}, {})
+
+// 鍥句緥閰嶇疆
+const landLegend = ref({
+ show: false,
+ icon: 'circle',
+ data: [],
+ right: '8%',
+ top: '40%',
+ orient: 'vertical',
+ textStyle: {
+ color: '#fff',
+ rich: {
+ unit: { color: '#fff', fontSize: 12, padding: [0, 10, 0, 0] },
+ text: { width: 60, color: '#fff', fontSize: 12 },
+ }
+ }
+})
+
+// 鎻愮ず妗嗛厤缃�
+const landTooltip = {
+ trigger: 'item',
+ alwaysShowContent: false,
+ position: function (pt) {
+ return [pt[0], 130]
+ },
+ formatter: function (params) {
+ // 纭繚 params.data 瀛樺湪
+ if (!params.data) return ''
+ const { name, value, rate } = params.data
+ return `${name}<br/>鏁伴噺锛�${value}涓�<br/>鍗犳瘮锛�${rate}%`
+ },
+}
+
+// 浣跨敤璁$畻灞炴�у鐞� Series
+const computedSeries = computed(() => {
+ return [
+ {
+ name: '涓嶅悎鏍兼鍝佸鐞嗗垎鏋�',
+ type: 'pie',
+ radius: ['35%', '55%'],
+ center: ['50%', '50%'],
+ label: {
+ show: true,
+ position: 'outside',
+ color: '#fff',
+ rich: {
+ ...dotRich,
+ parent: { fontSize: 14, fontWeight: 600, color: '#fff', lineHeight: 20 },
+ child: { fontSize: 12, color: '#fff', lineHeight: 18 },
+ },
+ formatter: function (params) {
+ if (!params.data) return ''
+ const dotKey = `dot${params.dataIndex % landColors.length}`
+ return `{${dotKey}|} {parent|${params.data.name} (${params.data.value}涓�)}`
+ },
+ },
+ labelLine: {
+ show: true,
+ length: 20,
+ lineStyle: { color: '#B8C8E0' },
+ },
+ data: dataList.value,
+ },
+ {
+ // 鍐呭湀瑁呴グ
+ type: 'pie',
+ radius: ['35%', '40%'],
+ center: ['50%', '50%'],
+ silent: true,
+ label: { show: false },
+ itemStyle: { color: 'rgba(0, 127, 255, 0.25)' },
+ data: [1],
+ },
+ ]
+})
+
+const chartStyle = { width: '100%', height: '126%' }
+const pieOptions = { backgroundColor: 'transparent' }
+
+// 鑳屾櫙澶勭悊閽╁瓙
+const { adjustBackgroundPosition, init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+ wrapperRef: pieWrapperRef,
+ backgroundRef: pieBackgroundRef,
+ offsetX: '-51.5%',
+ offsetY: '-39%',
+ watchData: dataList
+})
+
+const loadData = async () => {
+ try {
+ const res = await unqualifiedProductProcessingAnalysis()
+ if (res && res.code === 200) {
+ dataList.value = (res.data || []).map((it) => ({
+ name: it.name,
+ value: Number(it.value || 0),
+ rate: it.rate,
+ }))
+ landLegend.value.data = dataList.value.map((d) => d.name)
+
+ // 鏁版嵁鏇存柊鍚庡井璋冭儗鏅�
+ setTimeout(() => {
+ adjustBackgroundPosition()
+ }, 100)
+ }
+ } catch (e) {
+ console.error('鑾峰彇鏁版嵁澶辫触:', e)
+ }
+}
+
+onMounted(() => {
+ loadData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+ border: 1px solid #1a58b0;
+ padding: 18px;
+ width: 100%;
+ height: 420px;
+}
+
+.pie-chart-wrapper {
+ position: relative;
+ width: 100%;
+ height: 320px;
+}
+
+.pie-background {
+ position: absolute;
+ width: 360px;
+ height: 360px;
+ background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
+ background-size: contain;
+ background-position: center;
+ background-repeat: no-repeat;
+ z-index: 1;
+ pointer-events: none;
+ left: 50%;
+ top: 50%;
+ transform: translate(-51.5%, -39%);
+}
+</style>
\ No newline at end of file
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue b/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue
new file mode 100644
index 0000000..890e99a
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue
@@ -0,0 +1,132 @@
+<template>
+ <div>
+ <PanelHeader title="涓嶅悎鏍间骇鍝佹帓鍚�" />
+ <div class="main-panel panel-item-customers">
+ <div class="main-panel-container">
+ <div style="color: white" class="main-panel-box" v-for="(item, index) in panelList" :key="index">
+ <!-- <div style="flex: 1" class="main-panel-box-left">{{ item.rank }}</div> -->
+ <div style="flex: 1" class="main-panel-box-left">{{ item.productName }}</div>
+ <div style="flex: 3" class="main-panel-box-right">
+ <!-- <div class="main-panel-box-right-title">{{ item.productName }}</div> -->
+ <div class="main-panel-box-right-text">
+ <span>鎬绘暟閲忥細{{ item.total }}</span>
+ <span>宸插畬鎴愶細{{ item.finished }}</span>
+ <span>鍚堟牸鐜囷細{{ item.qualifiedRate }}%</span>
+ </div>
+ <div class="main-panel-box-right-progress">
+ <el-progress :percentage="item.percentage" :format="format" />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { unqualifiedProductRanking } from '@/api/viewIndex.js'
+import PanelHeader from './PanelHeader.vue'
+
+const panelList = ref([])
+
+const format = (percentage) => {
+ return `${percentage}%`
+}
+
+const fetchData = () => {
+ unqualifiedProductRanking()
+ .then((res) => {
+ if (res?.code === 200 && Array.isArray(res?.data)) {
+ const data = res.data
+ panelList.value = data.map((item, index) => {
+ const total = Number(item.totalCount) || 0
+ const finished = Number(item.completedCount) || 0
+ const passRate = Number(item.passRate) || 0
+
+ return {
+ rank: `Top${index + 1}`,
+ productName: item.productName || `浜у搧${index + 1}`,
+ total: total.toFixed(2),
+ finished: finished.toFixed(2),
+ qualifiedRate: passRate.toFixed(2),
+ percentage: Math.min(100, Math.max(0, passRate)), // 纭繚鐧惧垎姣斿湪0-100涔嬮棿
+ }
+ })
+ }
+ })
+ .catch((err) => {
+ console.error('鑾峰彇宸ュ崟鎵ц鏁堢巼鍒嗘瀽鏁版嵁澶辫触:', err)
+ })
+}
+
+onMounted(() => {
+ fetchData()
+})
+</script>
+
+<style scoped>
+.main-panel-box {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 40px;
+
+ .main-panel-box-left {
+ background: red;
+ border-radius: 20px;
+ text-align: center;
+ line-height: 32px;
+ margin: 0 20px;
+ }
+
+ .main-panel-box-right {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+
+ .main-panel-box-right-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #ffffff;
+ margin-bottom: 6px;
+ }
+
+ .main-panel-box-right-text {
+ font-size: 12px;
+ display: flex;
+ justify-content: space-between;
+ padding-right: 60px;
+ margin-bottom: 4px;
+ }
+
+ .main-panel-box-right-progress {
+ :deep(.el-progress__text) {
+ color: white !important;
+ }
+ }
+ }
+}
+
+.main-panel-container {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ height: 100%;
+ overflow-y: auto;
+}
+
+.main-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.panel-item-customers {
+ border: 1px solid #1a58b0;
+ padding: 18px;
+ width: 100%;
+ height: 449px;
+ overflow: hidden;
+}
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/index.vue b/src/views/reportAnalysis/qualityAnalysis/index.vue
new file mode 100644
index 0000000..1163154
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/index.vue
@@ -0,0 +1,289 @@
+<template>
+ <div class="scale-container">
+ <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
+ <!-- 鍏ㄥ睆鎸夐挳 - 绉诲姩鍒板乏涓婅 -->
+ <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
+ <svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+ <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
+ </svg>
+ <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+ <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
+ </svg>
+ </button>
+
+ <!-- 椤堕儴鏍囬鏍� -->
+ <div class="dashboard-header">
+ <div class="factory-name">杩涢攢璐ㄩ噺绫诲垎鏋�</div>
+ </div>
+
+ <!-- 涓昏鍐呭鍖哄煙 -->
+ <div class="dashboard-content">
+ <!-- 宸︿晶鍖哄煙 -->
+ <div class="left-panel">
+ <LeftTop />
+ </div>
+
+ <!-- 涓棿鍖哄煙 -->
+ <div class="center-panel">
+ <CenterTop />
+ <CenterCenter/>
+ <CenterBottom />
+ </div>
+
+ <!-- 鍙充晶鍖哄煙 -->
+ <div class="right-panel">
+ <RightTop />
+ <RightBottom />
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import autofit from 'autofit.js'
+import LeftBottom from './components/left-bottom.vue'
+import CenterCenter from './components/center-center.vue'
+import RightTop from './components/right-top.vue'
+import RightBottom from './components/right-bottom.vue'
+import useUserStore from '@/store/modules/user'
+import LeftTop from './components/left-top.vue'
+import CenterTop from './components/center-top.vue'
+import CenterBottom from './components/center-bottom.vue'
+
+// 鍏ㄥ睆鐩稿叧鐘舵��
+const isFullscreen = ref(false);
+
+// 缂╂斁姣斾緥
+const scaleRatio = ref(1)
+// 璁捐灏哄锛堝熀鍑嗗昂瀵革級- 鏍规嵁瀹為檯璁捐绋胯皟鏁�
+const designWidth = 1920
+const designHeight = 1080
+
+// 鐢ㄦ埛store
+const userStore = useUserStore()
+
+// 璁$畻缂╂斁姣斾緥
+const calculateScale = () => {
+ const container = document.querySelector('.scale-container')
+ if (!container) return
+
+ // 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
+ const rect = container.getBoundingClientRect?.()
+ const containerWidth = container.clientWidth || rect?.width || window.innerWidth
+ const containerHeight = container.clientHeight || rect?.height || window.innerHeight
+
+ // 璁$畻瀹介珮缂╂斁姣斾緥锛屽彇杈冨皬鍊间互淇濊瘉鍐呭瀹屾暣鏄剧ず锛堢瓑姣旂缉鏀撅級
+ const scaleX = containerWidth / designWidth
+ const scaleY = containerHeight / designHeight
+ scaleRatio.value = Math.min(scaleX, scaleY)
+}
+
+// 绐楀彛澶у皬鍙樺寲澶勭悊
+const handleResize = () => {
+ // 寤惰繜鎵ц锛岀‘淇滵OM鏇存柊瀹屾垚
+ setTimeout(() => {
+ calculateScale()
+ }, 100)
+}
+
+// 鍏ㄥ睆鍔熻兘瀹炵幇 - 閽堝scale-container鍏冪礌
+const toggleFullscreen = () => {
+ const element = document.querySelector('.scale-container')
+
+ if (!element) return
+
+ if (!isFullscreen.value) {
+ if (element.requestFullscreen) {
+ element.requestFullscreen()
+ } else if (element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen()
+ } else if (element.msRequestFullscreen) {
+ element.msRequestFullscreen()
+ }
+ } else {
+ if (document.exitFullscreen) {
+ document.exitFullscreen()
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen()
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen()
+ }
+ }
+}
+
+// 鐩戝惉鍏ㄥ睆鍙樺寲浜嬩欢
+const handleFullscreenChange = () => {
+ const fullscreenElement = document.fullscreenElement ||
+ document.webkitFullscreenElement ||
+ document.msFullscreenElement
+ isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container')
+
+ // 鍏ㄥ睆鐘舵�佸彉鍖栨椂锛屽欢杩熼噸鏂拌绠楃缉鏀炬瘮渚嬶紙纭繚DOM鏇存柊瀹屾垚锛�
+ setTimeout(() => {
+ calculateScale()
+ }, 200)
+}
+
+// 鐢熷懡鍛ㄦ湡閽╁瓙
+onMounted(() => {
+ // 浣跨敤nextTick纭繚DOM瀹屽叏娓叉煋鍚庡啀鍒濆鍖�
+ nextTick(() => {
+ // 璁$畻鍒濆缂╂斁姣斾緥
+ calculateScale()
+ })
+
+ window.addEventListener('resize', handleResize)
+ window.addEventListener('fullscreenchange', handleFullscreenChange)
+ window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
+ window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+})
+
+onBeforeUnmount(() => {
+ window.removeEventListener('resize', handleResize)
+ window.removeEventListener('fullscreenchange', handleFullscreenChange)
+ window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
+ window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
+ // 绉婚櫎鎴戜滑娣诲姞鐨刟utofit鍔ㄦ�佽皟鏁寸洃鍚櫒
+ if (window._autofitUpdateHandler) {
+ window.removeEventListener('resize', window._autofitUpdateHandler)
+ delete window._autofitUpdateHandler
+ }
+ // 鍏抽棴autofit
+ autofit.off()
+})
+</script>
+
+<style scoped>
+/* 澶栭儴缂╂斁瀹瑰櫒 - 鍗犳嵁鏁翠釜瑙嗗彛 */
+.scale-container {
+position: relative;
+width: 100%;
+/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
+height: calc(100vh - 84px);
+display: flex;
+align-items: center;
+justify-content: center;
+background-color: #000;
+overflow: hidden;
+}
+
+/* 鍐呴儴鍐呭鍖哄煙 - 鍥哄畾璁捐灏哄 */
+.data-dashboard {
+position: relative;
+width: 1920px;
+height: 1080px;
+background-image: url("@/assets/BI/backImage@2x.png");
+background-size: cover;
+background-position: center;
+background-repeat: no-repeat;
+transform-origin: center center;
+}
+
+/* 鍏ㄥ睆鐘舵�佺殑鏍峰紡 - 浣滅敤浜巗cale-container */
+.scale-container:fullscreen {
+width: 100vw;
+height: 100vh;
+margin: 0;
+padding: 0;
+background-color: #000;
+z-index: 9999;
+}
+
+/* Webkit娴忚鍣ㄥ墠缂� */
+.scale-container:-webkit-full-screen {
+width: 100vw;
+height: 100vh;
+margin: 0;
+padding: 0;
+background-color: #000;
+z-index: 9999;
+}
+
+/* MS娴忚鍣ㄥ墠缂� */
+.scale-container:-ms-fullscreen {
+width: 100vw;
+height: 100vh;
+margin: 0;
+padding: 0;
+background-color: #000;
+z-index: 9999;
+}
+
+
+.dashboard-header {
+position: relative;
+z-index: 1;
+height: 86px;
+background-image: url("@/assets/BI/biaoti.png");
+background-size: cover;
+background-repeat: no-repeat;
+display: flex;
+align-items: center;
+justify-content: center;
+}
+
+.factory-name {
+font-weight: 600;
+font-size: 52px;
+color: #FFFFFF;
+top: 16px;
+position: absolute;
+}
+
+.fullscreen-btn {
+position: absolute;
+top: 10px;
+left: 20px;
+width: 40px;
+height: 40px;
+background: rgba(0, 20, 60, 0.8);
+border: 1px solid rgba(0, 212, 255, 0.3);
+border-radius: 6px;
+color: #00d4ff;
+cursor: pointer;
+display: flex;
+align-items: center;
+justify-content: center;
+transition: all 0.3s;
+z-index: 10000;
+}
+
+.fullscreen-btn:hover {
+background: rgba(0, 30, 90, 0.9);
+border-color: rgba(0, 212, 255, 0.5);
+}
+
+.dashboard-content {
+position: relative;
+z-index: 1;
+display: flex;
+gap: 30px;
+padding: 0 30px;
+height: calc(100% - 86px);
+overflow: hidden;
+}
+
+/* 纭繚鍚勯潰鏉胯兘澶熸纭樉绀� */
+.left-panel, .center-panel, .right-panel {
+overflow: hidden;
+}
+
+.left-panel,
+.right-panel {
+flex: 1;
+display: flex;
+flex-direction: column;
+gap: 24px;
+width: 520px;
+}
+
+.center-panel {
+flex: 1.5;
+display: flex;
+flex-direction: column;
+gap: 20px;
+}
+
+</style>
\ No newline at end of file
diff --git a/src/views/reportAnalysis/reportManagement/index.vue b/src/views/reportAnalysis/reportManagement/index.vue
index 343a2c2..dc9d486 100644
--- a/src/views/reportAnalysis/reportManagement/index.vue
+++ b/src/views/reportAnalysis/reportManagement/index.vue
@@ -302,17 +302,17 @@
};
const getYearlyStatValue = (type, field) => {
- const stat = yearlyPassRateData.value.find(item => item.inspectType === type);
+ const stat = yearlyPassRateData.value.find(item => item.modelType === type);
return stat ? stat[field] : 0;
};
const getInspectStatValue = (type, field) => {
- const stat = inspectStatisticsData.value.find(item => item.inspectType === type);
+ const stat = inspectStatisticsData.value.find(item => item.modelType === type);
return stat ? stat[field] : 0;
};
const getPassRateStatValue = (type, field) => {
- const stat = passRateStatisticsData.value.find(item => item.inspectType === type);
+ const stat = passRateStatisticsData.value.find(item => item.modelType === type);
if (stat) {
if (field === 'completionRate' || field === 'passRate') {
return stat[field] ? Number(stat[field]).toFixed(0) + '%' : '0%';
@@ -387,7 +387,7 @@
const fetchTopParametersData = async () => {
try {
- const typeMap = { raw: 0, semi: 1, final: 2 };
+ const typeMap = { raw: 1, semi: 2, final: 3 };
const res = await getTopParameters(typeMap[activeTab.value]);
if (res.code === 200) {
topParametersData.value = res.data;
diff --git a/src/views/safeProduction/accidentReportingRecord/index.vue b/src/views/safeProduction/accidentReportingRecord/index.vue
index 28e638e..e362ba5 100644
--- a/src/views/safeProduction/accidentReportingRecord/index.vue
+++ b/src/views/safeProduction/accidentReportingRecord/index.vue
@@ -203,9 +203,9 @@
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button @click="dialogVisible = false">鍙栨秷</el-button>
<el-button type="primary"
@click="submitForm">纭畾</el-button>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
</template>
</el-dialog>
@@ -513,7 +513,7 @@
.then(res => {
tableLoading.value = false;
tableData.value = res.data.records;
- page.total = res.data.total;
+ page.value.total = res.data.total;
})
.catch(err => {
tableLoading.value = false;
@@ -524,7 +524,7 @@
const pagination = obj => {
page.value.current = obj.page;
page.value.size = obj.limit;
- handleQuery();
+ getList();
};
const currentUserId = ref("");
const currentUserName = ref("");
diff --git a/src/views/safeProduction/dangerInvestigation/index.vue b/src/views/safeProduction/dangerInvestigation/index.vue
index d585fc3..49965e9 100644
--- a/src/views/safeProduction/dangerInvestigation/index.vue
+++ b/src/views/safeProduction/dangerInvestigation/index.vue
@@ -111,13 +111,13 @@
</el-table-column>
<el-table-column fixed="right"
label="鎿嶄綔"
- min-width="250"
+ min-width="150"
align="center">
<template #default="scope">
- <el-button link
+ <!-- <el-button link
type="primary"
size="small"
- @click="openForm('edit', scope.row)">缂栬緫</el-button>
+ @click="openForm('edit', scope.row)">缂栬緫</el-button> -->
<el-button link
type="primary"
size="small"
@@ -125,11 +125,12 @@
<el-button link
type="primary"
size="small"
+ :disabled="scope.row.isRectify || scope.row.rectifyActualTime"
@click="openForm('edit2', scope.row)">鏁存敼</el-button>
<el-button link
type="primary"
size="small"
- :disabled="!scope.row.rectifyActualTime"
+ :disabled="!scope.row.rectifyActualTime || scope.row.verifyTime"
@click="openForm('edit3', scope.row)">楠屾敹</el-button>
</template>
</el-table-column>
@@ -419,8 +420,11 @@
v-model="fileListDialogVisible"
:show-upload-button="true"
:show-delete-button="true"
+ :is-show-pagination="true"
+ :page="filePagination"
:upload-method="handleUpload"
:delete-method="handleFileDelete"
+ @pagination="paginationSearch"
title="闄勪欢鍒楄〃" />
</div>
</template>
@@ -655,6 +659,14 @@
tableLoading.value = false;
tableData.value = res.data.records;
total.value = res.data.total;
+ tableData.value.forEach(item => {
+ // console.log(item.rectifyUserId, currentUserId.value, "=======");
+ if (Number(item.rectifyUserId) != Number(currentUserId.value)) {
+ item.isRectify = true;
+ } else {
+ item.isRectify = false;
+ }
+ });
return res;
})
.catch(() => {
@@ -889,6 +901,9 @@
proxy.$modal.msg("宸插彇娑�");
});
};
+ const isPeople = rectifyUserId => {
+ return Number(rectifyUserId) == Number(currentUserId.value);
+ };
/**
* 鍒ゆ柇鏄惁鍙互鍙戣揣
@@ -913,6 +928,11 @@
const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
return statusStr === "寰呭彂璐�" || statusStr === "瀹℃牳鎷掔粷";
};
+ const filePagination = ref({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
/**
* 涓嬭浇鏂囦欢
@@ -924,9 +944,15 @@
const currentFileRow = ref(null);
const downLoadFile = row => {
currentFileRow.value = row;
- fileListPage({ safeHiddenId: row.id }).then(res => {
+ fileListPage({
+ safeHiddenId: row.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
+ }).then(res => {
if (fileListRef.value) {
- fileListRef.value.open(res.data.records);
+ fileListRef.value.open(res.data.records || []);
+ console.log("res.data", res.data);
+ filePagination.value.total = res.data.total || 0;
}
});
};
@@ -958,11 +984,11 @@
};
onMounted(() => {
+ getCurrentFactoryName();
getList();
userListNoPage().then(res => {
userList.value = res.data;
});
- getCurrentFactoryName();
});
// 涓婁紶闄勪欢
const handleUpload = async () => {
@@ -1012,6 +1038,8 @@
// 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
const listRes = await fileListPage({
safeHiddenId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
});
if (listRes.code === 200 && fileListRef.value) {
const fileList = (listRes.data?.records || []).map(item => ({
@@ -1021,6 +1049,7 @@
...item,
}));
fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
}
// 杩斿洖鏂版枃浠朵俊鎭�
resolve({
@@ -1048,6 +1077,26 @@
input.click();
});
};
+ // 鍒嗛〉鏌ヨ鏂囦欢鍒楄〃
+ const paginationSearch = async (page, size) => {
+ filePagination.value.current = page;
+ filePagination.value.size = size;
+ const listRes = await fileListPage({
+ safeHiddenId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
+ });
+ if (listRes.code === 200) {
+ const fileList = (listRes.data?.records || []).map(item => ({
+ name: item.name,
+ url: item.url,
+ id: item.id,
+ ...item,
+ }));
+ fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
+ }
+ };
// 鍒犻櫎闄勪欢
const handleFileDelete = async row => {
try {
@@ -1058,6 +1107,8 @@
if (currentFileRow.value && fileListRef.value) {
const listRes = await fileListPage({
safeHiddenId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
});
if (listRes.code === 200) {
const fileList = (listRes.data?.records || []).map(item => ({
@@ -1067,6 +1118,7 @@
...item,
}));
fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
}
}
return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
diff --git a/src/views/safeProduction/emergencyPlanReview/index.vue b/src/views/safeProduction/emergencyPlanReview/index.vue
index 93658d4..eb68508 100644
--- a/src/views/safeProduction/emergencyPlanReview/index.vue
+++ b/src/views/safeProduction/emergencyPlanReview/index.vue
@@ -165,9 +165,9 @@
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button @click="dialogVisible = false">鍙栨秷</el-button>
<el-button type="primary"
@click="submitForm">纭畾</el-button>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
</template>
</el-dialog>
@@ -445,7 +445,7 @@
.then(res => {
tableLoading.value = false;
tableData.value = res.data.records;
- page.total = res.data.total;
+ page.value.total = res.data.total;
})
.catch(err => {
tableLoading.value = false;
@@ -456,7 +456,7 @@
const pagination = obj => {
page.value.current = obj.page;
page.value.size = obj.limit;
- handleQuery();
+ getList();
};
// 閫夋嫨鍙樺寲澶勭悊
diff --git a/src/views/safeProduction/hazardSourceLedger/index.vue b/src/views/safeProduction/hazardSourceLedger/index.vue
index 2a78c48..9aa131a 100644
--- a/src/views/safeProduction/hazardSourceLedger/index.vue
+++ b/src/views/safeProduction/hazardSourceLedger/index.vue
@@ -159,9 +159,9 @@
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button @click="dialogVisible = false">鍙栨秷</el-button>
<el-button type="primary"
@click="submitForm">纭畾</el-button>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
</template>
</el-dialog>
@@ -457,7 +457,7 @@
const pagination = obj => {
page.value.current = obj.page;
page.value.size = obj.limit;
- handleQuery();
+ getList();
};
// 閫夋嫨鍙樺寲澶勭悊
diff --git a/src/views/safeProduction/hazardousMaterialsControl/index.vue b/src/views/safeProduction/hazardousMaterialsControl/index.vue
index 0a2bd6d..b2048fe 100644
--- a/src/views/safeProduction/hazardousMaterialsControl/index.vue
+++ b/src/views/safeProduction/hazardousMaterialsControl/index.vue
@@ -244,9 +244,9 @@
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button @click="dialogVisible = false">鍙栨秷</el-button>
<el-button type="primary"
@click="submitForm">纭畾</el-button>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
</template>
</el-dialog>
@@ -258,9 +258,14 @@
<div>
<el-table :data="safeHazardList"
border
+ ref="safeHazardTableRef"
v-loading="safeHazardLoading"
- style="width: 100%"
- @row-click="handleSafeHazardSelect">
+ :selection="selectedSafeHazardIds"
+ @selection-change="handleSafeHazardSelectionChange"
+ style="width: 100%">
+ <el-table-column type="selection"
+ width="55"
+ :selectable="isSelectable" />
<el-table-column prop="code"
label="鍗遍櫓婧愮紪鐮�"
width="180"
@@ -302,6 +307,8 @@
</div>
<template #footer>
<span class="dialog-footer">
+ <el-button type="primary"
+ @click="handleSafeHazardSelect">纭畾</el-button>
<el-button @click="safeHazardSelectVisible = false">鍙栨秷</el-button>
</span>
</template>
@@ -541,11 +548,17 @@
}
};
const handleApplyQtyChange = () => {
+ if (Number(form.value.applyQty) < 0) {
+ ElMessage.error("棰嗙敤鏁伴噺涓嶈兘灏忎簬0");
+ form.value.applyQty = 0;
+ return;
+ }
if (form.value.applyQty > valueItem.value.stockQty) {
ElMessage.error("棰嗙敤鏁伴噺涓嶈兘澶т簬搴撳瓨鏁伴噺");
form.value.applyQty = "";
}
};
+ const selectedSafeHazardIds = ref([]);
// 寮�濮嬭嚜鍔ㄥ埛鏂�
const startAutoRefresh = () => {
@@ -580,7 +593,7 @@
const fetchSafeHazardList = () => {
safeHazardLoading.value = true;
return safeHazardListPage({
- page: safeHazardPage.value.current,
+ current: safeHazardPage.value.current,
size: safeHazardPage.value.size,
})
.then(res => {
@@ -592,12 +605,36 @@
});
};
- const handleSafeHazardSelect = item => {
+ const isSelectable = row => {
+ // 鍙湁搴撳瓨鏁伴噺澶т簬0鐨勮鎵嶈兘琚�夋嫨
+ return Number(row.stockQty) > 0;
+ };
+
+ const handleSafeHazardSelectionChange = selection => {
+ // 鍙繚鐣欐渶鍚庝竴涓�変腑鐨勯」
+ if (selection.length > 1) {
+ const lastSelected = selection[selection.length - 1];
+ selectedSafeHazardIds.value = [lastSelected];
+ proxy.$refs.safeHazardTableRef.clearSelection();
+ proxy.$refs.safeHazardTableRef.toggleRowSelection(lastSelected, true);
+ } else if (selection.length === 1) {
+ selectedSafeHazardIds.value = [selection[0]];
+ } else {
+ selectedSafeHazardIds.value = [];
+ }
+ };
+
+ const handleSafeHazardSelect = () => {
+ if (!selectedSafeHazardIds.value.length) {
+ ElMessage.error("璇烽�夋嫨涓�涓嵄闄╂簮");
+ return;
+ }
+
valueItem.value = {
- ...item,
+ ...selectedSafeHazardIds.value[0],
};
valueItem.value.type = getTypeLabel(valueItem.value.type);
- form.value.safeHazardId = item.id;
+ form.value.safeHazardId = selectedSafeHazardIds.value[0].id;
safeHazardSelectVisible.value = false;
};
@@ -611,12 +648,20 @@
const pagination1 = obj => {
page.value.current = obj.page;
page.value.size = obj.limit;
- handleQuery();
+ getList();
};
// 閫夋嫨鍙樺寲澶勭悊
const handleSelectionChange = selection => {
- selectedIds.value = selection.map(item => item.id);
+ // 涓昏〃鏍间篃鍙繚鐣欐渶鍚庝竴涓�変腑鐨勯」
+ if (selection.length > 1) {
+ const lastSelected = selection[selection.length - 1];
+ selectedIds.value = [lastSelected.id];
+ } else if (selection.length === 1) {
+ selectedIds.value = [selection[0].id];
+ } else {
+ selectedIds.value = [];
+ }
};
// 鎵撳紑琛ㄥ崟
@@ -741,7 +786,7 @@
.catch(err => {
ElMessage.error(err.msg);
});
- } else {
+ } else if (dialogType.value === "edit") {
await formRef1.value.validate();
safeHazardRecordUpdate({ ...form.value })
.then(res => {
@@ -754,6 +799,9 @@
.catch(err => {
ElMessage.error(err.msg);
});
+ } else if (dialogType.value === "view") {
+ // 鏌ョ湅妯″紡涓嬩笉鎻愪氦琛ㄥ崟
+ dialogVisible.value = false;
}
} catch (error) {
console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
diff --git a/src/views/safeProduction/safeQualifications/index.vue b/src/views/safeProduction/safeQualifications/index.vue
index d7c41f9..e61a39c 100644
--- a/src/views/safeProduction/safeQualifications/index.vue
+++ b/src/views/safeProduction/safeQualifications/index.vue
@@ -207,8 +207,11 @@
v-model="fileListDialogVisible"
:show-upload-button="true"
:show-delete-button="true"
+ :is-show-pagination="true"
+ :page="filePagination"
:upload-method="handleUpload"
:delete-method="handleFileDelete"
+ @pagination="paginationSearch"
title="闄勪欢鍒楄〃" />
</div>
</template>
@@ -514,12 +517,22 @@
const fileListRef = ref(null);
const fileListDialogVisible = ref(false);
const currentFileRow = ref(null);
+ const filePagination = ref({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
const downLoadFile = row => {
currentFileRow.value = row;
- fileListPage({ safeCertificationId: row.id }).then(res => {
+ fileListPage({
+ safeCertificationId: row.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
+ }).then(res => {
if (fileListRef.value) {
fileListRef.value.open(res.data.records);
}
+ filePagination.value.total = res.data.total || 0;
});
};
const currentFactoryName = ref("");
@@ -603,6 +616,8 @@
// 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
const listRes = await fileListPage({
safeCertificationId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
});
if (listRes.code === 200 && fileListRef.value) {
const fileList = (listRes.data?.records || []).map(item => ({
@@ -612,6 +627,7 @@
...item,
}));
fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
}
// 杩斿洖鏂版枃浠朵俊鎭�
resolve({
@@ -639,6 +655,26 @@
input.click();
});
};
+ const paginationSearch = async (page, size) => {
+ filePagination.value.current = page;
+ filePagination.value.size = size;
+ const listRes = await fileListPage({
+ safeCertificationId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
+ });
+ if (listRes.code === 200) {
+ const fileList = (listRes.data?.records || []).map(item => ({
+ name: item.name,
+ url: item.url,
+ id: item.id,
+ ...item,
+ }));
+
+ fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
+ }
+ };
// 鍒犻櫎闄勪欢
const handleFileDelete = async row => {
try {
@@ -649,6 +685,8 @@
if (currentFileRow.value && fileListRef.value) {
const listRes = await fileListPage({
safeCertificationId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
});
if (listRes.code === 200) {
const fileList = (listRes.data?.records || []).map(item => ({
@@ -658,6 +696,7 @@
...item,
}));
fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
}
}
return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
diff --git a/src/views/safeProduction/safetyTrainingAssessment/index.vue b/src/views/safeProduction/safetyTrainingAssessment/index.vue
index 8719673..cbc35b8 100644
--- a/src/views/safeProduction/safetyTrainingAssessment/index.vue
+++ b/src/views/safeProduction/safetyTrainingAssessment/index.vue
@@ -181,9 +181,9 @@
</el-form>
<template #footer>
<span class="dialog-footer">
- <el-button @click="dialogVisible = false">鍙栨秷</el-button>
<el-button type="primary"
@click="submitForm">纭畾</el-button>
+ <el-button @click="dialogVisible = false">鍙栨秷</el-button>
</span>
</template>
</el-dialog>
@@ -363,8 +363,11 @@
v-model="fileListDialogVisible"
:show-upload-button="true"
:show-delete-button="true"
+ :is-show-pagination="true"
+ :page="filePagination"
:upload-method="handleUpload"
:delete-method="handleFileDelete"
+ @pagination="paginationSearch"
title="闄勪欢鍒楄〃" />
</div>
</template>
@@ -626,7 +629,7 @@
{
name: "缁撴灉鏄庣粏",
type: "text",
- // disabled: row => row.state !== 2,
+ disabled: row => row.state == 0,
clickFun: row => {
viewResultDetail(row);
},
@@ -790,9 +793,14 @@
const currentFileRow = ref(null);
const downLoadFile = row => {
currentFileRow.value = row;
- safeTrainingFileListPage({ safeTrainingId: row.id }).then(res => {
+ safeTrainingFileListPage({
+ safeTrainingId: row.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
+ }).then(res => {
if (fileListRef.value) {
fileListRef.value.open(res.data.records);
+ filePagination.value.total = res.data?.total || 0;
}
});
};
@@ -844,6 +852,8 @@
// 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
const listRes = await safeTrainingFileListPage({
safeTrainingId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
});
if (listRes.code === 200 && fileListRef.value) {
const fileList = (listRes.data?.records || []).map(item => ({
@@ -853,6 +863,7 @@
...item,
}));
fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
}
// 杩斿洖鏂版枃浠朵俊鎭�
resolve({
@@ -880,6 +891,31 @@
input.click();
});
};
+ const filePagination = ref({
+ current: 1,
+ size: 10,
+ total: 0,
+ });
+ const paginationSearch = async (page, size) => {
+ filePagination.value.current = page;
+ filePagination.value.size = size;
+ const listRes = await safeTrainingFileListPage({
+ safeTrainingId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
+ });
+ if (listRes.code === 200) {
+ const fileList = (listRes.data?.records || []).map(item => ({
+ name: item.name,
+ url: item.url,
+ id: item.id,
+ ...item,
+ }));
+
+ fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
+ }
+ };
// 鍒犻櫎闄勪欢
const handleFileDelete = async row => {
try {
@@ -890,6 +926,8 @@
if (currentFileRow.value && fileListRef.value) {
const listRes = await safeTrainingFileListPage({
safeTrainingId: currentFileRow.value.id,
+ current: filePagination.value.current,
+ size: filePagination.value.size,
});
if (listRes.code === 200) {
const fileList = (listRes.data?.records || []).map(item => ({
@@ -899,6 +937,7 @@
...item,
}));
fileListRef.value.setList(fileList);
+ filePagination.value.total = listRes.data?.total || 0;
}
}
return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
@@ -971,7 +1010,7 @@
const pagination = obj => {
page.value.current = obj.page;
page.value.size = obj.limit;
- handleQuery();
+ getList();
};
// 閫夋嫨鍙樺寲澶勭悊
diff --git a/src/views/salesManagement/receiptPayment/index.vue b/src/views/salesManagement/receiptPayment/index.vue
index 7b03d7d..6ded92b 100644
--- a/src/views/salesManagement/receiptPayment/index.vue
+++ b/src/views/salesManagement/receiptPayment/index.vue
@@ -335,7 +335,7 @@
const getStatusTagType = (statusName = '') => {
const normalized = statusName.trim();
if (!normalized) return 'info';
- return normalized === '鏈畬鎴愬洖娆�' ? 'danger' : 'success';
+ return normalized === '鏈畬鎴愪粯娆�' ? 'danger' : 'success';
};
// 鏌ヨ鍒楄〃
/** 鎼滅储鎸夐挳鎿嶄綔 */
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index c126b6a..dfbbaec 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -118,6 +118,7 @@
<el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
<el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
<el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="120" show-overflow-tooltip />
+ <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
<el-table-column fixed="right" label="鎿嶄綔" min-width="100" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="openForm('edit', scope.row)">缂栬緫</el-button>
@@ -221,7 +222,8 @@
</el-row>
<el-table :data="productData" border @selection-change="productSelected" show-summary
:summary-method="summarizeMainTable">
- <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
+ <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
+ :selectable="(row) => !isProductShipped(row)" />
<el-table-column align="center" label="搴忓彿" type="index" width="60" />
<el-table-column label="浜у搧澶х被" prop="productCategory" />
<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
@@ -233,20 +235,22 @@
<el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
<el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center" v-if="operationType !== 'view'">
<template #default="scope">
- <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">缂栬緫</el-button>
+ <el-button link type="primary" size="small"
+ :disabled="isProductShipped(scope.row)"
+ @click="openProductForm('edit', scope.row,scope.$index)">缂栬緫</el-button>
</template>
</el-table-column>
</el-table>
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="澶囨敞路锛�" prop="remark">
- <el-input v-model="form.remark" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
+ <el-form-item label="澶囨敞锛�" prop="remarks">
+ <el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">
+ <el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
<el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
:headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
:on-success="handleUploadSuccess" :on-remove="handleRemove">
@@ -1001,8 +1005,10 @@
// 娣诲姞琛ㄨ绫诲悕鏂规硶
const tableRowClassName = ({ row }) => {
- const diff = row.deliveryDaysDiff;
+ if (!row.deliveryDate) return '';
+ if (row.isFh) return '';
+ const diff = row.deliveryDaysDiff;
if (diff === 15) {
return 'yellow';
} else if (diff === 10) {
@@ -1222,6 +1228,12 @@
const productIndex = ref(0);
// 鎵撳紑浜у搧寮规
const openProductForm = async (type, row, index) => {
+ // 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
+ if (type === "edit" && isProductShipped(row)) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+ return;
+ }
+
productOperationType.value = type;
productForm.value = {};
proxy.resetForm("productFormRef");
@@ -1288,6 +1300,14 @@
proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
return;
}
+
+ // 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
+ const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
+ if (shippedProducts.length > 0) {
+ proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
+ return;
+ }
+
if (operationType.value === "add") {
productSelectedRows.value.forEach((selectedRow) => {
const index = productData.value.findIndex(
@@ -1362,6 +1382,14 @@
proxy.$modal.msg("宸插彇娑�");
});
};
+/** 鍒ゆ柇鍗曚釜浜у搧鏄惁宸插彂璐э紙鏍规嵁shippingStatus鍒ゆ柇锛屽凡鍙戣揣鎴栧鏍搁�氳繃涓嶅彲缂栬緫鍜屽垹闄わ級 */
+const isProductShipped = (product) => {
+ if (!product) return false;
+ const status = String(product.shippingStatus || "").trim();
+ // 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
+ return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
+};
+
/** 鍒ゆ柇閿�鍞鍗曚笅鏄惁瀛樺湪宸插彂璐�/鍙戣揣瀹屾垚鐨勪骇鍝侊紙涓嶅彲鍒犻櫎锛� */
const hasShippedProducts = (products) => {
if (!products || !products.length) return false;
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index d6f6c80..666360a 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -231,43 +231,52 @@
<el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
<el-table-column prop="product" label="浜у搧鍚嶇О" width="200">
<template #default="scope">
- <el-tree-select
- v-model="scope.row.productId"
- placeholder="璇烽�夋嫨"
- clearable
- check-strictly
- @change="getModels($event, scope.row)"
- :data="productOptions"
- :render-after-expand="false"
- style="width: 100%"
- />
+ <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
+ <el-tree-select
+ v-model="scope.row.productId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ check-strictly
+ @change="getModels($event, scope.row)"
+ :data="productOptions"
+ :render-after-expand="false"
+ style="width: 100%"
+ />
+ </el-form-item>
</template>
</el-table-column>
- <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="150">
+ <el-table-column prop="specification" label="瑙勬牸鍨嬪彿" width="200">
<template #default="scope">
- <el-select
- v-model="scope.row.specificationId"
- placeholder="璇烽�夋嫨"
- clearable
- @change="getProductModel($event, scope.row)"
- >
- <el-option
- v-for="item in scope.row.modelOptions || []"
- :key="item.id"
- :label="item.model"
- :value="item.id"
- />
- </el-select>
+ <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
+ <el-select
+ v-model="scope.row.specificationId"
+ placeholder="璇烽�夋嫨"
+ clearable
+ @change="getProductModel($event, scope.row)"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in scope.row.modelOptions || []"
+ :key="item.id"
+ :label="item.model"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
</template>
</el-table-column>
<el-table-column prop="unit" label="鍗曚綅">
<template #default="scope">
- <el-input v-model="scope.row.unit" placeholder="鍗曚綅" />
+ <el-form-item :prop="`products.${scope.$index}.unit`" class="product-table-form-item">
+ <el-input v-model="scope.row.unit" placeholder="鍗曚綅" clearable/>
+ </el-form-item>
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="鍗曚环">
<template #default="scope">
- <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
+ <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
+ <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
+ </el-form-item>
</template>
</el-table-column>
<el-table-column label="鎿嶄綔" width="80" align="center">
@@ -393,13 +402,30 @@
totalAmount: 0
})
-const rules = {
+const baseRules = {
customer: [{ required: true, message: '璇烽�夋嫨瀹㈡埛', trigger: 'change' }],
salesperson: [{ required: true, message: '璇烽�夋嫨涓氬姟鍛�', trigger: 'change' }],
quotationDate: [{ required: true, message: '璇烽�夋嫨鎶ヤ环鏃ユ湡', trigger: 'change' }],
validDate: [{ required: true, message: '璇烽�夋嫨鏈夋晥鏈�', trigger: 'change' }],
paymentMethod: [{ required: true, message: '璇疯緭鍏ヤ粯娆炬柟寮�', trigger: 'blur' }]
}
+
+const productRowRules = {
+ productId: [{ required: true, message: '璇烽�夋嫨浜у搧鍚嶇О', trigger: 'change' }],
+ specificationId: [{ required: true, message: '璇烽�夋嫨瑙勬牸鍨嬪彿', trigger: 'change' }],
+ unit: [{ required: true, message: '璇峰~鍐欏崟浣�', trigger: 'blur' }],
+ unitPrice: [{ required: true, message: '璇峰~鍐欏崟浠�', trigger: 'change' }]
+}
+const rules = computed(() => {
+ const r = { ...baseRules }
+ ;(form.products || []).forEach((_, i) => {
+ r[`products.${i}.productId`] = productRowRules.productId
+ r[`products.${i}.specificationId`] = productRowRules.specificationId
+ r[`products.${i}.unit`] = productRowRules.unit
+ r[`products.${i}.unitPrice`] = productRowRules.unitPrice
+ })
+ return r
+})
const userList = ref([]);
const customerOption = ref([]);
@@ -774,7 +800,7 @@
ElMessage.warning('璇疯嚦灏戞坊鍔犱竴涓骇鍝�')
return
}
-
+
// 瀹℃壒浜哄繀濉牎楠�
const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
if (hasEmptyApprover) {
@@ -956,6 +982,17 @@
padding: 8px 0;
}
+.product-table-form-item {
+ margin-bottom: 0;
+ :deep(.el-form-item__content) {
+ margin-left: 0 !important;
+ }
+ :deep(.el-form-item__label) {
+ width: auto;
+ min-width: auto;
+ }
+}
+
.approver-nodes-container {
display: flex;
flex-wrap: wrap;
--
Gitblit v1.9.3