From b0c3dbefea78b106b7c680597361ea37930eaa0d Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期五, 30 一月 2026 16:12:27 +0800
Subject: [PATCH] Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-management into dev_New
---
src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue | 34 ++
src/views/equipmentManagement/measurementEquipment/index.vue | 6
src/views/equipmentManagement/spareParts/index.vue | 48 ++++
src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue | 26 ++
src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue | 34 ++
src/components/Echarts/echarts.vue | 65 ++++++
src/views/equipmentManagement/measurementEquipment/components/formDia.vue | 64 ++++++
src/hooks/useChartBackground.js | 133 +++++++++++++
src/views/reportAnalysis/productionAnalysis/components/left-top.vue | 34 ++
src/views/safeProduction/safetyTrainingAssessment/index.vue | 28 -
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue | 55 +++++
src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue | 35 ++
12 files changed, 504 insertions(+), 58 deletions(-)
diff --git a/src/components/Echarts/echarts.vue b/src/components/Echarts/echarts.vue
index a386bb7..0e07163 100644
--- a/src/components/Echarts/echarts.vue
+++ b/src/components/Echarts/echarts.vue
@@ -6,8 +6,10 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, watchEffect } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import * as echarts from 'echarts'
+
+const emit = defineEmits(['finished'])
// Props
const props = defineProps({
@@ -91,6 +93,47 @@
// Refs
const chartRef = ref(null)
let chartInstance = null
+let finishedHandler = null
+let initTimer = null
+let initAttempts = 0
+
+function clearInitTimer() {
+ if (initTimer) {
+ clearTimeout(initTimer)
+ initTimer = null
+ }
+}
+
+function isContainerReady() {
+ const el = chartRef.value
+ if (!el) return false
+ // offsetWidth/offsetHeight 鏇磋创杩戠湡瀹炲竷灞�锛堜负 0 寰�寰�浠h〃杩樻病甯冨眬/涓嶅彲瑙侊級
+ return el.offsetWidth > 0 && el.offsetHeight > 0
+}
+
+function initChartWhenReady() {
+ clearInitTimer()
+ initAttempts += 1
+
+ if (!isContainerReady()) {
+ // 绛夊鍣ㄧ湡姝f湁灏哄锛堥伩鍏嶉灞忓垵濮嬪寲鍋忕Щ/绌虹櫧锛岀儹鏇存柊鍚庢墠姝e父鐨勬儏鍐碉級
+ // 鏈�澶氶噸璇曠害 3 绉掞紝閬垮厤鏃犻檺寰幆
+ if (initAttempts < 60) {
+ initTimer = setTimeout(initChartWhenReady, 50)
+ }
+ return
+ }
+
+ if (chartInstance) return
+ chartInstance = echarts.init(chartRef.value)
+ finishedHandler = () => emit('finished')
+ chartInstance.on('finished', finishedHandler)
+ renderChart()
+ // setOption 鍚庤ˉ涓�娆� resize锛岀‘淇濋灞忓昂瀵告纭�
+ nextTick(() => {
+ if (chartInstance) chartInstance.resize()
+ })
+}
// Methods
function generateChart(option) {
@@ -139,26 +182,38 @@
// Lifecycle hooks
onMounted(() => {
- chartInstance = echarts.init(chartRef.value)
- renderChart()
+ initAttempts = 0
+ initChartWhenReady()
window.addEventListener('resize', windowResizeListener)
})
onBeforeUnmount(() => {
if (chartInstance) {
window.removeEventListener('resize', windowResizeListener)
+ if (finishedHandler) {
+ chartInstance.off('finished', finishedHandler)
+ finishedHandler = null
+ }
chartInstance.dispose()
chartInstance = null
}
+ clearInitTimer()
})
// Watch all reactive props that affect the chart
watch(
() => [props.xAxis, props.yAxis, props.series, props.legend, props.tooltip, props.visualMap],
() => {
- if (chartInstance) {
- renderChart()
+ // 濡傛灉棣栧睆杩樻病鍒濆鍖栨垚鍔燂紝绛夊緟瀹瑰櫒 ready 鍚庡啀娓叉煋
+ if (!chartInstance) {
+ initChartWhenReady()
+ return
}
+ renderChart()
+ // 鏁版嵁鍙樺寲鍚庤ˉ涓�娆� resize锛岄伩鍏嶅竷灞�鍙樺寲瀵艰嚧鐨勫亸绉�
+ nextTick(() => {
+ if (chartInstance) chartInstance.resize()
+ })
},
{ deep: true, immediate: true }
)
diff --git a/src/hooks/useChartBackground.js b/src/hooks/useChartBackground.js
new file mode 100644
index 0000000..d69a1fb
--- /dev/null
+++ b/src/hooks/useChartBackground.js
@@ -0,0 +1,133 @@
+import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
+
+/**
+ * 鍥捐〃鑳屾櫙浣嶇疆璋冩暣 composable
+ * @param {Object} options 閰嶇疆閫夐」
+ * @param {Ref} options.wrapperRef - 鍥捐〃瀹瑰櫒鐨� ref
+ * @param {Ref} options.backgroundRef - 鑳屾櫙鍏冪礌鐨� ref
+ * @param {String} options.left - 鑳屾櫙 left 浣嶇疆锛屽 '25%' 鎴� '50%'锛岄粯璁� '50%'
+ * @param {String} options.top - 鑳屾櫙 top 浣嶇疆锛屽 '50%'锛岄粯璁� '50%'
+ * @param {String} options.offsetX - X 杞村亸绉诲�硷紝濡� '-51.5%' 鎴� '-50%'锛岄粯璁� '-50%'
+ * @param {String} options.offsetY - Y 杞村亸绉诲�硷紝濡� '-39%' 鎴� '-50%'锛岄粯璁� '-50%'
+ * @param {Ref} options.watchData - 鍙�夛紝鐩戝惉鐨勬暟鎹彉鍖栵紝鏁版嵁鍙樺寲鏃堕噸鏂拌皟鏁翠綅缃�
+ * @returns {Function} adjustBackgroundPosition - 鎵嬪姩璋冩暣鑳屾櫙浣嶇疆鐨勬柟娉�
+ */
+export function useChartBackground(options = {}) {
+ const {
+ wrapperRef,
+ backgroundRef,
+ left = '50%',
+ top = '50%',
+ offsetX = '-50%',
+ offsetY = '-50%',
+ watchData = null
+ } = options
+
+ let resizeObserver = null
+ let intersectionObserver = null
+ let retryTimers = []
+
+ const clearRetryTimers = () => {
+ if (!retryTimers.length) return
+ retryTimers.forEach((t) => clearTimeout(t))
+ retryTimers = []
+ }
+
+ // 璋冩暣鑳屾櫙浣嶇疆
+ const adjustBackgroundPosition = () => {
+ nextTick(() => {
+ if (!wrapperRef?.value || !backgroundRef?.value) {
+ return
+ }
+
+ // 鍒濆鍖栭樁娈电粡甯稿嚭鐜帮細瀹瑰櫒灏氭湭鍙/灏哄涓� 0锛堥潪鍏ㄥ睆銆乼ab銆佸姩鐢荤瓑锛�
+ // 杩欑鎯呭喌涓嬪厛涓嶅榻愶紝绛� ResizeObserver / IntersectionObserver 鍐嶈Е鍙�
+ const rect = wrapperRef.value.getBoundingClientRect()
+ if (!rect.width || !rect.height) return
+
+ const background = backgroundRef.value
+
+ // 浣跨敤鐧惧垎姣斿畾浣� + transform 寰皟锛堣繖鏄渶鍙潬鐨勬柟寮忥級
+ background.style.left = left
+ background.style.top = top
+ background.style.transform = `translate(${offsetX}, ${offsetY})`
+ })
+ }
+
+ // 鍒濆鍖栭樁娈靛娆♀�滆ˉ鍋垮榻愨�濓紝瑕嗙洊 Echarts 棣栨娓叉煋/鍔ㄧ敾閫犳垚鐨勫欢杩熷竷灞�
+ const scheduleKickAlign = () => {
+ clearRetryTimers()
+ ;[0, 60, 180, 360, 800].forEach((ms) => {
+ retryTimers.push(
+ setTimeout(() => {
+ adjustBackgroundPosition()
+ }, ms)
+ )
+ })
+ }
+
+ // 绐楀彛 resize 澶勭悊
+ const resizeHandler = () => {
+ adjustBackgroundPosition()
+ }
+
+ // 濡傛灉鎻愪緵浜� watchData锛岀洃鍚暟鎹彉鍖栵紙闇�瑕佸湪 setup 闃舵鍒涘缓锛�
+ if (watchData) {
+ watch(watchData, () => {
+ adjustBackgroundPosition()
+ }, { deep: true })
+ }
+
+ // 鍒濆鍖�
+ const init = () => {
+ // 鐩戝惉绐楀彛 resize
+ window.addEventListener('resize', resizeHandler)
+
+ // 浣跨敤 ResizeObserver 鐩戝惉瀹瑰櫒灏哄鍙樺寲
+ nextTick(() => {
+ if (wrapperRef?.value && window.ResizeObserver) {
+ resizeObserver = new ResizeObserver(() => {
+ adjustBackgroundPosition()
+ })
+ resizeObserver.observe(wrapperRef.value)
+ }
+
+ // 鐩戝惉鈥滀粠涓嶅彲瑙佸埌鍙鈥濓紝瑙e喅鍒濆鍖栨椂鏈榻愪絾鐑洿鏂板張姝e父鐨勯棶棰�
+ if (wrapperRef?.value && window.IntersectionObserver) {
+ intersectionObserver = new IntersectionObserver(
+ (entries) => {
+ const entry = entries?.[0]
+ if (entry?.isIntersecting) {
+ scheduleKickAlign()
+ }
+ },
+ { threshold: 0.01 }
+ )
+ intersectionObserver.observe(wrapperRef.value)
+ }
+
+ // 鍒濆鍖栧娆¤ˉ鍋垮榻愶紝纭繚鍥捐〃娓叉煋瀹屾垚
+ scheduleKickAlign()
+ })
+ }
+
+ // 娓呯悊
+ const cleanup = () => {
+ window.removeEventListener('resize', resizeHandler)
+ clearRetryTimers()
+ if (resizeObserver) {
+ resizeObserver.disconnect()
+ resizeObserver = null
+ }
+ if (intersectionObserver) {
+ intersectionObserver.disconnect()
+ intersectionObserver = null
+ }
+ }
+
+ return {
+ adjustBackgroundPosition,
+ init,
+ cleanup
+ }
+}
diff --git a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
index b5c1ea1..b7fa07e 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
+++ b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -51,11 +51,14 @@
</el-form-item>
</el-col>
<el-col :span="12">
- <el-form-item label="鏈夋晥鏈燂細" prop="valid">
+ <el-form-item label="鏈夋晥鏃ユ湡(澶�)锛�" prop="valid">
<el-input
v-model="form.valid"
- placeholder="璇疯緭鍏�"
+ type="number"
+ placeholder="璇疯緭鍏ユ湁鏁堟湡澶╂暟"
clearable
+ :min="1"
+ @input="handleValidInput"
>
<template #append>鏃�</template>
</el-input>
@@ -152,7 +155,32 @@
rules: {
code: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
name: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
- valid: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+ valid: [
+ {required: true, message: "璇疯緭鍏�", trigger: "blur"},
+ {
+ validator: (rule, value, callback) => {
+ if (value === '' || value === null || value === undefined) {
+ callback();
+ return;
+ }
+ const numValue = Number(value);
+ if (isNaN(numValue)) {
+ callback(new Error('璇疯緭鍏ユ湁鏁堢殑鏁板瓧'));
+ return;
+ }
+ if (numValue <= 0) {
+ callback(new Error('鍙兘杈撳叆姝f暟'));
+ return;
+ }
+ if (!Number.isInteger(numValue)) {
+ callback(new Error('璇疯緭鍏ユ暣鏁�'));
+ return;
+ }
+ callback();
+ },
+ trigger: 'blur'
+ }
+ ],
recordDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
userId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
entryDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
@@ -233,6 +261,27 @@
}
}
+// 澶勭悊鏈夋晥鏃ユ湡杈撳叆锛屽彧鍏佽姝f暣鏁�
+const handleValidInput = (value) => {
+ if (value === '' || value === null || value === undefined) {
+ form.value.valid = '';
+ return;
+ }
+ // 杞崲涓哄瓧绗︿覆骞剁Щ闄ゆ墍鏈夐潪鏁板瓧瀛楃锛堝寘鎷礋鍙枫�佸皬鏁扮偣绛夛級
+ const numStr = String(value).replace(/[^0-9]/g, '');
+ if (numStr === '') {
+ form.value.valid = '';
+ return;
+ }
+ const numValue = parseInt(numStr, 10);
+ // 纭繚鏄鏁存暟锛堝ぇ浜�0锛�
+ if (numValue > 0 && !isNaN(numValue)) {
+ form.value.valid = numValue;
+ } else {
+ form.value.valid = '';
+ }
+}
+
const submitForm = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
diff --git a/src/views/equipmentManagement/measurementEquipment/components/formDia.vue b/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
index d2a1969..6b7feec 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
+++ b/src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -15,11 +15,20 @@
ref="formRef"
>
<el-row :gutter="30">
- <el-col :span="24">
+ <el-col :span="12">
<el-form-item label="鍑哄巶缂栧彿锛�" prop="code">
<el-input
v-model="form.code"
placeholder="璇疯緭鍏�"
+ clearable
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="璁¢噺鍣ㄥ叿鍚嶇О锛�" prop="name">
+ <el-input
+ v-model="form.name"
+ placeholder="璇疯緭鍏ヨ閲忓櫒鍏峰悕绉�"
clearable
/>
</el-form-item>
@@ -74,8 +83,11 @@
<el-form-item label="鏈夋晥鏃ユ湡(澶�)锛�" prop="valid">
<el-input
v-model="form.valid"
+ type="number"
placeholder="璇疯緭鍏ユ湁鏁堟湡澶╂暟"
clearable
+ :min="1"
+ @input="handleValidInput"
>
<template #append>鏃�</template>
</el-input>
@@ -171,6 +183,7 @@
const data = reactive({
form: {
code: "",
+ name: "",
instationLocation: "",
mostDate:"",
model: "",
@@ -184,6 +197,7 @@
},
rules: {
code: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+ name: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
model: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
validDate: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
nextDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
@@ -192,7 +206,32 @@
instationLocation: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
mostDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
cycle: [{required: true, message: "璇烽�夋嫨", trigger: "blur"}],
- valid: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+ valid: [
+ {required: true, message: "璇疯緭鍏�", trigger: "blur"},
+ {
+ validator: (rule, value, callback) => {
+ if (value === '' || value === null || value === undefined) {
+ callback();
+ return;
+ }
+ const numValue = Number(value);
+ if (isNaN(numValue)) {
+ callback(new Error('璇疯緭鍏ユ湁鏁堢殑鏁板瓧'));
+ return;
+ }
+ if (numValue <= 0) {
+ callback(new Error('鍙兘杈撳叆姝f暟'));
+ return;
+ }
+ if (!Number.isInteger(numValue)) {
+ callback(new Error('璇疯緭鍏ユ暣鏁�'));
+ return;
+ }
+ callback();
+ },
+ trigger: 'blur'
+ }
+ ],
unit: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
}
})
@@ -254,6 +293,27 @@
}
}
+// 澶勭悊鏈夋晥鏃ユ湡杈撳叆锛屽彧鍏佽姝f暣鏁�
+const handleValidInput = (value) => {
+ if (value === '' || value === null || value === undefined) {
+ form.value.valid = '';
+ return;
+ }
+ // 杞崲涓哄瓧绗︿覆骞剁Щ闄ゆ墍鏈夐潪鏁板瓧瀛楃锛堝寘鎷礋鍙枫�佸皬鏁扮偣绛夛級
+ const numStr = String(value).replace(/[^0-9]/g, '');
+ if (numStr === '') {
+ form.value.valid = '';
+ return;
+ }
+ const numValue = parseInt(numStr, 10);
+ // 纭繚鏄鏁存暟锛堝ぇ浜�0锛�
+ if (numValue > 0 && !isNaN(numValue)) {
+ form.value.valid = numValue;
+ } else {
+ form.value.valid = '';
+ }
+}
+
const submitForm = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
diff --git a/src/views/equipmentManagement/measurementEquipment/index.vue b/src/views/equipmentManagement/measurementEquipment/index.vue
index 4572f22..46ca100 100644
--- a/src/views/equipmentManagement/measurementEquipment/index.vue
+++ b/src/views/equipmentManagement/measurementEquipment/index.vue
@@ -82,6 +82,12 @@
minWidth:150,
align:"center"
},
+ {
+ label: "璁¢噺鍣ㄥ叿鍚嶇О",
+ prop: "name",
+ width: '160px',
+ align: "center",
+ },
{
label: "瀹夎浣嶇疆",
prop: "instationLocation",
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index eb0bdd5..0b0dae2 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -38,7 +38,7 @@
</el-table-column>
<el-table-column prop="price" label="浠锋牸" width="140"></el-table-column>
<el-table-column prop="quantity" label="鏁伴噺" width="140"></el-table-column>
- <el-table-column prop="description" label="鎻忚堪" width="150"></el-table-column>
+ <el-table-column prop="description" label="鎻忚堪"></el-table-column>
<el-table-column label="鎿嶄綔" width="150" fixed="right" align="center">
<template #default="{ row }">
<el-button
@@ -60,6 +60,18 @@
</template>
</el-table-column>
</el-table>
+ <!-- 鍒嗛〉缁勪欢 -->
+ <div class="pagination-container">
+ <el-pagination
+ v-model:current-page="pagination.current"
+ v-model:page-size="pagination.size"
+ :page-sizes="[10, 20, 50, 100]"
+ :total="pagination.total"
+ layout="total, sizes, prev, pager, next, jumper"
+ @size-change="handleSizeChange"
+ @current-change="handleCurrentChange"
+ />
+ </div>
</div>
<el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
@@ -147,6 +159,12 @@
const queryParams = reactive({
name: ''
});
+// 鍒嗛〉鍙傛暟
+const pagination = reactive({
+ current: 1,
+ size: 10,
+ total: 0
+});
// 琛ㄥ崟鏁版嵁
const form = reactive({
id:'',
@@ -215,7 +233,10 @@
const fetchListData = async () => {
loading.value = true;
try {
- const params = {};
+ const params = {
+ current: pagination.current,
+ size: pagination.size
+ };
if (queryParams.name) {
params.name = queryParams.name;
}
@@ -223,6 +244,7 @@
if (res.code === 200) {
renderTableData.value = res.data.records || [];
categories.value = res.data.records || [];
+ pagination.total = res.data.total || 0;
}
} catch (error) {
loading.value = false;
@@ -233,12 +255,27 @@
// 鏌ヨ
const handleQuery = () => {
+ pagination.current = 1;
fetchListData();
}
// 閲嶇疆鏌ヨ
const resetQuery = () => {
queryParams.name = '';
+ pagination.current = 1;
+ fetchListData();
+}
+
+// 鍒嗛〉澶у皬鏀瑰彉
+const handleSizeChange = (size) => {
+ pagination.size = size;
+ pagination.current = 1;
+ fetchListData();
+}
+
+// 褰撳墠椤垫敼鍙�
+const handleCurrentChange = (current) => {
+ pagination.current = current;
fetchListData();
}
@@ -373,6 +410,13 @@
margin-top: unset;
}
+.pagination-container {
+ margin-top: 20px;
+ display: flex;
+ justify-content: flex-end;
+ padding: 16px 0;
+}
+
.el-table__header-wrapper th {
background-color: #f5f7fa;
font-weight: 600;
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
index 7daf096..669c826 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
@@ -3,8 +3,8 @@
<PanelHeader title="閲囪喘鍝佸垎甯�" />
<div class="main-panel panel-item-customers">
<CarouselCards :items="cardItems" :visible-count="3" />
- <div class="pie-chart-wrapper">
- <div class="pie-background"></div>
+ <div class="pie-chart-wrapper" ref="pieWrapperRef">
+ <div class="pie-background" ref="pieBackgroundRef"></div>
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -22,11 +22,15 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
import { rawMaterialPurchaseAmountRatio } from '@/api/viewIndex.js'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
/**
* @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
@@ -164,6 +168,18 @@
textStyle: { color: '#B8C8E0' },
}
+// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉�
+// 鍥捐〃涓績鏄� ['25%', '50%']锛岃儗鏅渶瑕佸榻愬埌杩欎釜浣嶇疆
+const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+ wrapperRef: pieWrapperRef,
+ backgroundRef: pieBackgroundRef,
+ left: '25%', // 鍥捐〃涓績 X 鏄� 25%
+ top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
+ offsetX: '-51.5%', // X 杞村亸绉�
+ offsetY: '-50%', // Y 杞村亸绉�
+ watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+})
+
const fetchData = () => {
rawMaterialPurchaseAmountRatio()
.then((res) => {
@@ -191,6 +207,11 @@
onMounted(() => {
fetchData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
})
</script>
@@ -218,9 +239,6 @@
.pie-background {
position: absolute;
- left: 25%;
- top: 50%;
- transform: translate(-51.5%, -50%);
width: 310px;
height: 310px;
background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
@@ -229,5 +247,9 @@
background-repeat: no-repeat;
z-index: 1;
pointer-events: none;
+ /* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
+ left: 25%;
+ top: 50%;
+ transform: translate(-51.5%, -50%);
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
index 581020d..8fcaa42 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
@@ -3,8 +3,8 @@
<PanelHeader title="閿�鍞搧鍒嗗竷" />
<div class="main-panel panel-item-customers">
<CarouselCards :items="cardItems" :visible-count="3" />
- <div class="pie-chart-wrapper">
- <div class="pie-background"></div>
+ <div class="pie-chart-wrapper" ref="pieWrapperRef">
+ <div class="pie-background" ref="pieBackgroundRef"></div>
<Echarts
ref="echartsRef"
:chartStyle="chartStyle"
@@ -21,11 +21,15 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
import { productSalesAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
import Echarts from '@/components/Echarts/echarts.vue'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
/**
* @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
@@ -137,6 +141,17 @@
const cardItems = ref([])
+// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉曪紙涓庡叾浠栨枃浠朵繚鎸佷竴鑷达級
+const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+ wrapperRef: pieWrapperRef,
+ backgroundRef: pieBackgroundRef,
+ left: '25%', // 鍥捐〃涓績 X 鏄� 25%
+ top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
+ offsetX: '-51.5%', // X 杞村亸绉�
+ offsetY: '-50%', // Y 杞村亸绉�
+ watchData: pieDatas // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+})
+
const fetchData = () => {
productSalesAnalysis()
.then((res) => {
@@ -162,6 +177,11 @@
onMounted(() => {
fetchData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
})
</script>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
index c787fce..5b7e29e 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
@@ -2,8 +2,8 @@
<div>
<PanelHeader title="浜у搧澶х被" />
<div class="panel-item-customers">
- <div class="pie-chart-wrapper">
- <div class="pie-background"></div>
+ <div class="pie-chart-wrapper" ref="pieWrapperRef">
+ <div class="pie-background" ref="pieBackgroundRef"></div>
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -21,10 +21,15 @@
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, onBeforeUnmount } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import { productCategoryDistribution } from '@/api/viewIndex.js'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
+const chart = ref(null)
// 鏁版嵁鍒楄〃锛堟潵鑷帴鍙o級
const dataList = ref([])
@@ -170,6 +175,15 @@
textStyle: { color: '#B8C8E0' },
}
+// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉曪紝鍙嚜瀹氫箟鍋忕Щ鍊�
+const { adjustBackgroundPosition, init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+ wrapperRef: pieWrapperRef,
+ backgroundRef: pieBackgroundRef,
+ offsetX: '-51.5%', // X 杞村亸绉伙紝鍙姩鎬佽皟鏁�
+ offsetY: '-39%', // Y 杞村亸绉伙紝鍙姩鎬佽皟鏁�
+ watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+})
+
const loadData = async () => {
try {
const res = await productCategoryDistribution()
@@ -182,6 +196,8 @@
}))
landLegend.data = dataList.value.map((d) => d.name)
landSeries.value[0].data = dataList.value
+ // 鏁版嵁鍔犺浇瀹屾垚鍚庤皟鏁磋儗鏅綅缃�
+ adjustBackgroundPosition()
} catch (e) {
console.error('鑾峰彇浜у搧澶х被鍒嗗竷澶辫触:', e)
dataList.value = []
@@ -190,8 +206,14 @@
}
}
+
onMounted(() => {
loadData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
})
</script>
@@ -212,9 +234,6 @@
.pie-background {
position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-51.5%, -39%);
width: 360px;
height: 360px;
background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
@@ -223,5 +242,9 @@
background-repeat: no-repeat;
z-index: 1;
pointer-events: none;
+ /* 榛樿灞呬腑锛屼細鍦� JS 涓姩鎬佽皟鏁� */
+ left: 50%;
+ top: 50%;
+ transform: translate(-51.5%, -39%);
}
</style>
diff --git a/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
index 4a20578..3fe95d6 100644
--- a/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/financialAnalysis/components/left-bottom.vue
@@ -10,8 +10,8 @@
/>
</div>
<!-- <CarouselCards :items="cardItems" :visible-count="3" /> -->
- <div class="pie-chart-wrapper">
- <div class="pie-background"></div>
+ <div class="pie-chart-wrapper" ref="pieWrapperRef">
+ <div class="pie-background" ref="pieBackgroundRef"></div>
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -29,11 +29,15 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
import ProductTypeSwitch from './ProductTypeSwitch.vue'
import { expenseCompositionAnalysis } from '@/api/viewIndex.js'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
/**
* @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
@@ -185,6 +189,18 @@
textStyle: { color: '#B8C8E0' },
}
+// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉�
+// 鍥捐〃涓績鏄� ['25%', '50%']锛岃儗鏅渶瑕佸榻愬埌杩欎釜浣嶇疆
+const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+ wrapperRef: pieWrapperRef,
+ backgroundRef: pieBackgroundRef,
+ left: '25%', // 鍥捐〃涓績 X 鏄� 25%
+ top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
+ offsetX: '-51.5%', // X 杞村亸绉�
+ offsetY: '-50%', // Y 杞村亸绉�
+ watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+})
+
const fetchData = () => {
expenseCompositionAnalysis({ type: amountType.value })
.then((res) => {
@@ -216,6 +232,11 @@
onMounted(() => {
fetchData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
})
</script>
@@ -251,9 +272,6 @@
.pie-background {
position: absolute;
- left: 25%;
- top: 50%;
- transform: translate(-51.5%, -50%);
width: 310px;
height: 310px;
background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
@@ -262,5 +280,9 @@
background-repeat: no-repeat;
z-index: 1;
pointer-events: none;
+ /* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
+ left: 25%;
+ top: 50%;
+ transform: translate(-51.5%, -50%);
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
index 07dfb54..0cce7d6 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
@@ -5,8 +5,8 @@
<div class="filters-row">
<DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
</div>
- <div class="pie-chart-wrapper">
- <div class="pie-background"></div>
+ <div class="pie-chart-wrapper" ref="pieWrapperRef">
+ <div class="pie-background" ref="pieBackgroundRef"></div>
<Echarts
ref="echartsRef"
:chartStyle="chartStyle"
@@ -23,11 +23,15 @@
</template>
<script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
import { productSalesAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
import DateTypeSwitch from '@/views/reportAnalysis/financialAnalysis/components/DateTypeSwitch.vue'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
@@ -133,6 +137,18 @@
textStyle: { color: '#B8C8E0' },
}
+// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉�
+// 鍥捐〃涓績鏄� ['25%', '50%']锛岃儗鏅渶瑕佸榻愬埌杩欎釜浣嶇疆
+const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+ wrapperRef: pieWrapperRef,
+ backgroundRef: pieBackgroundRef,
+ left: '25%', // 鍥捐〃涓績 X 鏄� 25%
+ top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
+ offsetX: '-51.5%', // X 杞村亸绉�
+ offsetY: '-50%', // Y 杞村亸绉�
+ watchData: pieDatas // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
+})
+
const fetchData = () => {
productSalesAnalysis()
.then((res) => {
@@ -156,6 +172,11 @@
onMounted(() => {
fetchData()
+ initBackground()
+})
+
+onBeforeUnmount(() => {
+ cleanupBackground()
})
</script>
@@ -190,9 +211,6 @@
.pie-background {
position: absolute;
- left: 25%;
- top: 50%;
- transform: translate(-51.5%, -50%);
width: 310px;
height: 310px;
background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
@@ -201,5 +219,9 @@
background-repeat: no-repeat;
z-index: 1;
pointer-events: none;
+ /* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
+ left: 25%;
+ top: 50%;
+ transform: translate(-51.5%, -50%);
}
</style>
diff --git a/src/views/safeProduction/safetyTrainingAssessment/index.vue b/src/views/safeProduction/safetyTrainingAssessment/index.vue
index 0027c7f..1ab310f 100644
--- a/src/views/safeProduction/safetyTrainingAssessment/index.vue
+++ b/src/views/safeProduction/safetyTrainingAssessment/index.vue
@@ -2,23 +2,14 @@
<div class="app-container">
<div class="search_form">
<div>
- <span class="search_title">璇剧▼缂栧彿锛�</span>
- <el-input v-model="searchForm.courseCode"
- style="width: 240px"
- placeholder="璇疯緭鍏ュ煿璁紪鍙锋悳绱�"
- @change="handleQuery"
- clearable
- :prefix-icon="Search" />
- <span class="search_title ml10">鍩硅鏂瑰紡锛�</span>
- <el-select v-model="searchForm.trainingMode"
- clearable
- @change="handleQuery"
- style="width: 240px">
- <el-option v-for="item in trainingModeOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value" />
- </el-select>
+ <span class="search_title">鍩硅鏃ユ湡锛�</span>
+ <el-date-picker v-model="searchForm.trainingDate"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ @change="handleQuery"
+ type="date"
+ placeholder="璇烽�夋嫨"
+ clearable />
<el-button type="primary"
@click="handleQuery"
style="margin-left: 10px">
@@ -426,8 +417,7 @@
// 鍝嶅簲寮忔暟鎹�
const data = reactive({
searchForm: {
- courseCode: "",
- trainingMode: "",
+ trainingDate: "",
state: 0,
},
tableLoading: false,
--
Gitblit v1.9.3