已修改1个文件
已添加8个文件
已重命名1个文件
已删除1个文件
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request'; |
| | | import { AxiosPromise } from 'axios'; |
| | | import { BatchQuery, BatchVO } from '@/api/qms/batch/types'; |
| | | |
| | | /** |
| | | * æ¥è¯¢è´¨éå¥åº·åº¦ |
| | | * @param query |
| | | * @returns {*} |
| | | */ |
| | | |
| | | export const queryQualityHealth = (): AxiosPromise<any> => { |
| | | return request({ |
| | | url: '/qms/quality/health', |
| | | method: 'get' |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢æ¹æ¬¡ |
| | | export const queryBatchList = (query?: BatchQuery): AxiosPromise<BatchVO[]> => { |
| | | return request({ |
| | | url: '/qms/quality/batch', |
| | | method: 'get', |
| | | params: query |
| | | }); |
| | | }; |
| | | |
| | | export const queryPwbatchList = (): AxiosPromise<any> => { |
| | | return request({ |
| | | url: '/qms/quality/pwbatch', |
| | | method: 'get' |
| | | }); |
| | | }; |
| | | |
| | | export const queryNgrank = (): AxiosPromise<any> => { |
| | | return request({ |
| | | url: '/qms/quality/ngrank', |
| | | method: 'get' |
| | | }); |
| | | }; |
| | | |
| | | |
| | | |
| | | |
| | |
| | | <template> |
| | | <div class="app-container home"> |
| | | <el-row :gutter="20"> |
| | | <el-col :sm="24" :lg="12" style="padding-left: 20px"> |
| | | <h2>å
°å®è½¦é´è´¨é管çç³»ç»</h2> |
| | | <div class="p-5"> |
| | | <div style="display: flex" class="gap-4"> |
| | | |
| | | </el-col> |
| | | <el-card style="width: 100%;flex: 1;" class="mb-4"> |
| | | |
| | | <el-col :sm="24" :lg="12" style="padding-left: 20px"> |
| | | <div style="display: flex"> |
| | | <div> |
| | | <el-statistic :value="batchScore" :suffix="'å'" :precision="1" :value-style="{fontSize: '30px',fontWeight: 'bold'}"> |
| | | <template #title> |
| | | <div style="display: inline-flex; align-items: center;font-size: 18px"> |
| | | 综åè´¨éè¯å |
| | | <el-tooltip effect="dark" content="" placement="top"> |
| | | <el-icon style="margin-left: 4px" :size="12"> |
| | | <Warning /> |
| | | </el-icon> |
| | | </el-tooltip> |
| | | </div> |
| | | </template> |
| | | </el-statistic> |
| | | <div class="statistic-footer"> |
| | | <div class="footer-item"> |
| | | <span>è¾æ¨å¤©</span> |
| | | <span :class="{ 'green': batchScore > 9.4, 'red': batchScore <= 9.4 }"> |
| | | <el-icon v-if="batchScore > 9.4"> |
| | | <CaretTop /> |
| | | </el-icon> |
| | | <el-icon v-else> |
| | | <CaretBottom /> |
| | | </el-icon> |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div style="flex: 1;display: flex;justify-content: center;align-items: center"> |
| | | <el-image style="width: 60px; height: 60px" :src="q1" fit="contain" /> |
| | | </div> |
| | | </div> |
| | | |
| | | </el-col> |
| | | </el-row> |
| | | <el-divider /> |
| | | |
| | | </el-card> |
| | | <el-card style="flex: 1" class="mb-4"> |
| | | |
| | | |
| | | <div style="display: flex"> |
| | | <div> |
| | | <el-statistic :value="qualityRate" :suffix="'%'" :value-style="{fontSize: '30px',fontWeight: 'bold'}"> |
| | | <template #title> |
| | | <div style="display: inline-flex; align-items: center;font-size: 18px"> |
| | | è¯åç |
| | | <el-tooltip effect="dark" content="" placement="top"> |
| | | <el-icon style="margin-left: 4px" :size="12"> |
| | | <Warning /> |
| | | </el-icon> |
| | | </el-tooltip> |
| | | </div> |
| | | </template> |
| | | </el-statistic> |
| | | <div class="statistic-footer"> |
| | | <div class="footer-item"> |
| | | <span>è¾æ¨å¤©</span> |
| | | <span :class="{ 'green': qualityRate >= 95, 'red': qualityRate < 95 }"> |
| | | <el-icon v-if="qualityRate >= 95"> |
| | | <CaretTop /> |
| | | </el-icon> |
| | | <el-icon v-else> |
| | | <CaretBottom /> |
| | | </el-icon> |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div style="flex: 1;display: flex;justify-content: center;align-items: center"> |
| | | <el-image style="width: 60px; height: 60px" :src="q2" fit="contain" /> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | </el-card> |
| | | <el-card style="flex: 1" class="mb-4"> |
| | | |
| | | |
| | | <div style="display: flex"> |
| | | <div> |
| | | <el-statistic :value="checkNum" :suffix="'pcs'" :value-style="{fontSize: '30px',fontWeight: 'bold'}"> |
| | | <template #title> |
| | | <div style="display: inline-flex; align-items: center;font-size: 18px"> |
| | | æ£æµæ°é |
| | | <el-tooltip effect="dark" content="" placement="top"> |
| | | <el-icon style="margin-left: 4px" :size="12"> |
| | | <Warning /> |
| | | </el-icon> |
| | | </el-tooltip> |
| | | </div> |
| | | </template> |
| | | </el-statistic> |
| | | <div class="statistic-footer"> |
| | | <div class="footer-item"> |
| | | <span>è¾æ¨å¤©</span> |
| | | <span :class="{ 'green': batchScore > 1, 'red': batchScore <= 1 }"> |
| | | <el-icon v-if="batchScore > 1"> |
| | | <CaretTop /> |
| | | </el-icon> |
| | | <el-icon v-else> |
| | | <CaretBottom /> |
| | | </el-icon> |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div style="flex: 1;display: flex;justify-content: center;align-items: center"> |
| | | <el-image style="width: 60px; height: 60px" :src="q3" fit="contain" /> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | </el-card> |
| | | <el-card style="flex: 1" class="mb-4"> |
| | | |
| | | |
| | | <div style="display: flex"> |
| | | <div> |
| | | <el-statistic :value="ngCountNum" :suffix="'pcs'" :value-style="{fontSize: '30px',fontWeight: 'bold'}"> |
| | | <template #title> |
| | | <div style="display: inline-flex; align-items: center;font-size: 18px"> |
| | | å¼å¸¸æ°é |
| | | <el-tooltip effect="dark" content="" placement="top"> |
| | | <el-icon style="margin-left: 4px" :size="12"> |
| | | <Warning /> |
| | | </el-icon> |
| | | </el-tooltip> |
| | | </div> |
| | | </template> |
| | | </el-statistic> |
| | | <div class="statistic-footer"> |
| | | <div class="footer-item"> |
| | | <span>è¾æ¨å¤©</span> |
| | | <span :class="{ 'green': ngCountNum < 50, 'red': ngCountNum >= 50 }"> |
| | | <el-icon v-if="ngCountNum >= 50"> |
| | | <CaretTop /> |
| | | </el-icon> |
| | | <el-icon v-else> |
| | | <CaretBottom /> |
| | | </el-icon> |
| | | </span> |
| | | |
| | | <span style="margin-left: 10px">颿µå¼å¸¸ç:</span> |
| | | <span class="red" v-if="qualityRate > 80"> {{(((100-qualityRate)/ 3)+0.1).toFixed(1) }}% </span> |
| | | <span v-else > 3.7%</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div style="flex: 1;display: flex;justify-content: center;align-items: center"> |
| | | <el-image style="width: 60px; height: 60px" :src="q4" fit="contain" /> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | <!-- 设å¤å¥åº·ç¶æå¯è§å --> |
| | | <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> |
| | | <el-card shadow="hover" class="h-420" > |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å¥åº·åº¦è¯å</span> |
| | | </div> |
| | | </template> |
| | | <div ref="healthChartRef" class="h-400 w-full"></div> |
| | | </el-card> |
| | | |
| | | <el-card shadow="hover" class="h-420"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è´¨éé¢è¦ä¿¡æ¯</span> |
| | | </div> |
| | | </template> |
| | | <!-- <div class="grid grid-cols-2 gap-2">--> |
| | | <!-- <div class="p-2 bg-blue-50 rounded">--> |
| | | <!-- <p class="text-sm text-gray-600">é½çº³ä¿æ¤çµå(é»)</p>--> |
| | | <!-- <p class="text-xl">39.00 V ⤠result ⤠49.00 V</p>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="p-2 bg-yellow-50 rounded">--> |
| | | <!-- <p class="text-sm text-gray-600">è·ç¦»</p>--> |
| | | <!-- <p class="text-xl">4.75 mm ⤠result ⤠5.15 mm</p>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="p-2 bg-red-50 rounded">--> |
| | | <!-- <p class="text-sm text-gray-600">åé(é»)1</p>--> |
| | | <!-- <p class="text-xl">0.30 V ⤠result ⤠3.00 V</p>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="p-2 bg-green-50 rounded">--> |
| | | <!-- <p class="text-sm text-gray-600">åå·®</p>--> |
| | | <!-- <p class="text-xl">0.05 mm ⤠result ⤠1.00 mm</p>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="p-2 bg-blue-50 rounded">--> |
| | | <!-- <p class="text-sm text-gray-600">æ¼çµæµ(é»)1</p>--> |
| | | <!-- <p class="text-xl">result ⤠800.00 uA</p>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="p-2 bg-yellow-50 rounded">--> |
| | | <!-- <p class="text-sm text-gray-600"></p>--> |
| | | <!-- <p class="text-xl font-bold"></p>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | |
| | | <el-table size="large" :data="warnBatchList" style="width: 100%" :border="true" stripe> |
| | | <el-table-column prop="batchTime" label="é¢è¦æ¶é´" /> |
| | | <el-table-column prop="batchCode" label="æ¹æ¬¡å·" width="140" /> |
| | | <el-table-column prop="gw" label="设å¤/å·¥ä½" /> |
| | | <el-table-column prop="yc" label="å¼å¸¸ç±»å" /> |
| | | <el-table-column prop="cd" label="严éç¨åº¦"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusTagType(row.cd)">{{ row.cd }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | |
| | | |
| | | <el-card shadow="hover" class="mb-6"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ£æµæ¹æ¬¡</span> |
| | | <el-date-picker |
| | | v-model="batchDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | :disabled-date="disabledDate" |
| | | :shortcuts="shortcuts" |
| | | @change="changeBatchDate" |
| | | /> |
| | | </div> |
| | | </template> |
| | | <div class="grid grid-cols-1 gap-4"> |
| | | <!-- è¿éå°æ¾ç½®é¢è¦è¡¨æ ¼ --> |
| | | <el-table v-loading="loading" :data="batchList" style="width: 100%" :border="true" stripe> |
| | | <el-table-column prop="batchCode" label="æ¹æ¬¡å·" width="180" /> |
| | | <el-table-column prop="prodModel" label="产ååå·" width="180" /> |
| | | <el-table-column prop="num" label="æ£æµæ°é" width="120" /> |
| | | <el-table-column prop="okNum" label="è¯åæ°é" width="120" /> |
| | | <el-table-column prop="ngNum" label="ä¸è¯æ°é" width="120" /> |
| | | <el-table-column prop="lpv" label="è¯åç" width="120"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="goodProductsRate(row) >= 95 ? 'success' : 'warning'"> {{ goodProductsRate(row) }} % </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="whjy" label="ç»´æ¤å»ºè®®"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="goodProductsRate(row) >= 95 ? 'success' : 'warning'"> |
| | | {{ goodProductsRate(row) >= 95 ? 'è¯åçææ å·²ä¼å¼ï¼è¯·å
³æ³¨è®¾å¤ç»¼åæç' : 'é¢è¦ï¼æ£æ¥å·¥èºåæ°æ¯å¦ç¬¦åæ åï¼ä¼åå
³é®åæ°' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="180"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" @click="handleDetail(row)">详æ
</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-card> |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | <!-- å¤ä»¶ä¿¡æ¯ --> |
| | | <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> |
| | | <!-- æ¹æ¬¡åæ ¼çè¶å¿ --> |
| | | <el-card shadow="hover" class="h-420"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ¹æ¬¡åæ ¼çè¶å¿</span> |
| | | </div> |
| | | </template> |
| | | <div ref="batchRateTrendRef" class="h-400 w-full"></div> |
| | | </el-card> |
| | | |
| | | |
| | | <el-card shadow="hover" class="h-420"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è¿ä¸å¨NG项TOP5</span> |
| | | </div> |
| | | </template> |
| | | <div ref="ngChartRef" class="h-400 w-full"></div> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup name="Index" lang="ts"> |
| | | const goTarget = (url: string) => { |
| | | window.open(url, '__blank'); |
| | | import { onMounted, ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import * as echarts from 'echarts'; |
| | | |
| | | import q1 from '@/assets/images/quailty/q1.png'; |
| | | import q2 from '@/assets/images/quailty/q2.png'; |
| | | import q3 from '@/assets/images/quailty/q3.png'; |
| | | import q4 from '@/assets/images/quailty/q4.png'; |
| | | |
| | | import { queryQualityHealth, queryBatchList,queryPwbatchList ,queryNgrank} from '@/api/qms/index'; |
| | | import { BatchVO } from '@/api/qms/batch/types'; |
| | | import { listBatch } from '@/api/qms/batch'; |
| | | |
| | | const batchScore = ref(0); |
| | | const qualityRate = ref(0); |
| | | const checkNum = ref(0); |
| | | const ngCountNum = ref(0); |
| | | |
| | | const batchDate = ref(''); |
| | | |
| | | const healthChartRef = ref<HTMLElement | null>(null); |
| | | const batchRateTrendRef = ref<HTMLElement | null>(null); |
| | | const ngChartRef = ref<HTMLElement | null>(null); |
| | | const healthChart = ref(); |
| | | const batchRateChart = ref(); |
| | | const ngChart = ref(); |
| | | const router = useRouter(); |
| | | |
| | | const loading = ref(false); |
| | | const batchList = ref<BatchVO[]>([]); |
| | | const warnBatchList = ref<any>([]); |
| | | |
| | | |
| | | const gwList = [ |
| | | "å¾®æºçµç³»ç»è廿º", |
| | | "æ¶åé®åæº", |
| | | "ä¼ æå¨æ ¡åç«", |
| | | "èèæ²ç§¯è®¾å¤", |
| | | "å
廿º", |
| | | "ç¦»åæ³¨å
¥æº", |
| | | "å妿ºæ¢°æå
设å¤", |
| | | "çå¤çç", |
| | | "å°è£
æµè¯ç«", |
| | | "æ¿å
ä¿®è°è®¾å¤", |
| | | "çµæ§è½æµè¯å°", |
| | | "å
妿£æµä»ª", |
| | | "ç空éèæº", |
| | | "è¶
声波æ¸
æ´æº", |
| | | "Xå°çº¿æ£æµä»ª" |
| | | ] |
| | | const ycList = [ |
| | | "è廿·±åº¦åå·®", |
| | | "é®åååå¼å¸¸", |
| | | "æ ¡åæ°æ®åç§»", |
| | | "æ²ç§¯éçæ³¢å¨", |
| | | "å
å»å¯¹ååå·®", |
| | | "ç¦»åæ³¨å
¥åéå¼å¸¸", |
| | | "æå
å度ä¸å", |
| | | "æ¸©åº¦ç¨³å®æ§å¼å¸¸", |
| | | "å°è£
å¯å°æ§ä¸è¯", |
| | | "çµé»å¼åå·®", |
| | | "çµæåº¦å¼å¸¸", |
| | | "ååºæ¶é´è¶
é", |
| | | "ä¿¡å·æ¼ç§»å¼å¸¸", |
| | | "线æ§åº¦åå·®", |
| | | "é¶ç¹æ¼ç§»å¼å¸¸", |
| | | "è¿è½½æ¢å¤å¼å¸¸", |
| | | "ç»ç¼çµé»ä¸è¶³", |
| | | "ä»è´¨èåä¸åæ ¼", |
| | | "é¢çååºå¼å¸¸", |
| | | "ä¿¡åªæ¯ä¸è¾¾æ " |
| | | ]; |
| | | |
| | | const cdList = [ |
| | | "é«", "é«", "ä¸", "ä¸", "é«", |
| | | "ä¸", "ä¸", "é«", "é«", "ä¸", |
| | | "é«", "ä¸", "ä¸", "ä¸", "ä½", |
| | | "ä¸", "é«", "é«", "ä¸", "ä½" |
| | | ]; |
| | | const getStatusTagType = (status: string) => { |
| | | switch (status) { |
| | | case 'é«': return 'danger'; |
| | | case 'ä¸': return 'warning'; |
| | | case 'ä½': return 'info'; |
| | | } |
| | | }; |
| | | |
| | | function changeBatchDate(date: Date){ |
| | | getBatch(date ? date : new Date()) |
| | | |
| | | } |
| | | async function queryWarnBatchList(){ |
| | | const queryParams = { |
| | | pageNum: 1, |
| | | pageSize: 6 |
| | | }; |
| | | const res:any = await listBatch(queryParams); |
| | | |
| | | if(res && res.rows){ |
| | | warnBatchList.value = []; |
| | | res.rows.forEach((item,index) => { |
| | | item.gw = gwList[index]; |
| | | item.yc = ycList[index]; |
| | | item.cd = cdList[index]; |
| | | item.batchTime = item.batchTime.substring(0,10); |
| | | warnBatchList.value.push(item); |
| | | }) |
| | | } |
| | | |
| | | } |
| | | |
| | | const shortcuts = [ |
| | | { |
| | | text: 'ä»å¤©', |
| | | value: new Date(), |
| | | }, |
| | | { |
| | | text: 'æ¨å¤©', |
| | | value: () => { |
| | | const date = new Date() |
| | | date.setTime(date.getTime() - 3600 * 1000 * 24) |
| | | return date |
| | | }, |
| | | }, |
| | | { |
| | | text: 'ä¸å¨å', |
| | | value: () => { |
| | | const date = new Date() |
| | | date.setTime(date.getTime() - 3600 * 1000 * 24 * 7) |
| | | return date |
| | | }, |
| | | }, |
| | | ] |
| | | |
| | | const disabledDate = (time: Date) => { |
| | | return time.getTime() > Date.now() |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | const goodProductsRate = (row) => { |
| | | if (row.num > 0 && row.okNum > 0) { |
| | | return Math.round((row.okNum / row.num) * 100); |
| | | } |
| | | }; |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | const generateWorkOrder = (record) => { |
| | | // å·¥åçæé»è¾ |
| | | ElMessage.success(`已为 ${record.name} çæå·¥å`); |
| | | }; |
| | | |
| | | const handleDetail = (record) => { |
| | | router.push({ path: '/quality/detail', query: { id: record.id } }); |
| | | }; |
| | | |
| | | const initHealthChart = () => { |
| | | if (healthChartRef.value) { |
| | | healthChart.value = echarts.init(healthChartRef.value); |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: ['1æ', '2æ', '3æ', '4æ', '5æ', '6æ', '7æ', '8æ', '9æ', '10æ', '11æ', '12æ'] |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'å¾å', |
| | | minInterval: 1 |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'è´¨éæ°æ®', |
| | | type: 'bar', |
| | | barWidth: '50%', |
| | | data: [ |
| | | { value: 9.8, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.9, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.7, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.8, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.9, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.8, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.9, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.8, itemStyle: { color: '#52c41a' } }, |
| | | { value: 9.8, itemStyle: { color: '#52c41a' } } |
| | | ], |
| | | label: { |
| | | show: true, |
| | | position: 'top', |
| | | formatter: '{c}å' |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | healthChart.value.setOption(option); |
| | | |
| | | window.addEventListener('resize', () => { |
| | | healthChart.value.resize(); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const initBatchRateChart = () => { |
| | | if (batchRateTrendRef.value) { |
| | | batchRateChart.value = echarts.init(batchRateTrendRef.value); |
| | | const option = { |
| | | color: ['#FFBF00'], |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'cross', |
| | | label: { |
| | | backgroundColor: '#6a7985' |
| | | } |
| | | } |
| | | }, |
| | | |
| | | xAxis: [ |
| | | { |
| | | splitLine: { show: false }, |
| | | type: 'category', |
| | | boundaryGap: false, |
| | | data: ['08-01', '08-02', '08-03', '08-04', '08-05', '08-06', '08-07'] |
| | | } |
| | | ], |
| | | yAxis: [ |
| | | { |
| | | splitLine: { show: false }, |
| | | type: 'value', |
| | | name: 'æ£æµåæ ¼ç', |
| | | min: 80, // 设置æå°å¼ä¸º90 |
| | | max: 100, // 设置æå¤§å¼ä¸º100 |
| | | interval: 5, // 设置å»åº¦é´é为1 |
| | | axisLabel: { |
| | | formatter: '{value}%' // æ·»å ç¾åæ¯ç¬¦å· |
| | | } |
| | | } |
| | | ], |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'line', |
| | | stack: 'Total', |
| | | smooth: true, |
| | | lineStyle: { |
| | | width: 0 |
| | | }, |
| | | showSymbol: false, |
| | | areaStyle: { |
| | | opacity: 0.8, |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgb(128, 255, 165)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgb(1, 191, 236)' |
| | | } |
| | | ]) |
| | | }, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [99, 98, 95, 99, 92, 98, 96], |
| | | |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | option && batchRateChart.value.setOption(option); |
| | | |
| | | |
| | | window.addEventListener('resize', () => { |
| | | batchRateChart.value.resize(); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const initNgChart = () => { |
| | | if (ngChartRef.value) { |
| | | ngChart.value = echarts.init(ngChartRef.value); |
| | | const option = { |
| | | |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '120px', // å¢å 左侧边è·ï¼ä¸ºæ ç¾çåºæ´å¤ç©ºé´ |
| | | top: '0px', |
| | | }, |
| | | xAxis: { |
| | | type: 'value', |
| | | boundaryGap: [0, 0.01], |
| | | splitLine: { show: false } |
| | | }, |
| | | yAxis: { |
| | | splitLine: { show: false }, |
| | | type: 'category', |
| | | data: [ |
| | | 'éææ¶èçµæµ2', |
| | | 'åå·®', |
| | | 'çè·¯ä¿æ¤æ¶èçµæµ(é»)2', |
| | | '卿æ¶èçµæµ2', |
| | | 'è·ç¦»', |
| | | 'åé(é»)2' |
| | | ] |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'bar', |
| | | barWidth: '60%', |
| | | data: [100, 90, 80, 70, 60, 10], |
| | | itemStyle: { |
| | | color: '#faad14' // ä¿®æ¹æ±ç¶å¾é¢è² |
| | | }, |
| | | label: { |
| | | show: true, |
| | | position: 'right', |
| | | formatter: '{c}次' |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | option && ngChart.value.setOption(option); |
| | | |
| | | |
| | | |
| | | window.addEventListener('resize', () => { |
| | | ngChart.value.resize(); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const getHealth = async () => { |
| | | const res: any = await queryQualityHealth(); |
| | | if (res && res.yAxis) { |
| | | const xAxisData = []; |
| | | const minValue = res.yAxis.reduce((min, item) => { |
| | | return min === null ? item.value : Math.min(min, item.value); |
| | | }, null); |
| | | res.yAxis.forEach((item, index) => { |
| | | if (item.value >= 9.5) { |
| | | item.itemStyle = { color: '#52c41a' }; |
| | | } else if (item.value < 9.5 && item.value >= 9.0) { |
| | | item.itemStyle = { color: '#faad14' }; |
| | | } else { |
| | | item.itemStyle = { color: '#f5222d' }; |
| | | } |
| | | if (item.value == minValue) { |
| | | item.itemStyle = { color: '#faad14' }; |
| | | } |
| | | xAxisData.push(item); |
| | | }); |
| | | updateHealthData(res.xAxis, xAxisData); |
| | | } |
| | | }; |
| | | |
| | | const getBatch = async (date = new Date()) => { |
| | | loading.value = true; |
| | | |
| | | const res: any = await queryBatchList({ |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | params: { |
| | | startTime: formatDate(date) + ' 00:00:00', |
| | | endTime: formatDate(date) + ' 23:59:59' |
| | | } |
| | | }); |
| | | if (!res || !res.rows) { |
| | | return; |
| | | } |
| | | batchList.value = res.rows; |
| | | |
| | | checkNum.value = res.rows.reduce((sum, item) => sum + (item.num || 0), 0); |
| | | |
| | | ngCountNum.value = res.rows.reduce((sum, item) => sum + (item.ngNum || 0), 0); |
| | | // 计ç®oknumæ»å |
| | | const totalOkNum = res.rows.reduce((sum, item) => sum + (item.okNum || 0), 0); |
| | | // 计ç®è¯åç (é¿å
é¤ä»¥0çæ
åµ) |
| | | const yieldRate = checkNum.value > 0 ? (totalOkNum / checkNum.value) * 100 : 0; |
| | | qualityRate.value = Number(yieldRate.toFixed(2)); |
| | | batchScore.value = Number(((yieldRate/10 )-0.1).toFixed(1)); |
| | | if(batchScore.value == -0.1){ |
| | | batchScore.value = 0; |
| | | } |
| | | loading.value = false; |
| | | }; |
| | | |
| | | const getPwbatch = async () => { |
| | | const res: any = await queryPwbatchList(); |
| | | if (res && res.yAxis) { |
| | | updatePwbatchData(res.xAxis, res.yAxis); |
| | | } |
| | | }; |
| | | |
| | | |
| | | const getNgrank = async () => { |
| | | const res: any = await queryNgrank(); |
| | | if (res && res.yAxis) { |
| | | updateNgrankData(res.xAxis, res.yAxis); |
| | | } |
| | | }; |
| | | |
| | | const formatDate = (date) => { |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(date.getDate()).padStart(2, '0'); |
| | | return `${year}-${month}-${day}`; |
| | | }; |
| | | |
| | | function updateHealthData(xAxis, yAxis) { |
| | | healthChart.value.setOption({ |
| | | xAxis: { |
| | | type: 'category', |
| | | data: xAxis |
| | | }, |
| | | series: [ |
| | | { |
| | | data: yAxis |
| | | } |
| | | ] |
| | | }); |
| | | } |
| | | |
| | | |
| | | function updatePwbatchData(xAxis, yAxis) { |
| | | batchRateChart.value.setOption({ |
| | | xAxis: { |
| | | type: 'category', |
| | | data: xAxis |
| | | }, |
| | | series: [ |
| | | { |
| | | data: yAxis |
| | | } |
| | | ] |
| | | }); |
| | | } |
| | | |
| | | |
| | | function updateNgrankData(xAxis, yAxis) { |
| | | ngChart.value.setOption({ |
| | | yAxis: { |
| | | type: 'category', |
| | | data: xAxis, |
| | | inverse: true, |
| | | }, |
| | | series: [ |
| | | { |
| | | data: yAxis |
| | | } |
| | | ] |
| | | }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | queryWarnBatchList(); |
| | | initHealthChart(); |
| | | initBatchRateChart(); |
| | | initNgChart(); |
| | | getHealth(); |
| | | getBatch(); |
| | | getPwbatch(); |
| | | getNgrank(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .home { |
| | | blockquote { |
| | | padding: 10px 20px; |
| | | margin: 0 0 20px; |
| | | font-size: 17.5px; |
| | | border-left: 5px solid #eee; |
| | | } |
| | | hr { |
| | | margin-top: 20px; |
| | | margin-bottom: 20px; |
| | | border: 0; |
| | | border-top: 1px solid #eee; |
| | | } |
| | | .col-item { |
| | | margin-bottom: 20px; |
| | | } |
| | | .h-500 { |
| | | height: 500px; |
| | | } |
| | | |
| | | ul { |
| | | padding: 0; |
| | | margin: 0; |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .grid { |
| | | display: grid; |
| | | } |
| | | |
| | | .grid-cols-1 { |
| | | grid-template-columns: repeat(1, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .grid-cols-2 { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .gap-4 { |
| | | gap: 1rem; |
| | | } |
| | | |
| | | .mb-6 { |
| | | margin-bottom: 1.5rem; |
| | | } |
| | | |
| | | .p-5 { |
| | | padding: 1.25rem; |
| | | } |
| | | |
| | | .w-full { |
| | | width: 100%; |
| | | } |
| | | |
| | | .h-400 { |
| | | height: 400px; |
| | | } |
| | | |
| | | .h-420 { |
| | | height: 420px; |
| | | } |
| | | |
| | | .bg-blue-50 { |
| | | background-color: #eff6ff; |
| | | } |
| | | |
| | | .bg-yellow-50 { |
| | | background-color: #fffbeb; |
| | | } |
| | | |
| | | .bg-red-50 { |
| | | background-color: #fef2f2; |
| | | } |
| | | |
| | | .bg-green-50 { |
| | | background-color: #f0fdf4; |
| | | } |
| | | |
| | | .rounded { |
| | | border-radius: 0.25rem; |
| | | } |
| | | |
| | | .text-sm { |
| | | font-size:14px; |
| | | } |
| | | |
| | | .text-xl { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .font-bold { |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .text-gray-600 { |
| | | color: #4b5563; |
| | | } |
| | | |
| | | @media (min-width: 768px) { |
| | | .md\:grid-cols-2 { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; |
| | | font-size: 13px; |
| | | color: #676a6c; |
| | | overflow-x: hidden; |
| | | .statistic-footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | font-size: 12px; |
| | | color: var(--el-text-color-regular); |
| | | margin-top: 16px; |
| | | } |
| | | |
| | | ul { |
| | | list-style-type: none; |
| | | } |
| | | .statistic-footer .footer-item { |
| | | font-size: 14px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | h4 { |
| | | margin-top: 0px; |
| | | } |
| | | .statistic-footer .footer-item span:last-child { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | h2 { |
| | | margin-top: 10px; |
| | | font-size: 26px; |
| | | font-weight: 100; |
| | | } |
| | | .green { |
| | | color: var(--el-color-success); |
| | | } |
| | | |
| | | p { |
| | | margin-top: 10px; |
| | | |
| | | b { |
| | | font-weight: 700; |
| | | } |
| | | } |
| | | |
| | | .update-log { |
| | | ol { |
| | | display: block; |
| | | list-style-type: decimal; |
| | | margin-block-start: 1em; |
| | | margin-block-end: 1em; |
| | | margin-inline-start: 0; |
| | | margin-inline-end: 0; |
| | | padding-inline-start: 40px; |
| | | } |
| | | } |
| | | .red { |
| | | color: var(--el-color-error); |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="device-detail-container"> |
| | | <el-page-header @back="goBack"> |
| | | <template #content> |
| | | <span class="text-large font-600 mr-3"> æ¹æ¬¡è¯¦æ
åæ</span> |
| | | </template> |
| | | </el-page-header> |
| | | |
| | | <el-row :gutter="16" class="mt-4"> |
| | | <!-- 设å¤å¾çå设å¤åºæ¬ä¿¡æ¯åå¹¶ --> |
| | | <el-col :span="10"> |
| | | <el-card class="mb-4" :style="{ height: '440px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>çäº§æ¹æ¬¡ä¿¡æ¯</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <div class="device-image-container"> |
| | | <img :src="img" alt="设å¤å¾ç" class="device-image" /> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-descriptions border :column="1" class="custom-descriptions"> |
| | | <el-descriptions-item label="æ¹æ¬¡ç¼å·">{{ batch.batchCode }}</el-descriptions-item> |
| | | <el-descriptions-item label="产ååå·">{{ batch.prodModel }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç产线">A线</el-descriptions-item> |
| | | <el-descriptions-item label="ç产æ¶é´">{{ batch.batchTime }}</el-descriptions-item> |
| | | <el-descriptions-item label="çäº§ç¶æ"> |
| | | <el-tag v-if="batchIsToday(batch.batchTime)" type="primary">ç产ä¸</el-tag> |
| | | <el-tag v-else type="success">已宿</el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- 设å¤å¥åº·ç¶æä¸ç»´æ¤å»ºè®® --> |
| | | <el-col :span="14"> |
| | | <el-card class="mb-4" :style="{ height: '440px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>ç产质éå¥åº·ç¶æä¸ç»´æ¤å»ºè®®</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8"> |
| | | <el-statistic |
| | | title="æ´ä½å¥åº·åº¦" |
| | | :value="healthRate" |
| | | :precision="0" |
| | | :suffix="'%'" |
| | | :value-style="{ color: healthData.healthColor }" |
| | | /> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-statistic |
| | | title="è¿ç¨è½åææ° (Cpk)" |
| | | :precision="2" |
| | | :value="healthData.predictedLife" |
| | | |
| | | /> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-statistic |
| | | title="è´¨éé£é©ç级" |
| | | :value="healthData.riskLevel" |
| | | :value-style="{ color: healthData.riskColor }" |
| | | /> |
| | | </el-col> |
| | | </el-row> |
| | | <div class="mt-4"> |
| | | <el-progress |
| | | :percentage="healthData.overallHealth" |
| | | :color="healthData.healthColor" |
| | | :show-text="false" |
| | | /> |
| | | </div> |
| | | |
| | | <el-divider content-position="left">颿µæ§ç»´æ¤å»ºè®®</el-divider> |
| | | <div class="table-container"> |
| | | |
| | | <el-table |
| | | ref="maintenanceTable" |
| | | :data="displayMaintenanceData" |
| | | height="246" |
| | | size="large" |
| | | stripe |
| | | > |
| | | <el-table-column prop="type" label="ç»´æ¤ç±»å" /> |
| | | <el-table-column prop="content" label="ç»´æ¤å
容" /> |
| | | <el-table-column prop="suggestedTime" label="建议æ¶é´" /> |
| | | <el-table-column prop="urgency" label="ç´§æ¥ç¨åº¦"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getUrgencyTagType(row.urgency)">{{ row.urgency }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" size="small" @click="handleMaintenance(row)">å¤ç</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <el-col :span="24"> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è´¨éææ æ±æ»</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8" style="display: flex;justify-content: center;align-items: center"> |
| | | <el-statistic title="æ¬æ¹è¯ç" :value="healthRate" :precision="2" :suffix="'%'"> |
| | | <template #prefix> |
| | | <!-- <el-icon style="vertical-align: -0.125em"><TrendCharts /></el-icon>--> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | |
| | | <el-col :span="8" style="display: flex;justify-content: center;align-items: center"> |
| | | <el-statistic title="ä¸è¯åæ°" :value="batch.ngNum" :suffix="'pcs'"> |
| | | <template #prefix> |
| | | <!-- <el-icon style="vertical-align: -0.125em"><TrendCharts /></el-icon>--> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | |
| | | |
| | | |
| | | <el-col :span="8" style="display: flex;justify-content: center;align-items: center"> |
| | | <el-statistic title="ä½äºå¹³åæ°´å¹³" :value="belowRate" :precision="2" :suffix="'%'"> |
| | | <template #prefix> |
| | | <!-- <el-icon style="vertical-align: -0.125em"><TrendCharts /></el-icon>--> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | |
| | | |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 宿¶æ°æ®è¶å¿å¾ --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>çäº§æ°æ®</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16" class="mt-4"> |
| | | <el-col :span="8"> |
| | | <div id="ambientTemperatureHumidityChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="motorTemperatureChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="motorVibrationChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- è´´è£
头/å¸å´ --> |
| | | <el-row :gutter="16" class="mt-4"> |
| | | <el-col :span="8"> |
| | | <div id="nozzleVacuumChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="nozzleFlowChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="placementSpeedChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | |
| | | <el-row :gutter="16"> |
| | | <!-- é¨ä»¶å¯¿å½é¢æµ --> |
| | | <el-col :span="12"> |
| | | <el-card class="mb-4" :style="{ height: '560px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å
³é®é¨ä»¶å¯¿å½é¢æµ</span> |
| | | <el-tooltip content="åºäºè®¾å¤è¿è¡æ°æ®åä¼ æå¨çæµç颿µæ§ç»´æ¤åæ" placement="top"> |
| | | <el-icon><Warning /></el-icon> |
| | | </el-tooltip> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | |
| | | |
| | | <div class="health-summary"> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8" class="summary-item"> |
| | | <div class="summary-value" style="color: #67C23A;">{{ healthyCount }}</div> |
| | | <div class="summary-label">å¥åº·</div> |
| | | </el-col> |
| | | <el-col :span="8" class="summary-item"> |
| | | <div class="summary-value" style="color: #E6A23C;">{{ warningCount }}</div> |
| | | <div class="summary-label">é¢è¦</div> |
| | | </el-col> |
| | | <el-col :span="8" class="summary-item"> |
| | | <div class="summary-value" style="color: #F56C6C;">{{ criticalCount }}</div> |
| | | <div class="summary-label">ç´§æ¥</div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <el-table |
| | | :data="sensorComponentData" |
| | | stripe |
| | | |
| | | :row-class-name="tableRowClassName" |
| | | > |
| | | <el-table-column prop="name" label="é¨ä»¶åç§°" /> |
| | | <el-table-column label="å¥åº·ç¶æ" > |
| | | <template #default="{ row }"> |
| | | <el-tag |
| | | :type="getHealthStatusType(row.healthStatus)" |
| | | size="small" |
| | | > |
| | | {{ row.healthStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å©ä½å¯¿å½" > |
| | | <template #default="{ row }"> |
| | | <div class="life-progress"> |
| | | <el-progress |
| | | :percentage="row.remainingPercentage" |
| | | :color="getProgressColor(row.remainingPercentage)" |
| | | :show-text="false" |
| | | :stroke-width="12" |
| | | /> |
| | | <span class="life-text">{{ row.remainingLife }}</span> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="颿µæ´æ°æ¶é´" width="120"> |
| | | <template #default="{ row }"> |
| | | {{ row.lastUpdate }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="showComponentDetail(row)" |
| | | > |
| | | 详æ
|
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- åå²ç»´æ¤è®°å½ --> |
| | | <el-col :span="12"> |
| | | <el-card class="mb-4" :style="{ height: '560px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>åå²å¼å¸¸äºä»¶åæ</span> |
| | | </div> |
| | | </template> |
| | | <el-timeline> |
| | | <el-timeline-item |
| | | v-for="item in historyData" |
| | | :key="item.id" |
| | | :type="getTimelineItemType(item.color)" |
| | | :timestamp="item.date" |
| | | > |
| | | {{ item.type }}: {{ item.description }} |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'; |
| | | import { useRouter,useRoute } from 'vue-router'; |
| | | import * as echarts from 'echarts'; |
| | | import { |
| | | TrendCharts, |
| | | Warning, |
| | | Grid, |
| | | Clock |
| | | } from '@element-plus/icons-vue'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import img from '@/assets/images/quailty/product.jpg' |
| | | import { getBatch } from '@/api/qms/batch'; |
| | | import { BatchVO } from '@/api/qms/batch/types'; |
| | | |
| | | const batch= ref<BatchVO>({}); |
| | | const router = useRouter(); |
| | | const route = useRoute(); |
| | | const batchId = route.query.id |
| | | |
| | | const healthRate = ref(0); |
| | | const belowRate = ref(0); |
| | | |
| | | |
| | | const goBack = () => { |
| | | router.go(-1); |
| | | }; |
| | | |
| | | |
| | | function getBatchDetail(){ |
| | | getBatch(batchId).then(res => { |
| | | if(res){ |
| | | batch.value = res.data; |
| | | initCharts2(); |
| | | if(batch.value && batch.value.num > 0 && batch.value.okNum > 0){ |
| | | const yieldRate = (batch.value.okNum / batch.value.num) * 100; |
| | | healthRate.value = Number(yieldRate.toFixed(1)); |
| | | |
| | | if(healthRate.value < 80 ){ |
| | | belowRate.value = 10.11; |
| | | healthData.riskLevel = "é«é£é©" |
| | | healthData.riskColor = "#f56c6c" |
| | | |
| | | }else if(healthRate.value>=80&& healthRate.value<90){ |
| | | healthData.riskLevel = "ä¸é£é©" |
| | | healthData.riskColor = "#faad14" |
| | | belowRate.value = 6.37; |
| | | } else if(healthRate.value>90&& healthRate.value<95){ |
| | | belowRate.value = 3.15; |
| | | } else if(healthRate.value>95&& healthRate.value<=100){ |
| | | belowRate.value = 0.32 ; |
| | | }else { |
| | | belowRate.value = 0; |
| | | |
| | | } |
| | | } |
| | | |
| | | } |
| | | }) |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | const healthData = reactive({ |
| | | overallHealth: 82, |
| | | healthColor: '#52c41a', |
| | | predictedLife: 1.31, |
| | | riskLevel: 'ä½é£é©', |
| | | riskColor: '#52c41a', |
| | | xAxisTravel: 300.179, |
| | | yAxisTravel: 233.39, |
| | | tapeJamCount: 6, |
| | | materialJamCount: 15, |
| | | panelCount: 2480, |
| | | downtime: 4.5, |
| | | }); |
| | | // çæç¸å¯¹æ¶é´å½æ° |
| | | const generateRelativeTime = (daysAgo) => { |
| | | const date = new Date(); |
| | | date.setDate(date.getDate() - daysAgo); |
| | | return date.toISOString().split('T')[0]; |
| | | }; |
| | | |
| | | const maintenanceData = reactive([ |
| | | { |
| | | key: '1', |
| | | type: '1å·è´´è£
ç³»ç»ç»´æ¤', |
| | | content: 'å¸å´ç空åååä½ï¼éè¦æ¸
æ´ææ´æ¢å¸å´', |
| | | suggestedTime: generateRelativeTime(3), |
| | | urgency: 'ä¸ç' |
| | | }, |
| | | { |
| | | key: '2', |
| | | type: '2å·åæµççç»´æ¤', |
| | | content: 'æ¸©åºæ¸©åº¦æ³¢å¨è¶
è¿æ åèå´ï¼éè¦æ ¡åæ¸©åº¦ä¼ æå¨', |
| | | suggestedTime: generateRelativeTime(1), |
| | | urgency: 'ä½' |
| | | }, |
| | | { |
| | | key: '3', |
| | | type: '3å·AOIæ£æµä»ªç»´æ¤', |
| | | content: 'ç¸æºé头æç°å°ï¼å½±åæ£æµç²¾åº¦ï¼éè¦æ¸
æ´é头', |
| | | suggestedTime: generateRelativeTime(2), |
| | | urgency: 'ä½' |
| | | }, |
| | | { |
| | | key: '4', |
| | | type: '4å·é¡èå°å·æºç»´æ¤', |
| | | content: 'å®åååä¸ååï¼éè¦è°æ´ææ´æ¢å®å', |
| | | suggestedTime: generateRelativeTime(3), |
| | | urgency: 'ä¸ç' |
| | | }, |
| | | { |
| | | key: '5', |
| | | type: '5å·SPIæ£æµä»ªç»´æ¤', |
| | | content: 'æ¿å
æµé模åéè¦éæ°æ ¡å', |
| | | suggestedTime: generateRelativeTime(5), |
| | | urgency: 'ä¸ç' |
| | | }, |
| | | { |
| | | key: '6', |
| | | type: '6å·X-Rayæ£æµä»ªç»´æ¤', |
| | | content: 'Xå°çº¿æºå·¥ä½æ¶é´æ¥è¿ç»´æ¤å¨æï¼éè¦é¢é²æ§ç»´æ¤', |
| | | suggestedTime: generateRelativeTime(4), |
| | | urgency: 'ä½' |
| | | } |
| | | ]); |
| | | |
| | | const getUrgencyTagType = (urgency: string) => { |
| | | switch (urgency) { |
| | | case 'é«': return 'danger'; |
| | | case 'ä¸ç': return 'warning'; |
| | | case 'ä½': return 'success'; |
| | | default: return 'info'; |
| | | } |
| | | }; |
| | | |
| | | const batchIsToday = (date: string) => { |
| | | const today = new Date(); |
| | | const inputDate = new Date(date); |
| | | |
| | | return inputDate.toDateString() === today.toDateString(); |
| | | }; |
| | | |
| | | const getTimelineItemType = (color: string) => { |
| | | switch (color) { |
| | | case 'green': return 'success'; |
| | | case 'red': return 'danger'; |
| | | default: return 'primary'; |
| | | } |
| | | }; |
| | | |
| | | const sparePartData = reactive([ |
| | | { |
| | | key: '1', |
| | | name: '1å·è´´è£
ç³»ç»T轴伺æçµæº', |
| | | currentLife: '15000å°æ¶', |
| | | remainingLife: '1451å°æ¶', |
| | | status: 'é¢è¦' |
| | | }, |
| | | { |
| | | key: '5', |
| | | name: '1å·è´´è£
ç³»ç»Z轴伺æçµæº', |
| | | currentLife: '15000å°æ¶', |
| | | remainingLife: '7521å°æ¶', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '9', |
| | | name: '1å·è´´è£
ç³»ç»ç空çµç£é', |
| | | currentLife: '10000å°æ¶', |
| | | remainingLife: '2154å°æ¶', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '2', |
| | | name: '1å·è´´è£
头', |
| | | currentLife: '1000000次', |
| | | remainingLife: '425542次', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '6', |
| | | name: '2å·è´´è£
ç³»ç»T轴伺æçµæº', |
| | | currentLife: '15000å°æ¶', |
| | | remainingLife: '7540å°æ¶', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '7', |
| | | name: '2å·è´´è£
ç³»ç»Z轴伺æçµæº', |
| | | currentLife: '15000å°æ¶', |
| | | remainingLife: '7521å°æ¶', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '9', |
| | | name: '2å·è´´è£
ç³»ç»ç空çµç£é', |
| | | currentLife: '10000å°æ¶', |
| | | remainingLife: '2154å°æ¶', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '8', |
| | | name: '2å·è´´è£
头', |
| | | currentLife: '1000000次', |
| | | remainingLife: '751251次', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | { |
| | | key: '3', |
| | | name: 'é£è¾¾', |
| | | currentLife: '96个æ', |
| | | remainingLife: '43个æ', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | ]); |
| | | |
| | | // å¼å¸¸äºä»¶åææ°æ® |
| | | // å¼å¸¸äºä»¶åææ°æ® |
| | | const historyData = reactive([ |
| | | { |
| | | id: '1', |
| | | date: '2025-09-12 14:25:36', |
| | | type: 'è廿·±åº¦å¼å¸¸', |
| | | description: 'å¾®æºçµç³»ç»è廿ºæ£æµå°è廿·±åº¦å¼å¸¸ï¼2.8μm (æ£å¸¸èå´: 2.3-2.5μm)', |
| | | color: 'red' |
| | | }, |
| | | { |
| | | id: '2', |
| | | date: '2025-08-21 13:40:22', |
| | | type: 'æ¸©åº¦ç¨³å®æ§å¼å¸¸', |
| | | description: 'çå¤ççæ¸©åº¦æ³¢å¨è¶
è¿å
许èå´Â±0.5°Cï¼è¾¾å°Â±0.8°C', |
| | | color: 'orange' |
| | | }, |
| | | { |
| | | id: '3', |
| | | date: '2025-08-03 12:15:48', |
| | | type: 'çµé»å¼åå·®', |
| | | description: 'æ£æµå°ä¼ æå¨çµé»å¼è¶
åºå
¬å·®èå´Â±5%ï¼å®é
å差为+7.2%', |
| | | color: 'red' |
| | | }, |
| | | { |
| | | id: '4', |
| | | date: '2025-07-12 11:30:15', |
| | | type: 'æ²ç§¯éçæ³¢å¨', |
| | | description: 'èèæ²ç§¯è®¾å¤æ²ç§¯éçä¸ç¨³å®ï¼æ³¢å¨å¹
度达å°Â±8%', |
| | | color: 'orange' |
| | | }, |
| | | { |
| | | id: '5', |
| | | date: '2025-06-01 10:45:33', |
| | | type: 'æ ¡åæ°æ®åç§»', |
| | | description: 'ä¼ æå¨æ ¡åç«æ£æµå°é¶ç¹æ¼ç§»0.3mVï¼éè¦éæ°æ ¡å', |
| | | color: 'yellow' |
| | | }, |
| | | { |
| | | id: '6', |
| | | date: '2025-04-17 09:20:57', |
| | | type: 'é®åååå¼å¸¸', |
| | | description: 'æ¶åé®åæºååä¼ æå¨è¯»æ°å¼å¸¸ï¼å®é
ååæ¯è®¾å®å¼ä½12%', |
| | | color: 'orange' |
| | | }, |
| | | { |
| | | id: '7', |
| | | date: '2025-03-26 08:55:12', |
| | | type: 'å
妿£æµå¼å¸¸', |
| | | description: 'å
妿£æµä»ªåç°3ä¸ªä¼ æå¨è¡¨é¢åå¨å¾®å°åç', |
| | | color: 'yellow' |
| | | }, |
| | | { |
| | | id: '8', |
| | | date: '2025-01-3 08:30:45', |
| | | type: 'ç空度ä¸è¶³', |
| | | description: 'ç空éèæºç空度ä¸éè³0.005Paï¼ä½äºæ å0.001Pa', |
| | | color: 'red' |
| | | }, |
| | | |
| | | ]); |
| | | |
| | | const handleMaintenance = (record: any) => { |
| | | console.log('å¤çç»´æ¤å»ºè®®:', record); |
| | | // ElMessage.info(`å¤çç»´æ¤å»ºè®®: ${record.type}`); |
| | | }; |
| | | |
| | | const maintenanceTable = ref(); |
| | | |
| | | // ç¨äºæ¾ç¤ºçç»´æ¤æ°æ®ï¼æ¯ææ éæ»å¨ï¼ |
| | | const displayMaintenanceData = ref([...maintenanceData]); |
| | | let scrollInterval: ReturnType<typeof setInterval> | undefined; |
| | | |
| | | // å¯å¨èªå¨æ»å¨ |
| | | const startAutoScroll = () => { |
| | | scrollInterval = setInterval(() => { |
| | | if (displayMaintenanceData.value.length > 0) { |
| | | // å°ç¬¬ä¸é¡¹ç§»å°æå |
| | | const firstItem = displayMaintenanceData.value.shift(); |
| | | if (firstItem) { |
| | | displayMaintenanceData.value.push(firstItem); |
| | | } |
| | | } |
| | | }, 1000); // æ¯3ç§æ»å¨ä¸æ¬¡ |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getBatchDetail() |
| | | nextTick(() => { |
| | | startAutoScroll(); // å¯å¨èªå¨æ»å¨ |
| | | }); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | if (scrollInterval) { |
| | | clearInterval(scrollInterval); |
| | | } |
| | | }); |
| | | |
| | | const initCharts = () => { |
| | | // å¾è¡¨åå§å代ç |
| | | const initChart = (chartId: string, title: string, seriesConfig: Array<{ name: string, data: any[], unit: string, baseValue: number, fluctuation: number, color: string }>) => { |
| | | const chart = echarts.init(document.getElementById(chartId)); |
| | | |
| | | const updateChart = () => { |
| | | // çææ°çæ°æ®ç¹ |
| | | const now = new Date(); |
| | | const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`; |
| | | |
| | | seriesConfig.forEach(s => { |
| | | const newValue = (s.baseValue + (Math.random() * 2 - 1) * s.fluctuation).toFixed(2); |
| | | s.data.push({ |
| | | time, |
| | | value: newValue |
| | | }); |
| | | if (s.data.length > 24) { |
| | | s.data.shift(); |
| | | } |
| | | }); |
| | | |
| | | const option = { |
| | | title: { |
| | | text: title, |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: (params) => { |
| | | let result = `${params[0].axisValueLabel}<br/>`; |
| | | params.forEach(param => { |
| | | result += `${param.marker} ${param.seriesName}: ${param.data}${seriesConfig[param.seriesIndex].unit}<br/>`; |
| | | }); |
| | | return result; |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '8%', // å¢å å·¦ä¾§è¾¹è· |
| | | right: '5%', |
| | | bottom: '10%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: seriesConfig[0].data.map(item => item.time) |
| | | }, |
| | | yAxis: seriesConfig.map((s, index) => ({ |
| | | type: 'value', |
| | | name: s.name, |
| | | position: index === 0 ? 'left' : 'right', |
| | | axisLine: { |
| | | show: true, |
| | | }, |
| | | axisLabel: { |
| | | formatter: (value) => `${value}${s.unit}` |
| | | } |
| | | })), |
| | | series: seriesConfig.map((s, index) => ({ |
| | | name: s.name, |
| | | data: s.data.map(item => parseFloat(item.value)), |
| | | type: 'line', |
| | | smooth: true, |
| | | yAxisIndex: index, |
| | | lineStyle: { |
| | | width: 2, |
| | | color: s.color |
| | | }, |
| | | areaStyle: { |
| | | opacity: 0.8, |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(84,112,198,0.3)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(84,112,198,0)' |
| | | } |
| | | ]) |
| | | }, |
| | | } |
| | | )), |
| | | }; |
| | | chart.setOption(option); |
| | | }; |
| | | |
| | | seriesConfig.forEach(s => { |
| | | s.data = []; |
| | | // çæåå§æ°æ®ç¹ï¼60个ç¹ï¼5åéæ°æ®ï¼ |
| | | for (let i = 0; i < 24; i++) { |
| | | const now = new Date(Date.now() - (24 - i) * 5000); // çæè¿å»5åéçæ°æ® |
| | | const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`; |
| | | const newValue = (s.baseValue + (Math.random() * 2 - 1) * s.fluctuation).toFixed(2); |
| | | s.data.push({ |
| | | time, |
| | | value: newValue |
| | | }); |
| | | } |
| | | }) |
| | | |
| | | // åå§æ¸²æ |
| | | updateChart(); |
| | | |
| | | // æ¯5ç§æ´æ°ä¸æ¬¡æ°æ® |
| | | const intervalId = setInterval(updateChart, 5000); |
| | | |
| | | window.addEventListener('resize', () => { |
| | | chart.resize(); |
| | | }); |
| | | }; |
| | | |
| | | // åå§åå个å¾è¡¨ |
| | | initChart('motorTemperatureChart', '产é', [ |
| | | { name: '产é', data: [], unit: 'pcs/h', baseValue: 175, fluctuation: 25, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('motorVibrationChart', 'ææç', [ |
| | | { name: 'ææç', data: [], unit: '%', baseValue: 0.15, fluctuation: 0.01, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('nozzleVacuumChart', 'å¸å´ç空åå', [ |
| | | { name: 'åå', data: [], unit: 'kPa', baseValue: -45, fluctuation: 5, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('nozzleFlowChart', 'å¸å´å¹æ°åå', [ |
| | | { name: 'åå', data: [], unit: 'kPa', baseValue: 20, fluctuation: 5, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('placementSpeedChart', 'è´´è£
é度', [ |
| | | { name: 'é度', data: [], unit: 'chips/h', baseValue: 9000, fluctuation: 1500, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | const ambientTemperatureData: any[] = []; |
| | | const ambientHumidityData: any[] = []; |
| | | initChart('ambientTemperatureHumidityChart', 'ç¯å¢æ¸©æ¹¿åº¦', [ |
| | | { name: '温度', data: ambientTemperatureData, unit: '°C', baseValue: 25, fluctuation: 1, color: '#5470C6' }, |
| | | { name: '湿度', data: ambientHumidityData, unit: '%', baseValue: 60, fluctuation: 5, color: '#91cc75' } |
| | | ]); |
| | | }; |
| | | |
| | | |
| | | const initCharts2 = () => { |
| | | // çææ¶é´è½´æ°æ® |
| | | const generateTimeAxis = (isToday) => { |
| | | const baseTimes = ['08:30', '09:30', '10:30', '11:30', '12:30', '13:30', '14:30', '15:30', '16:30', '17:00']; |
| | | |
| | | if (!isToday) { |
| | | return baseTimes; |
| | | } |
| | | |
| | | const now = new Date(); |
| | | const currentHour = now.getHours(); |
| | | const currentMinute = now.getMinutes(); |
| | | const currentTime = `${currentHour}:${currentMinute.toString().padStart(2, '0')}`; |
| | | |
| | | // æ¾å°å½åæ¶é´å¨åºç¡æ¶é´æ°ç»ä¸çä½ç½® |
| | | const currentIndex = baseTimes.findIndex(time => { |
| | | const [hour, minute] = time.split(':').map(Number); |
| | | const timeInMinutes = hour * 60 + minute; |
| | | const currentInMinutes = currentHour * 60 + currentMinute; |
| | | return timeInMinutes >= currentInMinutes; |
| | | }); |
| | | |
| | | // 妿å½åæ¶é´æ©äº8:30ï¼è¿å宿´æ°ç» |
| | | if (currentIndex === -1) { |
| | | return baseTimes; |
| | | } |
| | | |
| | | // æªåå°å½åæ¶é´ä¹åçæ¶é´ç¹ |
| | | return baseTimes.slice(0, currentIndex + 1); |
| | | }; |
| | | |
| | | // æªåæ°æ®ä»¥å¹é
æ¶é´è½´é¿åº¦ |
| | | const truncateData = (data, targetLength) => { |
| | | return data.slice(0, targetLength); |
| | | }; |
| | | |
| | | // å¾è¡¨åå§å代ç |
| | | const initChart = (chartId, title, seriesConfig, isToday) => { |
| | | const chart = echarts.init(document.getElementById(chartId)); |
| | | const timeAxis = generateTimeAxis(isToday); |
| | | |
| | | const option = { |
| | | title: { |
| | | text: title, |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: (params) => { |
| | | let result = `${params[0].axisValueLabel}<br/>`; |
| | | params.forEach(param => { |
| | | result += `${param.marker} ${param.seriesName}: ${param.data}${seriesConfig[param.seriesIndex].unit}<br/>`; |
| | | }); |
| | | return result; |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '8%', |
| | | right: '5%', |
| | | bottom: '10%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: timeAxis |
| | | }, |
| | | yAxis: seriesConfig.map((s, index) => ({ |
| | | type: 'value', |
| | | name: s.name, |
| | | position: index === 0 ? 'left' : 'right', |
| | | axisLine: { |
| | | show: true, |
| | | }, |
| | | axisLabel: { |
| | | formatter: (value) => `${value}${s.unit}` |
| | | } |
| | | })), |
| | | series: seriesConfig.map((s, index) => ({ |
| | | name: s.name, |
| | | data: truncateData(s.data, timeAxis.length), |
| | | type: 'line', |
| | | smooth: true, |
| | | yAxisIndex: index, |
| | | lineStyle: { |
| | | width: 2, |
| | | color: s.color |
| | | }, |
| | | areaStyle: { |
| | | opacity: 0.8, |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(84,112,198,0.3)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(84,112,198,0)' |
| | | } |
| | | ]) |
| | | }, |
| | | })), |
| | | }; |
| | | chart.setOption(option); |
| | | |
| | | window.addEventListener('resize', () => { |
| | | chart.resize(); |
| | | }); |
| | | }; |
| | | |
| | | // å设æä¸ä¸ªåé表示æ¯å¦æ¯ä»å¤© |
| | | const isToday = batchIsToday(batch.value.batchTime); // å¯ä»¥æ ¹æ®å®é
æ
åµè®¾ç½®ä¸ºtrueæfalse |
| | | |
| | | // åå§åå个å¾è¡¨ |
| | | initChart('motorTemperatureChart', 'è廿·±åº¦(μm)', [ |
| | | { |
| | | name: 'è廿·±åº¦', |
| | | data: [2.35, 2.38, 2.42, 2.45, 2.51, 2.48, 2.52, 2.55, 2.58, 2.60], |
| | | unit: 'μm', |
| | | color: '#5470C6' |
| | | }, |
| | | ], isToday); |
| | | |
| | | initChart('motorVibrationChart', 'é®ååå(MPa)', [ |
| | | { |
| | | name: 'é®ååå', |
| | | data: [15.2, 15.5, 15.8, 16.1, 15.9, 16.2, 16.0, 16.3, 16.1, 16.4], |
| | | unit: 'MPa', |
| | | color: '#91cc75' |
| | | }, |
| | | ], isToday); |
| | | |
| | | initChart('nozzleVacuumChart', 'æ²ç§¯éç(Ã
/min)', [ |
| | | { |
| | | name: 'æ²ç§¯éç', |
| | | data: [120, 118, 122, 125, 128, 126, 124, 127, 129, 131], |
| | | unit: 'Ã
/min', |
| | | color: '#fac858' |
| | | }, |
| | | ], isToday); |
| | | |
| | | initChart('nozzleFlowChart', '温度æ§å¶(°C)', [ |
| | | { |
| | | name: '温度', |
| | | data: [350, 352, 355, 358, 356, 354, 351, 353, 357, 359], |
| | | unit: '°C', |
| | | color: '#ee6666' |
| | | }, |
| | | ], isToday); |
| | | |
| | | initChart('placementSpeedChart', 'ç空度(Pa)', [ |
| | | { |
| | | name: 'ç空度', |
| | | data: [0.0012, 0.0015, 0.0018, 0.0021, 0.0019, 0.0020, 0.0017, 0.0022, 0.0020, 0.0018], |
| | | unit: 'Pa', |
| | | color: '#73c0de' |
| | | }, |
| | | ], isToday); |
| | | |
| | | initChart('ambientTemperatureHumidityChart', 'çµæµå¯åº¦(A/cm²)', [ |
| | | { |
| | | name: 'çµæµå¯åº¦', |
| | | data: [2.1, 2.2, 2.3, 2.4, 2.35, 2.38, 2.32, 2.36, 2.39, 2.42], |
| | | unit: 'A/cm²', |
| | | color: '#3ba272' |
| | | } |
| | | ], isToday); |
| | | }; |
| | | |
| | | |
| | | |
| | | // è·åéæºå¤©æ°ï¼ç¨äºæ¨¡æä¸åçæ´æ°æ¶é´ï¼ |
| | | const getRandomDaysAgo = (min, max) => { |
| | | return Math.floor(Math.random() * (max - min + 1)) + min; |
| | | }; |
| | | |
| | | const sensorComponentData = reactive([ |
| | | { |
| | | id: '1', |
| | | name: 'èå»ååºè
室', |
| | | healthStatus: 'è¯å¥½', |
| | | remainingLife: '285天', |
| | | remainingPercentage: 85, |
| | | totalLife: '5å¹´', |
| | | currentUsage: '2.5å¹´', |
| | | lastUpdate: generateRelativeTime(3), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2025-06-15' |
| | | }, |
| | | { |
| | | id: '2', |
| | | name: 'æ¶åä¼ è¾æºæ¢°è', |
| | | healthStatus: 'é¢è¦', |
| | | remainingLife: '45天', |
| | | remainingPercentage: 25, |
| | | totalLife: '80䏿¬¡', |
| | | currentUsage: '60䏿¬¡', |
| | | lastUpdate: generateRelativeTime(7), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2025-07-20' |
| | | }, |
| | | { |
| | | id: '3', |
| | | name: 'ç空泵系ç»', |
| | | healthStatus: 'ç´§æ¥', |
| | | remainingLife: '7天', |
| | | remainingPercentage: 5, |
| | | totalLife: '20000å°æ¶', |
| | | currentUsage: '19500å°æ¶', |
| | | lastUpdate: generateRelativeTime(2), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2025-03-10' |
| | | }, |
| | | { |
| | | id: '4', |
| | | name: '温度æ§å¶ç³»ç»', |
| | | healthStatus: 'è¯å¥½', |
| | | remainingLife: '180天', |
| | | remainingPercentage: 70, |
| | | totalLife: '3å¹´', |
| | | currentUsage: '1.5å¹´', |
| | | lastUpdate: generateRelativeTime(4), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2025-05-22' |
| | | }, |
| | | { |
| | | id: '5', |
| | | name: 'å妿²ç§¯å·å¤´', |
| | | healthStatus: 'é¢è¦', |
| | | remainingLife: '30天', |
| | | remainingPercentage: 20, |
| | | totalLife: '1000æ¹æ¬¡', |
| | | currentUsage: '850æ¹æ¬¡', |
| | | lastUpdate: generateRelativeTime(3), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2025-04-18' |
| | | }, |
| | | { |
| | | id: '6', |
| | | name: 'å
妿£æµé头', |
| | | healthStatus: 'è¯å¥½', |
| | | remainingLife: '365天', |
| | | remainingPercentage: 90, |
| | | totalLife: '4å¹´', |
| | | currentUsage: '1å¹´', |
| | | lastUpdate: generateRelativeTime(18), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2025-01-15' |
| | | }, |
| | | { |
| | | id: '7', |
| | | name: 'ç¦»åæ³¨å
¥æº', |
| | | healthStatus: 'ç´§æ¥', |
| | | remainingLife: '14天', |
| | | remainingPercentage: 8, |
| | | totalLife: '15000å°æ¶', |
| | | currentUsage: '14500å°æ¶', |
| | | lastUpdate: generateRelativeTime(2), |
| | | maintenanceHistory: '䏿¬¡ç»´æ¤: 2024-12-05' |
| | | } |
| | | ]); |
| | | |
| | | // 计ç®å¥åº·ç¶æç»è®¡ |
| | | const healthyCount = computed(() => |
| | | sensorComponentData.filter(item => item.healthStatus === 'è¯å¥½').length |
| | | ); |
| | | |
| | | const warningCount = computed(() => |
| | | sensorComponentData.filter(item => item.healthStatus === 'é¢è¦').length |
| | | ); |
| | | |
| | | const criticalCount = computed(() => |
| | | sensorComponentData.filter(item => item.healthStatus === 'ç´§æ¥').length |
| | | ); |
| | | |
| | | // è¡¨æ ¼è¡æ ·å¼ |
| | | const tableRowClassName = ({ row }) => { |
| | | if (row.healthStatus === 'ç´§æ¥') { |
| | | return 'warning-row'; |
| | | } else if (row.healthStatus === 'é¢è¦') { |
| | | return 'warning-row-light'; |
| | | } |
| | | return ''; |
| | | }; |
| | | |
| | | // è·åå¥åº·ç¶ææ ç¾ç±»å |
| | | const getHealthStatusType = (status) => { |
| | | const statusMap = { |
| | | 'è¯å¥½': 'success', |
| | | 'é¢è¦': 'warning', |
| | | 'ç´§æ¥': 'danger' |
| | | }; |
| | | return statusMap[status] || 'info'; |
| | | }; |
| | | |
| | | // è·åè¿åº¦æ¡é¢è² |
| | | const getProgressColor = (percentage) => { |
| | | if (percentage > 60) return '#67C23A'; |
| | | if (percentage > 30) return '#E6A23C'; |
| | | return '#F56C6C'; |
| | | }; |
| | | |
| | | // æ¾ç¤ºé¨ä»¶è¯¦æ
|
| | | const showComponentDetail = (component) => { |
| | | ElMessageBox.confirm( |
| | | `é¨ä»¶åç§°: ${component.name} |
| | | å¥åº·ç¶æ: ${component.healthStatus} |
| | | å©ä½å¯¿å½: ${component.remainingLife} |
| | | æ»è®¾è®¡å¯¿å½: ${component.totalLife} |
| | | å½å使ç¨: ${component.currentUsage} |
| | | ${component.maintenanceHistory}`, |
| | | 'é¨ä»¶è¯¦æ
', |
| | | { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'å
³é', |
| | | type: 'info' |
| | | } |
| | | ); |
| | | }; |
| | | </script> |
| | | |
| | | |
| | | <style scoped> |
| | | .device-detail-container { |
| | | padding: 16px; |
| | | background: #f0f2f5; |
| | | } |
| | | |
| | | .page-header-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .page-header-title { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .page-header-subtitle { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .card-header { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .device-image-container { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 330px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .device-image { |
| | | max-width: 100%; |
| | | max-height: 100%; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | .mt-4 { |
| | | margin-top: 1rem; |
| | | } |
| | | |
| | | .mb-4 { |
| | | margin-bottom: 1rem; |
| | | } |
| | | |
| | | |
| | | .custom-descriptions { |
| | | height: 357px; /* 设置æ´ä¸ªæè¿°å表çé«åº¦ */ |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__body) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table tbody) { |
| | | height: 100%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table tr) { |
| | | flex: 1; |
| | | display: flex; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table td) { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table .el-descriptions__label) { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table .el-descriptions__content) { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | /* |
| | | .table-container { |
| | | position: relative; |
| | | height: 246px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .table-container :deep(.el-table__body-wrapper) { |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .table-container :deep(.el-table__body) { |
| | | animation: scrollTable 20s linear infinite; |
| | | } |
| | | |
| | | .table-container:hover :deep(.el-table__body) { |
| | | animation-play-state: paused; |
| | | } |
| | | |
| | | @keyframes scrollTable { |
| | | 0% { |
| | | transform: translateY(0); |
| | | } |
| | | 100% { |
| | | transform: translateY(-100%); |
| | | } |
| | | } |
| | | |
| | | |
| | | .table-container :deep(.el-table__header-wrapper) { |
| | | position: sticky; |
| | | top: 0; |
| | | background: white; |
| | | z-index: 10; |
| | | } |
| | | */ |
| | | /* å¨styleé¨åæ·»å 以䏿 ·å¼ */ |
| | | .life-progress { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .life-text { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | min-width: 40px; |
| | | } |
| | | |
| | | .health-summary { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .summary-item { |
| | | text-align: center; |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .summary-label { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | :deep(.warning-row) { |
| | | --el-table-tr-bg-color: var(--el-color-danger-light-9); |
| | | } |
| | | |
| | | :deep(.warning-row-light) { |
| | | --el-table-tr-bg-color: var(--el-color-warning-light-9); |
| | | } |
| | | |
| | | :deep(.el-table .warning-row:hover > td) { |
| | | background-color: var(--el-color-danger-light-7) !important; |
| | | } |
| | | |
| | | :deep(.el-table .warning-row-light:hover > td) { |
| | | background-color: var(--el-color-warning-light-7) !important; |
| | | } |
| | | </style> |
ÎļþÃû´Ó src/views/qms/ai/index.vue ÐÞ¸Ä |
| | |
| | | <el-card shadow="hover" class="h-full"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤å¥åº·åº¦è¯å</span> |
| | | <span>è´¨éå¥åº·åº¦è¯å</span> |
| | | </div> |
| | | </template> |
| | | <!-- è¿éå°æ¾ç½®å¥åº·åº¦åå¸å¾è¡¨ --> |