| | |
| | | "jsbarcode": "^3.11.6", |
| | | "less": "^4.1.1", |
| | | "qrcode": "^1.5.4", |
| | | "three": "^0.160.0", |
| | | "vue": "^3.2.37", |
| | | "vue-barcode": "^1.3.0", |
| | | "vue-draggable-next": "^2.0.1", |
| | |
| | | qrcode: |
| | | specifier: ^1.5.4 |
| | | version: 1.5.4 |
| | | three: |
| | | specifier: ^0.160.0 |
| | | version: 0.160.1 |
| | | vue: |
| | | specifier: ^3.2.37 |
| | | version: 3.5.30 |
| | |
| | | |
| | | thenify@3.3.1: |
| | | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} |
| | | |
| | | three@0.160.1: |
| | | resolution: {integrity: sha512-Bgl2wPJypDOZ1stAxwfWAcJ0WQf7QzlptsxkjYiURPz+n5k4RBDLsq+6f9Y75TYxn6aHLcWz+JNmwTOXWrQTBQ==} |
| | | |
| | | through@2.3.8: |
| | | resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} |
| | |
| | | dependencies: |
| | | any-promise: 1.3.0 |
| | | |
| | | three@0.160.1: {} |
| | | |
| | | through@2.3.8: {} |
| | | |
| | | to-arraybuffer@1.0.1: {} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | // stockChat.js - åºå3Dæ¥ç卿©å±é
ç½® |
| | | let extension = { |
| | | components: { |
| | | gridHeader: '', |
| | | gridBody: '', |
| | | gridFooter: '', |
| | | modelHeader: '', |
| | | modelBody: '', |
| | | modelFooter: '' |
| | | }, |
| | | tableAction: '', |
| | | buttons: { view: [], box: [], detail: [] }, |
| | | methods: { |
| | | onInit() { |
| | | // æ©å±åå§å |
| | | }, |
| | | onInited() { |
| | | // æ¡æ¶åå§å宿å |
| | | } |
| | | } |
| | | }; |
| | | export default extension; |
| | |
| | | path: '/stockView', |
| | | name: 'stockView', |
| | | component: () => import('@/views/stock/stockView.vue') |
| | | } |
| | | , { |
| | | path: '/stockChat', |
| | | name: 'stockChat', |
| | | component: () => import('@/views/stock/stockChat.vue') |
| | | }, { |
| | | path: '/stockQuantityChangeRecord', |
| | | name: 'stockQuantityChangeRecord', |
| | |
| | | <template> |
| | | <div class="title"></div> |
| | | <div class="dashboard-container"> |
| | | <!-- é¡¶é¨ï¼æ¬æåºå
¥åºè¶å¿ (å
¨å®½) --> |
| | | <div class="chart-row full-width"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">æ¬æåºå
¥åºè¶å¿</div> |
| | | <div id="chart-monthly-trend" class="chart-content"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第äºè¡ï¼ä»æ¥/æ¬å¨åºå
¥åºå¯¹æ¯ --> |
| | | <div class="chart-row"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">仿¥åºå
¥åºå¯¹æ¯</div> |
| | | <div id="chart-today" class="chart-content"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="card-title">æ¬å¨åºå
¥åºå¯¹æ¯</div> |
| | | <div id="chart-week" class="chart-content"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第ä¸è¡ï¼æ¬æå¯¹æ¯/åºåæ»é --> |
| | | <div class="chart-row"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">æ¬æåºå
¥åºå¯¹æ¯</div> |
| | | <div id="chart-month" class="chart-content"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="card-title">å½ååºåæ»é</div> |
| | | <div class="stock-total"> |
| | | <div class="total-number">{{ overviewData.TotalStock || 0 }}</div> |
| | | <div class="total-label">æç</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第åè¡ï¼åºé¾åå¸/ä»åºåå¸ --> |
| | | <div class="chart-row"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">åºååºé¾åå¸</div> |
| | | <div id="chart-stock-age" class="chart-content"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="card-title">åä»åºåºååå¸</div> |
| | | <div id="chart-warehouse" class="chart-content"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref, reactive } from 'vue' |
| | | import * as echarts from "echarts"; |
| | | |
| | | export default { |
| | | setup() { |
| | | name: "Home", |
| | | data() { |
| | | return { |
| | | charts: {}, |
| | | overviewData: { |
| | | TodayInbound: 0, |
| | | TodayOutbound: 0, |
| | | MonthInbound: 0, |
| | | MonthOutbound: 0, |
| | | TotalStock: 0 |
| | | }, |
| | | weeklyData: [], |
| | | monthlyData: [], |
| | | stockAgeData: [], |
| | | warehouseData: [] |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.initCharts(); |
| | | this.loadData(); |
| | | window.addEventListener("resize", this.handleResize); |
| | | }, |
| | | beforeUnmount() { |
| | | window.removeEventListener("resize", this.handleResize); |
| | | Object.values(this.charts).forEach(chart => chart.dispose()); |
| | | }, |
| | | methods: { |
| | | handleResize() { |
| | | Object.values(this.charts).forEach(chart => chart.resize()); |
| | | }, |
| | | |
| | | initCharts() { |
| | | this.charts.monthlyTrend = echarts.init(document.getElementById("chart-monthly-trend")); |
| | | this.charts.today = echarts.init(document.getElementById("chart-today")); |
| | | this.charts.week = echarts.init(document.getElementById("chart-week")); |
| | | this.charts.month = echarts.init(document.getElementById("chart-month")); |
| | | this.charts.stockAge = echarts.init(document.getElementById("chart-stock-age")); |
| | | this.charts.warehouse = echarts.init(document.getElementById("chart-warehouse")); |
| | | }, |
| | | |
| | | async loadData() { |
| | | await this.loadOverview(); |
| | | await this.loadWeeklyStats(); |
| | | await this.loadMonthlyStats(); |
| | | await this.loadStockAgeDistribution(); |
| | | await this.loadStockByWarehouse(); |
| | | }, |
| | | |
| | | async loadOverview() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/Overview"); |
| | | if (res.Status && res.Data) { |
| | | this.overviewData = res.Data; |
| | | this.updateTodayChart(); |
| | | this.updateWeekChart(); |
| | | this.updateMonthChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½æ»è§æ°æ®å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadWeeklyStats() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/WeeklyStats", { weeks: 12 }); |
| | | if (res.Status && res.Data) { |
| | | this.weeklyData = res.Data; |
| | | this.updateWeekChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½æ¯å¨ç»è®¡å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadMonthlyStats() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/MonthlyStats", { months: 12 }); |
| | | if (res.Status && res.Data) { |
| | | this.monthlyData = res.Data; |
| | | this.updateMonthlyTrendChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½æ¯æç»è®¡å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadStockAgeDistribution() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/StockAgeDistribution"); |
| | | if (res.Status && res.Data) { |
| | | this.stockAgeData = res.Data; |
| | | this.updateStockAgeChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½åºé¾åå¸å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadStockByWarehouse() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/StockByWarehouse"); |
| | | if (res.Status && res.Data) { |
| | | this.warehouseData = res.Data; |
| | | this.updateWarehouseChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½ä»åºåå¸å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | updateTodayChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: ["仿¥"], |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: [this.overviewData.TodayInbound], itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "bar", data: [this.overviewData.TodayOutbound], itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.today.setOption(option, true); |
| | | }, |
| | | |
| | | updateWeekChart() { |
| | | const thisWeek = this.getThisWeekData(this.weeklyData); |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: ["æ¬å¨"], |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: [thisWeek.Inbound], itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "bar", data: [thisWeek.Outbound], itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.week.setOption(option, true); |
| | | }, |
| | | |
| | | getThisWeekData(weeklyData) { |
| | | if (!weeklyData || weeklyData.length === 0) return { Inbound: 0, Outbound: 0 }; |
| | | const thisWeekKey = this.getCurrentWeekKey(); |
| | | const thisWeek = weeklyData.find(w => w.Week === thisWeekKey); |
| | | return thisWeek || { Inbound: 0, Outbound: 0 }; |
| | | }, |
| | | |
| | | getCurrentWeekKey() { |
| | | const now = new Date(); |
| | | const diff = (7 + (now.getDay() - 1)) % 7; |
| | | const monday = new Date(now); |
| | | monday.setDate(now.getDate() - diff); |
| | | const year = monday.getFullYear(); |
| | | const jan1 = new Date(year, 0, 1); |
| | | const weekNum = Math.ceil(((monday - jan1) / 86400000 + jan1.getDay() + 1) / 7); |
| | | return `${year}-W${String(weekNum).padStart(2, "0")}`; |
| | | }, |
| | | |
| | | updateMonthChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: ["æ¬æ"], |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: [this.overviewData.MonthInbound], itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "bar", data: [this.overviewData.MonthOutbound], itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.month.setOption(option, true); |
| | | }, |
| | | |
| | | updateMonthlyTrendChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: this.monthlyData.map(m => m.Month), |
| | | axisLabel: { color: "#fff", rotate: 45 } |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: "value", |
| | | name: "æ°é", |
| | | axisLabel: { color: "#fff" } |
| | | } |
| | | ], |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: this.monthlyData.map(m => m.Inbound), itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "line", data: this.monthlyData.map(m => m.Outbound), itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.monthlyTrend.setOption(option, true); |
| | | }, |
| | | |
| | | updateStockAgeChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: this.stockAgeData.map(s => s.Range), |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { |
| | | type: "bar", |
| | | data: this.stockAgeData.map(s => s.Count), |
| | | itemStyle: { color: "#5470c6" } |
| | | } |
| | | ] |
| | | }; |
| | | this.charts.stockAge.setOption(option, true); |
| | | }, |
| | | |
| | | updateWarehouseChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: this.warehouseData.map(w => w.Warehouse), |
| | | axisLabel: { color: "#fff", rotate: 30 } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { |
| | | type: "bar", |
| | | data: this.warehouseData.map(w => w.Count), |
| | | itemStyle: { color: "#5470c6" } |
| | | } |
| | | ] |
| | | }; |
| | | this.charts.warehouse.setOption(option, true); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .title { |
| | | line-height: 70vh; |
| | | text-align: center; |
| | | font-size: 28px; |
| | | color: orange; |
| | | .dashboard-container { |
| | | padding: 20px; |
| | | background-color: #0e1a2b; |
| | | min-height: calc(100vh - 60px); |
| | | } |
| | | </style> |
| | | |
| | | .chart-row { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .chart-row.full-width { |
| | | width: 100%; |
| | | } |
| | | |
| | | .chart-card { |
| | | flex: 1; |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border: 1px solid rgba(25, 186, 139, 0.17); |
| | | border-radius: 4px; |
| | | padding: 15px; |
| | | position: relative; |
| | | } |
| | | |
| | | .chart-card::before { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 10px; |
| | | height: 10px; |
| | | border-top: 2px solid #02a6b5; |
| | | border-left: 2px solid #02a6b5; |
| | | } |
| | | |
| | | .chart-card::after { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | width: 10px; |
| | | height: 10px; |
| | | border-top: 2px solid #02a6b5; |
| | | border-right: 2px solid #02a6b5; |
| | | } |
| | | |
| | | .card-title { |
| | | color: #fff; |
| | | font-size: 16px; |
| | | text-align: center; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .chart-content { |
| | | height: 280px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .stock-total { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 280px; |
| | | } |
| | | |
| | | .total-number { |
| | | font-size: 64px; |
| | | font-weight: bold; |
| | | color: #67caca; |
| | | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif; |
| | | } |
| | | |
| | | .total-label { |
| | | font-size: 18px; |
| | | color: #fcf0d8; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | /* å
¨å®½å¾è¡¨ */ |
| | | .full-width .chart-card { |
| | | flex: none; |
| | | width: 100%; |
| | | } |
| | | |
| | | .full-width .chart-content { |
| | | height: 350px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="stock-chat-container"> |
| | | <!-- ä»åº Tabs --> |
| | | <el-tabs v-model="activeWarehouse" @tab-change="onWarehouseChange"> |
| | | <el-tab-pane |
| | | v-for="wh in warehouseList" |
| | | :key="wh.warehouseId || wh.id" |
| | | :label="wh.warehouseName" |
| | | :name="wh.warehouseId || wh.id" |
| | | /> |
| | | </el-tabs> |
| | | |
| | | <!-- å·¥å
·æ --> |
| | | <div class="toolbar"> |
| | | <el-select v-model="filterStockStatus" placeholder="åºåç¶æçé" style="width: 160px" clearable> |
| | | <el-option label="ç»çæå(1)" :value="1" /> |
| | | <el-option label="å
¥åºç¡®è®¤(3)" :value="3" /> |
| | | <el-option label="å
¥åºå®æ(6)" :value="6" /> |
| | | <el-option label="åºåºéå®(7)" :value="7" /> |
| | | <el-option label="åºåºå®æ(8)" :value="8" /> |
| | | <el-option label="空æç(22)" :value="22" /> |
| | | </el-select> |
| | | <el-select v-model="filterMaterielCode" placeholder="ç©æçé" style="width: 140px" clearable> |
| | | <el-option v-for="code in materielCodeList" :key="code" :label="code" :value="code" /> |
| | | </el-select> |
| | | <el-select v-model="filterBatchNo" placeholder="æ¹æ¬¡çé" style="width: 140px" clearable> |
| | | <el-option v-for="batch in batchNoList" :key="batch" :label="batch" :value="batch" /> |
| | | </el-select> |
| | | <el-button @click="resetCamera">éç½®è§è§</el-button> |
| | | <el-button type="primary" @click="refreshData" :loading="refreshing">å·æ°æ°æ®</el-button> |
| | | </div> |
| | | |
| | | <!-- 3D Canvas --> |
| | | <div ref="canvasContainer" class="canvas-container" /> |
| | | |
| | | <!-- ç¶æå¾ä¾ --> |
| | | <div class="legend"> |
| | | <div class="legend-title">è´§ä½ç¶æ</div> |
| | | <div v-for="item in legendItems" :key="item.status" class="legend-item"> |
| | | <span class="color-box" :style="{ background: item.color }" /> |
| | | <span>{{ item.label }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 详æ
ä¾§è¾¹é¢æ¿ --> |
| | | <el-drawer v-model="detailDialogVisible" title="åºå详æ
" direction="rtl" size="500px"> |
| | | <div v-if="selectedLocation" class="detail-content"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="è´§ä½ç¼å·">{{ selectedLocation.locationCode }}</el-descriptions-item> |
| | | <el-descriptions-item label="è´§ä½ç¶æ">{{ getLocationStatusText(selectedLocation.locationStatus) }}</el-descriptions-item> |
| | | <el-descriptions-item label="æçç¼å·">{{ selectedLocation.palletCode || 'æ ' }}</el-descriptions-item> |
| | | <el-descriptions-item label="åºåç¶æ">{{ getStockStatusText(selectedLocation.stockStatus) }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ»åºå">{{ selectedLocation.stockQuantity }}{{ selectedLocation.unit || '' }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <!-- åºåæç»è¡¨æ ¼ --> |
| | | <div v-if="selectedLocation.details && selectedLocation.details.length > 0" class="detail-table"> |
| | | <h4>åºåæç»</h4> |
| | | <el-table :data="selectedLocation.details" border size="small" max-height="400"> |
| | | <el-table-column prop="materielCode" label="ç©æç¼ç " width="100" /> |
| | | <el-table-column prop="materielName" label="ç©æåç§°" min-width="120" show-overflow-tooltip /> |
| | | <el-table-column prop="batchNo" label="æ¹æ¬¡å·" width="100" show-overflow-tooltip /> |
| | | <el-table-column prop="stockQuantity" label="æ°é" width="70" align="right" /> |
| | | <el-table-column prop="unit" label="åä½" width="50" align="center" /> |
| | | <el-table-column prop="effectiveDate" label="æææ" width="100" /> |
| | | </el-table> |
| | | </div> |
| | | <div v-else class="no-detail"> |
| | | <el-empty description="ææ åºåæç»" /> |
| | | </div> |
| | | </div> |
| | | </el-drawer> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted, watch, nextTick, getCurrentInstance } from 'vue' |
| | | import * as THREE from 'three' |
| | | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' |
| | | import * as signalR from '@microsoft/signalr' |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | // SignalR è¿æ¥ |
| | | let connection = null |
| | | |
| | | // é¢è²å¸¸é - åºäºå®é
æä¸¾å¼ |
| | | // è´§ä½ç¶æ(locationStatus): 0=空é², 1=éå®, 10=æè´§éå®, 20=空é²éå®, 99=大æçéå®, 100=æè´§ |
| | | // åºåç¶æ(stockStatus): 1=ç»çæå, 3=å
¥åºç¡®è®¤, 6=å
¥åºå®æ, 7=åºåºéå®, 8=åºåºå®æ, 9=ç§»åºéå®ç |
| | | const COLOR_MAP = { |
| | | // è´§ä½ç¶æé¢è² |
| | | LOC_FREE: 0x90EE90, // 0=ç©ºé² - æµ
ç»¿è² |
| | | LOC_LOCK: 0xFF6B6B, // 1=éå® - çº¢è² |
| | | LOC_INSTOCK_LOCK: 0xFFA500, // 10=æè´§éå® - æ©è² |
| | | LOC_FREE_LOCK: 0xFFD700, // 20=空é²éå® - éè² |
| | | LOC_PALLET_LOCK: 0x9370DB, // 99=大æçéå® - ç´«è² |
| | | LOC_INSTOCK: 0x409EFF, // 100=æè´§ - èè² |
| | | |
| | | // åºåç¶æé¢è² |
| | | STOCK_PENDING: 0x00CED1, // 1=ç»çæå - æ·±éè² |
| | | STOCK_CONFIRMED: 0x87CEEB, // 3=å
¥åºç¡®è®¤ - 天èè² |
| | | STOCK_COMPLETED: 0x32CD32, // 6=å
¥åºå®æ - äº®ç»¿è² |
| | | STOCK_OUT_LOCK: 0xFF6347, // 7=åºåºéå® - çªè红 |
| | | STOCK_OUT_COMPLETED: 0x228B22, // 8=åºåºå®æ - 森æç»¿ |
| | | STOCK_TRANSFER_LOCK: 0xFF8C00, // 9=ç§»åºéå® - æ·±æ©è² |
| | | STOCK_COMPLETED_NO_ORDER: 0x20B2AA, // 10=å
¥åºå®ææªå»ºåºåºå |
| | | STOCK_RETURN: 0xFF4500, // 11=éåº |
| | | STOCK_MANUAL_PENDING: 0x48D1CC, // 12=æå¨ç»çæå |
| | | STOCK_MANUAL_CONFIRMED: 0x7FFFD4, // 13=æå¨ç»çå
¥åºç¡®è®¤ |
| | | STOCK_PICK_COMPLETED: 0x6B8E23, // 14=æ£é宿 |
| | | STOCK_MES_RETURN: 0xDC143C, // 21=MESéåº |
| | | STOCK_EMPTY_TRAY: 0xDDA0DD, // 22=空æçåºå - æ¢
çº¢è² |
| | | STOCK_GROUP_CANCEL: 0xDEB887, // 99=ç»çæ¤é - æéè² |
| | | STOCK_IN_CANCEL: 0xA0522D, // 199=å
¥åºæ¤é - èµè² |
| | | } |
| | | |
| | | // å¾ä¾é¡¹ - è´§ä½ç¶æ |
| | | const legendItems = [ |
| | | { status: 'loc_free', label: '空é²(0)', color: '#90EE90' }, |
| | | { status: 'loc_lock', label: 'éå®(1)', color: '#FF6B6B' }, |
| | | { status: 'loc_instock_lock', label: 'æè´§éå®(10)', color: '#FFA500' }, |
| | | { status: 'loc_free_lock', label: '空é²éå®(20)', color: '#FFD700' }, |
| | | { status: 'loc_pallet_lock', label: '大æçéå®(99)', color: '#9370DB' }, |
| | | { status: 'loc_instock', label: 'æè´§(100)', color: '#409EFF' }, |
| | | ] |
| | | |
| | | // Refs |
| | | const canvasContainer = ref(null) |
| | | |
| | | // ç¶æ |
| | | const activeWarehouse = ref(null) |
| | | const warehouseList = ref([]) |
| | | const filterStockStatus = ref(null) |
| | | const filterMaterielCode = ref(null) |
| | | const filterBatchNo = ref(null) |
| | | const materielCodeList = ref([]) |
| | | const batchNoList = ref([]) |
| | | const detailDialogVisible = ref(false) |
| | | const selectedLocation = ref(null) |
| | | const refreshing = ref(false) |
| | | |
| | | // Three.js ç¸å
³ |
| | | let scene, camera, renderer, controls, raycaster, mouse |
| | | let locationMesh = null |
| | | let locationData = [] |
| | | let originalLocationData = [] // ä¿ååå§å®æ´æ°æ®ï¼ç¨äºçéæ¢å¤ |
| | | let animationId = null |
| | | let locationIdToInstanceId = new Map() // locationId -> instanceId æ å° |
| | | |
| | | // SignalR åå§å |
| | | function initSignalR() { |
| | | proxy.http.post('api/User/GetCurrentUserInfo').then((result) => { |
| | | connection = new signalR.HubConnectionBuilder() |
| | | .withAutomaticReconnect() |
| | | .withUrl(`${proxy.http.ipAddress}stockHub?userName=${result.data.userName}`) |
| | | .build(); |
| | | |
| | | connection.start().catch((err) => console.log('SignalRè¿æ¥å¤±è´¥:', err)); |
| | | |
| | | connection.on('StockUpdated', (update) => { |
| | | console.log('æ¶å°åºåæ´æ°:', update) |
| | | // æ´æ°å¯¹åºè´§ä½çæ°æ® |
| | | const idx = locationData.findIndex(x => x.locationId === update.locationId); |
| | | if (idx !== -1) { |
| | | locationData[idx].stockQuantity = update.stockQuantity; |
| | | locationData[idx].stockStatus = update.stockStatus; |
| | | locationData[idx].palletCode = update.palletCode; |
| | | locationData[idx].locationStatus = update.locationStatus; |
| | | // æ´æ°åºåæç» |
| | | if (update.details && update.details.length > 0) { |
| | | locationData[idx].details = update.details; |
| | | } |
| | | // éè¿æ å°æ¾å°å®ä¾IDï¼æ´æ°é¢è² |
| | | const instanceId = locationIdToInstanceId.get(update.locationId); |
| | | if (instanceId !== undefined) { |
| | | updateInstanceColor(instanceId, update.locationStatus); |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | // æ´æ°å个货ä½é¢è² |
| | | function updateInstanceColor(instanceId, locationStatus) { |
| | | if (!locationMesh) return; |
| | | // æ ¹æ®è´§ä½ç¶æè·åé¢è² |
| | | const color = getLocationColorByStatus(locationStatus); |
| | | locationMesh.setColorAt(instanceId, new THREE.Color(color)); |
| | | locationMesh.instanceColor.needsUpdate = true; |
| | | } |
| | | |
| | | // æ ¹æ®è´§ä½ç¶æè·åé¢è² |
| | | function getLocationColorByStatus(locStatus) { |
| | | switch (locStatus) { |
| | | case 0: return COLOR_MAP.LOC_FREE // ç©ºé² |
| | | case 1: return COLOR_MAP.LOC_LOCK // éå® |
| | | case 10: return COLOR_MAP.LOC_INSTOCK_LOCK // æè´§éå® |
| | | case 20: return COLOR_MAP.LOC_FREE_LOCK // 空é²éå® |
| | | case 99: return COLOR_MAP.LOC_PALLET_LOCK // 大æçéå® |
| | | case 100: return COLOR_MAP.LOC_INSTOCK // æè´§ |
| | | default: return COLOR_MAP.LOC_FREE // é»è®¤ç©ºé²è² |
| | | } |
| | | } |
| | | |
| | | // è·åè´§ä½é¢è² - åªæ ¹æ®è´§ä½ç¶æ |
| | | function getLocationColor(location) { |
| | | const locStatus = location.locationStatus |
| | | |
| | | // æ ¹æ®è´§ä½ç¶æå¤æé¢è² |
| | | switch (locStatus) { |
| | | case 0: return COLOR_MAP.LOC_FREE // ç©ºé² |
| | | case 1: return COLOR_MAP.LOC_LOCK // éå® |
| | | case 10: return COLOR_MAP.LOC_INSTOCK_LOCK // æè´§éå® |
| | | case 20: return COLOR_MAP.LOC_FREE_LOCK // 空é²éå® |
| | | case 99: return COLOR_MAP.LOC_PALLET_LOCK // 大æçéå® |
| | | case 100: return COLOR_MAP.LOC_INSTOCK // æè´§ |
| | | default: return COLOR_MAP.LOC_FREE // é»è®¤ç©ºé²è² |
| | | } |
| | | } |
| | | |
| | | // è·åè´§ä½ç¶æææ¬ |
| | | function getLocationStatusText(status) { |
| | | const map = { |
| | | 0: '空é²', |
| | | 1: 'éå®', |
| | | 10: 'æè´§éå®', |
| | | 20: '空é²éå®', |
| | | 99: '大æçéå®', |
| | | 100: 'æè´§' |
| | | } |
| | | return map[status] || 'æªç¥(' + status + ')' |
| | | } |
| | | |
| | | // è·ååºåç¶æææ¬ |
| | | function getStockStatusText(status) { |
| | | const map = { |
| | | 0: 'æ åºå', |
| | | 1: 'ç»çæå', |
| | | 3: 'å
¥åºç¡®è®¤', |
| | | 6: 'å
¥åºå®æ', |
| | | 7: 'åºåºéå®', |
| | | 8: 'åºåºå®æ', |
| | | 9: 'ç§»åºéå®', |
| | | 10: 'å
¥åºå®ææªå»ºåºåºå', |
| | | 11: 'éåº', |
| | | 12: 'æå¨ç»çæå', |
| | | 13: 'æå¨ç»çå
¥åºç¡®è®¤', |
| | | 14: 'æ£é宿', |
| | | 21: 'MESéåº', |
| | | 22: '空æçåºå', |
| | | 99: 'ç»çæ¤é', |
| | | 199: 'å
¥åºæ¤é' |
| | | } |
| | | return map[status] || 'æªç¥(' + status + ')' |
| | | } |
| | | |
| | | // å è½½ä»åºå表 |
| | | async function loadWarehouseList() { |
| | | try { |
| | | const res = await proxy.http.get('/api/Warehouse/GetAll') |
| | | if (res.status && res.data) { |
| | | warehouseList.value = res.data |
| | | if (res.data.length > 0) { |
| | | activeWarehouse.value = res.data[0].warehouseId || res.data[0].id |
| | | await loadWarehouseData(activeWarehouse.value) |
| | | } |
| | | } |
| | | } catch (e) { |
| | | console.error('å è½½ä»åºå表失败', e) |
| | | } |
| | | } |
| | | |
| | | // å è½½ä»åºè´§ä½æ°æ® |
| | | async function loadWarehouseData(warehouseId) { |
| | | try { |
| | | const res = await proxy.http.get(`/api/StockInfo/Get3DLayout?warehouseId=${warehouseId}`) |
| | | if (res.status && res.data) { |
| | | const data = res.data |
| | | originalLocationData = data.locations || [] // ä¿ååå§å®æ´æ°æ® |
| | | locationData = [...originalLocationData] // å½åæ¾ç¤ºæ°æ® |
| | | // 使ç¨å端è¿åççéå表 |
| | | materielCodeList.value = data.materielCodeList || [] |
| | | batchNoList.value = data.batchNoList || [] |
| | | // 渲æè´§ä½ |
| | | renderLocations() |
| | | } |
| | | } catch (e) { |
| | | console.error('å è½½è´§ä½æ°æ®å¤±è´¥', e) |
| | | } |
| | | } |
| | | |
| | | // åå§å Three.js åºæ¯ |
| | | function initThreeJS() { |
| | | if (!canvasContainer.value) return |
| | | |
| | | const width = canvasContainer.value.clientWidth |
| | | const height = canvasContainer.value.clientHeight |
| | | |
| | | // åå»ºåºæ¯ |
| | | scene = new THREE.Scene() |
| | | scene.background = new THREE.Color(0x1a1a2e) |
| | | |
| | | // åå»ºç¸æº |
| | | camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000) |
| | | camera.position.set(20, 20, 20) |
| | | camera.lookAt(0, 0, 0) |
| | | |
| | | // å建渲æå¨ |
| | | renderer = new THREE.WebGLRenderer({ antialias: true }) |
| | | renderer.setPixelRatio(window.devicePixelRatio) |
| | | renderer.setSize(width, height) |
| | | canvasContainer.value.appendChild(renderer.domElement) |
| | | |
| | | // åå»ºè½¨éæ§å¶å¨ |
| | | controls = new OrbitControls(camera, renderer.domElement) |
| | | controls.enableDamping = true |
| | | controls.dampingFactor = 0.05 |
| | | |
| | | // å建ç¯å¢å
|
| | | const ambientLight = new THREE.AmbientLight(0xffffff, 0.5) |
| | | scene.add(ambientLight) |
| | | |
| | | // å建主å®åå
|
| | | const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0) |
| | | directionalLight.position.set(50, 100, 50) |
| | | scene.add(directionalLight) |
| | | |
| | | // å建补å
|
| | | const fillLight = new THREE.DirectionalLight(0xffffff, 0.3) |
| | | fillLight.position.set(-50, 50, -50) |
| | | scene.add(fillLight) |
| | | |
| | | // å建å°é¢ |
| | | const groundGeometry = new THREE.PlaneGeometry(100, 100) |
| | | const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x2d2d2d }) |
| | | const ground = new THREE.Mesh(groundGeometry, groundMaterial) |
| | | ground.rotation.x = -Math.PI / 2 |
| | | ground.position.y = -0.5 |
| | | scene.add(ground) |
| | | |
| | | // åå»ºç½æ ¼ |
| | | const gridHelper = new THREE.GridHelper(100, 50) |
| | | scene.add(gridHelper) |
| | | |
| | | // å建å°çº¿æ£æµå¨ |
| | | raycaster = new THREE.Raycaster() |
| | | mouse = new THREE.Vector2() |
| | | |
| | | // æ·»å ç¹å»äºä»¶ |
| | | canvasContainer.value.addEventListener('mousedown', onCanvasClick) |
| | | |
| | | // å¼å§æ¸²æå¾ªç¯ |
| | | animate() |
| | | } |
| | | |
| | | // 渲æè´§ä½ |
| | | function renderLocations() { |
| | | if (!scene) return |
| | | console.log('渲æè´§ä½ï¼åå§æ°æ®æ»æ°:', originalLocationData.length) |
| | | // 妿åå§æ°æ®ä¸ºç©ºï¼å°è¯éæ°å è½½ |
| | | if (originalLocationData.length === 0) { |
| | | console.warn('åå§æ°æ®ä¸ºç©ºï¼éæ°å è½½...') |
| | | if (activeWarehouse.value) { |
| | | loadWarehouseData(activeWarehouse.value) |
| | | } |
| | | return |
| | | } |
| | | |
| | | // ç§»é¤æ§çè´§ä½ç½æ ¼ |
| | | console.log("ð ~ renderLocations ~ locationMesh:", locationMesh) |
| | | if (locationMesh) { |
| | | scene.remove(locationMesh) |
| | | locationMesh.geometry.dispose() |
| | | if (Array.isArray(locationMesh.material)) { |
| | | locationMesh.material.forEach(m => m.dispose()) |
| | | } else { |
| | | locationMesh.material.dispose() |
| | | } |
| | | locationMesh = null |
| | | } |
| | | |
| | | // è¿æ»¤æ°æ® - å§ç»ä»åå§å®æ´æ°æ®è¿æ»¤ |
| | | let filteredData = [...originalLocationData] |
| | | if (filterStockStatus.value !== null) { |
| | | filteredData = filteredData.filter(loc => loc.stockStatus === filterStockStatus.value) |
| | | } |
| | | if (filterMaterielCode.value) { |
| | | filteredData = filteredData.filter(loc => loc.materielCode === filterMaterielCode.value) |
| | | } |
| | | if (filterBatchNo.value) { |
| | | filteredData = filteredData.filter(loc => loc.batchNo === filterBatchNo.value) |
| | | } |
| | | |
| | | console.log('è¿æ»¤åæ°æ®æ°é:', filteredData.length, 'ç鿡件:', filterStockStatus.value, filterMaterielCode.value, filterBatchNo.value) |
| | | |
| | | // å建 InstancedMesh |
| | | const geometry = new THREE.BoxGeometry(1.5, 1, 1.5) |
| | | const material = new THREE.MeshStandardMaterial({ |
| | | color: 0xffffff, |
| | | roughness: 0.5, |
| | | metalness: 0.1 |
| | | }) |
| | | |
| | | // å¦æè¿æ»¤åæ æ°æ®ï¼å建空ç InstancedMesh |
| | | if (filteredData.length === 0) { |
| | | locationMesh = new THREE.InstancedMesh(geometry, material, 0) |
| | | scene.add(locationMesh) |
| | | return |
| | | } |
| | | |
| | | locationMesh = new THREE.InstancedMesh(geometry, material, filteredData.length) |
| | | |
| | | // æ¸
空并é建æ å° |
| | | locationIdToInstanceId.clear() |
| | | |
| | | const dummy = new THREE.Object3D() |
| | | const color = new THREE.Color() |
| | | |
| | | filteredData.forEach((location, i) => { |
| | | const x = (location.column - 1) * 2 |
| | | const y = location.layer * 1.5 |
| | | const z = (location.row - 1) * 2 |
| | | |
| | | dummy.position.set(x, y, z) |
| | | dummy.updateMatrix() |
| | | locationMesh.setMatrixAt(i, dummy.matrix) |
| | | |
| | | // 设置é¢è² |
| | | color.setHex(getLocationColor(location)) |
| | | locationMesh.setColorAt(i, color) |
| | | |
| | | // å»ºç«æ å°: locationId -> instanceId (i) |
| | | locationIdToInstanceId.set(location.locationId, i) |
| | | |
| | | if (i === 0) { |
| | | console.log('First location:', location, { x, y, z }) |
| | | } |
| | | }) |
| | | |
| | | locationMesh.instanceMatrix.needsUpdate = true |
| | | if (locationMesh.instanceColor) locationMesh.instanceColor.needsUpdate = true |
| | | |
| | | scene.add(locationMesh) |
| | | } |
| | | |
| | | // å¨ç»å¾ªç¯ |
| | | function animate() { |
| | | animationId = requestAnimationFrame(animate) |
| | | controls.update() |
| | | renderer.render(scene, camera) |
| | | } |
| | | |
| | | // ç¹å»ç»å¸ |
| | | function onCanvasClick(event) { |
| | | if (!canvasContainer.value || !locationMesh) return |
| | | |
| | | const rect = canvasContainer.value.getBoundingClientRect() |
| | | mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 |
| | | mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 |
| | | |
| | | raycaster.setFromCamera(mouse, camera) |
| | | const intersects = raycaster.intersectObject(locationMesh) |
| | | |
| | | if (intersects.length > 0) { |
| | | const instanceId = intersects[0].instanceId |
| | | // è·ååå§æ°æ®ç´¢å¼ï¼èèè¿æ»¤åçæ°æ®ï¼ |
| | | let filteredData = [...originalLocationData] |
| | | if (filterStockStatus.value !== null) { |
| | | filteredData = filteredData.filter(loc => loc.stockStatus === filterStockStatus.value) |
| | | } |
| | | if (filterMaterielCode.value) { |
| | | filteredData = filteredData.filter(loc => loc.materielCode === filterMaterielCode.value) |
| | | } |
| | | if (filterBatchNo.value) { |
| | | filteredData = filteredData.filter(loc => loc.batchNo === filterBatchNo.value) |
| | | } |
| | | |
| | | if (instanceId < filteredData.length) { |
| | | selectedLocation.value = filteredData[instanceId] |
| | | detailDialogVisible.value = true |
| | | // èç¦ç¸æº |
| | | focusCamera(selectedLocation.value) |
| | | } |
| | | } |
| | | } |
| | | |
| | | // èç¦ç¸æºå°è´§ä½ |
| | | function focusCamera(location) { |
| | | if (!camera || !controls) return |
| | | |
| | | const targetX = (location.column - 1) * 2 |
| | | const targetY = location.layer * 1.5 |
| | | const targetZ = (location.row - 1) * 2 |
| | | |
| | | const offsetX = 5 |
| | | const offsetY = 5 |
| | | const offsetZ = 5 |
| | | |
| | | const targetPosition = new THREE.Vector3(targetX + offsetX, targetY + offsetY, targetZ + offsetZ) |
| | | |
| | | // ä½¿ç¨ lerp å¹³æ»ç§»å¨ |
| | | const startPosition = camera.position.clone() |
| | | const duration = 500 |
| | | const startTime = Date.now() |
| | | |
| | | function lerpMove() { |
| | | const elapsed = Date.now() - startTime |
| | | const t = Math.min(elapsed / duration, 1) |
| | | const easeT = 1 - Math.pow(1 - t, 3) // easeOutCubic |
| | | |
| | | camera.position.lerpVectors(startPosition, targetPosition, easeT) |
| | | controls.target.set(targetX, targetY, targetZ) |
| | | |
| | | if (t < 1) { |
| | | requestAnimationFrame(lerpMove) |
| | | } |
| | | } |
| | | |
| | | lerpMove() |
| | | } |
| | | |
| | | // éç½®ç¸æº |
| | | function resetCamera() { |
| | | if (!camera || !controls) return |
| | | camera.position.set(20, 20, 20) |
| | | controls.target.set(0, 0, 0) |
| | | } |
| | | |
| | | // å·æ°æ°æ® |
| | | async function refreshData() { |
| | | refreshing.value = true |
| | | try { |
| | | // éç½®ç鿡件 |
| | | filterStockStatus.value = null |
| | | filterMaterielCode.value = null |
| | | filterBatchNo.value = null |
| | | // éæ°å è½½å½åä»åºæ°æ® |
| | | if (activeWarehouse.value) { |
| | | await loadWarehouseData(activeWarehouse.value) |
| | | } |
| | | } finally { |
| | | refreshing.value = false |
| | | } |
| | | } |
| | | |
| | | // ä»åºåæ¢ |
| | | async function onWarehouseChange(warehouseId) { |
| | | await loadWarehouseData(warehouseId) |
| | | } |
| | | |
| | | // çå¬çéåå |
| | | watch(filterStockStatus, async () => { |
| | | await nextTick() |
| | | if (originalLocationData.length > 0) { |
| | | renderLocations() |
| | | } |
| | | }) |
| | | watch(filterMaterielCode, async () => { |
| | | await nextTick() |
| | | if (originalLocationData.length > 0) { |
| | | renderLocations() |
| | | } |
| | | }) |
| | | watch(filterBatchNo, async () => { |
| | | await nextTick() |
| | | if (originalLocationData.length > 0) { |
| | | renderLocations() |
| | | } |
| | | }) |
| | | |
| | | // çªå£å¤§å°åå |
| | | function onWindowResize() { |
| | | if (!canvasContainer.value || !camera || !renderer) return |
| | | const width = canvasContainer.value.clientWidth |
| | | const height = canvasContainer.value.clientHeight |
| | | camera.aspect = width / height |
| | | camera.updateProjectionMatrix() |
| | | renderer.setSize(width, height) |
| | | } |
| | | |
| | | // ç»ä»¶æè½½ |
| | | onMounted(() => { |
| | | initThreeJS() |
| | | loadWarehouseList() |
| | | initSignalR() |
| | | window.addEventListener('resize', onWindowResize) |
| | | }) |
| | | |
| | | // ç»ä»¶å¸è½½ |
| | | onUnmounted(() => { |
| | | if (animationId) { |
| | | cancelAnimationFrame(animationId) |
| | | } |
| | | if (canvasContainer.value) { |
| | | canvasContainer.value.removeEventListener('mousedown', onCanvasClick) |
| | | } |
| | | window.removeEventListener('resize', onWindowResize) |
| | | if (renderer) { |
| | | renderer.dispose() |
| | | } |
| | | if (connection) { |
| | | connection.stop() |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .stock-chat-container { |
| | | width: 100%; |
| | | height: calc(100vh - 120px); |
| | | position: relative; |
| | | overflow: visible; |
| | | } |
| | | .toolbar { |
| | | position: absolute; |
| | | top: 60px; |
| | | left: 20px; |
| | | z-index: 10; |
| | | display: flex; |
| | | gap: 10px; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | padding: 10px; |
| | | border-radius: 4px; |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.15); |
| | | } |
| | | .canvas-container { |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | } |
| | | .legend { |
| | | position: absolute; |
| | | bottom: 20px; |
| | | right: 20px; |
| | | background: rgba(0, 0, 0, 0.7); |
| | | padding: 10px; |
| | | border-radius: 4px; |
| | | color: white; |
| | | z-index: 10; |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | } |
| | | .legend-title { |
| | | font-weight: bold; |
| | | font-size: 13px; |
| | | margin-bottom: 6px; |
| | | color: #fff; |
| | | } |
| | | .legend-divider { |
| | | height: 1px; |
| | | background: rgba(255, 255, 255, 0.3); |
| | | margin: 8px 0; |
| | | } |
| | | .legend-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | margin-bottom: 4px; |
| | | font-size: 12px; |
| | | } |
| | | .color-box { |
| | | width: 16px; |
| | | height: 16px; |
| | | border-radius: 2px; |
| | | flex-shrink: 0; |
| | | } |
| | | .detail-content { |
| | | padding: 20px; |
| | | } |
| | | .detail-table { |
| | | margin-top: 20px; |
| | | } |
| | | .detail-table h4 { |
| | | margin-bottom: 10px; |
| | | color: #303133; |
| | | } |
| | | .no-detail { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | :deep(.el-drawer__body) { |
| | | padding: 20px; |
| | | } |
| | | </style> |
| | |
| | | { title: "å建人", field: "creater", type: "like" }, |
| | | ], |
| | | [ |
| | | { title: "ä»»å¡ç±»å",field: "taskType",type: "selectList",dataKey: "taskType",data: [],}, |
| | | { title: "ä»»å¡ç±»å",field: "taskType",type: "selectList",dataKey: "taskTypeEnum",data: [],}, |
| | | { title: "ä»»å¡ç¶æ",field: "taskStatus",type: "selectList",dataKey: "taskStatusEnum",data: [],}, |
| | | { title: "å··éå·", field: "roadway", type: "like" }, |
| | | ], |
| | |
| | | type: "int", |
| | | width: 120, |
| | | align: "left", |
| | | bind: { key: "taskType", data: [] }, |
| | | bind: { key: "taskTypeEnum", data: [] }, |
| | | }, |
| | | { |
| | | field: "taskStatus", |
| | |
| | | "Bash(cd \"E:/è¿
é·ä¸è½½/WIDESEA_WMSServer\" && dotnet build --configuration Debug 2>&1 | tail -20)", |
| | | "Bash(cd \"E:/è¿
é·ä¸è½½/WIDESEA_WMSServer\" && dotnet build --configuration Debug 2>&1 | tail -25)", |
| | | "Bash(cd \"E:/è¿
é·ä¸è½½/WIDESEA_WMSServer\" && dotnet build --configuration Debug 2>&1 | grep -A 2 -B 2 \"error CS0\")", |
| | | "Bash(cd \"E:/è¿
é·ä¸è½½/WIDESEA_WMSServer\" && dotnet build --configuration Debug 2>&1 | tail -10)" |
| | | "Bash(cd \"E:/è¿
é·ä¸è½½/WIDESEA_WMSServer\" && dotnet build --configuration Debug 2>&1 | tail -10)", |
| | | "Bash(dotnet build:*)", |
| | | "Bash(git add:*)", |
| | | "Bash(git commit:*)" |
| | | ] |
| | | } |
| | | } |
| | |
| | | Undefined = 0, |
| | | |
| | | /// <summary> |
| | | /// å°æç |
| | | /// æ¨¡åæ®µ |
| | | /// </summary> |
| | | [Description("å°æç")] |
| | | [Description("æ¨¡åæ®µ")] |
| | | SmallPallet = 1, |
| | | |
| | | /// <summary> |
| | | /// 䏿ç |
| | | /// å·ç»æ®µ |
| | | /// </summary> |
| | | [Description("䏿ç")] |
| | | [Description("å·ç»æ®µ")] |
| | | MediumPallet = 2, |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | /// <summary> |
| | | /// ä»åº |
| | | /// HA57 = æ·®å®äºå - æ¿æä»<br/> |
| | | /// HA58 = æ·®å®äºå - PPä»<br/> |
| | | /// HA60 = æ·®å®äºå - è¾
æä»<br/> |
| | | /// HA64 = æ·®å®äºå - æµè¯æ¶ä»<br/> |
| | | /// HA71 = æ·®å®äºå - æåä»<br/> |
| | | /// HA72 = æ·®å®äºå - å°¾æ°ä»<br/> |
| | | /// HA73 = æ·®å®äºå - ç åä»<br/> |
| | | /// HA101 = æ·®å®äºå - æåä»å¹³åº<br/> |
| | | /// HA152 = æ·®å®äºå - å¹²èä»<br/> |
| | | /// HA153 = æ·®å®äºå - 油墨ä»<br/> |
| | | /// GW1 = éç
¤äºå - 髿¸©1å·ä»åº<br/> |
| | | /// CW1 = éç
¤äºå - 常温1å·ä»åº<br/> |
| | | /// HCFR1 = éç
¤äºå - å容1å·ä»åº<br/> |
| | | /// GW2 = éç
¤äºå - 髿¸©2å·ä»åº<br/> |
| | | /// ZJ1 = éç
¤äºå - æ£æå·ä»<br/> |
| | | /// FJ1 = éç
¤äºå - è´æå·ä»<br/> |
| | | |
| | | /// </summary> |
| | | public enum WarehouseEnum |
| | | { |
| | | /// <summary> |
| | | /// æ¿æä» |
| | | /// 髿¸©1å·ä»åº |
| | | /// </summary> |
| | | [Description("æ¿æä»")] |
| | | HA57, |
| | | [Description("髿¸©1å·ä»åº")] |
| | | GW1 = 1, |
| | | /// <summary> |
| | | /// PPä» |
| | | /// 常温1å·ä»åº |
| | | /// </summary> |
| | | [Description("PPä»")] |
| | | HA58, |
| | | [Description("常温1å·ä»åº")] |
| | | CW1 = 2, |
| | | /// <summary> |
| | | /// è¾
æä» |
| | | /// å容1å·ä»åº |
| | | /// </summary> |
| | | [Description("è¾
æä»")] |
| | | HA60, |
| | | [Description("å容1å·ä»åº")] |
| | | HCFR1 = 3, |
| | | /// <summary> |
| | | /// æµè¯æ¶ä» |
| | | /// 髿¸©2å·ä»åº |
| | | /// </summary> |
| | | [Description("æµè¯æ¶ä»")] |
| | | HA64, |
| | | [Description("髿¸©2å·ä»åº")] |
| | | GW2 = 4, |
| | | /// <summary> |
| | | /// æåä» |
| | | /// æ£æå·ä» |
| | | /// </summary> |
| | | [Description("æåä»")] |
| | | HA71, |
| | | [Description("æ£æå·ä»")] |
| | | ZJ1 = 5, |
| | | /// <summary> |
| | | /// å°¾æ°ä» |
| | | /// è´æå·ä» |
| | | /// </summary> |
| | | [Description("å°¾æ°ä»")] |
| | | HA72, |
| | | /// <summary> |
| | | /// ç åä» |
| | | /// </summary> |
| | | [Description("ç åä»")] |
| | | HA73, |
| | | /// <summary> |
| | | /// æåä»å¹³åº |
| | | /// </summary> |
| | | [Description("æåä»å¹³åº")] |
| | | HA101, |
| | | /// <summary> |
| | | /// å¹²èä» |
| | | /// </summary> |
| | | [Description("å¹²èä»")] |
| | | HA152, |
| | | /// <summary> |
| | | /// æ²¹å¢¨ä» |
| | | /// </summary> |
| | | [Description("油墨ä»")] |
| | | HA153 |
| | | [Description("è´æå·ä»")] |
| | | FJ1 = 6 |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | namespace WIDESEA_DTO.Stock |
| | | { |
| | | /// <summary> |
| | | /// åºå3Då¸å±æ°æ®ä¼ è¾å¯¹è±¡ |
| | | /// </summary> |
| | | public class Stock3DLayoutDTO |
| | | { |
| | | /// <summary> |
| | | /// ä»åºID |
| | | /// </summary> |
| | | public int WarehouseId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»åºåç§° |
| | | /// </summary> |
| | | public string WarehouseName { get; set; } = string.Empty; |
| | | |
| | | /// <summary> |
| | | /// æå¤§è¡æ° |
| | | /// </summary> |
| | | public int MaxRow { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§åæ° |
| | | /// </summary> |
| | | public int MaxColumn { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§å±æ° |
| | | /// </summary> |
| | | public int MaxLayer { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç¼ç çéå表 |
| | | /// </summary> |
| | | public List<string> MaterielCodeList { get; set; } = new List<string>(); |
| | | |
| | | /// <summary> |
| | | /// æ¹æ¬¡å·çéå表 |
| | | /// </summary> |
| | | public List<string> BatchNoList { get; set; } = new List<string>(); |
| | | |
| | | /// <summary> |
| | | /// è´§ä½æ°ç» |
| | | /// </summary> |
| | | public List<Location3DItemDTO> Locations { get; set; } = new List<Location3DItemDTO>(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 3Dè´§ä½é¡¹æ°æ®ä¼ è¾å¯¹è±¡ |
| | | /// </summary> |
| | | public class Location3DItemDTO |
| | | { |
| | | /// <summary> |
| | | /// è´§ä½ID |
| | | /// </summary> |
| | | public int LocationId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è´§ä½ç¼ç |
| | | /// </summary> |
| | | public string LocationCode { get; set; } = string.Empty; |
| | | |
| | | /// <summary> |
| | | /// è¡ |
| | | /// </summary> |
| | | public int Row { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å |
| | | /// </summary> |
| | | public int Column { get; set; } |
| | | |
| | | /// <summary> |
| | | /// å± |
| | | /// </summary> |
| | | public int Layer { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è´§ä½ç¶æ |
| | | /// </summary> |
| | | public int LocationStatus { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åºåç¶æ |
| | | /// </summary> |
| | | public int StockStatus { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åºåæ°é |
| | | /// </summary> |
| | | public float StockQuantity { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æå¤§å®¹é |
| | | /// </summary> |
| | | public float MaxCapacity { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçç¼ç |
| | | /// </summary> |
| | | public string? PalletCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç¼ç |
| | | /// </summary> |
| | | public string? MaterielCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æåç§° |
| | | /// </summary> |
| | | public string? MaterielName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¹æ¬¡å· |
| | | /// </summary> |
| | | public string? BatchNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åºåæç»å表 |
| | | /// </summary> |
| | | public List<StockDetailItemDTO> Details { get; set; } = new(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºåæç»é¡¹DTO |
| | | /// </summary> |
| | | public class StockDetailItemDTO |
| | | { |
| | | public int Id { get; set; } |
| | | public string? MaterielCode { get; set; } |
| | | public string? MaterielName { get; set; } |
| | | public string? BatchNo { get; set; } |
| | | public float StockQuantity { get; set; } |
| | | public string? Unit { get; set; } |
| | | public string? ProductionDate { get; set; } |
| | | public string? EffectiveDate { get; set; } |
| | | public string? OrderNo { get; set; } |
| | | public int Status { get; set; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.ComponentModel.DataAnnotations; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Text.Json.Serialization; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace WIDESEA_DTO.Task |
| | | { |
| | | public class AGVResponse |
| | | { |
| | | /// <summary> |
| | | /// 请æ±ç»æä»£ç - å¿
å¡«ï¼trueæåï¼false失败 |
| | | /// </summary> |
| | | public bool Code { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 请æ±ç»æè¯´æ - éå¡«ï¼æ¯å¦æåï¼æåè¿å空ï¼å¼å¸¸è¿åå¼å¸¸ä¿¡æ¯ |
| | | /// </summary> |
| | | public string Msg { get; set; } |
| | | /// <summary> |
| | | /// å
¥åºå£ç¼å· - éå¡« |
| | | /// </summary> |
| | | public string Devid { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçå· - éå¡« |
| | | /// </summary> |
| | | public string Traynumber { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ´æç»å«-å度 - éå¡« |
| | | /// </summary> |
| | | public string Group { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 宽度 - éå¡« |
| | | /// </summary> |
| | | public int? Width { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°æ®å表 - éå¡« |
| | | /// </summary> |
| | | public List<string> Data { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¡ç å· - éå¡« |
| | | /// </summary> |
| | | public string Labelnumber { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç¼ç - éå¡« |
| | | /// </summary> |
| | | public string Productno { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©ææè¿° - éå¡« |
| | | /// </summary> |
| | | public string Productname { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°é - éå¡« |
| | | /// </summary> |
| | | public string Quantity { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åä½ - éå¡« |
| | | /// </summary> |
| | | public string Uomcode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç±»å - éå¡« |
| | | /// </summary> |
| | | public string Producttype { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 产åºè®¾å¤ - éå¡« |
| | | /// </summary> |
| | | public string Equipment { get; set; } |
| | | |
| | | /// <summary> |
| | | /// äº§åºæ¶é´ - éå¡« |
| | | /// </summary> |
| | | public string Productiondate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¸éæ¶é´ - éå¡« |
| | | /// </summary> |
| | | public string Lowerlimittime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢è¦æ¶é´ - éå¡« |
| | | /// </summary> |
| | | public string Warningtime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¶
ææ¶é´ - éå¡« |
| | | /// </summary> |
| | | public string Overduetime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 - éå¡« |
| | | /// </summary> |
| | | public string Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 - éå¡« |
| | | /// </summary> |
| | | public string Define2 { get; set; } |
| | | |
| | | public AGVResponse OK(AGVDataDto aGVDataDto = null) |
| | | { |
| | | Msg = ""; |
| | | Code = true; |
| | | if (aGVDataDto != null) |
| | | { |
| | | // å°AGVDataDtoç屿§èµå¼ç»AGVResponse |
| | | Devid = aGVDataDto.DevId; |
| | | Traynumber = aGVDataDto.TrayNumber; |
| | | Group = aGVDataDto.Group; |
| | | Width = aGVDataDto.Width; |
| | | Data = aGVDataDto.Data; |
| | | Labelnumber = aGVDataDto.LabelNumber; |
| | | Productno = aGVDataDto.ProductNo; |
| | | Productname = aGVDataDto.ProductName; |
| | | Quantity = aGVDataDto.Quantity; |
| | | Uomcode = aGVDataDto.UomCode; |
| | | Producttype = aGVDataDto.ProductType; |
| | | Equipment = aGVDataDto.Equipment; |
| | | Productiondate = aGVDataDto.ProductionDate; |
| | | Lowerlimittime = aGVDataDto.LowerLimitTime; |
| | | Warningtime = aGVDataDto.WarningTime; |
| | | Overduetime = aGVDataDto.OverdueTime; |
| | | Define1 = aGVDataDto.Define1; |
| | | Define2 = aGVDataDto.Define2; |
| | | } |
| | | return this; |
| | | } |
| | | public AGVResponse Error(string msg) |
| | | { |
| | | Msg = msg; |
| | | Code = false; |
| | | return this; |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// AGVååºæ°æ®é¡¹ |
| | | /// </summary> |
| | | /// <summary> |
| | | /// AGVæ°æ®DTO |
| | | /// </summary> |
| | | public class AGVDataDto |
| | | { |
| | | /// <summary> |
| | | /// å
¥åºå£ç¼å· - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("devid")] |
| | | [StringLength(50, ErrorMessage = "å
¥åºå£ç¼å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string DevId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçå· - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("traynumber")] |
| | | [StringLength(50, ErrorMessage = "æçå·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TrayNumber { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ´æç»å«-å度 - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("group")] |
| | | [StringLength(50, ErrorMessage = "æ´æç»å«é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Group { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 宽度 - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("width")] |
| | | public int? Width { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°æ®å表 - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("data")] |
| | | public List<string> Data { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¡ç å· - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("labelnumber")] |
| | | [StringLength(50, ErrorMessage = "æ¡ç å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string LabelNumber { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç¼ç - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("productno")] |
| | | [StringLength(50, ErrorMessage = "ç©æç¼ç é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©ææè¿° - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("productname")] |
| | | [StringLength(50, ErrorMessage = "ç©ææè¿°é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°é - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("quantity")] |
| | | [StringLength(50, ErrorMessage = "æ°éé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Quantity { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åä½ - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("uomcode")] |
| | | [StringLength(50, ErrorMessage = "åä½é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string UomCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç±»å - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("producttype")] |
| | | [StringLength(50, ErrorMessage = "ç©æç±»åé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 产åºè®¾å¤ - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("equipment")] |
| | | [StringLength(50, ErrorMessage = "产åºè®¾å¤é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Equipment { get; set; } |
| | | |
| | | /// <summary> |
| | | /// äº§åºæ¶é´ - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("productiondate")] |
| | | [StringLength(50, ErrorMessage = "äº§åºæ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductionDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¸éæ¶é´ - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("lowerlimittime")] |
| | | [StringLength(50, ErrorMessage = "ä¸éæ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string LowerLimitTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢è¦æ¶é´ - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("warningtime")] |
| | | [StringLength(50, ErrorMessage = "é¢è¦æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string WarningTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¶
ææ¶é´ - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("overduetime")] |
| | | [StringLength(50, ErrorMessage = "è¶
ææ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string OverdueTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("define1")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段1é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 - éå¡« |
| | | /// </summary> |
| | | [JsonPropertyName("define2")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段2é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Define2 { get; set; } |
| | | } |
| | | /// <summary> |
| | | /// ä»»å¡åå»ºæ°æ®ä¼ è¾å¯¹è±¡ |
| | | /// </summary> |
| | | public class ApplyInOutDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskid")] |
| | | [Required(ErrorMessage = "ä»»å¡å·ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ä»»å¡å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 1-æ¨¡åæ®µï¼2-å·ç»æ®µ |
| | | /// </summary> |
| | | [JsonPropertyName("floor")] |
| | | [Required(ErrorMessage = "æ¥¼å±æ®µä¸è½ä¸ºç©º")] |
| | | [Range(1, 2, ErrorMessage = "æ¥¼å±æ®µå¼å¿
é¡»æ¯1æ2")] |
| | | public int Floor { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 1-é´æï¼2-鳿 |
| | | /// </summary> |
| | | [JsonPropertyName("yinyang")] |
| | | [Required(ErrorMessage = "é´é³æä¸è½ä¸ºç©º")] |
| | | [Range(1, 2, ErrorMessage = "é´é³æå¼å¿
é¡»æ¯1æ2")] |
| | | public int YinYang { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 1-å
¥åºï¼2-åºåº |
| | | /// </summary> |
| | | [JsonPropertyName("inout")] |
| | | [Required(ErrorMessage = "åºå
¥åºç±»åä¸è½ä¸ºç©º")] |
| | | [Range(1, 2, ErrorMessage = "åºå
¥åºç±»åå¼å¿
é¡»æ¯1æ2")] |
| | | public int InOut { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç±»å |
| | | /// </summary> |
| | | [JsonPropertyName("materialtype")] |
| | | [Required(ErrorMessage = "ç©æç±»åä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ç©æç±»åé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string MaterialType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©ææè¿° |
| | | /// </summary> |
| | | [JsonPropertyName("materialname")] |
| | | [Required(ErrorMessage = "ç©ææè¿°ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ç©ææè¿°é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string MaterialName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçå· |
| | | /// </summary> |
| | | [JsonPropertyName("traynumber")] |
| | | [StringLength(50, ErrorMessage = "æçå·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TrayNumber { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ´æç»å«-å度 |
| | | /// </summary> |
| | | [JsonPropertyName("group")] |
| | | [StringLength(50, ErrorMessage = "æ´æç»å«é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Group { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 宽度 |
| | | /// </summary> |
| | | [JsonPropertyName("width")] |
| | | public int? Width { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°æ®éå |
| | | /// </summary> |
| | | [JsonPropertyName("data")] |
| | | public List<string> Data { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ¡ç å· |
| | | /// </summary> |
| | | [JsonPropertyName("labelnumber")] |
| | | [StringLength(50, ErrorMessage = "æ¡ç å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string LabelNumber { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç¼ç |
| | | /// </summary> |
| | | [JsonPropertyName("productno")] |
| | | [StringLength(50, ErrorMessage = "ç©æç¼ç é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©ææè¿° |
| | | /// </summary> |
| | | [JsonPropertyName("productname")] |
| | | [StringLength(50, ErrorMessage = "ç©ææè¿°é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æ°é |
| | | /// </summary> |
| | | [JsonPropertyName("quantity")] |
| | | [StringLength(50, ErrorMessage = "æ°éé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Quantity { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åä½ |
| | | /// </summary> |
| | | [JsonPropertyName("uomcode")] |
| | | [StringLength(50, ErrorMessage = "åä½é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string UomCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ç©æç±»å |
| | | /// </summary> |
| | | [JsonPropertyName("producttype")] |
| | | [StringLength(50, ErrorMessage = "ç©æç±»åé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 产åºè®¾å¤ |
| | | /// </summary> |
| | | [JsonPropertyName("equipment")] |
| | | [StringLength(50, ErrorMessage = "产åºè®¾å¤é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Equipment { get; set; } |
| | | |
| | | /// <summary> |
| | | /// äº§åºæ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("productiondate")] |
| | | [StringLength(50, ErrorMessage = "äº§åºæ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ProductionDate { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä¸éæ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("lowerlimittime")] |
| | | [StringLength(50, ErrorMessage = "ä¸éæ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string LowerLimitTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢è¦æ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("warningtime")] |
| | | [StringLength(50, ErrorMessage = "é¢è¦æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string WarningTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¶
ææ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("overduetime")] |
| | | [StringLength(50, ErrorMessage = "è¶
ææ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string OverdueTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 |
| | | /// </summary> |
| | | [JsonPropertyName("define1")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段1é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 |
| | | /// </summary> |
| | | [JsonPropertyName("define2")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段2é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Define2 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¯·æ±æ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("reqtime")] |
| | | [Required(ErrorMessage = "è¯·æ±æ¶é´ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "è¯·æ±æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ReqTime { get; set; } |
| | | } |
| | | /// <summary> |
| | | /// è¾é线ç³è¯·è¿å
¥è¯·æ±æ¨¡å |
| | | /// </summary> |
| | | public class ApplyEnterDto |
| | | { |
| | | /// <summary> |
| | | /// 设å¤ID |
| | | /// </summary> |
| | | [JsonPropertyName("devid")] |
| | | [Required(ErrorMessage = "设å¤IDä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "设å¤IDé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string DevId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åºå
¥åºç±»å 1-å
¥åºï¼2-åºåº |
| | | /// </summary> |
| | | [JsonPropertyName("inout")] |
| | | [Required(ErrorMessage = "åºå
¥åºç±»åä¸è½ä¸ºç©º")] |
| | | [Range(1, 2, ErrorMessage = "åºå
¥åºç±»åå¼å¿
é¡»æ¯1æ2")] |
| | | public int InOut { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskid")] |
| | | [Required(ErrorMessage = "ä»»å¡å·ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ä»»å¡å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 |
| | | /// </summary> |
| | | [JsonPropertyName("define1")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段1é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 |
| | | /// </summary> |
| | | [JsonPropertyName("define2")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段2é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string Define2 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¯·æ±æ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("reqtime")] |
| | | [Required(ErrorMessage = "è¯·æ±æ¶é´ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "è¯·æ±æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ReqTime { get; set; } |
| | | } |
| | | /// <summary> |
| | | /// åºåºå®æåé¦ |
| | | /// </summary> |
| | | public class OutTaskCompleteDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskid")] |
| | | [Required(ErrorMessage = "ä»»å¡å·ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ä»»å¡å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TaskId { get; set; } |
| | | /// <summary> |
| | | /// åºåºå£ç¼å· |
| | | /// </summary> |
| | | [JsonPropertyName("devid")] |
| | | [Required(ErrorMessage = "åºåºå£ç¼å·ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "åºåºå£ç¼å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string DevId { get; set; } |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 |
| | | /// </summary> |
| | | [JsonPropertyName("define1")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段1é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string? Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 |
| | | /// </summary> |
| | | [JsonPropertyName("define2")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段2é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string? Define2 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¯·æ±æ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("reqtime")] |
| | | [Required(ErrorMessage = "è¯·æ±æ¶é´ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "è¯·æ±æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ReqTime { get; set; } |
| | | } |
| | | /// <summary> |
| | | /// åæ¾è´§å®æè¯·æ±æ¨¡å |
| | | /// </summary> |
| | | public class TaskCompleteDto |
| | | { |
| | | /// <summary> |
| | | /// 设å¤ID |
| | | /// </summary> |
| | | [JsonPropertyName("devid")] |
| | | [Required(ErrorMessage = "设å¤IDä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "设å¤IDé¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string DevId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åºå
¥åºç±»å 1-å
¥åºï¼2-åºåº |
| | | /// </summary> |
| | | [JsonPropertyName("inout")] |
| | | [Required(ErrorMessage = "åºå
¥åºç±»åä¸è½ä¸ºç©º")] |
| | | [Range(1, 2, ErrorMessage = "åºå
¥åºç±»åå¼å¿
é¡»æ¯1æ2")] |
| | | public int InOut { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskid")] |
| | | [Required(ErrorMessage = "ä»»å¡å·ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ä»»å¡å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 |
| | | /// </summary> |
| | | [JsonPropertyName("define1")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段1é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string? Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 |
| | | /// </summary> |
| | | [JsonPropertyName("define2")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段2é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string? Define2 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¯·æ±æ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("reqtime")] |
| | | [Required(ErrorMessage = "è¯·æ±æ¶é´ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "è¯·æ±æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ReqTime { get; set; } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡åæ¶æ°æ®ä¼ è¾å¯¹è±¡ |
| | | /// </summary> |
| | | public class TaskCancelDto |
| | | { |
| | | /// <summary> |
| | | /// ä»»å¡å· |
| | | /// </summary> |
| | | [JsonPropertyName("taskid")] |
| | | [Required(ErrorMessage = "ä»»å¡å·ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "ä»»å¡å·é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string TaskId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段1 |
| | | /// </summary> |
| | | [JsonPropertyName("define1")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段1é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string? Define1 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// é¢çèªå®ä¹å段2 |
| | | /// </summary> |
| | | [JsonPropertyName("define2")] |
| | | [StringLength(50, ErrorMessage = "èªå®ä¹å段2é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string? Define2 { get; set; } |
| | | |
| | | /// <summary> |
| | | /// è¯·æ±æ¶é´ |
| | | /// </summary> |
| | | [JsonPropertyName("reqtime")] |
| | | [Required(ErrorMessage = "è¯·æ±æ¶é´ä¸è½ä¸ºç©º")] |
| | | [StringLength(50, ErrorMessage = "è¯·æ±æ¶é´é¿åº¦ä¸è½è¶
è¿50个å符")] |
| | | public string ReqTime { get; set; } |
| | | } |
| | | } |
| | |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_IStockService |
| | |
| | | /// <param name="locationCode">è´§ä½ç¼ç </param> |
| | | /// <returns>åºåä¿¡æ¯</returns> |
| | | Task<Dt_StockInfo> GetStockInfoAsync(string palletCode, string locationCode); |
| | | |
| | | /// <summary> |
| | | /// è·åä»åº3Då¸å±æ°æ® |
| | | /// </summary> |
| | | /// <param name="warehouseId">ä»åºID</param> |
| | | /// <returns>3Då¸å±DTO</returns> |
| | | Task<Stock3DLayoutDTO> Get3DLayoutAsync(int warehouseId); |
| | | } |
| | | } |
| | |
| | | /// <param name="stock"></param> |
| | | /// <returns></returns> |
| | | Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock); |
| | | |
| | | |
| | | #region æå·åºä»»å¡æ¨¡å |
| | | /// <summary> |
| | | /// åºå
¥åºç³è¯· |
| | | /// </summary> |
| | | /// <param name="applyInOutDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public Task<AGVResponse> ApplyInOutAsync(ApplyInOutDto applyInOutDto); |
| | | |
| | | /// <summary> |
| | | /// æå¨åºåºå®æåé¦ç»AGV |
| | | /// </summary> |
| | | /// <param name="outTaskCompleteDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public Task<WebResponseContent> OutTaskComplete(OutTaskCompleteDto outTaskCompleteDto); |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡å®ææ¥å£ |
| | | /// </summary> |
| | | /// <param name="wCSTask"></param> |
| | | /// <returns></returns> |
| | | //public Task<WebResponseContent> TaskCompleted(WCSTaskDTO wCSTask); |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡åæ¶ |
| | | /// </summary> |
| | | /// <param name="taskCancelDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public Task<AGVResponse> TaskCancelAsync(TaskCancelDto taskCancelDto); |
| | | /// <summary> |
| | | /// åæ¾è´§å®æ |
| | | /// </summary> |
| | | /// <param name="taskCompleteDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public Task<AGVResponse> TaskCompleteAsync(TaskCompleteDto taskCompleteDto); |
| | | /// <summary> |
| | | /// è¾é线ç³è¯·è¿å
¥ |
| | | /// </summary> |
| | | /// <param name="applyEnterDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public Task<AGVResponse> ApplyEnterAsync(ApplyEnterDto applyEnterDto); |
| | | |
| | | #endregion æå·åºä»»å¡æ¨¡å |
| | | } |
| | | } |
| | |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_DTO.Stock; |
| | |
| | | |
| | | public interface ITask_HtyService : IService<Dt_Task_Hty> |
| | | { |
| | | /// <summary> |
| | | /// è·åä»»å¡åå²ä»å¨æ¥å£ |
| | | /// </summary> |
| | | IRepository<Dt_Task_Hty> Repository { get; } |
| | | } |
| | |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_Model.Models; |
| | | |
| | |
| | | public IRepository<Dt_StockInfo> Repository => BaseDal; |
| | | |
| | | /// <summary> |
| | | /// è´§ä½ä¿¡æ¯æå¡æ¥å£ï¼ç¨äºè·åä»åºè´§ä½ä¿¡æ¯ï¼ |
| | | /// </summary> |
| | | private readonly ILocationInfoService _locationInfoService; |
| | | |
| | | /// <summary> |
| | | /// ä»åºä¿¡æ¯æå¡æ¥å£ï¼ç¨äºè·åä»åºåºæ¬ä¿¡æ¯ï¼ |
| | | /// </summary> |
| | | private readonly IWarehouseService _warehouseService; |
| | | |
| | | /// <summary> |
| | | /// æé 彿° |
| | | /// </summary> |
| | | /// <param name="baseDal">åºç¡æ°æ®è®¿é®å¯¹è±¡</param> |
| | | public StockInfoService(IRepository<Dt_StockInfo> baseDal) : base(baseDal) |
| | | public StockInfoService(IRepository<Dt_StockInfo> baseDal, ILocationInfoService locationInfoService, IWarehouseService warehouseService) : base(baseDal) |
| | | { |
| | | _locationInfoService = locationInfoService; |
| | | _warehouseService = warehouseService; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | return await BaseDal.QueryFirstAsync(x => x.PalletCode == palletCode && x.LocationCode == locationCode); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åä»åº3Då¸å±æ°æ® |
| | | /// </summary> |
| | | /// <param name="warehouseId">ä»åºID</param> |
| | | /// <returns>3Då¸å±DTO</returns> |
| | | public async Task<Stock3DLayoutDTO> Get3DLayoutAsync(int warehouseId) |
| | | { |
| | | // 1. æ¥è¯¢ä»åºä¿¡æ¯ |
| | | var warehouse = await _warehouseService.Repository.QueryFirstAsync(x => x.WarehouseId == warehouseId); |
| | | |
| | | // 2. æ¥è¯¢è¯¥ä»åºææè´§ä½ |
| | | var locations = await _locationInfoService.Repository.QueryDataAsync(x => x.WarehouseId == warehouseId); |
| | | |
| | | // 3. æ¥è¯¢è¯¥ä»åºææåºåä¿¡æ¯ï¼å
å«Details导èªå±æ§ï¼ |
| | | var stockInfos = await Repository.QueryDataNavAsync(x => x.WarehouseId == warehouseId && x.LocationId != 0); |
| | | |
| | | // 4. æåç©æç¼å·åæ¹æ¬¡å·å表ï¼å»éï¼ |
| | | var materielCodeList = stockInfos |
| | | .Where(s => s.Details != null) |
| | | .SelectMany(s => s.Details) |
| | | .Select(d => d.MaterielCode) |
| | | .Where(c => !string.IsNullOrEmpty(c)) |
| | | .Distinct() |
| | | .ToList(); |
| | | |
| | | var batchNoList = stockInfos |
| | | .Where(s => s.Details != null) |
| | | .SelectMany(s => s.Details) |
| | | .Select(d => d.BatchNo) |
| | | .Where(b => !string.IsNullOrEmpty(b)) |
| | | .Distinct() |
| | | .ToList(); |
| | | |
| | | // 5. å建åºååå
¸ç¨äºå¿«éæ¥æ¾ï¼ä»¥LocationId为é®ï¼ |
| | | var stockDict = stockInfos.ToDictionary(s => s.LocationId, s => s); |
| | | |
| | | // 6. æ å°æ¯ä¸ªè´§ä½å°Location3DItemDTO |
| | | const float defaultMaxCapacity = 100f; |
| | | var locationItems = locations.Select(loc => |
| | | { |
| | | var item = new Location3DItemDTO |
| | | { |
| | | LocationId = loc.Id, |
| | | LocationCode = loc.LocationCode, |
| | | Row = loc.Row, |
| | | Column = loc.Column, |
| | | Layer = loc.Layer, |
| | | LocationStatus = loc.LocationStatus, |
| | | MaxCapacity = defaultMaxCapacity |
| | | }; |
| | | |
| | | // å°è¯ä»åºååå
¸ä¸è·ååºåä¿¡æ¯ |
| | | if (stockDict.TryGetValue(loc.Id, out var stockInfo)) |
| | | { |
| | | // 空æç乿åºåè®°å½ï¼åªæ¯ä¸å
å«æç» |
| | | item.PalletCode = stockInfo.PalletCode; |
| | | item.StockStatus = stockInfo.StockStatus; // ç´æ¥ä½¿ç¨å端åºåç¶æ |
| | | |
| | | // åªæå½Detailsä¸ä¸ºnullä¸ææ°æ®æ¶æå¤çåºåæç» |
| | | if (stockInfo.Details != null && stockInfo.Details.Any()) |
| | | { |
| | | item.StockQuantity = stockInfo.Details.Sum(d => d.StockQuantity); |
| | | |
| | | // è·å第ä¸ä¸ªæç»çç©æä¿¡æ¯ï¼å¦æåå¨ï¼ |
| | | var firstDetail = stockInfo.Details.FirstOrDefault(); |
| | | if (firstDetail != null) |
| | | { |
| | | item.MaterielCode = firstDetail.MaterielCode; |
| | | item.MaterielName = firstDetail.MaterielName; |
| | | item.BatchNo = firstDetail.BatchNo; |
| | | } |
| | | |
| | | // å¡«å
åºåæç»å表 |
| | | item.Details = stockInfo.Details.Select(d => new StockDetailItemDTO |
| | | { |
| | | Id = d.Id, |
| | | MaterielCode = d.MaterielCode, |
| | | MaterielName = d.MaterielName, |
| | | BatchNo = d.BatchNo, |
| | | StockQuantity = d.StockQuantity, |
| | | Unit = d.Unit, |
| | | ProductionDate = d.ProductionDate, |
| | | EffectiveDate = d.EffectiveDate, |
| | | OrderNo = d.OrderNo, |
| | | Status = d.Status |
| | | }).ToList(); |
| | | } |
| | | else |
| | | { |
| | | // 空æçï¼æ æç»ï¼ |
| | | item.StockQuantity = 0; |
| | | item.Details = new List<StockDetailItemDTO>(); // ç¡®ä¿æ¯ç©ºå表èénull |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // æ åºåè®°å½ï¼è´§ä½ä¸ºç©º |
| | | item.StockStatus = 0; // ç©ºé² |
| | | item.StockQuantity = 0; |
| | | } |
| | | |
| | | return item; |
| | | }).ToList(); |
| | | |
| | | // 7. 计ç®ä»åºå°ºå¯¸ |
| | | var maxRow = locations.Any() ? locations.Max(l => l.Row) : 0; |
| | | var maxColumn = locations.Any() ? locations.Max(l => l.Column) : 0; |
| | | var maxLayer = locations.Any() ? locations.Max(l => l.Layer) : 0; |
| | | |
| | | // 8. æå»ºè¿åç»æ |
| | | return new Stock3DLayoutDTO |
| | | { |
| | | WarehouseId = warehouseId, |
| | | WarehouseName = warehouse?.WarehouseName ?? string.Empty, |
| | | MaxRow = maxRow, |
| | | MaxColumn = maxColumn, |
| | | MaxLayer = maxLayer, |
| | | MaterielCodeList = materielCodeList, |
| | | BatchNoList = batchNoList, |
| | | Locations = locationItems |
| | | }; |
| | | } |
| | | } |
| | | } |
| | |
| | | using SqlSugar; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_Model.Models; |
| | | |
| | |
| | | public IStockInfo_HtyService StockInfo_HtyService { get; } |
| | | |
| | | /// <summary> |
| | | /// Mesæ¥å£æå¡ |
| | | /// </summary> |
| | | public IMesService _mesService { get; } |
| | | |
| | | /// <summary> |
| | | /// æé 彿° |
| | | /// </summary> |
| | | /// <param name="stockInfoDetailService">åºåæç»æå¡</param> |
| | |
| | | IStockInfoDetailService stockInfoDetailService, |
| | | IStockInfoService stockInfoService, |
| | | IStockInfoDetail_HtyService stockInfoDetail_HtyService, |
| | | IStockInfo_HtyService stockInfo_HtyService) |
| | | IStockInfo_HtyService stockInfo_HtyService, |
| | | IMesService mesService) |
| | | { |
| | | StockInfoDetailService = stockInfoDetailService; |
| | | StockInfoService = stockInfoService; |
| | | StockInfoDetail_HtyService = stockInfoDetail_HtyService; |
| | | StockInfo_HtyService = stockInfo_HtyService; |
| | | _mesService = mesService; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | Status = StockStatusEmun.ç»çæå.GetHashCode(), |
| | | }).ToList(); |
| | | |
| | | var bindRequest = new BindContainerRequest |
| | | { |
| | | ContainerCode = stock?.TargetPalletNo, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = now, |
| | | OperationType = 0, // 0代表ç»ç |
| | | ContainerSfcList = details.Select(d => new ContainerSfcItem |
| | | { |
| | | Sfc = d.SerialNumber, |
| | | Location = d.InboundOrderRowNo.ToString(), |
| | | }).ToList() |
| | | }; |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | var existingStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.TargetPalletNo); |
| | |
| | | Creater = "system", |
| | | Details = details |
| | | }; |
| | | |
| | | result = StockInfoService.Repository.AddData(entity, x => x.Details); |
| | | return result ? content.OK("ç»çæå") : content.Error("ç»ç失败"); |
| | | if (!result) return content.Error("ç»ç失败"); |
| | | |
| | | var mesResult = _mesService.BindContainer(bindRequest); |
| | | if (mesResult == null || mesResult.Data == null || !mesResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"ç»çæåï¼ä½MESç»å®å¤±è´¥: {mesResult?.Data?.Msg ?? mesResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | return content.OK("ç»çæå"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | |
| | | if (await StockInfo_HtyService.Repository.AddDataAsync(CreateStockHistory(new[] { sourceStock, targetStock }, "æ¢ç")) <= 0) |
| | | return content.Error("æ¢çåå²è®°å½ä¿å失败"); |
| | | |
| | | // è°ç¨MESè§£ç»æºæççµè¯ |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = stock.SourcePalletNo, |
| | | SfcList = detailEntities.Select(d => d.SerialNumber).ToList() |
| | | }; |
| | | var unbindResult = _mesService.UnBindContainer(unbindRequest); |
| | | if (unbindResult == null || unbindResult.Data == null || !unbindResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"æ¢çæåï¼ä½MESè§£ç»å¤±è´¥: {unbindResult?.Data?.Msg ?? unbindResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | |
| | | detailEntities.ForEach(d => d.StockId = targetStock.Id); |
| | | var result = await StockInfoDetailService.Repository.UpdateDataAsync(detailEntities); |
| | | if (!result) return content.Error("æ¢ç失败"); |
| | | |
| | | // è°ç¨MESç»å®ç®æ æççµè¯ |
| | | var bindRequest = new BindContainerRequest |
| | | { |
| | | ContainerCode = stock.TargetPalletNo, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | OperationType = 0, |
| | | ContainerSfcList = detailEntities.Select(d => new ContainerSfcItem |
| | | { |
| | | Sfc = d.SerialNumber, |
| | | Location = d.InboundOrderRowNo.ToString() |
| | | }).ToList() |
| | | }; |
| | | var bindResult = _mesService.BindContainer(bindRequest); |
| | | if (bindResult == null || bindResult.Data == null || !bindResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"æ¢çæåï¼ä½MESç»å®å¤±è´¥: {bindResult?.Data?.Msg ?? bindResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | |
| | | return content.OK("æ¢çæå"); |
| | | }); |
| | | } |
| | |
| | | if (await StockInfo_HtyService.Repository.AddDataAsync(CreateStockHistory(new[] { sourceStock }, "æç")) <= 0) |
| | | return content.Error("æçåå²è®°å½ä¿å失败"); |
| | | |
| | | // è°ç¨MESè§£ç»çµè¯ |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = stock.SourcePalletNo, |
| | | SfcList = detailEntities.Select(d => d.SerialNumber).ToList() |
| | | }; |
| | | var unbindResult = _mesService.UnBindContainer(unbindRequest); |
| | | if (unbindResult == null || unbindResult.Data == null || !unbindResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"æçæåï¼ä½MESè§£ç»å¤±è´¥: {unbindResult?.Data?.Msg ?? unbindResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | |
| | | var result = await StockInfoDetailService.Repository.DeleteDataAsync(detailEntities); |
| | | if (!result) return content.Error("æç失败"); |
| | | return content.OK("æçæå"); |
| | |
| | | </PropertyGroup> |
| | | |
| | | <ItemGroup> |
| | | <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_IRecordService\WIDESEA_IRecordService.csproj" /> |
| | | <ProjectReference Include="..\WIDESEA_IStockService\WIDESEA_IStockService.csproj" /> |
| | | </ItemGroup> |
| | |
| | | using MapsterMapper; |
| | | using Microsoft.Extensions.Configuration; |
| | | using SqlSugar; |
| | | using System.DirectoryServices.Protocols; |
| | | using System.Text.Json; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Common.WareHouseEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_Core.Core; |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.GradingMachine; |
| | | using WIDESEA_DTO.MES; |
| | | using WIDESEA_DTO.Stock; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_IBasicService; |
| | |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | private readonly IConfiguration _configuration; |
| | | private readonly RoundRobinService _roundRobinService; |
| | | private readonly IMesService _mesService; |
| | | private readonly ITask_HtyService _task_HtyService; |
| | | private readonly IStockInfo_HtyService _stockInfo_HtyService; |
| | | private readonly IUnitOfWorkManage _unitOfWorkManage; |
| | | |
| | | public IRepository<Dt_Task> Repository => BaseDal; |
| | | |
| | |
| | | ILocationInfoService locationInfoService, |
| | | HttpClientHelper httpClientHelper, |
| | | IConfiguration configuration, |
| | | RoundRobinService roundRobinService) : base(BaseDal) |
| | | RoundRobinService roundRobinService, |
| | | IMesService mesService, |
| | | ITask_HtyService task_HtyService, |
| | | IStockInfo_HtyService stockInfo_HtyService, |
| | | IUnitOfWorkManage unitOfWorkManage) : base(BaseDal) |
| | | { |
| | | _mapper = mapper; |
| | | _stockInfoService = stockInfoService; |
| | |
| | | _httpClientHelper = httpClientHelper; |
| | | _configuration = configuration; |
| | | _roundRobinService = roundRobinService; |
| | | _mesService = mesService; |
| | | _task_HtyService = task_HtyService; |
| | | _stockInfo_HtyService = stockInfo_HtyService; |
| | | _unitOfWorkManage = unitOfWorkManage; |
| | | } |
| | | |
| | | #region WCSé»è¾å¤ç |
| | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | // 夿æ¯ä¸æ¯æå·åºä»»å¡ |
| | | //if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1) |
| | | //{ |
| | | // return WebResponseContent.Instance.OK(); |
| | | //} |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | WebResponseContent content = new WebResponseContent(); |
| | | stockInfo.LocationCode = location.LocationCode; |
| | | stockInfo.LocationId = location.Id; |
| | | stockInfo.OutboundDate = task.Roadway switch |
| | |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | return await CompleteTaskAsync(task); |
| | | // è°ç¨MESæçè¿ç« |
| | | var inboundRequest = new InboundInContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = taskDto.PalletCode |
| | | }; |
| | | var inboundResult = _mesService.InboundInContainer(inboundRequest); |
| | | if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESè¿ç«å¤±è´¥: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | return await CompleteTaskAsync(task, "å
¥åºå®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | // 夿æ¯ä¸æ¯æå·åºä»»å¡ |
| | | if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1) |
| | | { |
| | | OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto() |
| | | { |
| | | TaskId = task.OrderNo, |
| | | DevId = task.TargetAddress, |
| | | ReqTime = DateTime.Now.ToString() |
| | | }; |
| | | return await OutTaskComplete(outTaskCompleteDto); |
| | | } |
| | | |
| | | WebResponseContent content = new WebResponseContent(); |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationId = 0; |
| | |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | return await CompleteTaskAsync(task); |
| | | |
| | | // è°ç¨MESæçåºç« |
| | | var outboundRequest = new OutboundInContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = taskDto.PalletCode, |
| | | ParamList = new List<ParamItem>() |
| | | }; |
| | | var outboundResult = _mesService.OutboundInContainer(outboundRequest); |
| | | if (outboundResult == null || outboundResult.Data == null || !outboundResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESåºç«å¤±è´¥: {outboundResult?.Data?.Msg ?? outboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | |
| | | return await CompleteTaskAsync(task, "åºåºå®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | |
| | | if (!updateSourceResult || !updateTargetResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ç§»åºä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return await CompleteTaskAsync(task); |
| | | return await CompleteTaskAsync(task, "ç§»åºå®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 空æçå
¥åºå®æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationCode = location.LocationCode; |
| | | stockInfo.LocationId = location.Id; |
| | | stockInfo.StockStatus = StockStatusEmun.空æçåºå.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | // ä¿åä»»å¡åå² |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = "空æçå
¥åºå®æ"; |
| | | if (await _task_HtyService.Repository.AddDataAsync(historyTask) <= 0) |
| | | return WebResponseContent.Instance.Error("ä»»å¡åå²ä¿å失败"); |
| | | |
| | | // ä¿ååºååå² |
| | | var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo); |
| | | historyStock.InsertTime = DateTime.Now; |
| | | historyStock.OperateType = "空æçå
¥åºå®æ"; |
| | | if (await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) <= 0) |
| | | return WebResponseContent.Instance.Error("åºååå²ä¿å失败"); |
| | | |
| | | var deleteResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å建空æçåºåºä»»å¡ |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| | |
| | | { |
| | | try |
| | | { |
| | | |
| | | var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空æçåºå.GetHashCode()); |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçåºåä¿¡æ¯"); |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 空æçåºåºå®æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationId = 0; |
| | | stockInfo.LocationCode = null; |
| | | stockInfo.StockStatus = StockStatusEmun.åºåºå®æ.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | // ä¿åä»»å¡åå² |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = "空æçåºåºå®æ"; |
| | | if (await _task_HtyService.Repository.AddDataAsync(historyTask) <= 0) |
| | | return WebResponseContent.Instance.Error("ä»»å¡åå²ä¿å失败"); |
| | | |
| | | // ä¿ååºååå² |
| | | var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo); |
| | | historyStock.InsertTime = DateTime.Now; |
| | | historyStock.OperateType = "空æçåºåºå®æ"; |
| | | if (await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) <= 0) |
| | | return WebResponseContent.Instance.Error("åºååå²ä¿å失败"); |
| | | |
| | | var deleteResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä¿®æ¹ä»»å¡ç¶æï¼æ ¹æ®ä»»å¡IDä¿®æ¹ä¸ºæå®ç¶æï¼ |
| | | /// </summary> |
| | | /// <param name="taskId"></param> |
| | |
| | | return WebResponseContent.Instance.OK("ä¿®æ¹æå", tasks); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | { |
| | | return WebResponseContent.Instance.Error($"ä¿®æ¹å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ¥æ¾æçæ¯å¦æä»»å¡ |
| | |
| | | /// <summary> |
| | | /// 宿任å¡åç»ä¸å¤çï¼å é¤ä»»å¡æ°æ®ï¼ |
| | | /// </summary> |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task) |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType = "") |
| | | { |
| | | var deleteTaskResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteTaskResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | // ä¿çåå²å¯¹è±¡æå»ºé»è¾ï¼åç»å¯æ¥å
¥åå²è¡¨è½åº |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = operateType; |
| | | var saveResult = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0; |
| | | if (!saveResult) return WebResponseContent.Instance.Error("ä»»å¡åå²ä¿å失败"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | } |
| | |
| | | } |
| | | |
| | | #endregion åå®¹ææ¥å£ |
| | | |
| | | #region æå·åºä»»å¡æ¨¡å |
| | | |
| | | public string AGV_OutTaskComplete = WIDESEA_Core.Helper.AppSettings.Configuration["AGV_OutTaskComplete"]; // 䏿¥AGVåºåºè¾éçº¿å®æ |
| | | public string WCS_ReceiveTask = WIDESEA_Core.Helper.AppSettings.Configuration["WCS_ReceiveTask"]; // WMSè¾é线任å¡ä¸å |
| | | |
| | | /// <summary> |
| | | /// åºå
¥åºç³è¯· |
| | | /// </summary> |
| | | /// <param name="applyInOutDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> ApplyInOutAsync(ApplyInOutDto applyInOutDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | // åæ°éªè¯ |
| | | if (applyInOutDto == null) return aGVResponse.Error("请æ±åæ°ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.TrayNumber)) return aGVResponse.Error("æçå·ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.TaskId)) return aGVResponse.Error("ä»»å¡å·ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.MaterialType)) return aGVResponse.Error("ç©æç±»åä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.MaterialName)) return aGVResponse.Error("ç©ææè¿°ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.ReqTime)) return aGVResponse.Error("è¯·æ±æ¶é´ä¸è½ä¸ºç©º"); |
| | | if (applyInOutDto.Floor != 1 && applyInOutDto.Floor != 2) return aGVResponse.Error($"æ¥¼å±æ®µé误ï¼å¿
须为1(æ¨¡åæ®µ)æ2(å·ç»æ®µ)ï¼å½åå¼ï¼{applyInOutDto.Floor}"); |
| | | if (applyInOutDto.YinYang != 1 && applyInOutDto.YinYang != 2) return aGVResponse.Error($"é´é³æé误ï¼å¿
须为1(é´æ)æ2(鳿)ï¼å½åå¼ï¼{applyInOutDto.YinYang}"); |
| | | if (applyInOutDto.InOut != 1 && applyInOutDto.InOut != 2) return aGVResponse.Error($"åºå
¥åºç±»åé误ï¼å¿
须为1(å
¥åº)æ2(åºåº)ï¼å½åå¼ï¼{applyInOutDto.InOut}"); |
| | | if (applyInOutDto.InOut == 1) // å
¥åº |
| | | { |
| | | if (applyInOutDto.Width == null || applyInOutDto.Width <= 0) return aGVResponse.Error("å
¥åºæ¶å®½åº¦ä¸è½ä¸ºç©ºä¸å¿
须大äº0"); |
| | | if (string.IsNullOrWhiteSpace(applyInOutDto.Group)) return aGVResponse.Error("å
¥åºæ¶æ´æç»å«ä¸è½ä¸ºç©º"); |
| | | } |
| | | // æ£æ¥ä»»å¡æ¯å¦å·²åå¨ |
| | | var existingTask = await BaseDal.QueryFirstAsync(x => x.PalletCode == applyInOutDto.TrayNumber); |
| | | if (existingTask != null) return aGVResponse.Error($"WMSå·²æå½åä»»å¡ï¼ä¸å¯éå¤ä¸åï¼ä»»å¡å·ï¼{applyInOutDto.TaskId}"); |
| | | // åå»ºä»»å¡ |
| | | Dt_Task task = new Dt_Task |
| | | { |
| | | OrderNo = applyInOutDto.TaskId, // 使ç¨AGVçä»»å¡å· |
| | | PalletCode = applyInOutDto.TrayNumber, |
| | | PalletType = applyInOutDto.Floor, |
| | | //WarehouseId = applyInOutDto.YinYang, |
| | | Grade = 1, |
| | | Creater = "AGV", |
| | | CreateDate = DateTime.Now, |
| | | Remark = $"ç©æç±»åï¼{applyInOutDto.MaterialType}ï¼ç©ææè¿°ï¼{applyInOutDto.MaterialName}" |
| | | }; |
| | | // æ ¹æ®é´æ/é³æè®¾ç½®å··éåç«å° |
| | | if (applyInOutDto.YinYang == 1) // é´æ |
| | | { |
| | | // è´§ç©å°è¾¾ååé
è´§ç©ä½ |
| | | task.Roadway = WarehouseEnum.FJ1.ToString(); |
| | | task.WarehouseId = (int)WarehouseEnum.FJ1; |
| | | task.SourceAddress = applyInOutDto.InOut == 1 ? "D10010" : "D10020"; // å
¥åºå£/åºåºå£ |
| | | task.NextAddress = "D10080"; // é´æå
Œ
±åºåºå£ |
| | | task.TargetAddress = "é´æå·åº"; |
| | | } |
| | | else if (applyInOutDto.YinYang == 2) // 鳿 |
| | | { |
| | | task.Roadway = WarehouseEnum.ZJ1.ToString(); |
| | | task.WarehouseId = (int)WarehouseEnum.ZJ1; |
| | | task.SourceAddress = applyInOutDto.InOut == 1 ? "D10100" : "D10090"; // å
¥åºå£/åºåºå£ |
| | | task.NextAddress = "D10160"; // 鳿å
Œ
±åºåºå£ |
| | | task.TargetAddress = "æ£æå·åº"; |
| | | } |
| | | // æ ¹æ®åºå
¥åºç±»åè®¾ç½®ç®æ å°å |
| | | if (applyInOutDto.InOut == 1) // å
¥åº |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(applyInOutDto.TrayNumber); |
| | | if (stockInfo != null) return aGVResponse.Error($"å½åæç{applyInOutDto.TrayNumber}å·²ç»å
¥åºäº"); |
| | | |
| | | task.TaskType = (int)TaskInboundTypeEnum.Inbound; |
| | | task.TaskStatus = (int)TaskInStatusEnum.InNew; |
| | | task.CurrentAddress = task.SourceAddress; // å½åå¨å
¥åºå£ |
| | | // ä¿åä»»å¡ |
| | | var result = await BaseDal.AddDataAsync(task); |
| | | } |
| | | else // åºåº |
| | | { |
| | | // 夿æ¯å¦ç§»åº |
| | | task.TaskType = (int)TaskOutboundTypeEnum.Outbound; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutNew; |
| | | task.CurrentAddress = task.SourceAddress; // å½åå¨å
¥åºå£ |
| | | // æ¥æ¾åºå |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(applyInOutDto.TrayNumber); |
| | | if (stockInfo == null) return aGVResponse.Error($"æªæ¾å°æç{applyInOutDto.TrayNumber}çåºåä¿¡æ¯"); |
| | | // éªè¯åºåæå±æ¯å¦æ£ç¡® |
| | | if (stockInfo.WarehouseId != applyInOutDto.YinYang) return aGVResponse.Error($"æç{applyInOutDto.TrayNumber}ä¸å±äºå½å{(applyInOutDto.YinYang == 1 ? "é´æ" : "鳿")}"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.å
¥åºå®æ) return aGVResponse.Error($"æç{applyInOutDto.TrayNumber}æ£å¨ç§»å¨ä¸ï¼è¯·ç¨åï¼"); |
| | | task.SourceAddress = stockInfo.LocationCode; // æºå°åä¸ºè´§ä½ |
| | | task.CurrentAddress = stockInfo.LocationCode; // å½åä½ç½®å¨è´§ä½ |
| | | task.TargetAddress = applyInOutDto.YinYang == 1 ? "D10020" : "D10090"; // ç®æ å°å为åºåºå£ |
| | | |
| | | // ä¿åä»»å¡ |
| | | var result = await BaseDal.AddDataAsync(task); |
| | | // 夿æ¯å¦ç§»åº |
| | | //var transferTask = await _locationInfoService.TransferCheckAsync(result); |
| | | } |
| | | |
| | | // æå»ºååºæ°æ® |
| | | AGVDataDto aGVDataDto = new AGVDataDto |
| | | { |
| | | DevId = applyInOutDto.InOut == 1 ? task.SourceAddress : task.TargetAddress, |
| | | TrayNumber = task.PalletCode, |
| | | Group = applyInOutDto.Group, |
| | | Width = applyInOutDto.Width ?? 0, |
| | | LabelNumber = applyInOutDto.LabelNumber, |
| | | ProductNo = applyInOutDto.ProductNo, |
| | | ProductName = applyInOutDto.ProductName, |
| | | Quantity = applyInOutDto.Quantity, |
| | | UomCode = applyInOutDto.UomCode, |
| | | ProductType = applyInOutDto.ProductType, |
| | | Equipment = applyInOutDto.Equipment, |
| | | ProductionDate = applyInOutDto.ProductionDate, |
| | | LowerLimitTime = applyInOutDto.LowerLimitTime, |
| | | WarningTime = applyInOutDto.WarningTime, |
| | | OverdueTime = applyInOutDto.OverdueTime |
| | | }; |
| | | |
| | | return aGVResponse.OK(aGVDataDto); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return aGVResponse.Error($"WMSä»»å¡å建æ¥å£é误: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå¨åºåºå®æåé¦ç»AGV |
| | | /// </summary> |
| | | /// <param name="outTaskCompleteDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public async Task<WebResponseContent> OutTaskComplete(OutTaskCompleteDto outTaskCompleteDto) |
| | | { |
| | | WebResponseContent webResponse = new WebResponseContent(); |
| | | try |
| | | { |
| | | if (outTaskCompleteDto == null) return webResponse.Error("请æ±åæ°ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(outTaskCompleteDto.TaskId)) return webResponse.Error("ä»»å¡å·ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(outTaskCompleteDto.DevId)) return webResponse.Error("åºåºå£ç¼å·ä¸è½ä¸ºç©º"); |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == outTaskCompleteDto.TaskId); |
| | | if (task == null) return webResponse.Error("æªæ¾å°ä»»å¡ä¿¡æ¯"); |
| | | outTaskCompleteDto.ReqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); |
| | | var httpResponse = _httpClientHelper.Post<AGVResponse>(AGV_OutTaskComplete, outTaskCompleteDto.ToJson()).Data; |
| | | // 夿è¿ç¨æ¥å£è¿åæ¯å¦æå |
| | | if (httpResponse != null && httpResponse.Data != null) |
| | | { |
| | | AGVResponse agvResponse = httpResponse; |
| | | // æ ¹æ®codeåæ®µå¤æï¼code为true表示æå |
| | | if (agvResponse.Code == true) |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) return webResponse.Error($"æªæ¾å°æç{task.PalletCode}çåºåä¿¡æ¯"); |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) return webResponse.Error($"æªæ¾å°æç{stockInfo.LocationCode}çè´§ä½ä¿¡æ¯"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.åºåºéå® || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | { |
| | | return webResponse.Error($"å½ååºå{stockInfo.StockStatus}æè
è´§ä½{locationInfo.LocationStatus}ç¶æä¿¡æ¯é误"); |
| | | } |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.Free; |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.DeleteData(task); |
| | | _locationInfoService.UpdateData(locationInfo); |
| | | _stockInfoService.DeleteData(stockInfo); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return webResponse.OK(agvResponse.Msg); |
| | | } |
| | | else |
| | | { |
| | | // 失败ï¼è¿åAGVè¿åçéè¯¯ä¿¡æ¯ |
| | | return webResponse.Error(string.IsNullOrWhiteSpace(agvResponse.Msg) |
| | | ? "AGVæ¥å£è°ç¨å¤±è´¥" |
| | | : agvResponse.Msg); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // HTTPè¯·æ±æ¬èº«å¤±è´¥ |
| | | return webResponse.Error(httpResponse?.Msg ?? "AGVæ¥å£è°ç¨å¼å¸¸"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return webResponse.Error($"WMSä»»å¡å®ææ¥å£é误ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | ///// <summary> |
| | | ///// ä»»å¡å®ææ¥å£ |
| | | ///// </summary> |
| | | ///// <param name="wCSTask"></param> |
| | | ///// <returns></returns> |
| | | //public async Task<WebResponseContent> TaskCompleted(WCSTaskDTO wCSTask) |
| | | //{ |
| | | // WebResponseContent webResponse = new WebResponseContent(); |
| | | // try |
| | | // { |
| | | // Dt_Task task = await BaseDal.QueryFirstAsync(x => x.TaskId == wCSTask.TaskNum && x.PalletCode == wCSTask.PalletCode); |
| | | // if (task == null) return webResponse.Error("æªæ¾å°ä»»å¡ä¿¡æ¯"); |
| | | // // åºåºå®æåé¦ |
| | | // OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto() |
| | | // { |
| | | // TaskId = task.OrderNo, |
| | | // DevId = task.TargetAddress, |
| | | // ReqTime = DateTime.Now.ToString() |
| | | // }; |
| | | // return await OutTaskComplete(outTaskCompleteDto); |
| | | // } |
| | | // catch (Exception ex) |
| | | // { |
| | | // return webResponse.Error($"WMSä»»å¡å®ææ¥å£é误ï¼{ex.Message}"); |
| | | // } |
| | | //} |
| | | /// <summary> |
| | | /// è¾é线ç³è¯·è¿å
¥ |
| | | /// </summary> |
| | | /// <param name="applyEnterDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> ApplyEnterAsync(ApplyEnterDto applyEnterDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | // åæ°éªè¯ |
| | | if (applyEnterDto == null) return aGVResponse.Error("请æ±åæ°ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyEnterDto.DevId)) return aGVResponse.Error("设å¤ç¼å·ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(applyEnterDto.TaskId)) return aGVResponse.Error("ä»»å¡å·ä¸è½ä¸ºç©º"); |
| | | if (applyEnterDto.InOut != 1 && applyEnterDto.InOut != 2) return aGVResponse.Error($"åºå
¥åºç±»åé误ï¼å¿
须为1(å
¥åº)æ2(åºåº)ï¼å½åå¼ï¼{applyEnterDto.InOut}"); |
| | | |
| | | // æ¥è¯¢ä»»å¡æ¯å¦åå¨ |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == applyEnterDto.TaskId); |
| | | if (task == null) return aGVResponse.Error($"æªæ¾å°ä»»å¡ä¿¡æ¯ï¼ä»»å¡å·ï¼{applyEnterDto.TaskId}"); |
| | | if (applyEnterDto.InOut == 1 && task.TaskType == (int)TaskInboundTypeEnum.Inbound && task.TaskStatus == (int)TaskInStatusEnum.InNew) |
| | | { |
| | | aGVResponse.OK(); |
| | | } |
| | | else if (applyEnterDto.InOut == 2 && task.TaskType == (int)TaskOutboundTypeEnum.Outbound && task.TaskStatus == (int)TaskStatusEnum.Line_Finish) |
| | | { |
| | | aGVResponse.OK(); |
| | | } |
| | | return aGVResponse.Error($"è¾é线{applyEnterDto.DevId}å½åç¹å¿ï¼è¯·ç¨åéè¯"); |
| | | |
| | | // Dt_Task.SourceAddress询é®wcsç«å°1-é´æå
¥å£å«D10010åD10020ï¼2-鳿D10090åD10100æ¯å¦æè´§ç© |
| | | //var httpResponse = _httpClientHelper.Post<WebResponseContent>("http://127.0.0.1:9999/api/Task/ApplyInOut", JsonSerializer.Serialize(task)).Data; |
| | | //if (httpResponse != null && httpResponse.Status == true) |
| | | //{ |
| | | // _unitOfWorkManage.BeginTran(); |
| | | // task.TaskStatus = (int)TaskStatusEnum.AGV_Executing; |
| | | // await BaseDal.UpdateDataAsync(task); |
| | | // _unitOfWorkManage.CommitTran(); |
| | | // return aGVResponse.OK(); |
| | | //} |
| | | //else |
| | | //{ |
| | | // return aGVResponse.Error($"ç«å°{task.SourceAddress}å·²ç»è½½è´§"); |
| | | //} |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | //_unitOfWorkManage.RollbackTran(); |
| | | return aGVResponse.Error($"WMSè¾é线ç³è¯·æ¥å£é误ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¾è´§å®æ |
| | | /// </summary> |
| | | /// <param name="taskCompleteDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> TaskCompleteAsync(TaskCompleteDto taskCompleteDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | if (taskCompleteDto == null) return aGVResponse.Error("请æ±åæ°ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(taskCompleteDto.TaskId)) return aGVResponse.Error("ä»»å¡å·ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(taskCompleteDto.DevId)) return aGVResponse.Error("设å¤ç¼å·ä¸è½ä¸ºç©º"); |
| | | if (taskCompleteDto.InOut != 1 && taskCompleteDto.InOut != 2) return aGVResponse.Error($"åºå
¥åºç±»åé误ï¼å¿
须为1(å
¥åº)æ2(åºåº)ï¼å½åå¼ï¼{taskCompleteDto.InOut}"); |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCompleteDto.TaskId); |
| | | if (task == null) return aGVResponse.Error($"æªæ¾å°ä»»å¡ä¿¡æ¯ï¼ä»»å¡å·ï¼{taskCompleteDto.TaskId}"); |
| | | if (taskCompleteDto.InOut == 2) |
| | | { |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) return aGVResponse.Error($"æªæ¾å°æç{task.PalletCode}çåºåä¿¡æ¯"); |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) return aGVResponse.Error($"æªæ¾å°æç{stockInfo.LocationCode}çè´§ä½ä¿¡æ¯"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.åºåºéå® || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | { |
| | | return aGVResponse.Error($"å½ååºå{stockInfo.StockStatus}æè
è´§ä½{locationInfo.LocationStatus}ç¶æä¿¡æ¯é误"); |
| | | } |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.Free; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutFinish; |
| | | _unitOfWorkManage.BeginTran(); |
| | | _stockInfoService.DeleteData(stockInfo); |
| | | await _locationInfoService.UpdateLocationInfoAsync(locationInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.èªå¨å®æ : OperateTypeEnum.äººå·¥å®æ); |
| | | _unitOfWorkManage.CommitTran(); |
| | | } |
| | | else |
| | | { |
| | | //æ¥æ¾å¯ç¨è´§ä½ |
| | | var availableLocation = await _locationInfoService.GetLocationInfo(task.Roadway); |
| | | |
| | | if (availableLocation == null) return aGVResponse.Error("æ å¯ç¨çå
¥åºè´§ä½"); |
| | | |
| | | task.TargetAddress = availableLocation.LocationCode; |
| | | // éç¥WCSå
¥åº |
| | | var wcsTaskDto = _mapper.Map<WCSTaskDTO>(task); |
| | | var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, JsonSerializer.Serialize(wcsTaskDto)); |
| | | if (httpResponse == null) return aGVResponse.Error("ä¸åWCS失败"); |
| | | |
| | | task.TaskStatus = (int)TaskStatusEnum.Line_Executing; |
| | | task.Dispatchertime = DateTime.Now; |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress); |
| | | if (locationInfo == null) return aGVResponse.Error($"æªæ¾å°æç{task.TargetAddress}çè´§ä½ä¿¡æ¯"); |
| | | if (locationInfo.LocationStatus != (int)LocationStatusEnum.InStock) |
| | | { |
| | | return aGVResponse.Error($"å½åè´§ä½{locationInfo.LocationStatus}ç¶æä¿¡æ¯é误"); |
| | | } |
| | | Dt_StockInfo dt_Stock = new Dt_StockInfo() |
| | | { |
| | | PalletCode = task.PalletCode, |
| | | StockStatus = (int)StockStatusEmun.å
¥åºç¡®è®¤, |
| | | LocationCode = locationInfo.LocationCode, |
| | | WarehouseId = task.WarehouseId, |
| | | Creater = "AGV", |
| | | CreateDate = DateTime.Now |
| | | }; |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.FreeLock; |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.UpdateData(task); |
| | | _locationInfoService.UpdateData(locationInfo); |
| | | _stockInfoService.AddData(dt_Stock); |
| | | _unitOfWorkManage.CommitTran(); |
| | | } |
| | | return aGVResponse.OK(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return aGVResponse.Error($"WMSåæ¾è´§å®ææ¥å£é误ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡åæ¶ |
| | | /// </summary> |
| | | /// <param name="taskCancelDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | public async Task<AGVResponse> TaskCancelAsync(TaskCancelDto taskCancelDto) |
| | | { |
| | | AGVResponse aGVResponse = new AGVResponse(); |
| | | try |
| | | { |
| | | if (taskCancelDto == null) return aGVResponse.Error("请æ±åæ°ä¸è½ä¸ºç©º"); |
| | | if (string.IsNullOrWhiteSpace(taskCancelDto.TaskId)) return aGVResponse.Error("ä»»å¡å·ä¸è½ä¸ºç©º"); |
| | | |
| | | var task = await BaseDal.QueryFirstAsync(x => x.OrderNo == taskCancelDto.TaskId); |
| | | // 没æä»»å¡å¼ºå¶åæ¶ |
| | | if (task == null) return aGVResponse.OK(); |
| | | |
| | | if (task.TaskStatus == (int)TaskInStatusEnum.InNew) |
| | | { |
| | | task.TaskStatus = (int)TaskInStatusEnum.InCancel; |
| | | // å
¥åºä»»å¡ç´æ¥å é¤ |
| | | _unitOfWorkManage.BeginTran(); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.èªå¨å®æ : OperateTypeEnum.äººå·¥å®æ); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return aGVResponse.OK(); |
| | | } |
| | | else if (task.TaskStatus == (int)TaskOutStatusEnum.OutNew) |
| | | { |
| | | // åºåºä»»å¡æ¢å¤åºå |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode); |
| | | if (stockInfo == null) return aGVResponse.Error($"æªæ¾å°æç{task.PalletCode}çåºåä¿¡æ¯"); |
| | | var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode); |
| | | if (locationInfo == null) return aGVResponse.Error($"æªæ¾å°æç{stockInfo.LocationCode}çè´§ä½ä¿¡æ¯"); |
| | | if (stockInfo.StockStatus != (int)StockStatusEmun.åºåºéå® || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock) |
| | | { |
| | | return aGVResponse.Error($"å½ååºå{stockInfo.StockStatus}æè
è´§ä½{locationInfo.LocationStatus}ç¶æä¿¡æ¯é误"); |
| | | } |
| | | stockInfo.StockStatus = (int)StockStatusEmun.å
¥åºå®æ; |
| | | locationInfo.LocationStatus = (int)LocationStatusEnum.InStock; |
| | | task.TaskStatus = (int)TaskOutStatusEnum.OutCancel; |
| | | _unitOfWorkManage.BeginTran(); |
| | | _locationInfoService.UpdateData(locationInfo); |
| | | _stockInfoService.UpdateData(stockInfo); |
| | | BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.èªå¨å®æ : OperateTypeEnum.äººå·¥å®æ); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return aGVResponse.OK(); |
| | | } |
| | | return aGVResponse.Error("ä»»å¡å·²ç»å¨æ§è¡ä¸ï¼ä¸å¯åæ¶"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return aGVResponse.Error($"WMSä»»å¡åæ¶æ¥å£é误ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion æå·åºä»»å¡æ¨¡å |
| | | } |
| | | } |
| | | } |
| | |
| | | public class Task_HtyService : ServiceBase<Dt_Task_Hty, IRepository<Dt_Task_Hty>>, ITask_HtyService |
| | | { |
| | | /// <summary> |
| | | /// è·åä»»å¡åå²ä»å¨æ¥å£ |
| | | /// </summary> |
| | | public IRepository<Dt_Task_Hty> Repository => BaseDal; |
| | | |
| | | /// <summary> |
| | | /// æé 彿° |
| | | /// </summary> |
| | | /// <param name="baseDal">åºç¡æ°æ®è®¿é®å¯¹è±¡</param> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using Microsoft.AspNetCore.SignalR; |
| | | using Microsoft.Extensions.Hosting; |
| | | using Microsoft.Extensions.Logging; |
| | | using System; |
| | | using System.Collections.Concurrent; |
| | | using System.Linq; |
| | | using System.Threading; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_Model.Models; |
| | | using WIDESEA_WMSServer.Hubs; |
| | | |
| | | namespace WIDESEA_WMSServer.BackgroundServices |
| | | { |
| | | /// <summary> |
| | | /// åºåçæ§åå°æå¡ |
| | | /// å®ææ£æ¥åºååè´§ä½æ°æ®ååå¹¶éè¿SignalRæ¨éå°å端 |
| | | /// </summary> |
| | | public class StockMonitorBackgroundService : BackgroundService |
| | | { |
| | | private readonly ILogger<StockMonitorBackgroundService> _logger; |
| | | private readonly IHubContext<StockHub> _hubContext; |
| | | private readonly IServiceProvider _serviceProvider; |
| | | |
| | | // è´§ä½ç¶æå¿«ç
§ï¼key = LocationId |
| | | private ConcurrentDictionary<int, LocationSnapshot> _lastLocationSnapshots = new(); |
| | | |
| | | // çæ§é´éï¼æ¯«ç§ï¼ |
| | | private const int MonitorIntervalMs = 3000; |
| | | |
| | | public StockMonitorBackgroundService( |
| | | ILogger<StockMonitorBackgroundService> logger, |
| | | IHubContext<StockHub> hubContext, |
| | | IServiceProvider serviceProvider) |
| | | { |
| | | _logger = logger; |
| | | _hubContext = hubContext; |
| | | _serviceProvider = serviceProvider; |
| | | } |
| | | |
| | | protected override async Task ExecuteAsync(CancellationToken stoppingToken) |
| | | { |
| | | _logger.LogInformation("åºåçæ§åå°æå¡å·²å¯å¨"); |
| | | |
| | | // çå¾
åºç¨å®å
¨å¯å¨ |
| | | await Task.Delay(5000, stoppingToken); |
| | | |
| | | while (!stoppingToken.IsCancellationRequested) |
| | | { |
| | | try |
| | | { |
| | | await CheckChangesAsync(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError(ex, "æ£æ¥æ°æ®ååæ¶åçé误"); |
| | | } |
| | | |
| | | await Task.Delay(MonitorIntervalMs, stoppingToken); |
| | | } |
| | | |
| | | _logger.LogInformation("åºåçæ§åå°æå¡å·²åæ¢"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥è´§ä½ååºååå |
| | | /// </summary> |
| | | private async Task CheckChangesAsync() |
| | | { |
| | | using var scope = _serviceProvider.CreateScope(); |
| | | var stockService = scope.ServiceProvider.GetRequiredService<IStockInfoService>(); |
| | | var locationRepo = scope.ServiceProvider.GetRequiredService<IRepository<Dt_LocationInfo>>(); |
| | | |
| | | // 1. è·åææè´§ä½æ°æ® |
| | | var allLocations = await locationRepo.QueryDataAsync(x => x.LocationStatus != 99); // æé¤ç¦ç¨çè´§ä½ |
| | | |
| | | // 2. è·åææåºåæ°æ®ï¼å
嫿ç»ï¼ |
| | | var allStockData = await stockService.Repository.Db.Queryable<Dt_StockInfo>() |
| | | .Includes(x => x.Details) |
| | | .ToListAsync(); |
| | | |
| | | // æå»ºåºååå
¸ï¼LocationId -> StockInfo |
| | | var stockDict = allStockData |
| | | .Where(s => s.LocationId > 0) |
| | | .ToDictionary(s => s.LocationId, s => s); |
| | | |
| | | // æå»ºå½åè´§ä½å¿«ç
§åå
¸ |
| | | var currentSnapshots = new ConcurrentDictionary<int, LocationSnapshot>(); |
| | | |
| | | foreach (var location in allLocations) |
| | | { |
| | | // è·å该货ä½çåºåä¿¡æ¯ |
| | | stockDict.TryGetValue(location.Id, out var stock); |
| | | |
| | | // 计ç®åºåæ°é |
| | | float totalQuantity = 0; |
| | | string detailsHash = string.Empty; |
| | | if (stock?.Details != null && stock.Details.Any()) |
| | | { |
| | | totalQuantity = stock.Details.Sum(d => d.StockQuantity); |
| | | detailsHash = GenerateDetailsHash(stock.Details.ToList()); |
| | | } |
| | | |
| | | var snapshot = new LocationSnapshot |
| | | { |
| | | LocationId = location.Id, |
| | | WarehouseId = location.WarehouseId, |
| | | LocationCode = location.LocationCode, |
| | | LocationStatus = location.LocationStatus, |
| | | PalletCode = stock?.PalletCode, |
| | | StockStatus = stock?.StockStatus ?? 0, |
| | | StockQuantity = totalQuantity, |
| | | DetailsHash = detailsHash |
| | | }; |
| | | |
| | | currentSnapshots.TryAdd(location.Id, snapshot); |
| | | |
| | | // æ£æ¥æ¯å¦æåå |
| | | if (_lastLocationSnapshots.TryGetValue(location.Id, out var lastSnapshot)) |
| | | { |
| | | // æ£æµååï¼è´§ä½ç¶æãåºåç¶æãæ°éãæç»åå |
| | | if (lastSnapshot.LocationStatus != snapshot.LocationStatus || |
| | | lastSnapshot.StockStatus != snapshot.StockStatus || |
| | | lastSnapshot.PalletCode != snapshot.PalletCode || |
| | | Math.Abs(lastSnapshot.StockQuantity - snapshot.StockQuantity) > 0.001f || |
| | | lastSnapshot.DetailsHash != snapshot.DetailsHash) |
| | | { |
| | | // æå»ºæ´æ°DTO |
| | | var update = new StockUpdateDTO |
| | | { |
| | | LocationId = snapshot.LocationId, |
| | | WarehouseId = snapshot.WarehouseId, |
| | | PalletCode = snapshot.PalletCode, |
| | | StockQuantity = snapshot.StockQuantity, |
| | | StockStatus = snapshot.StockStatus, |
| | | LocationStatus = snapshot.LocationStatus, |
| | | Details = BuildDetailDtos(stock?.Details?.ToList()) |
| | | }; |
| | | |
| | | await _hubContext.Clients.All.SendAsync("StockUpdated", update); |
| | | _logger.LogDebug("æ°æ®å忍é: LocationId={LocationId}, LocStatus={LocStatus}, StockStatus={StockStatus}, Quantity={Quantity}", |
| | | snapshot.LocationId, snapshot.LocationStatus, snapshot.StockStatus, snapshot.StockQuantity); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æ´æ°å¿«ç
§æ°æ® |
| | | _lastLocationSnapshots = currentSnapshots; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// çææç»æ°æ®åå¸ |
| | | /// </summary> |
| | | private string GenerateDetailsHash(List<Dt_StockInfoDetail> details) |
| | | { |
| | | if (details == null || !details.Any()) return string.Empty; |
| | | |
| | | var hashString = string.Join("|", details |
| | | .OrderBy(d => d.Id) |
| | | .Select(d => $"{d.Id}:{d.MaterielCode}:{d.BatchNo}:{d.StockQuantity}")); |
| | | return hashString.GetHashCode().ToString(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æå»ºæç»DTOå表 |
| | | /// </summary> |
| | | private List<StockDetailUpdateDTO> BuildDetailDtos(List<Dt_StockInfoDetail> details) |
| | | { |
| | | if (details == null || !details.Any()) return new List<StockDetailUpdateDTO>(); |
| | | |
| | | return details.Select(d => new StockDetailUpdateDTO |
| | | { |
| | | Id = d.Id, |
| | | MaterielCode = d.MaterielCode, |
| | | MaterielName = d.MaterielName, |
| | | BatchNo = d.BatchNo, |
| | | StockQuantity = d.StockQuantity, |
| | | Unit = d.Unit, |
| | | Status = d.Status |
| | | }).ToList(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è´§ä½å¿«ç
§ |
| | | /// </summary> |
| | | private class LocationSnapshot |
| | | { |
| | | public int LocationId { get; set; } |
| | | public int WarehouseId { get; set; } |
| | | public string LocationCode { get; set; } |
| | | public int LocationStatus { get; set; } |
| | | public string PalletCode { get; set; } |
| | | public int StockStatus { get; set; } |
| | | public float StockQuantity { get; set; } |
| | | public string DetailsHash { get; set; } |
| | | } |
| | | } |
| | | } |
| | |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åææä»åº |
| | | /// </summary> |
| | | /// <returns>ä»åºå表</returns> |
| | | [HttpGet("GetAll")] |
| | | public async Task<WebResponseContent> GetAll() |
| | | { |
| | | var result = await Service.Repository.QueryDataAsync(x => x.WarehouseStatus == 1); |
| | | return WebResponseContent.Instance.OK(data: result); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using Microsoft.AspNetCore.Mvc; |
| | | using SqlSugar; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_WMSServer.Controllers.Dashboard |
| | | { |
| | | /// <summary> |
| | | /// 仪表ç |
| | | /// </summary> |
| | | [Route("api/Dashboard")] |
| | | [ApiController] |
| | | public class DashboardController : ControllerBase |
| | | { |
| | | private readonly ISqlSugarClient _db; |
| | | |
| | | public DashboardController(ISqlSugarClient db) |
| | | { |
| | | _db = db; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ»è§æ°æ® |
| | | /// </summary> |
| | | [HttpGet("Overview")] |
| | | public async Task<WebResponseContent> Overview() |
| | | { |
| | | try |
| | | { |
| | | var today = DateTime.Today; |
| | | var firstDayOfMonth = new DateTime(today.Year, today.Month, 1); |
| | | |
| | | // 仿¥å
¥åºæ° |
| | | var todayInbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 500 && t.TaskType < 600) |
| | | .CountAsync(); |
| | | |
| | | // 仿¥åºåºæ° |
| | | var todayOutbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 100 && t.TaskType < 200) |
| | | .CountAsync(); |
| | | |
| | | // æ¬æå
¥åºæ° |
| | | var monthInbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 500 && t.TaskType < 600) |
| | | .CountAsync(); |
| | | |
| | | // æ¬æåºåºæ° |
| | | var monthOutbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 100 && t.TaskType < 200) |
| | | .CountAsync(); |
| | | |
| | | // å½åæ»åºå |
| | | var totalStock = await _db.Queryable<Dt_StockInfo>().CountAsync(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, new |
| | | { |
| | | TodayInbound = todayInbound, |
| | | TodayOutbound = todayOutbound, |
| | | MonthInbound = monthInbound, |
| | | MonthOutbound = monthOutbound, |
| | | TotalStock = totalStock |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ»è§æ°æ®è·å失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¯æ¥ç»è®¡ |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 注æï¼æ°æ®å¨ SQL å±è¿æ»¤åï¼å¨åºç¨å±ææ¥æåç»ã |
| | | /// SqlSugar ç GroupBy 䏿¯æå¯¹ .Date è¿æ ·ç计ç®åç´æ¥çæ SQL GROUP BYï¼ |
| | | /// å æ¤éç¨æ¤æ¹å¼ä»¥ç¡®ä¿è·¨æ°æ®åºå
¼å®¹æ§ã |
| | | /// </remarks> |
| | | [HttpGet("DailyStats")] |
| | | public async Task<WebResponseContent> DailyStats([FromQuery] int days = 30) |
| | | { |
| | | try |
| | | { |
| | | if (days <= 0) days = 30; |
| | | if (days > 365) days = 365; |
| | | |
| | | var startDate = DateTime.Today.AddDays(-days + 1); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => t.InsertTime.Date) |
| | | .Select(g => new |
| | | { |
| | | Date = g.Key.ToString("yyyy-MM-dd"), |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Date) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ¯æ¥ç»è®¡è·å失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¯å¨ç»è®¡ |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 注æï¼æ°æ®å¨ SQL å±è¿æ»¤åï¼å¨åºç¨å±æ ISO 8601 å¨é®åç»ã |
| | | /// å¨é®ä¸º "YYYY-Www" æ ¼å¼ï¼æ æ³ç´æ¥å¨ SQL å±ç¨ GROUP BY å®ç°ã |
| | | /// </remarks> |
| | | [HttpGet("WeeklyStats")] |
| | | public async Task<WebResponseContent> WeeklyStats([FromQuery] int weeks = 12) |
| | | { |
| | | try |
| | | { |
| | | if (weeks <= 0) weeks = 12; |
| | | |
| | | var startDate = DateTime.Today.AddDays(-weeks * 7); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => GetWeekKey(t.InsertTime)) |
| | | .Select(g => new |
| | | { |
| | | Week = g.Key, |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Week) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ¯å¨ç»è®¡è·å失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | private string GetWeekKey(DateTime date) |
| | | { |
| | | // è·åå¨ä¸å¼å§çå¨ (ISO 8601) |
| | | var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7; |
| | | var monday = date.AddDays(-diff); |
| | | var weekNum = System.Globalization.CultureInfo.InvariantCulture |
| | | .Calendar.GetWeekOfYear(monday, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); |
| | | return $"{monday.Year}-W{weekNum:D2}"; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¯æç»è®¡ |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// 注æï¼æ°æ®å¨ SQL å±è¿æ»¤åï¼å¨åºç¨å±æå¹´æåç»ã |
| | | /// SqlSugar ç GroupBy 䏿¯æå¿å对象 (Year, Month) ç´æ¥æ å°å° SQL GROUP BYï¼ |
| | | /// å æ¤éç¨æ¤æ¹å¼ä»¥ç¡®ä¿è·¨æ°æ®åºå
¼å®¹æ§ã |
| | | /// </remarks> |
| | | [HttpGet("MonthlyStats")] |
| | | public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12) |
| | | { |
| | | try |
| | | { |
| | | if (months <= 0) months = 12; |
| | | |
| | | var startDate = DateTime.Today.AddMonths(-months + 1); |
| | | startDate = new DateTime(startDate.Year, startDate.Month, 1); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => new { t.InsertTime.Year, t.InsertTime.Month }) |
| | | .Select(g => new |
| | | { |
| | | Month = $"{g.Key.Year}-{g.Key.Month:D2}", |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Month) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æ¯æç»è®¡è·å失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºååºé¾åå¸ |
| | | /// </summary> |
| | | [HttpGet("StockAgeDistribution")] |
| | | public async Task<WebResponseContent> StockAgeDistribution() |
| | | { |
| | | try |
| | | { |
| | | var today = DateTime.Today; |
| | | |
| | | // ä½¿ç¨ SQL ç´æ¥åç»ç»è®¡ï¼é¿å
å è½½æææ°æ®å°å
å |
| | | var result = new[] |
| | | { |
| | | new { Range = "7天å
", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) <= 7).CountAsync() }, |
| | | new { Range = "7-30天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) > 7 && SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) <= 30).CountAsync() }, |
| | | new { Range = "30-90天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) > 30 && SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) <= 90).CountAsync() }, |
| | | new { Range = "90天以ä¸", Count = await _db.Queryable<Dt_StockInfo>().Where(s => SqlFunc.DateDiff(DateType.Day, s.CreateDate, today) > 90).CountAsync() } |
| | | }; |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"åºååºé¾åå¸è·å失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åä»åºåºååå¸ |
| | | /// </summary> |
| | | /// <remarks> |
| | | /// ä½¿ç¨ SQL GROUP BY 卿°æ®åºå±é¢èåï¼é¿å
å è½½å
¨é¨åºåè®°å½å°å
åã |
| | | /// </remarks> |
| | | [HttpGet("StockByWarehouse")] |
| | | public async Task<WebResponseContent> StockByWarehouse() |
| | | { |
| | | try |
| | | { |
| | | // æ¥è¯¢ä»åºåç§° |
| | | var warehouses = await _db.Queryable<Dt_Warehouse>() |
| | | .Select(w => new { w.WarehouseId, w.WarehouseName }) |
| | | .ToListAsync(); |
| | | var warehouseDict = warehouses.ToDictionary(w => w.WarehouseId, w => w.WarehouseName); |
| | | |
| | | // ä½¿ç¨ SQL GROUP BY 卿°æ®åºå±é¢èåï¼ä»
è¿åèåç»æ |
| | | var stockGroups = await _db.Queryable<Dt_StockInfo>() |
| | | .GroupBy(s => s.WarehouseId) |
| | | .Select(s => new { s.WarehouseId, Count = SqlFunc.AggregateCount(s.Id) }) |
| | | .ToListAsync(); |
| | | |
| | | var result = stockGroups |
| | | .Select(g => new |
| | | { |
| | | Warehouse = warehouseDict.TryGetValue(g.WarehouseId, out var name) ? name : $"ä»åº{g.WarehouseId}", |
| | | Count = g.Count |
| | | }) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"åä»åºåºååå¸è·å失败: {ex.Message}"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | { |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// è·åä»åº3Då¸å± |
| | | /// </summary> |
| | | /// <param name="warehouseId">ä»åºID</param> |
| | | /// <returns>3Då¸å±æ°æ®</returns> |
| | | [HttpGet("Get3DLayout")] |
| | | public async Task<WebResponseContent> Get3DLayout(int warehouseId) |
| | | { |
| | | var result = await Service.Get3DLayoutAsync(warehouseId); |
| | | return WebResponseContent.Instance.OK(data: result); |
| | | } |
| | | } |
| | | } |
| | |
| | | using Microsoft.AspNetCore.Authorization; |
| | | using Microsoft.AspNetCore.Http; |
| | | using Microsoft.AspNetCore.Mvc; |
| | | using System.DirectoryServices.Protocols; |
| | | using WIDESEA_Common.CommonEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseController; |
| | |
| | | { |
| | | return await Service.GetPalletCodeCellAsync(input); |
| | | } |
| | | |
| | | #region æå·åºä»»å¡æ¨¡å |
| | | |
| | | /// <summary> |
| | | /// åºå
¥åºç³è¯· |
| | | /// </summary> |
| | | /// <param name="applyInOutDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | [HttpPost("ApplyInOut"), AllowAnonymous] |
| | | public async Task<AGVResponse> ApplyInOutAsync([FromBody] ApplyInOutDto applyInOutDto) |
| | | { |
| | | return await Service.ApplyInOutAsync(applyInOutDto); |
| | | } |
| | | /// <summary> |
| | | /// æå¨åºåºå®æåé¦ç»AGV |
| | | /// </summary> |
| | | /// <param name="outTaskCompleteDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | [HttpPost, Route("OutTaskComplete"), AllowAnonymous] |
| | | public async Task<WebResponseContent> OutTaskComplete([FromBody] OutTaskCompleteDto outTaskCompleteDto) |
| | | { |
| | | return await Service.OutTaskComplete(outTaskCompleteDto); |
| | | } |
| | | ///// <summary> |
| | | ///// ä»»å¡å®æ |
| | | ///// </summary> |
| | | ///// <param name="wCSTask">请æ±åæ°</param> |
| | | ///// <returns></returns> |
| | | //[HttpPost, Route("TaskCompleted"), AllowAnonymous] |
| | | //public async Task<WebResponseContent?> TaskCompleted([FromBody] WCSTaskDTO wCSTask) |
| | | //{ |
| | | // return await Service.TaskCompleted(wCSTask); |
| | | //} |
| | | /// <summary> |
| | | /// è¾é线ç³è¯·è¿å
¥ |
| | | /// </summary> |
| | | /// <param name="applyEnterDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | [HttpPost("ApplyEnter"), AllowAnonymous] |
| | | public async Task<AGVResponse?> ApplyEnterAsync([FromBody] ApplyEnterDto applyEnterDto) |
| | | { |
| | | return await Service.ApplyEnterAsync(applyEnterDto); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¾è´§å®æ |
| | | /// </summary> |
| | | /// <param name="taskCompleteDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | [HttpPost("TaskComplete"), AllowAnonymous] |
| | | public async Task<AGVResponse?> TaskCompleteAsync([FromBody] TaskCompleteDto taskCompleteDto) |
| | | { |
| | | return await Service.TaskCompleteAsync(taskCompleteDto); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä»»å¡åæ¶ |
| | | /// </summary> |
| | | /// <param name="taskCancelDto">请æ±åæ°</param> |
| | | /// <returns></returns> |
| | | [HttpPost("TaskCancel"), AllowAnonymous] |
| | | public async Task<AGVResponse?> TaskCancelAsync([FromBody] TaskCancelDto taskCancelDto) |
| | | { |
| | | return await Service.TaskCancelAsync(taskCancelDto); |
| | | } |
| | | #endregion æå·åºä»»å¡æ¨¡å |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | using Microsoft.AspNetCore.SignalR; |
| | | using System.Collections.Generic; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace WIDESEA_WMSServer.Hubs |
| | | { |
| | | public class StockHub : Hub |
| | | { |
| | | /// <summary> |
| | | /// åéåºåæ´æ° |
| | | /// </summary> |
| | | public async Task SendStockUpdate(StockUpdateDTO update) |
| | | { |
| | | await Clients.All.SendAsync("StockUpdated", update); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºåæ´æ°DTOï¼SignalRæ¨éç¨ï¼ |
| | | /// </summary> |
| | | public class StockUpdateDTO |
| | | { |
| | | public int LocationId { get; set; } |
| | | public int WarehouseId { get; set; } |
| | | public string PalletCode { get; set; } |
| | | public float StockQuantity { get; set; } |
| | | public int StockStatus { get; set; } |
| | | public int LocationStatus { get; set; } |
| | | public List<StockDetailUpdateDTO> Details { get; set; } = new(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºåæç»æ´æ°DTO |
| | | /// </summary> |
| | | public class StockDetailUpdateDTO |
| | | { |
| | | public int Id { get; set; } |
| | | public string MaterielCode { get; set; } |
| | | public string MaterielName { get; set; } |
| | | public string BatchNo { get; set; } |
| | | public float StockQuantity { get; set; } |
| | | public string Unit { get; set; } |
| | | public int Status { get; set; } |
| | | } |
| | | } |
| | |
| | | builder.Services.AddDbSetup(); // Db æ°æ®åºé
ç½® |
| | | builder.Services.AddInitializationHostServiceSetup(); // åºç¨ç¨åºåå§åæå¡æ³¨å |
| | | builder.Services.AddHostedService<AutoOutboundTaskBackgroundService>(); // å¯å¨èªå¨åºåºä»»å¡åå°æå¡ |
| | | builder.Services.AddHostedService<StockMonitorBackgroundService>(); // å¯å¨åºåçæ§åå°æå¡ |
| | | // builder.Services.AddHostedService<PermissionDataHostService>(); // æéæ°æ®æå¡ |
| | | builder.Services.AddAutoMapperSetup(); |
| | | |
| | |
| | | options.Filters.Add(typeof(ApiAuthorizeFilter)); |
| | | options.Filters.Add(typeof(ActionExecuteFilter)); |
| | | }); |
| | | |
| | | builder.Services.AddSignalR(); |
| | | |
| | | builder.Services.AddScoped<HttpClientHelper>(); |
| | | |
| | |
| | | |
| | | app.MapControllers(); |
| | | |
| | | app.MapHub<WIDESEA_WMSServer.Hubs.StockHub>("/stockHub"); |
| | | |
| | | app.Run(); |
| | |
| | | //è¿æ¥å符串 |
| | | //"ConnectionString": "HTI6FB1H05Krd07mNm9yBCNhofW6edA5zLs9TY~MNthRYW3kn0qKbMIsGp~3yyPDF1YZUCPBQx8U0Jfk4PH~ajNFXVIwlH85M3F~v_qKYQ3CeAz3q1mLVDn8O5uWt1~3Ut2V3KRkEwYHvW2oMDN~QIDXPxDgXN0R2oTIhc9dNu7QNaLEknblqmHhjaNSSpERdDVZIgHnMKejU_SL49tralBkZmDNi0hmkbL~837j1NWe37u9fJKmv91QPb~16JsuI9uu0EvNZ06g6PuZfOSAeFH9GMMIZiketdcJG3tHelo=", |
| | | "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", |
| | | //"ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", |
| | | //"ConnectionString": "Data Source=10.30.4.92;Initial Catalog=WMS_TC;User ID=sa;Password=duo123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", |
| | | //æ§WMSæ°æ®åºè¿æ¥ |
| | | //"TeConnectionString": "Data Source=10.30.4.92;Initial Catalog=TeChuang;User ID=sa;Password=duo123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", |
| | |
| | | "MES": { |
| | | "BaseUrl": "http://localhost:5000", |
| | | "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjMwMTcyNzM5Mzk5NzYxOTIwIiwibmFtZSI6IlBBQ0voo4XphY3lt6XkvY0wMSIsIkZhY3RvcnlJZCI6IjEyMzQ1NiIsIlNpdGVJZCI6IjEyMzQ1NiIsIkNvZGUiOiJYWExQQUNLMDRBRTAzMiIsIm5iZiI6MTcwNDE4NzY5MCwiZXhwIjoyMTQ1NjkxNjkwLCJpc3MiOiJodHRwczovL3d3dy5oeW1zb24uY29tIiwiYXVkIjoiaHR0cHM6Ly93d3cuaHltc29uLmNvbSJ9.An1BE7UgfcSP--LtTOmmmWVE2RQFPDahLkDg1xy5KqY" |
| | | } |
| | | }, |
| | | "AGV_OutTaskComplete": "http://localhost:9999/OutTaskComplete", // 䏿¥AGVåºåºè¾éçº¿å®æ |
| | | "WCS_ReceiveTask": "http://localhost:9292/api/Task/ReceiveTask" // WMSè¾é线任å¡ä¸å |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # MES æçè¿ç«åºç«éæå®ç°è®¡å |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ TaskService çå
¥åºå®æ/åºåºå®ææ¹æ³ä¸éæ MES è¿ç«/åºç«è°ç¨ï¼æ°å¢ç©ºæçå
¥åº/åºåºå®ææ¹æ³ã |
| | | |
| | | **Architecture:** å¨ `ExecuteWithinTransactionAsync` äºå¡å
æ·»å MES è°ç¨ï¼MES 失败åäºå¡åæ»ã |
| | | |
| | | **Tech Stack:** ASP.NET Core 6.0, IMesService, ExecuteWithinTransactionAsync |
| | | |
| | | --- |
| | | |
| | | ## 任塿»è§ |
| | | |
| | | | ä»»å¡ | æ¹æ³ | æä½ | |
| | | |------|------|------| |
| | | | Task 1 | `InboundFinishTaskAsync` | æ·»å `InboundInContainer` è°ç¨ | |
| | | | Task 2 | `OutboundFinishTaskAsync` | æ·»å `OutboundInContainer` è°ç¨ | |
| | | | Task 3 | `InboundFinishTaskTrayAsync`ï¼æ°å¢ï¼ | 空æçå
¥åºå®æï¼æ é MES | |
| | | | Task 4 | `OutboundFinishTaskTrayAsync`ï¼æ°å¢ï¼ | 空æçåºåºå®æï¼æ é MES | |
| | | |
| | | --- |
| | | |
| | | ## ä»»å¡åç½®æ¡ä»¶ |
| | | |
| | | `TaskService` éæ³¨å
¥ `IMesService`ãæ£æ¥ç°ææé 彿°æ¯å¦å·²æï¼ |
| | | ```csharp |
| | | private readonly IMesService _mesService; |
| | | ``` |
| | | |
| | | 妿ä¸åå¨ï¼éæ·»å ã |
| | | |
| | | --- |
| | | |
| | | ## Task 1: ä¿®æ¹ InboundFinishTaskAsync æ·»å MES è¿ç«è°ç¨ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼`InboundFinishTaskAsync` æ¹æ³ï¼çº¦ç¬¬ 215 è¡ï¼ |
| | | |
| | | - [ ] **Step 1: æ¥çå½å代ç 确认ä¸ä¸æ** |
| | | |
| | | 读å `TaskService.cs` 第 199-240 è¡ï¼ç¡®è®¤ `CompleteTaskAsync` è°ç¨çä½ç½®ã |
| | | |
| | | - [ ] **Step 2: å¨ CompleteTaskAsync ä¹åæ·»å MES InboundInContainer è°ç¨** |
| | | |
| | | å¨ `return await CompleteTaskAsync(task);` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // è°ç¨MESæçè¿ç« |
| | | var inboundRequest = new InboundInContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = taskDto.PalletCode |
| | | }; |
| | | var inboundResult = _mesService.InboundInContainer(inboundRequest); |
| | | if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESè¿ç«å¤±è´¥: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æ·»å using å¼ç¨ï¼å¦æéè¦ï¼** |
| | | |
| | | 确认æä»¶é¡¶é¨å·²æ `using WIDESEA_IBasicService;` å `using WIDESEA_DTO.MES;`ãå¦ææ²¡æï¼æ·»å ã |
| | | |
| | | - [ ] **Step 4: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 5: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): InboundFinishTaskAsyncæ·»å MESè¿ç«è°ç¨" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 2: ä¿®æ¹ OutboundFinishTaskAsync æ·»å MES åºç«è°ç¨ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼`OutboundFinishTaskAsync` æ¹æ³ï¼çº¦ç¬¬ 258 è¡ï¼ |
| | | |
| | | - [ ] **Step 1: æ¥çå½å代ç 确认ä¸ä¸æ** |
| | | |
| | | 读å `TaskService.cs` 第 258-280 è¡ï¼ç¡®è®¤ `CompleteTaskAsync` è°ç¨çä½ç½®ã |
| | | |
| | | - [ ] **Step 2: å¨ CompleteTaskAsync ä¹åæ·»å MES OutboundInContainer è°ç¨** |
| | | |
| | | å¨ `return await CompleteTaskAsync(task);` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // è°ç¨MESæçåºç« |
| | | var outboundRequest = new OutboundInContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainerCode = taskDto.PalletCode, |
| | | ParamList = new List<ParamItem>() |
| | | }; |
| | | var outboundResult = _mesService.OutboundInContainer(outboundRequest); |
| | | if (outboundResult == null || outboundResult.Data == null || !outboundResult.Data.IsSuccess) |
| | | { |
| | | return content.Error($"ä»»å¡å®æå¤±è´¥ï¼MESåºç«å¤±è´¥: {outboundResult?.Data?.Msg ?? outboundResult?.ErrorMessage ?? "æªç¥é误"}"); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): OutboundFinishTaskAsyncæ·»å MESåºç«è°ç¨" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 3: æ°å¢ InboundFinishTaskTrayAsync 空æçå
¥åºå®ææ¹æ³ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼å¨ `InboundFinishTaskTrayAsync` æ¹æ³ä¹åæ·»å æ°æ¹æ³ï¼ |
| | | |
| | | - [ ] **Step 1: æ¥çç°æ InboundFinishTaskTrayAsync æ¹æ³ä½ç½®** |
| | | |
| | | 读å `TaskService.cs` 第 330-350 è¡ï¼ç¡®è®¤ `CreateTaskInboundTrayAsync` ä¹åçä½ç½®ã |
| | | |
| | | - [ ] **Step 2: æ·»å æ°æ¹æ³ InboundFinishTaskTrayAsync** |
| | | |
| | | å¨ `CreateTaskInboundTrayAsync` æ¹æ³ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// 空æçå
¥åºå®æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationCode = location.LocationCode; |
| | | stockInfo.LocationId = location.Id; |
| | | stockInfo.StockStatus = StockStatusEmun.空æçåºå.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.InStock.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | var deleteResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): æ°å¢InboundFinishTaskTrayAsync空æçå
¥åºå®ææ¹æ³" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 4: æ°å¢ OutboundFinishTaskTrayAsync 空æçåºåºå®ææ¹æ³ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼å¨ `OutboundFinishTaskTrayAsync` æ¹æ³ä¹åæ·»å æ°æ¹æ³ï¼ |
| | | |
| | | - [ ] **Step 1: æ¥çç°æ GetOutBoundTrayTaskAsync æ¹æ³ä½ç½®** |
| | | |
| | | 读å `TaskService.cs` 第 357-393 è¡ï¼ç¡®è®¤ `GetOutBoundTrayTaskAsync` ä¹åçä½ç½®ã |
| | | |
| | | - [ ] **Step 2: æ·»å æ°æ¹æ³ OutboundFinishTaskTrayAsync** |
| | | |
| | | å¨ `GetOutBoundTrayTaskAsync` æ¹æ³ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | /// <summary> |
| | | /// 空æçåºåºå®æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | { |
| | | try |
| | | { |
| | | var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode); |
| | | if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡"); |
| | | |
| | | var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress); |
| | | if (location == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçè´§ä½"); |
| | | |
| | | var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode); |
| | | if (stockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºåºåä¿¡æ¯"); |
| | | |
| | | return await ExecuteWithinTransactionAsync(async () => |
| | | { |
| | | stockInfo.LocationId = 0; |
| | | stockInfo.LocationCode = null; |
| | | stockInfo.StockStatus = StockStatusEmun.åºåºå®æ.GetHashCode(); |
| | | |
| | | location.LocationStatus = LocationStatusEnum.Free.GetHashCode(); |
| | | |
| | | var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location); |
| | | var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo); |
| | | if (!updateLocationResult || !updateStockResult) |
| | | return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | var deleteResult = await BaseDal.DeleteDataAsync(task); |
| | | if (!deleteResult) return WebResponseContent.Instance.Error("ä»»å¡å®æå¤±è´¥"); |
| | | |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return WebResponseContent.Instance.Error($"宿任å¡å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): æ°å¢OutboundFinishTaskTrayAsync空æçåºåºå®ææ¹æ³" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 5: æ´ä½æå»ºéªè¯ |
| | | |
| | | - [ ] **Step 1: æå»ºæ´ä¸ªè§£å³æ¹æ¡** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_WMSServer.sln |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯éè¯¯ãæ è¦åã |
| | | |
| | | --- |
| | | |
| | | ## éªè¯æ£æ¥æ¸
å |
| | | |
| | | - [ ] `InboundFinishTaskAsync` ä¸ `InboundInContainer` å¨ `CompleteTaskAsync` ä¹å |
| | | - [ ] `OutboundFinishTaskAsync` ä¸ `OutboundInContainer` å¨ `CompleteTaskAsync` ä¹å |
| | | - [ ] ææ MES è°ç¨æ£æ¥ `mesResult.Data?.IsSuccess` |
| | | - [ ] éè¯¯ä¿¡æ¯æ ¼å¼ï¼`"ä»»å¡å®æå¤±è´¥ï¼MES{æä½}失败: {é误信æ¯}"` |
| | | - [ ] `InboundFinishTaskTrayAsync` å `OutboundFinishTaskTrayAsync` æ°å¢æ¹æ³ç¾åæ£ç¡® |
| | | - [ ] è§£å³æ¹æ¡æå»ºæ é误 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # MES çµè¯ç»å®è§£ç»éæå®ç°è®¡å |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ StockService çç»çãæ¢çãæçæä½ä¸æ£ç¡®è°ç¨ MES çµè¯ç»å®/è§£ç»æ¥å£ï¼å¹¶å° MES è°ç¨çº³å
¥ WMS äºå¡å
ã |
| | | |
| | | **Architecture:** ä¿®æ¹ `StockService` ä¸ä¸ªæ¹æ³ï¼å° `_mesService.BindContainer()` / `_mesService.UnBindContainer()` æ£ç¡®å°ç¨ `await` è°ç¨å¹¶æ£æ¥è¿åç»æï¼MES 失败åäºå¡åæ»ã |
| | | |
| | | **Tech Stack:** ASP.NET Core 6.0, SqlSugar, IMesService |
| | | |
| | | --- |
| | | |
| | | ## 任塿»è§ |
| | | |
| | | | ä»»å¡ | æ¹æ³ | æä½ | |
| | | |------|------|------| |
| | | | Task 1 | `GroupPalletAsync` | ä¿®å¤ `_mesService.BindContainer()` ç¼ºå° await åç»ææ£æ¥ | |
| | | | Task 2 | `ChangePalletAsync` | æ·»å è§£ç»æºæç + ç»å®ç®æ æç | |
| | | | Task 3 | `SplitPalletAsync` | æ·»å è§£ç»çµè¯ | |
| | | |
| | | --- |
| | | |
| | | ## Task 1: ä¿®å¤ GroupPalletAsync ä¸ç MES è°ç¨ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_StockService/StockSerivce.cs:132-176` |
| | | |
| | | - [ ] **Step 1: æ¥çå½å代ç 确认ä¸ä¸æ** |
| | | |
| | | 读å `StockSerivce.cs` 第 132-176 è¡ï¼ç¡®è®¤ `bindRequest` 对象çæå»ºå `_mesService.BindContainer()` è°ç¨çä½ç½®ã |
| | | |
| | | - [ ] **Step 2: ä¿®æ¹ BindContainer è°ç¨ä¸º await å¹¶æ£æ¥ç»æ** |
| | | |
| | | å°ç¬¬ 166 è¡ï¼ |
| | | ```csharp |
| | | _mesService.BindContainer() |
| | | ``` |
| | | |
| | | ä¿®æ¹ä¸ºï¼ |
| | | ```csharp |
| | | var mesResult = await _mesService.BindContainer(bindRequest); |
| | | if (mesResult == null || !mesResult.Success) |
| | | { |
| | | return content.Error($"ç»çæåï¼ä½MESç»å®å¤±è´¥: {mesResult?.Message ?? "æªç¥é误"}"); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_StockService/WIDESEA_StockService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_StockService/StockSerivce.cs |
| | | git commit -m "fix(StockService): GroupPalletAsyncæ£ç¡®await MES BindContainerè°ç¨å¹¶æ£æ¥ç»æ" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 2: ä¿®æ¹ ChangePalletAsync æ·»å MES è§£ç»åç»å®è°ç¨ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_StockService/StockSerivce.cs:181-240` |
| | | |
| | | - [ ] **Step 1: æ¥çå½å代ç 确认ä¸ä¸æ** |
| | | |
| | | 读å `StockSerivce.cs` 第 181-240 è¡ï¼ç¡®è®¤ï¼ |
| | | - `detailEntities` åéå®ä¹ä½ç½®ï¼å
å«è¦æ¢çççµè¯æç»ï¼ |
| | | - `targetStock.Id` èµå¼ä½ç½® |
| | | - `return content.OK("æ¢çæå")` ä¹åçé»è¾ |
| | | |
| | | - [ ] **Step 2: 卿´æ°åºåæç»åæ·»å UnBindContainer è°ç¨** |
| | | |
| | | å¨ç¬¬ 231 è¡ `var result = await StockInfoDetailService.Repository.UpdateDataAsync(detailEntities);` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // è°ç¨MESè§£ç»æºæççµè¯ |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = stock.SourcePalletNo, |
| | | SfcList = detailEntities.Select(d => d.SerialNumber).ToList() |
| | | }; |
| | | var unbindResult = await _mesService.UnBindContainer(unbindRequest); |
| | | if (unbindResult == null || !unbindResult.Success) |
| | | { |
| | | return content.Error($"æ¢çæåï¼ä½MESè§£ç»å¤±è´¥: {unbindResult?.Message ?? "æªç¥é误"}"); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: 卿´æ°åºåæç»åæ·»å BindContainer è°ç¨** |
| | | |
| | | å¨ç¬¬ 231 è¡ä¹åã`return content.OK("æ¢çæå");` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // è°ç¨MESç»å®ç®æ æççµè¯ |
| | | var bindRequest = new BindContainerRequest |
| | | { |
| | | ContainerCode = stock.TargetPalletNo, |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | OperationType = 0, |
| | | ContainerSfcList = detailEntities.Select(d => new ContainerSfcItem |
| | | { |
| | | Sfc = d.SerialNumber, |
| | | Location = d.InboundOrderRowNo.ToString() |
| | | }).ToList() |
| | | }; |
| | | var bindResult = await _mesService.BindContainer(bindRequest); |
| | | if (bindResult == null || !bindResult.Success) |
| | | { |
| | | return content.Error($"æ¢çæåï¼ä½MESç»å®å¤±è´¥: {bindResult?.Message ?? "æªç¥é误"}"); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 4: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_StockService/WIDESEA_StockService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 5: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_StockService/StockSerivce.cs |
| | | git commit -m "feat(StockService): ChangePalletAsyncæ·»å MESè§£ç»åç»å®è°ç¨" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 3: ä¿®æ¹ SplitPalletAsync æ·»å MES è§£ç»è°ç¨ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_StockService/StockSerivce.cs:245-286` |
| | | |
| | | - [ ] **Step 1: æ¥çå½å代ç 确认ä¸ä¸æ** |
| | | |
| | | 读å `StockSerivce.cs` 第 245-286 è¡ï¼ç¡®è®¤ï¼ |
| | | - `detailEntities` åéå®ä¹åå
å«ççµè¯å表 |
| | | - `return content.OK("æçæå");` ä¹åçé»è¾ |
| | | |
| | | - [ ] **Step 2: å¨å é¤åºåæç»åæ·»å UnBindContainer è°ç¨** |
| | | |
| | | å¨ç¬¬ 277 è¡ `var result = await StockInfoDetailService.Repository.DeleteDataAsync(detailEntities);` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // è°ç¨MESè§£ç»çµè¯ |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = stock.SourcePalletNo, |
| | | SfcList = detailEntities.Select(d => d.SerialNumber).ToList() |
| | | }; |
| | | var unbindResult = await _mesService.UnBindContainer(unbindRequest); |
| | | if (unbindResult == null || !unbindResult.Success) |
| | | { |
| | | return content.Error($"æçæåï¼ä½MESè§£ç»å¤±è´¥: {unbindResult?.Message ?? "æªç¥é误"}"); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_StockService/WIDESEA_StockService.csproj |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_StockService/StockSerivce.cs |
| | | git commit -m "feat(StockService): SplitPalletAsyncæ·»å MESè§£ç»è°ç¨" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 4: æ´ä½æå»ºéªè¯ |
| | | |
| | | - [ ] **Step 1: æå»ºæ´ä¸ªè§£å³æ¹æ¡** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_WMSServer.sln |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯éè¯¯ãæ è¦åã |
| | | |
| | | - [ ] **Step 2: æäº¤æææ´æ¹** |
| | | |
| | | 妿ä¹å没æåå¹¶æäº¤ï¼å¨æ¤æ§è¡æç»æäº¤ã |
| | | |
| | | --- |
| | | |
| | | ## éªè¯æ£æ¥æ¸
å |
| | | |
| | | - [ ] `GroupPalletAsync` ä¸ `await _mesService.BindContainer()` æ£ç¡® await |
| | | - [ ] `ChangePalletAsync` ä¸å
UnBind å Bindï¼é¡ºåºæ£ç¡® |
| | | - [ ] `SplitPalletAsync` ä¸ UnBind å¨ Delete ä¹å |
| | | - [ ] ææ MES è°ç¨æ£æ¥ `Success` 屿§ |
| | | - [ ] éè¯¯ä¿¡æ¯æ ¼å¼ç»ä¸ï¼`"{æä½}æåï¼ä½MES{æä½}失败: {é误信æ¯}"` |
| | | - [ ] è§£å³æ¹æ¡æå»ºæ é误 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # é¦é¡µä»ªè¡¨çå¾è¡¨åè½å®ç°è®¡å |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ WMS å端é¦é¡µæ·»å 仪表çå¾è¡¨ï¼å±ç¤ºåºå
¥åºç»è®¡ååºåæ°æ® |
| | | |
| | | **Architecture:** |
| | | - åç«¯ï¼æ°å»º DashboardControllerï¼æä¾6个ç»è®¡æ¥å£ï¼ä½¿ç¨ SqlSugar ç´æ¥æ¥è¯¢ Dt_Task_Htyï¼å·²å®æä»»å¡åå²è¡¨ï¼å Dt_StockInfo 表 |
| | | - å端ï¼éå Home.vueï¼ä½¿ç¨ ECharts 5.0.2 å®ç°ä»ªè¡¨çå¸å±ï¼å¤ç¨ bigdata.vue ä¸ç ECharts ä½¿ç¨æ¨¡å¼ |
| | | - æ°æ®æ¥æºï¼Dt_Task_Hty.InsertTimeï¼ä»»å¡å®ææ¶é´ï¼ï¼TaskType åºåå
¥åº(500-599)/åºåº(100-199) |
| | | |
| | | **Tech Stack:** ASP.NET Core 6.0, Vue 3, ECharts 5.0.2, SqlSugar |
| | | |
| | | --- |
| | | |
| | | ## æä»¶ç»æ |
| | | |
| | | ``` |
| | | å端æ°å¢: |
| | | - WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs (ä»ªè¡¨çæ§å¶å¨) |
| | | |
| | | å端修æ¹: |
| | | - WIDESEA_WMSClient/src/views/Home.vue (éå为仪表ç页é¢) |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## å®ç°ä»»å¡ |
| | | |
| | | ### Task 1: å建å端 DashboardController |
| | | |
| | | **æä»¶:** |
| | | - Create: `WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs` |
| | | |
| | | **说æ:** å建 DashboardControllerï¼å
å«6个 API æ¥å£ |
| | | |
| | | - [ ] **Step 1: å建æ§å¶å¨æä»¶** |
| | | |
| | | ```csharp |
| | | using Microsoft.AspNetCore.Mvc; |
| | | using SqlSugar; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_WMSServer.Controllers.Dashboard |
| | | { |
| | | /// <summary> |
| | | /// 仪表ç |
| | | /// </summary> |
| | | [Route("api/Dashboard")] |
| | | [ApiController] |
| | | public class DashboardController : ControllerBase |
| | | { |
| | | private readonly ISqlSugarClient _db; |
| | | |
| | | public DashboardController(ISqlSugarClient db) |
| | | { |
| | | _db = db; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ»è§æ°æ® |
| | | /// </summary> |
| | | [HttpGet("Overview")] |
| | | public async Task<WebResponseContent> Overview() |
| | | { |
| | | // å®ç°è§ Step 2 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¯æ¥ç»è®¡ |
| | | /// </summary> |
| | | [HttpGet("DailyStats")] |
| | | public async Task<WebResponseContent> DailyStats([FromQuery] int days = 30) |
| | | { |
| | | // å®ç°è§ Step 3 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¯å¨ç»è®¡ |
| | | /// </summary> |
| | | [HttpGet("WeeklyStats")] |
| | | public async Task<WebResponseContent> WeeklyStats([FromQuery] int weeks = 12) |
| | | { |
| | | // å®ç°è§ Step 4 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¯æç»è®¡ |
| | | /// </summary> |
| | | [HttpGet("MonthlyStats")] |
| | | public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12) |
| | | { |
| | | // å®ç°è§ Step 5 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åºååºé¾åå¸ |
| | | /// </summary> |
| | | [HttpGet("StockAgeDistribution")] |
| | | public async Task<WebResponseContent> StockAgeDistribution() |
| | | { |
| | | // å®ç°è§ Step 6 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åä»åºåºååå¸ |
| | | /// </summary> |
| | | [HttpGet("StockByWarehouse")] |
| | | public async Task<WebResponseContent> StockByWarehouse() |
| | | { |
| | | // å®ç°è§ Step 7 |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 2: å®ç° Overview æ¥å£** |
| | | |
| | | å¨ Overview æ¹æ³ä¸å®ç°ï¼ |
| | | |
| | | ```csharp |
| | | public async Task<WebResponseContent> Overview() |
| | | { |
| | | var today = DateTime.Today; |
| | | var firstDayOfMonth = new DateTime(today.Year, today.Month, 1); |
| | | |
| | | // 仿¥å
¥åºæ° |
| | | var todayInbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 500 && t.TaskType < 600) |
| | | .CountAsync(); |
| | | |
| | | // 仿¥åºåºæ° |
| | | var todayOutbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= today && t.TaskType >= 100 && t.TaskType < 200) |
| | | .CountAsync(); |
| | | |
| | | // æ¬æå
¥åºæ° |
| | | var monthInbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 500 && t.TaskType < 600) |
| | | .CountAsync(); |
| | | |
| | | // æ¬æåºåºæ° |
| | | var monthOutbound = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= firstDayOfMonth && t.TaskType >= 100 && t.TaskType < 200) |
| | | .CountAsync(); |
| | | |
| | | // å½åæ»åºå |
| | | var totalStock = await _db.Queryable<Dt_StockInfo>().CountAsync(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, new |
| | | { |
| | | TodayInbound = todayInbound, |
| | | TodayOutbound = todayOutbound, |
| | | MonthInbound = monthInbound, |
| | | MonthOutbound = monthOutbound, |
| | | TotalStock = totalStock |
| | | }); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 3: å®ç° DailyStats æ¥å£** |
| | | |
| | | ```csharp |
| | | public async Task<WebResponseContent> DailyStats([FromQuery] int days = 30) |
| | | { |
| | | if (days <= 0) days = 30; |
| | | if (days > 365) days = 365; |
| | | |
| | | var startDate = DateTime.Today.AddDays(-days + 1); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => t.InsertTime.Date) |
| | | .Select(g => new |
| | | { |
| | | Date = g.Key.ToString("yyyy-MM-dd"), |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Date) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 4: å®ç° WeeklyStats æ¥å£** |
| | | |
| | | ```csharp |
| | | public async Task<WebResponseContent> WeeklyStats([FromQuery] int weeks = 12) |
| | | { |
| | | if (weeks <= 0) weeks = 12; |
| | | |
| | | var startDate = DateTime.Today.AddDays(-weeks * 7); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => GetWeekKey(t.InsertTime)) |
| | | .Select(g => new |
| | | { |
| | | Week = g.Key, |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Week) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | |
| | | private string GetWeekKey(DateTime date) |
| | | { |
| | | // è·åå¨ä¸å¼å§çå¨ (ISO 8601) |
| | | var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7; |
| | | var monday = date.AddDays(-diff); |
| | | var weekNum = System.Globalization.CultureInfo.InvariantCulture |
| | | .Calendar.GetWeekOfYear(monday, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); |
| | | return $"{monday.Year}-W{weekNum:D2}"; |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 5: å®ç° MonthlyStats æ¥å£** |
| | | |
| | | ```csharp |
| | | public async Task<WebResponseContent> MonthlyStats([FromQuery] int months = 12) |
| | | { |
| | | if (months <= 0) months = 12; |
| | | |
| | | var startDate = DateTime.Today.AddMonths(-months + 1); |
| | | startDate = new DateTime(startDate.Year, startDate.Month, 1); |
| | | |
| | | var query = await _db.Queryable<Dt_Task_Hty>() |
| | | .Where(t => t.InsertTime >= startDate) |
| | | .Select(t => new { t.InsertTime, t.TaskType }) |
| | | .ToListAsync(); |
| | | |
| | | var result = query |
| | | .GroupBy(t => new { t.InsertTime.Year, t.InsertTime.Month }) |
| | | .Select(g => new |
| | | { |
| | | Month = $"{g.Key.Year}-{g.Key.Month:D2}", |
| | | Inbound = g.Count(t => t.TaskType >= 500 && t.TaskType < 600), |
| | | Outbound = g.Count(t => t.TaskType >= 100 && t.TaskType < 200) |
| | | }) |
| | | .OrderBy(x => x.Month) |
| | | .ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 6: å®ç° StockAgeDistribution æ¥å£** |
| | | |
| | | ```csharp |
| | | public async Task<WebResponseContent> StockAgeDistribution() |
| | | { |
| | | var now = DateTime.Now; |
| | | |
| | | // ä½¿ç¨ SQL ç´æ¥åç»ç»è®¡ï¼é¿å
å è½½æææ°æ®å°å
å |
| | | var result = new[] |
| | | { |
| | | new { Range = "7天å
", Count = await _db.Queryable<Dt_StockInfo>().Where(s => EF.Functions.DateDiffDay(s.CreateDate, now) <= 7).CountAsync() }, |
| | | new { Range = "7-30天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => EF.Functions.DateDiffDay(s.CreateDate, now) > 7 && EF.Functions.DateDiffDay(s.CreateDate, now) <= 30).CountAsync() }, |
| | | new { Range = "30-90天", Count = await _db.Queryable<Dt_StockInfo>().Where(s => EF.Functions.DateDiffDay(s.CreateDate, now) > 30 && EF.Functions.DateDiffDay(s.CreateDate, now) <= 90).CountAsync() }, |
| | | new { Range = "90天以ä¸", Count = await _db.Queryable<Dt_StockInfo>().Where(s => EF.Functions.DateDiffDay(s.CreateDate, now) > 90).CountAsync() } |
| | | }; |
| | | |
| | | return WebResponseContent.Instance.OK(null, result); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 7: å®ç° StockByWarehouse æ¥å£** |
| | | |
| | | ```csharp |
| | | public async Task<WebResponseContent> StockByWarehouse() |
| | | { |
| | | var result = await _db.Queryable<Dt_StockInfo>() |
| | | .GroupBy(s => s.WarehouseId) |
| | | .Select(g => new |
| | | { |
| | | WarehouseId = g.Key, |
| | | Count = g.Count() |
| | | }) |
| | | .ToListAsync(); |
| | | |
| | | // èæ¥ä»åºåç§° |
| | | var warehouseIds = result.Select(x => x.WarehouseId).ToList(); |
| | | var warehouses = await _db.Queryable<Dt_Warehouse>() |
| | | .Where(w => warehouseIds.Contains(w.WarehouseId)) |
| | | .Select(w => new { w.WarehouseId, w.WarehouseName }) |
| | | .ToListAsync(); |
| | | |
| | | var finalResult = result.Select(r => |
| | | { |
| | | var wh = warehouses.FirstOrDefault(w => w.WarehouseId == r.WarehouseId); |
| | | return new |
| | | { |
| | | Warehouse = wh?.WarehouseName ?? $"ä»åº{r.WarehouseId}", |
| | | Count = r.Count |
| | | }; |
| | | }).ToList(); |
| | | |
| | | return WebResponseContent.Instance.OK(null, finalResult); |
| | | } |
| | | ``` |
| | | |
| | | - [ ] **Step 8: æäº¤ä»£ç ** |
| | | |
| | | ```bash |
| | | git add WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs |
| | | git commit -m "feat(Dashboard): æ·»å ä»ªè¡¨çæ§å¶å¨ï¼å
å«6个ç»è®¡æ¥å£" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### Task 2: éåå端 Home.vue å®ç°ä»ªè¡¨ç |
| | | |
| | | **æä»¶:** |
| | | - Modify: `WIDESEA_WMSClient/src/views/Home.vue` |
| | | |
| | | **说æ:** éå为空ç½çé¦é¡µï¼å®ç°ä»ªè¡¨çå¾è¡¨å¸å± |
| | | |
| | | - [ ] **Step 1: éå Home.vue 模æ¿é¨å** |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="dashboard-container"> |
| | | <!-- é¡¶é¨ï¼æ¬æåºå
¥åºè¶å¿ (å
¨å®½) --> |
| | | <div class="chart-row full-width"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">æ¬æåºå
¥åºè¶å¿</div> |
| | | <div id="chart-monthly-trend" class="chart-content"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第äºè¡ï¼ä»æ¥/æ¬å¨åºå
¥åºå¯¹æ¯ --> |
| | | <div class="chart-row"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">仿¥åºå
¥åºå¯¹æ¯</div> |
| | | <div id="chart-today" class="chart-content"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="card-title">æ¬å¨åºå
¥åºå¯¹æ¯</div> |
| | | <div id="chart-week" class="chart-content"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第ä¸è¡ï¼æ¬æå¯¹æ¯/åºåæ»é --> |
| | | <div class="chart-row"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">æ¬æåºå
¥åºå¯¹æ¯</div> |
| | | <div id="chart-month" class="chart-content"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="card-title">å½ååºåæ»é</div> |
| | | <div class="stock-total"> |
| | | <div class="total-number">{{ overviewData.TotalStock || 0 }}</div> |
| | | <div class="total-label">æç</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第åè¡ï¼åºé¾åå¸/ä»åºåå¸ --> |
| | | <div class="chart-row"> |
| | | <div class="chart-card"> |
| | | <div class="card-title">åºååºé¾åå¸</div> |
| | | <div id="chart-stock-age" class="chart-content"></div> |
| | | </div> |
| | | <div class="chart-card"> |
| | | <div class="card-title">åä»åºåºååå¸</div> |
| | | <div id="chart-warehouse" class="chart-content"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | ``` |
| | | |
| | | - [ ] **Step 2: éåèæ¬é¨å** |
| | | |
| | | ```javascript |
| | | <script> |
| | | import * as echarts from "echarts"; |
| | | |
| | | export default { |
| | | name: "Home", |
| | | data() { |
| | | return { |
| | | charts: {}, |
| | | overviewData: { |
| | | TodayInbound: 0, |
| | | TodayOutbound: 0, |
| | | MonthInbound: 0, |
| | | MonthOutbound: 0, |
| | | TotalStock: 0 |
| | | }, |
| | | weeklyData: [], |
| | | monthlyData: [], |
| | | stockAgeData: [], |
| | | warehouseData: [] |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.initCharts(); |
| | | this.loadData(); |
| | | window.addEventListener("resize", this.handleResize); |
| | | }, |
| | | beforeUnmount() { |
| | | window.removeEventListener("resize", this.handleResize); |
| | | Object.values(this.charts).forEach(chart => chart.dispose()); |
| | | }, |
| | | methods: { |
| | | handleResize() { |
| | | Object.values(this.charts).forEach(chart => chart.resize()); |
| | | }, |
| | | |
| | | initCharts() { |
| | | this.charts.monthlyTrend = echarts.init(document.getElementById("chart-monthly-trend")); |
| | | this.charts.today = echarts.init(document.getElementById("chart-today")); |
| | | this.charts.week = echarts.init(document.getElementById("chart-week")); |
| | | this.charts.month = echarts.init(document.getElementById("chart-month")); |
| | | this.charts.stockAge = echarts.init(document.getElementById("chart-stock-age")); |
| | | this.charts.warehouse = echarts.init(document.getElementById("chart-warehouse")); |
| | | }, |
| | | |
| | | async loadData() { |
| | | await this.loadOverview(); |
| | | await this.loadWeeklyStats(); |
| | | await this.loadMonthlyStats(); |
| | | await this.loadStockAgeDistribution(); |
| | | await this.loadStockByWarehouse(); |
| | | }, |
| | | |
| | | async loadOverview() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/Overview"); |
| | | if (res.Status && res.Data) { |
| | | this.overviewData = res.Data; |
| | | this.updateTodayChart(); |
| | | this.updateWeekChart(); |
| | | this.updateMonthChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½æ»è§æ°æ®å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadWeeklyStats() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/WeeklyStats", { weeks: 12 }); |
| | | if (res.Status && res.Data) { |
| | | this.weeklyData = res.Data; |
| | | this.updateWeekChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½æ¯å¨ç»è®¡å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadMonthlyStats() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/MonthlyStats", { months: 12 }); |
| | | if (res.Status && res.Data) { |
| | | this.monthlyData = res.Data; |
| | | this.updateMonthlyTrendChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½æ¯æç»è®¡å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadStockAgeDistribution() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/StockAgeDistribution"); |
| | | if (res.Status && res.Data) { |
| | | this.stockAgeData = res.Data; |
| | | this.updateStockAgeChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½åºé¾åå¸å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | async loadStockByWarehouse() { |
| | | try { |
| | | const res = await this.http.get("/api/Dashboard/StockByWarehouse"); |
| | | if (res.Status && res.Data) { |
| | | this.warehouseData = res.Data; |
| | | this.updateWarehouseChart(); |
| | | } |
| | | } catch (e) { |
| | | console.error("å è½½ä»åºåå¸å¤±è´¥", e); |
| | | } |
| | | }, |
| | | |
| | | // æ´æ°ä»æ¥å¯¹æ¯å¾è¡¨ |
| | | updateTodayChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: ["仿¥"], |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: [this.overviewData.TodayInbound], itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "bar", data: [this.overviewData.TodayOutbound], itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.today.setOption(option, true); |
| | | }, |
| | | |
| | | // æ´æ°æ¬å¨å¯¹æ¯å¾è¡¨ |
| | | updateWeekChart() { |
| | | // æ¬å¨æ°æ®ä» weeklyData ä¸è®¡ç®å½å¨æ°æ® |
| | | const thisWeek = this.getThisWeekData(this.weeklyData); |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: ["æ¬å¨"], |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: [thisWeek.Inbound], itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "bar", data: [thisWeek.Outbound], itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.week.setOption(option, true); |
| | | }, |
| | | |
| | | getThisWeekData(weeklyData) { |
| | | if (!weeklyData || weeklyData.length === 0) return { Inbound: 0, Outbound: 0 }; |
| | | const thisWeekKey = this.getCurrentWeekKey(); |
| | | const thisWeek = weeklyData.find(w => w.Week === thisWeekKey); |
| | | return thisWeek || { Inbound: 0, Outbound: 0 }; |
| | | }, |
| | | |
| | | getCurrentWeekKey() { |
| | | const now = new Date(); |
| | | const diff = (7 + (now.getDay() - 1)) % 7; |
| | | const monday = new Date(now); |
| | | monday.setDate(now.getDate() - diff); |
| | | const year = monday.getFullYear(); |
| | | const month = monday.getMonth() + 1; |
| | | const day = monday.getDate(); |
| | | // ISO week start (Monday) |
| | | const jan1 = new Date(year, 0, 1); |
| | | const weekNum = Math.ceil(((monday - jan1) / 86400000 + jan1.getDay() + 1) / 7); |
| | | return `${year}-W${String(weekNum).padStart(2, "0")}`; |
| | | }, |
| | | |
| | | // æ´æ°æ¬æå¯¹æ¯å¾è¡¨ |
| | | updateMonthChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: ["æ¬æ"], |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { name: "å
¥åº", type: "bar", data: [this.overviewData.MonthInbound], itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "bar", data: [this.overviewData.MonthOutbound], itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.month.setOption(option, true); |
| | | }, |
| | | |
| | | // æ´æ°æåº¦è¶å¿å¾è¡¨ï¼æçº¿å¾ï¼ |
| | | updateMonthlyTrendChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | legend: { data: ["å
¥åº", "åºåº"], textStyle: { color: "#fff" } }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: this.monthlyData.map(m => m.Month), |
| | | axisLabel: { color: "#fff", rotate: 45 } |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: "value", |
| | | name: "æ°é", |
| | | axisLabel: { color: "#fff" } |
| | | } |
| | | ], |
| | | series: [ |
| | | { name: "å
¥åº", type: "line", data: this.monthlyData.map(m => m.Inbound), itemStyle: { color: "#5470c6" } }, |
| | | { name: "åºåº", type: "line", data: this.monthlyData.map(m => m.Outbound), itemStyle: { color: "#91cc75" } } |
| | | ] |
| | | }; |
| | | this.charts.monthlyTrend.setOption(option, true); |
| | | }, |
| | | |
| | | // æ´æ°åºé¾åå¸å¾è¡¨ |
| | | updateStockAgeChart() { |
| | | const option = { |
| | | tooltip: { trigger: "item" }, |
| | | legend: { data: this.stockAgeData.map(s => s.Range), textStyle: { color: "#fff" } }, |
| | | series: [ |
| | | { |
| | | type: "pie", |
| | | radius: "60%", |
| | | data: this.stockAgeData.map((s, i) => ({ |
| | | name: s.Range, |
| | | value: s.Count |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)" |
| | | } |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | this.charts.stockAge.setOption(option, true); |
| | | }, |
| | | |
| | | // æ´æ°ä»åºåå¸å¾è¡¨ |
| | | updateWarehouseChart() { |
| | | const option = { |
| | | tooltip: { trigger: "axis" }, |
| | | xAxis: { |
| | | type: "category", |
| | | data: this.warehouseData.map(w => w.Warehouse), |
| | | axisLabel: { color: "#fff", rotate: 30 } |
| | | }, |
| | | yAxis: { |
| | | type: "value", |
| | | axisLabel: { color: "#fff" } |
| | | }, |
| | | series: [ |
| | | { |
| | | type: "bar", |
| | | data: this.warehouseData.map(w => w.Count), |
| | | itemStyle: { color: "#5470c6" } |
| | | } |
| | | ] |
| | | }; |
| | | this.charts.warehouse.setOption(option, true); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æ·»å æ ·å¼** |
| | | |
| | | ```vue |
| | | <style scoped> |
| | | .dashboard-container { |
| | | padding: 20px; |
| | | background-color: #0e1a2b; |
| | | min-height: calc(100vh - 60px); |
| | | } |
| | | |
| | | .chart-row { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .chart-row.full-width { |
| | | width: 100%; |
| | | } |
| | | |
| | | .chart-card { |
| | | flex: 1; |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border: 1px solid rgba(25, 186, 139, 0.17); |
| | | border-radius: 4px; |
| | | padding: 15px; |
| | | position: relative; |
| | | } |
| | | |
| | | .chart-card::before { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 10px; |
| | | height: 10px; |
| | | border-top: 2px solid #02a6b5; |
| | | border-left: 2px solid #02a6b5; |
| | | } |
| | | |
| | | .chart-card::after { |
| | | content: ""; |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | width: 10px; |
| | | height: 10px; |
| | | border-top: 2px solid #02a6b5; |
| | | border-right: 2px solid #02a6b5; |
| | | } |
| | | |
| | | .card-title { |
| | | color: #fff; |
| | | font-size: 16px; |
| | | text-align: center; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .chart-content { |
| | | height: 280px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .stock-total { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 280px; |
| | | } |
| | | |
| | | .total-number { |
| | | font-size: 64px; |
| | | font-weight: bold; |
| | | color: #67caca; |
| | | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif; |
| | | } |
| | | |
| | | .total-label { |
| | | font-size: 18px; |
| | | color: #fcf0d8; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | /* å
¨å®½å¾è¡¨ */ |
| | | .full-width .chart-card { |
| | | flex: none; |
| | | width: 100%; |
| | | } |
| | | |
| | | .full-width .chart-content { |
| | | height: 350px; |
| | | } |
| | | </style> |
| | | ``` |
| | | |
| | | - [ ] **Step 4: æäº¤ä»£ç ** |
| | | |
| | | ```bash |
| | | git add WIDESEA_WMSClient/src/views/Home.vue |
| | | git commit -m "feat(Home): éåé¦é¡µä¸ºä»ªè¡¨çå¾è¡¨é¡µé¢" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### Task 3: éªè¯å®ç° |
| | | |
| | | - [ ] **Step 1: æå»ºå端** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer |
| | | dotnet build WIDESEA_WMSServer.sln |
| | | ``` |
| | | |
| | | é¢æï¼æå»ºæåï¼æ é误 |
| | | |
| | | - [ ] **Step 2: æå»ºå端** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSClient |
| | | yarn build |
| | | ``` |
| | | |
| | | é¢æï¼æå»ºæåï¼æ é误 |
| | | |
| | | - [ ] **Step 3: å¯å¨å端æµè¯ API** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer/WIDESEA_WMSServer |
| | | dotnet run |
| | | ``` |
| | | |
| | | ä½¿ç¨æµè§å¨æ Postman æµè¯ï¼ |
| | | - `GET http://localhost:9291/api/Dashboard/Overview` |
| | | - `GET http://localhost:9291/api/Dashboard/DailyStats?days=30` |
| | | - `GET http://localhost:9291/api/Dashboard/WeeklyStats?weeks=12` |
| | | - `GET http://localhost:9291/api/Dashboard/MonthlyStats?months=12` |
| | | - `GET http://localhost:9291/api/Dashboard/StockAgeDistribution` |
| | | - `GET http://localhost:9291/api/Dashboard/StockByWarehouse` |
| | | |
| | | 颿ï¼åæ¥å£è¿å JSON æ°æ®ï¼æ ¼å¼ç¬¦åè®¾è®¡ææ¡£ |
| | | |
| | | --- |
| | | |
| | | ## æ»ç» |
| | | |
| | | ### å端ï¼DashboardControllerï¼ |
| | | | æ¥å£ | è·¯ç± | 说æ | |
| | | |------|------|------| |
| | | | Overview | GET /api/Dashboard/Overview | æ»è§æ°æ® | |
| | | | DailyStats | GET /api/Dashboard/DailyStats?days=30 | æ¯æ¥ç»è®¡ | |
| | | | WeeklyStats | GET /api/Dashboard/WeeklyStats?weeks=12 | æ¯å¨ç»è®¡ | |
| | | | MonthlyStats | GET /api/Dashboard/MonthlyStats?months=12 | æ¯æç»è®¡ | |
| | | | StockAgeDistribution | GET /api/Dashboard/StockAgeDistribution | åºé¾åå¸ | |
| | | | StockByWarehouse | GET /api/Dashboard/StockByWarehouse | ä»åºåå¸ | |
| | | |
| | | ### å端ï¼Home.vueï¼ |
| | | | å¾è¡¨ | ç»ä»¶ ID | å¾è¡¨ç±»å | |
| | | |------|---------|----------| |
| | | | æ¬æåºå
¥åºè¶å¿ | chart-monthly-trend | æçº¿å¾ | |
| | | | 仿¥åºå
¥åºå¯¹æ¯ | chart-today | æ±ç¶å¾ | |
| | | | æ¬å¨åºå
¥åºå¯¹æ¯ | chart-week | æ±ç¶å¾ | |
| | | | æ¬æåºå
¥åºå¯¹æ¯ | chart-month | æ±ç¶å¾ | |
| | | | å½ååºåæ»é | (æ°åå¡ç) | - | |
| | | | åºååºé¾åå¸ | chart-stock-age | é¥¼å¾ | |
| | | | åä»åºåºååå¸ | chart-warehouse | æ±ç¶å¾ | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # åºå3Dæ¥çå¨ Implementation Plan |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. |
| | | |
| | | **Goal:** å®ç°åºå3Dæ¥çå¨ï¼ç¨æ·å¯å¨ Three.js 3D åºæ¯ä¸å·¡è§ä»åºãç¹å»è´§ä½æ¥çåºå详æ
|
| | | |
| | | **Architecture:** å端 Vue 3 + Element Plus + Three.jsï¼å端 ASP.NET Core 6 Web API + SignalR 宿¶æ¨é |
| | | |
| | | **Tech Stack:** Three.js, @microsoft/signalr, Element Plus, Vue 3 Composition API |
| | | |
| | | --- |
| | | |
| | | ## æä»¶ç»æ |
| | | |
| | | ``` |
| | | å端 (WIDESEA_WMSServer) |
| | | âââ WIDESEA_DTO/Stock/Stock3DLayoutDTO.cs # 3Då¸å±ååºDTO [æ°å»º] |
| | | âââ WIDESEA_IStockService/IStockInfoService.cs # æ·»å Get3DLayoutAsyncæ¹æ³ç¾å [ä¿®æ¹] |
| | | âââ WIDESEA_StockService/StockInfoService.cs # å®ç°Get3DLayoutAsync [ä¿®æ¹] |
| | | âââ WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs # æ·»å Get3DLayoutç«¯ç¹ [ä¿®æ¹] |
| | | âââ WIDESEA_WMSServer/Hubs/StockHub.cs # SignalR Hub [æ°å»º] |
| | | |
| | | å端 (WIDESEA_WMSClient) |
| | | âââ package.json # æ·»å threeä¾èµ [ä¿®æ¹] |
| | | âââ src/router/viewGird.js # 注åè·¯ç± [ä¿®æ¹] |
| | | âââ src/views/stock/stockChat.vue # 主页é¢ç»ä»¶ [æ°å»º] |
| | | âââ src/extension/stock/stockChat.js # æ©å±é
ç½® [æ°å»º] |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## å®ç°ä»»å¡ |
| | | |
| | | ### Task 1: å端 - å建 Stock3DLayoutDTO |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEA_WMSServer/WIDESEA_DTO/Stock/Stock3DLayoutDTO.cs` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | å建两个 DTO ç±»ï¼ |
| | | 1. `Stock3DLayoutDTO` - å
å«ä»åºåºæ¬ä¿¡æ¯ã尺寸ãçéå表ãè´§ä½æ°ç» |
| | | 2. `Location3DItemDTO` - å
å«å个货ä½çææ3D渲ææéæ°æ® |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - DTO å
嫿æ spec ä¸å®ä¹çåæ®µ |
| | | - å½åç©ºé´æ£ç¡® |
| | | - å¯ä»¥è¢« Service 屿£ç¡®å¼ç¨ |
| | | |
| | | ```csharp |
| | | namespace WIDESEA_DTO.Stock |
| | | { |
| | | /// <summary> |
| | | /// ä»åº3Då¸å±ååºDTO |
| | | /// </summary> |
| | | public class Stock3DLayoutDTO |
| | | { |
| | | public int WarehouseId { get; set; } |
| | | public string WarehouseName { get; set; } |
| | | public int MaxRow { get; set; } |
| | | public int MaxColumn { get; set; } |
| | | public int MaxLayer { get; set; } |
| | | public List<string> MaterielCodeList { get; set; } = new(); |
| | | public List<string> BatchNoList { get; set; } = new(); |
| | | public List<Location3DItemDTO> Locations { get; set; } = new(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è´§ä½3Dæ°æ®é¡¹ |
| | | /// </summary> |
| | | public class Location3DItemDTO |
| | | { |
| | | public int LocationId { get; set; } |
| | | public string LocationCode { get; set; } |
| | | public int Row { get; set; } |
| | | public int Column { get; set; } |
| | | public int Layer { get; set; } |
| | | public int LocationStatus { get; set; } // 0=空, 1=å ç¨, 2=éå®, 3=ç¦ç¨ |
| | | public int StockStatus { get; set; } // 0=æ è´§, 1=æè´§, 2=åºåç´§å¼ , 3=已满 |
| | | public float StockQuantity { get; set; } |
| | | public float MaxCapacity { get; set; } |
| | | public string? PalletCode { get; set; } |
| | | public string? MaterielCode { get; set; } |
| | | public string? MaterielName { get; set; } |
| | | public string? BatchNo { get; set; } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### Task 2: å端 - æ´æ° IStockInfoService æ¥å£ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_WMSServer/WIDESEA_IStockService/IStockInfoService.cs` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | 卿¥å£ä¸æ·»å æ¹æ³ç¾åï¼ |
| | | ```csharp |
| | | /// <summary> |
| | | /// è·åä»åº3Då¸å±æ°æ® |
| | | /// </summary> |
| | | /// <param name="warehouseId">ä»åºID</param> |
| | | /// <returns>3Då¸å±DTO</returns> |
| | | Task<Stock3DLayoutDTO> Get3DLayoutAsync(int warehouseId); |
| | | ``` |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - æ¹æ³ç¾åæ£ç¡® |
| | | - æ·»å äºææ¡£æ³¨é |
| | | - å¼ç¨äº Stock3DLayoutDTO |
| | | |
| | | --- |
| | | |
| | | ### Task 3: å端 - å®ç° Get3DLayoutAsync |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | å®ç° Get3DLayoutAsync æ¹æ³ï¼ |
| | | 1. æ¥è¯¢ä»åºä¿¡æ¯ |
| | | 2. æ¥è¯¢è¯¥ä»åºææè´§ä½ |
| | | 3. æ¥è¯¢åºåä¿¡æ¯ï¼å
嫿ç»ï¼ |
| | | 4. æåç©æç¼å·åæ¹æ¬¡å·å表 |
| | | 5. æ å°å° Location3DItemDTO |
| | | 6. 计ç®ä»åºå°ºå¯¸ |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - æ¹æ³è½æ£ç¡®è¿å Stock3DLayoutDTO |
| | | - ææ locationStatus å stockStatus 弿£ç¡®æ å° |
| | | - æ§è½éåä¸åä»åºï¼1000-5000è´§ä½ï¼ |
| | | |
| | | --- |
| | | |
| | | ### Task 4: å端 - æ·»å API ç«¯ç¹ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | æ·»å 端ç¹ï¼ |
| | | ```csharp |
| | | /// <summary> |
| | | /// è·åä»åº3Då¸å± |
| | | /// </summary> |
| | | /// <param name="warehouseId">ä»åºID</param> |
| | | /// <returns>3Då¸å±æ°æ®</returns> |
| | | [HttpGet("Get3DLayout")] |
| | | public async Task<WebResponseContent> Get3DLayout(int warehouseId) |
| | | { |
| | | var result = await Service.Get3DLayoutAsync(warehouseId); |
| | | return WebResponseContent.Instance.OK(result); |
| | | } |
| | | ``` |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - è·¯ç±æ£ç¡®ï¼GET /api/StockInfo/Get3DLayout?warehouseId={id} |
| | | - è¿åæ ¼å¼ç¬¦å WebResponseContent è§è |
| | | |
| | | --- |
| | | |
| | | ### Task 5: å端 - å建 SignalR Hub |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEA_WMSServer/WIDESEA_WMSServer/Hubs/StockHub.cs` |
| | | - Modify: `WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | 1. å建 StockHub ç±»ï¼ç»§æ¿ Microsoft.AspNetCore.SignalR.Hub |
| | | 2. æ·»å SendStockUpdate æ¹æ³ä¾å¤é¨è°ç¨ |
| | | 3. å¨ Program.cs 䏿³¨å SignalR æå¡å¹¶æ å° Hub |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - Hub å¯è¢«åç«¯è¿æ¥ |
| | | - SendStockUpdate æ¹æ³åå¨ä¸å¯è¢«è°ç¨ |
| | | |
| | | --- |
| | | |
| | | ### Task 6: å端 - å®è£
Three.js ä¾èµ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_WMSClient/package.json` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | æ·»å three ä¾èµå° package.jsonï¼ |
| | | ```json |
| | | "three": "^0.160.0" |
| | | ``` |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - package.json å
å« three ä¾èµ |
| | | - çæ¬å·åçï¼^0.160.0 ææ´æ°ç¨³å®çï¼ |
| | | |
| | | --- |
| | | |
| | | ### Task 7: å端 - 注åè·¯ç± |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_WMSClient/src/router/viewGird.js` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | å¨ stockView è·¯ç±åæ·»å ï¼ |
| | | ```javascript |
| | | { |
| | | path: '/stockChat', |
| | | name: 'stockChat', |
| | | component: () => import('@/views/stock/stockChat.vue') |
| | | } |
| | | ``` |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - è·¯ç±æ³¨åæ£ç¡® |
| | | - ä¸å
¶ä»è·¯ç±æ ¼å¼ä¸è´ |
| | | |
| | | --- |
| | | |
| | | ### Task 8: å端 - å建 stockChat.vue 主ç»ä»¶ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEA_WMSClient/src/views/stock/stockChat.vue` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | ç»ä»¶å¿
é¡»å
å«ï¼ |
| | | 1. ä»åº Tabsï¼el-tabsï¼ |
| | | 2. å·¥å
·æ ï¼çé + éç½®è§è§æé®ï¼ |
| | | 3. 3D Canvas å®¹å¨ |
| | | 4. ç¶æå¾ä¾ |
| | | 5. 详æ
å¼¹çªï¼el-dialog fullscreenï¼ |
| | | |
| | | Three.js åºæ¯ï¼ |
| | | 1. åºæ¯åå§åï¼èæ¯è² 0x1a1a2eï¼ |
| | | 2. éè§ç¸æº |
| | | 3. WebGLRenderer |
| | | 4. OrbitControlsï¼é»å°¼å¯ç¨çè½¨éæ§å¶å¨ï¼ |
| | | 5. ç¯å¢å
+ å®åå
|
| | | 6. å°é¢ï¼PlaneGeometryï¼ç½æ ¼ï¼ |
| | | 7. InstancedMesh æ¹é渲æè´§ä½ |
| | | 8. Raycaster ç¹å»æ¾å |
| | | 9. ç¸æº lerp èç¦å¨ç» |
| | | |
| | | é¢è²ç¼ç ï¼å端å®ç°ï¼ï¼ |
| | | - DISABLED(3): 0x2d2d2d |
| | | - LOCKED(2): 0xF56C6C |
| | | - EMPTY(0/æ è´§): 0x4a4a4a |
| | | - HAS_STOCK(1): 0x409EFF |
| | | - LOW_STOCK(2): 0xE6A23C |
| | | - FULL(3): 0x67C23A |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - 页é¢å¯ä»¥æ£å¸¸å è½½ |
| | | - Three.js åºæ¯æ£ç¡®åå§å |
| | | - ç¹å»è´§ä½è½æ¾ç¤ºè¯¦æ
å¼¹çª |
| | | - é¢è²ç¼ç æ£ç¡® |
| | | |
| | | --- |
| | | |
| | | ### Task 9: å端 - å建æ©å±é
ç½®æä»¶ |
| | | |
| | | **Files:** |
| | | - Create: `WIDESEA_WMSClient/src/extension/stock/stockChat.js` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | å建æ åæ©å±æä»¶æ ¼å¼ï¼ |
| | | ```javascript |
| | | let extension = { |
| | | components: { |
| | | gridHeader: '', |
| | | gridBody: '', |
| | | gridFooter: '', |
| | | modelHeader: '', |
| | | modelBody: '', |
| | | modelFooter: '' |
| | | }, |
| | | tableAction: '', |
| | | buttons: { view: [], box: [], detail: [] }, |
| | | methods: { |
| | | onInit() {}, |
| | | onInited() {} |
| | | } |
| | | }; |
| | | export default extension; |
| | | ``` |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - 符å项ç®ç°ææ©å±æä»¶æ¨¡å¼ |
| | | |
| | | --- |
| | | |
| | | ### Task 10: å端 - éæ SignalR 宿¶æ´æ° |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_WMSClient/src/views/stock/stockChat.vue` |
| | | |
| | | **详ç»è§èï¼** |
| | | |
| | | 1. å¨ onMounted ä¸åå§å SignalR è¿æ¥ |
| | | 2. è¿æ¥ /stockHub |
| | | 3. çå¬ StockUpdated äºä»¶ |
| | | 4. æ´æ°å¯¹åºè´§ä½ç stockQuantity å stockStatus |
| | | 5. å¨ææ´æ°è´§ä½é¢è² |
| | | 6. å¨ onUnmounted 䏿å¼è¿æ¥ |
| | | |
| | | **éªæ¶æ åï¼** |
| | | - SignalR è¿æ¥æ£å¸¸å»ºç« |
| | | - æ¶å°æ´æ°æ¶è´§ä½é¢è²è½å¨æåå |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ä»»å¡åºååå²è®°å½å®ç°è®¡å |
| | | |
| | | > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| | | |
| | | **Goal:** å¨ TaskService çä»»å¡å®ææ¹æ³ä¸æ·»å ä»»å¡åå²ååºååå²ä¿åã |
| | | |
| | | **Architecture:** 注å
¥ `ITask_HtyService` å `IStockInfo_HtyService`ï¼ä¿®æ¹ `CompleteTaskAsync` æ·»å åå²ä¿åï¼ä¸¤ä¸ªç©ºæçæ¹æ³å
èæ·»å åå²ä¿åã |
| | | |
| | | **Tech Stack:** ASP.NET Core 6.0, MapsterMapper, SqlSugar |
| | | |
| | | --- |
| | | |
| | | ## 任塿»è§ |
| | | |
| | | | ä»»å¡ | å
容 | |
| | | |------|------| |
| | | | Task 1 | 注å
¥æå¡ï¼ITask_HtyService, IStockInfo_HtyServiceï¼ | |
| | | | Task 2 | ä¿®æ¹ CompleteTaskAsync æ·»å ä»»å¡åå²ä¿å | |
| | | | Task 3 | ä¿®æ¹ 3 个è°ç¨æ¹ä¼ å
¥ operateType | |
| | | | Task 4 | InboundFinishTaskTrayAsync æ·»å åå²ä¿å | |
| | | | Task 5 | OutboundFinishTaskTrayAsync æ·»å åå²ä¿å | |
| | | |
| | | --- |
| | | |
| | | ## Task 1: 注å
¥åå²æå¡ |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs` |
| | | |
| | | - [ ] **Step 1: æ·»å åæ®µ** |
| | | |
| | | å¨ `_mesService` åæ®µåæ·»å ï¼ |
| | | ```csharp |
| | | private readonly ITask_HtyService _task_HtyService; |
| | | private readonly IStockInfo_HtyService _stockInfo_HtyService; |
| | | ``` |
| | | |
| | | - [ ] **Step 2: ä¿®æ¹æé 彿°** |
| | | |
| | | 卿é 彿°åæ°ä¸æ·»å ï¼ |
| | | ```csharp |
| | | ITask_HtyService task_HtyService, |
| | | IStockInfo_HtyService stockInfo_HtyService, |
| | | ``` |
| | | |
| | | 卿é 彿°ä½å
æ·»å ï¼ |
| | | ```csharp |
| | | _task_HtyService = task_HtyService; |
| | | _stockInfo_HtyService = stockInfo_HtyService; |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): 注å
¥ITask_HtyServiceåIStockInfo_HtyService" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 2: ä¿®æ¹ CompleteTaskAsync æ·»å ä»»å¡åå²ä¿å |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼`CompleteTaskAsync` æ¹æ³ï¼ |
| | | |
| | | - [ ] **Step 1: ä¿®æ¹æ¹æ³ç¾å** |
| | | |
| | | å°ï¼ |
| | | ```csharp |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task) |
| | | ``` |
| | | |
| | | ä¿®æ¹ä¸ºï¼ |
| | | ```csharp |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType) |
| | | ``` |
| | | |
| | | - [ ] **Step 2: ä¿®æ¹åå²ä¿åé»è¾** |
| | | |
| | | å°ï¼ |
| | | ```csharp |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | ``` |
| | | |
| | | ä¿®æ¹ä¸ºï¼ |
| | | ```csharp |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = operateType; |
| | | var saveResult = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0; |
| | | if (!saveResult) return WebResponseContent.Instance.Error("ä»»å¡åå²ä¿å失败"); |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | - [ ] **Step 4: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): CompleteTaskAsyncæ·»å ä»»å¡åå²ä¿åé»è¾" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 3: ä¿®æ¹ 3 个è°ç¨æ¹ä¼ å
¥ operateType |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs` |
| | | |
| | | - [ ] **Step 1: ä¿®æ¹ InboundFinishTaskAsync ç CompleteTaskAsync è°ç¨** |
| | | |
| | | å°ï¼ |
| | | ```csharp |
| | | return await CompleteTaskAsync(task); |
| | | ``` |
| | | |
| | | ä¿®æ¹ä¸ºï¼ |
| | | ```csharp |
| | | return await CompleteTaskAsync(task, "å
¥åºå®æ"); |
| | | ``` |
| | | |
| | | - [ ] **Step 2: ä¿®æ¹ OutboundFinishTaskAsync ç CompleteTaskAsync è°ç¨** |
| | | |
| | | å°ï¼ |
| | | ```csharp |
| | | return await CompleteTaskAsync(task); |
| | | ``` |
| | | |
| | | ä¿®æ¹ä¸ºï¼ |
| | | ```csharp |
| | | return await CompleteTaskAsync(task, "åºåºå®æ"); |
| | | ``` |
| | | |
| | | - [ ] **Step 3: ä¿®æ¹ RelocationFinishTaskAsync ç CompleteTaskAsync è°ç¨** |
| | | |
| | | å°ï¼ |
| | | ```csharp |
| | | return await CompleteTaskAsync(task); |
| | | ``` |
| | | |
| | | ä¿®æ¹ä¸ºï¼ |
| | | ```csharp |
| | | return await CompleteTaskAsync(task, "ç§»åºå®æ"); |
| | | ``` |
| | | |
| | | - [ ] **Step 4: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | - [ ] **Step 5: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): ä»»å¡å®ææ¹æ³ä¼ å
¥æ£ç¡®çOperateType" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 4: InboundFinishTaskTrayAsync æ·»å åå²ä¿å |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼`InboundFinishTaskTrayAsync` æ¹æ³ï¼çº¦ç¬¬ 403 è¡ï¼ |
| | | |
| | | - [ ] **Step 1: å¨å é¤ä»»å¡åæ·»å ä»»å¡åå²ååºååå²ä¿å** |
| | | |
| | | å¨ `var deleteResult = await BaseDal.DeleteDataAsync(task);` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // ä¿åä»»å¡åå² |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = "空æçå
¥åºå®æ"; |
| | | if (await _task_HtyService.Repository.AddDataAsync(historyTask) <= 0) |
| | | return content.Error("ä»»å¡åå²ä¿å失败"); |
| | | |
| | | // ä¿ååºååå² |
| | | var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo); |
| | | historyStock.InsertTime = DateTime.Now; |
| | | historyStock.OperateType = "空æçå
¥åºå®æ"; |
| | | if (await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) <= 0) |
| | | return content.Error("åºååå²ä¿å失败"); |
| | | ``` |
| | | |
| | | - [ ] **Step 2: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): InboundFinishTaskTrayAsyncæ·»å ä»»å¡ååºååå²ä¿å" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 5: OutboundFinishTaskTrayAsync æ·»å åå²ä¿å |
| | | |
| | | **Files:** |
| | | - Modify: `WIDESEA_TaskInfoService/TaskService.cs`ï¼`OutboundFinishTaskTrayAsync` æ¹æ³ï¼çº¦ç¬¬ 487 è¡ï¼ |
| | | |
| | | - [ ] **Step 1: å¨å é¤ä»»å¡åæ·»å ä»»å¡åå²ååºååå²ä¿å** |
| | | |
| | | å¨ `var deleteResult = await BaseDal.DeleteDataAsync(task);` ä¹åæ·»å ï¼ |
| | | |
| | | ```csharp |
| | | // ä¿åä»»å¡åå² |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = "空æçåºåºå®æ"; |
| | | if (await _task_HtyService.Repository.AddDataAsync(historyTask) <= 0) |
| | | return content.Error("ä»»å¡åå²ä¿å失败"); |
| | | |
| | | // ä¿ååºååå² |
| | | var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo); |
| | | historyStock.InsertTime = DateTime.Now; |
| | | historyStock.OperateType = "空æçåºåºå®æ"; |
| | | if (await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) <= 0) |
| | | return content.Error("åºååå²ä¿å失败"); |
| | | ``` |
| | | |
| | | - [ ] **Step 2: æå»ºéªè¯** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj |
| | | ``` |
| | | |
| | | - [ ] **Step 3: æäº¤** |
| | | |
| | | ```bash |
| | | git add WIDESEA_TaskInfoService/TaskService.cs |
| | | git commit -m "feat(TaskService): OutboundFinishTaskTrayAsyncæ·»å ä»»å¡ååºååå²ä¿å" |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## Task 6: æ´ä½æå»ºéªè¯ |
| | | |
| | | - [ ] **Step 1: æå»ºæ´ä¸ªè§£å³æ¹æ¡** |
| | | |
| | | ```bash |
| | | cd WIDESEA_WMSServer && dotnet build WIDESEA_WMSServer.sln |
| | | ``` |
| | | |
| | | 确认æ ç¼è¯é误ã |
| | | |
| | | --- |
| | | |
| | | ## éªè¯æ£æ¥æ¸
å |
| | | |
| | | - [ ] `ITask_HtyService` å `IStockInfo_HtyService` 已注å
¥ |
| | | - [ ] `CompleteTaskAsync` ç¾å已修æ¹ä¸ºå¸¦ `operateType` åæ° |
| | | - [ ] `InboundFinishTaskAsync` ä¼ å
¥ `"å
¥åºå®æ"` |
| | | - [ ] `OutboundFinishTaskAsync` ä¼ å
¥ `"åºåºå®æ"` |
| | | - [ ] `RelocationFinishTaskAsync` ä¼ å
¥ `"ç§»åºå®æ"` |
| | | - [ ] `InboundFinishTaskTrayAsync` æ·»å äºä»»å¡åå²ååºååå² |
| | | - [ ] `OutboundFinishTaskTrayAsync` æ·»å äºä»»å¡åå²ååºååå² |
| | | - [ ] è§£å³æ¹æ¡æå»ºæ é误 |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # MES æçè¿ç«åºç«éæè®¾è®¡ |
| | | |
| | | ## æ¦è¿° |
| | | |
| | | å¨ `TaskService` çå
¥åºå®æ/åºåºå®ææ¹æ³ä¸éæ MES æçè¿ç«/åºç«è°ç¨ï¼æ°å¢ç©ºæçå
¥åº/åºåºå®ææ¹æ³ã |
| | | |
| | | ## äºå¡çç¥ |
| | | |
| | | MES è°ç¨çº³å
¥ `ExecuteWithinTransactionAsync` å
â MES 失败æ¶äºå¡åæ»ï¼WMS æ°æ®ä¸è½åºã |
| | | |
| | | ## æ°æ®æµ |
| | | |
| | | | æ¹æ³ | æä½ | MES è°ç¨ | |
| | | |------|------|----------| |
| | | | `InboundFinishTaskAsync` | å
¥åºå®æ | `InboundInContainer` | |
| | | | `OutboundFinishTaskAsync` | åºåºå®æ | `OutboundInContainer` | |
| | | | `InboundFinishTaskTrayAsync`ï¼æ°å¢ï¼ | 空æçå
¥åºå®æ | æ | |
| | | | `OutboundFinishTaskTrayAsync`ï¼æ°å¢ï¼ | 空æçåºåºå®æ | æ | |
| | | |
| | | ## æ¶åæä»¶ |
| | | |
| | | - `WIDESEA_TaskInfoService/TaskService.cs` |
| | | |
| | | ## 详ç»è®¾è®¡ |
| | | |
| | | ### 1. å
¥åºå®æ (InboundFinishTaskAsync) |
| | | |
| | | **ç°æé»è¾ï¼äºå¡å
ï¼ï¼** |
| | | - æ´æ°åºåä¿¡æ¯ï¼LocationCode, OutboundDate, StockStatusï¼ |
| | | - æ´æ°è´§ä½ç¶æä¸º InStock |
| | | - è°ç¨ CompleteTaskAsync å é¤ä»»å¡ |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | - å¨ `CompleteTaskAsync` ä¹åæ·»å MES `InboundInContainer` è°ç¨ |
| | | - 请æ±åæ°ï¼ |
| | | - `EquipmentCode = "STK-GROUP-001"` |
| | | - `ResourceCode = "STK-GROUP-001"` |
| | | - `ContainerCode = taskDto.PalletCode` |
| | | - `LocalTime = DateTime.Now` |
| | | - MES 失败 â äºå¡åæ»ï¼è¿åé误 |
| | | |
| | | ### 2. åºåºå®æ (OutboundFinishTaskAsync) |
| | | |
| | | **ç°æé»è¾ï¼äºå¡å
ï¼ï¼** |
| | | - æ´æ°åºåä¿¡æ¯ï¼LocationId=0, LocationCode=null, OutboundDateï¼ |
| | | - æ´æ°è´§ä½ç¶æä¸º Free |
| | | - è°ç¨ CompleteTaskAsync å é¤ä»»å¡ |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | - å¨ `CompleteTaskAsync` ä¹åæ·»å MES `OutboundInContainer` è°ç¨ |
| | | - 请æ±åæ°ï¼ |
| | | - `EquipmentCode = "STK-GROUP-001"` |
| | | - `ResourceCode = "STK-GROUP-001"` |
| | | - `ContainerCode = taskDto.PalletCode` |
| | | - `LocalTime = DateTime.Now` |
| | | - `OutboundInContainerRequest` æ `ParamList` åæ®µï¼ç®å为空å表 `new List<ParamItem>()` |
| | | - MES 失败 â äºå¡åæ»ï¼è¿åé误 |
| | | |
| | | ### 3. æ°å¢ç©ºæçå
¥åºå®æ (InboundFinishTaskTrayAsync) |
| | | |
| | | **æ¹æ³ç¾åï¼** |
| | | ```csharp |
| | | public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | ``` |
| | | |
| | | **é»è¾ï¼** |
| | | 1. æ¥è¯¢ä»»å¡ï¼æçå· = taskDto.PalletCodeï¼ |
| | | 2. æ¥è¯¢è´§ä½ä¿¡æ¯ |
| | | 3. æ¥è¯¢åºåä¿¡æ¯ |
| | | 4. äºå¡å
ï¼ |
| | | - æ´æ°åºå LocationCode/LocationId |
| | | - æ´æ°è´§ä½ç¶æä¸º InStock |
| | | - æ´æ°åºåç¶æä¸ºç©ºæçåºå |
| | | - å é¤ä»»å¡ï¼ä¸è°ç¨ CompleteTaskAsyncï¼èªå·±å®ç°ï¼ |
| | | 5. æ é MES è°ç¨ |
| | | |
| | | ### 4. æ°å¢ç©ºæçåºåºå®æ (OutboundFinishTaskTrayAsync) |
| | | |
| | | **æ¹æ³ç¾åï¼** |
| | | ```csharp |
| | | public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto) |
| | | ``` |
| | | |
| | | **é»è¾ï¼** |
| | | 1. æ¥è¯¢ä»»å¡ï¼æçå· = taskDto.PalletCodeï¼ |
| | | 2. æ¥è¯¢è´§ä½ä¿¡æ¯ |
| | | 3. æ¥è¯¢åºåä¿¡æ¯ |
| | | 4. äºå¡å
ï¼ |
| | | - æ´æ°åºå LocationId=0, LocationCode=null |
| | | - æ´æ°è´§ä½ç¶æä¸º Free |
| | | - æ´æ°åºåç¶æä¸ºåºåºå®æ |
| | | - å é¤ä»»å¡ï¼èªå·±å®ç°ï¼ |
| | | 5. æ é MES è°ç¨ |
| | | |
| | | ## é误å¤ç |
| | | |
| | | - MES è°ç¨æ£æ¥ `mesResult.Data?.IsSuccess`ï¼MES ä¸å¡å±æåï¼ |
| | | - é误è¿åï¼`"ä»»å¡å®æå¤±è´¥ï¼MESè¿ç«å¤±è´¥: {mesResult?.Data?.Msg ?? mesResult?.ErrorMessage ?? "æªç¥é误"}"` |
| | | - 空æçæ¹æ³æ MES é误å¤ç |
| | | |
| | | ## 设å¤ç¼ç |
| | | |
| | | ç»ä¸ä½¿ç¨ `STK-GROUP-001`ï¼ä¸ç»çä¿æä¸è´ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # MES çµè¯ç»å®è§£ç»éæè®¾è®¡ |
| | | |
| | | ## æ¦è¿° |
| | | |
| | | å¨ `StockService` çç»çãæ¢çãæçæä½ä¸ï¼å° MES çµè¯ç»å®/è§£ç»è°ç¨çº³å
¥ WMS äºå¡å
ï¼ç¡®ä¿åºåæ°æ®ä¸ MES ç¶æä¸è´ã |
| | | |
| | | ## äºå¡çç¥ |
| | | |
| | | **MES è°ç¨çº³å
¥ WMS äºå¡å
** â 妿 MES è°ç¨å¤±è´¥ï¼æ´ä¸ªäºå¡åæ»ï¼WMS åºåæ°æ®ä¸ä¼ååã |
| | | |
| | | ## æ°æ®æµ |
| | | |
| | | | æä½ | MES è°ç¨ | æ¶æº | |
| | | |------|----------|------| |
| | | | **ç»ç** | `BindContainer` | WMS åºååå
¥å | |
| | | | **æ¢ç** | `UnBindContainer` â `BindContainer` | è§£ç»å¨æ¢åºåï¼ç»å®å¨æ¢å
¥å | |
| | | | **æç** | `UnBindContainer` | åºåæç»å é¤å | |
| | | |
| | | ## æ¶åæä»¶ |
| | | |
| | | - `WIDESEA_StockService/StockSerivce.cs` |
| | | |
| | | ## 详ç»è®¾è®¡ |
| | | |
| | | ### 1. ç»ç (GroupPalletAsync) |
| | | |
| | | **ç°æé»è¾ï¼** |
| | | - å¨äºå¡å
æ§è¡ WMS åºååå
¥ |
| | | - 第 166 è¡å·²åå¨ `_mesService.BindContainer()` è°ç¨ï¼ä½ç¼ºå° `await` åç»æå¤ç |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | - ä¿®å¤ä¸º `await _mesService.BindContainer(bindRequest)` |
| | | - æ£æ¥è¿åç»æï¼`result.Success == false` æ¶äºå¡åæ» |
| | | - é误è¿åï¼`"ç»çæåï¼ä½MESè°ç¨å¤±è´¥: {MESé误}"` |
| | | |
| | | ### 2. æ¢ç (ChangePalletAsync) |
| | | |
| | | **ç°æé»è¾ï¼** |
| | | - äºå¡å
ï¼æ¥è¯¢æºæçåç®æ æç â æ´æ°åºåæç»ç `StockId` |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | - 卿´æ°åºåæç»åï¼è°ç¨ `UnBindContainer` è§£ç»æºæççµè¯ |
| | | - 卿´æ°åºåæç»åï¼è°ç¨ `BindContainer` ç»å®å°ç®æ æç |
| | | - MES 失败 â äºå¡åæ» |
| | | |
| | | **UnBindContainer è¯·æ±æå»ºï¼** |
| | | ```csharp |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = stock.SourcePalletNo, |
| | | SfcList = detailEntities.Select(d => d.SerialNumber).ToList() |
| | | }; |
| | | ``` |
| | | |
| | | ### 3. æç (SplitPalletAsync) |
| | | |
| | | **ç°æé»è¾ï¼** |
| | | - äºå¡å
ï¼æ¥è¯¢åºåæç» â å 餿ç»è®°å½ |
| | | |
| | | **ä¿®æ¹åï¼** |
| | | - å é¤åï¼è°ç¨ `UnBindContainer` è§£ç»çµè¯ |
| | | - MES 失败 â äºå¡åæ» |
| | | |
| | | **UnBindContainer è¯·æ±æå»ºï¼** |
| | | ```csharp |
| | | var unbindRequest = new UnBindContainerRequest |
| | | { |
| | | EquipmentCode = "STK-GROUP-001", |
| | | ResourceCode = "STK-GROUP-001", |
| | | LocalTime = DateTime.Now, |
| | | ContainCode = stock.SourcePalletNo, |
| | | SfcList = detailEntities.Select(d => d.SerialNumber).ToList() |
| | | }; |
| | | ``` |
| | | |
| | | ## é误å¤ç |
| | | |
| | | ç»ä¸é误å¤ççç¥ï¼ |
| | | - MES è°ç¨å¤±è´¥æ¶ï¼äºå¡åæ» |
| | | - è¿åï¼`"{æä½}æåï¼ä½MESè°ç¨å¤±è´¥: {MESé误}"` |
| | | |
| | | å
¶ä¸ `{MESé误}` æ¥èª `result.Message`ã |
| | | |
| | | ## 设å¤ç¼ç |
| | | |
| | | 硬ç¼ç `EquipmentCode = "STK-GROUP-001"` å `ResourceCode = "STK-GROUP-001"`ï¼ä¸ç»çç°æé»è¾ä¿æä¸è´ã |
| | | |
| | | ## å®ç°è¦ç¹ |
| | | |
| | | - ææ MES è°ç¨ä½¿ç¨ `await` |
| | | - æ£æ¥ `HttpResponseResult<MesResponse>` ç `Success` 屿§ |
| | | - MES è°ç¨å¤±è´¥æ¶è¿åé误信æ¯ï¼äºå¡èªå¨åæ»ï¼`ExecuteWithinTransactionAsync` å¨ `result.Status != true` æ¶åæ»ï¼ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # é¦é¡µä»ªè¡¨çå¾è¡¨åè½è®¾è®¡ |
| | | |
| | | ## 1. æ¦è¿° |
| | | |
| | | å¨ WMS å端é¦é¡µæ·»å 仪表çå¾è¡¨ï¼å±ç¤ºä»åºçåºå
¥åºç»è®¡ååºåæ°æ®ãæ°æ®åºäºä»»å¡å®ææ¶é´è¿è¡ç»è®¡ã |
| | | |
| | | > **æ°æ®æ¥æºè¯´æï¼** 已宿任å¡åå¨å¨åå²è¡¨ `Dt_Task_Hty` ä¸ï¼ä½¿ç¨ `InsertTime`ï¼ä»»å¡å®æåç§»å
¥åå²çæ¶é´ï¼ä½ä¸ºä»»å¡å®ææ¶é´ã |
| | | |
| | | ## 2. 页é¢å¸å± |
| | | |
| | | éç¨æ··åå¸å±ï¼é¡¶é¨å¤§å¾å±ç¤ºè¶å¿ï¼ä¸æ¹ç½æ ¼å±ç¤ºå¯¹æ¯å详æ
ã |
| | | |
| | | ``` |
| | | âââââââââââââââââââââââââââââââââââââââââââââââââââ |
| | | â æ¬æåºå
¥åºè¶å¿ï¼å
¨å®½ï¼æçº¿+æ±ç¶ç»åï¼ â |
| | | âââââââââââââââââââââââ¬âââââââââââââââââââââââââââ⤠|
| | | â 仿¥åºå
¥åºå¯¹æ¯ â æ¬å¨åºå
¥åºå¯¹æ¯ â |
| | | â ï¼æ±ç¶å¾ï¼ â ï¼æ±ç¶å¾ï¼ â |
| | | âââââââââââââââââââââââ¼âââââââââââââââââââââââââââ⤠|
| | | â æ¬æåºå
¥åºå¯¹æ¯ â å½ååºåæ»é â |
| | | â ï¼æ±ç¶å¾ï¼ â ï¼æ°åå¡çï¼ â |
| | | âââââââââââââââââââââââ¼âââââââââââââââââââââââââââ⤠|
| | | â åºååºé¾åå¸ â åä»åºåºååå¸ â |
| | | â ï¼æ±ç¶å¾ï¼ â ï¼æ±ç¶å¾ï¼ â |
| | | âââââââââââââââââââââââ´ââââââââââââââââââââââââââââ |
| | | ``` |
| | | |
| | | > 第ä¸è¡ï¼æ¬æè¶å¿å¾ï¼ï¼å
¨å®½æ¾ç¤º |
| | | > 䏿¹ 2x2 ç½æ ¼ï¼4个å¾è¡¨åååå¸ |
| | | |
| | | ## 3. å端æ¥å£è®¾è®¡ |
| | | |
| | | ### 3.1 æ»è§æ¥å£ |
| | | |
| | | **GET** `/api/Dashboard/Overview` |
| | | |
| | | è¿åé¦é¡µå è½½æ¶éè¦çæææ±æ»æ°åï¼ä¸æ¬¡è°ç¨è·åå
³é®ææ ã |
| | | |
| | | **ååºæ°æ®ï¼** |
| | | ```json |
| | | { |
| | | "Status": true, |
| | | "Data": { |
| | | "TodayInbound": 120, |
| | | "TodayOutbound": 95, |
| | | "MonthInbound": 3500, |
| | | "MonthOutbound": 3200, |
| | | "TotalStock": 45000 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | > 注ï¼åºåæ°éåä½ä¸º"æçæ°"æ"çµè¯æ°"ï¼åå³äºå®é
ä¸å¡ç»è®¡å£å¾ã |
| | | |
| | | ### 3.2 æ¯æ¥ç»è®¡æ¥å£ |
| | | |
| | | **GET** `/api/Dashboard/DailyStats?days=30` |
| | | |
| | | è¿åè¿Nå¤©çæ¯æ¥åºå
¥åºç»è®¡ã |
| | | |
| | | **åæ°ï¼** |
| | | - `days`ï¼å¤©æ°ï¼é»è®¤ 30ï¼æå¤§ 365 |
| | | - ç»è®¡åºäºæå¡å¨æ¬å°æ¶é´ï¼å½æ¥æ°æ®å
å«å½æ¥ç»è®¡ |
| | | |
| | | **ååºæ°æ®ï¼** |
| | | ```json |
| | | { |
| | | "Status": true, |
| | | "Data": [ |
| | | { "Date": "2026-03-01", "Inbound": 120, "Outbound": 95 }, |
| | | { "Date": "2026-03-02", "Inbound": 150, "Outbound": 130 }, |
| | | ... |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 3.3 æ¯å¨ç»è®¡æ¥å£ |
| | | |
| | | **GET** `/api/Dashboard/WeeklyStats?weeks=12` |
| | | |
| | | è¿åè¿Nå¨çæ¯å¨åºå
¥åºç»è®¡ãå¨ä»å¨ä¸å¼å§è®¡ç®ã |
| | | |
| | | **åæ°ï¼** |
| | | - `weeks`ï¼å¨æ°ï¼é»è®¤ 12 |
| | | |
| | | **ååºæ°æ®ï¼** |
| | | ```json |
| | | { |
| | | "Status": true, |
| | | "Data": [ |
| | | { "Week": "2026-W09", "Inbound": 850, "Outbound": 780 }, |
| | | { "Week": "2026-W10", "Inbound": 920, "Outbound": 870 }, |
| | | ... |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 3.4 æ¯æç»è®¡æ¥å£ |
| | | |
| | | **GET** `/api/Dashboard/MonthlyStats?months=12` |
| | | |
| | | è¿åè¿Næçæ¯æåºå
¥åºç»è®¡ã |
| | | |
| | | **åæ°ï¼** |
| | | - `months`ï¼ææ°ï¼é»è®¤ 12 |
| | | |
| | | **ååºæ°æ®ï¼** |
| | | ```json |
| | | { |
| | | "Status": true, |
| | | "Data": [ |
| | | { "Month": "2025-04", "Inbound": 3500, "Outbound": 3200 }, |
| | | { "Month": "2025-05", "Inbound": 3800, "Outbound": 3600 }, |
| | | ... |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 3.5 åºååºé¾å叿¥å£ |
| | | |
| | | **GET** `/api/Dashboard/StockAgeDistribution` |
| | | |
| | | è¿ååºååºé¾å叿°æ®ã |
| | | |
| | | **ååºæ°æ®ï¼** |
| | | ```json |
| | | { |
| | | "Status": true, |
| | | "Data": [ |
| | | { "Range": "7天å
", "Count": 12000 }, |
| | | { "Range": "7-30天", "Count": 18000 }, |
| | | { "Range": "30-90天", "Count": 10000 }, |
| | | { "Range": "90天以ä¸", "Count": 5000 } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 3.6 åä»åºåºåå叿¥å£ |
| | | |
| | | **GET** `/api/Dashboard/StockByWarehouse` |
| | | |
| | | è¿ååä»åºçåºåæ°éåå¸ã |
| | | |
| | | **ååºæ°æ®ï¼** |
| | | ```json |
| | | { |
| | | "Status": true, |
| | | "Data": [ |
| | | { "Warehouse": "ä»åºA", "Count": 15000 }, |
| | | { "Warehouse": "ä»åºB", "Count": 12000 }, |
| | | { "Warehouse": "ä»åºC", "Count": 18000 } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ## 4. ææ¯å®ç° |
| | | |
| | | ### 4.1 å端å®ç° |
| | | |
| | | **æ°å¢æä»¶ï¼** |
| | | - `WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs` - ä»ªè¡¨çæ§å¶å¨ |
| | | |
| | | **æ°æ®æ¥æºï¼** |
| | | - `Dt_Task_Hty` 表ï¼å·²å®æä»»å¡çåå²è¡¨ï¼ä½¿ç¨ `InsertTime` ä½ä¸ºä»»å¡å®ææ¶é´ |
| | | - `TaskTypeEnum` æä¸¾ï¼å
¥åºä»»å¡èå´ 500-600ï¼å¦ `Inbound=510`ï¼ï¼åºåºä»»å¡èå´ 100-200ï¼å¦ `Outbound=100`ï¼ |
| | | - `Dt_StockInfo` 表ï¼å½ååºåæ°æ®ï¼ä½¿ç¨ `CreateDate` ä½ä¸ºå
¥åºæ¶é´è®¡ç®åºé¾ |
| | | |
| | | **ç»è®¡æ°æ®é»è¾ï¼** |
| | | - æ `InsertTime` çæ¥æ/å¨/æåç»ç»è®¡ä»»å¡æ°é |
| | | - å
¥åºå¤æï¼`TaskType >= 500 && TaskType < 600` |
| | | - åºåºå¤æï¼`TaskType >= 100 && TaskType < 200` |
| | | - åºé¾ = å½åæ¶é´ - `CreateDate` |
| | | |
| | | ### 4.2 å端å®ç° |
| | | |
| | | **ä¿®æ¹æä»¶ï¼** |
| | | - `WIDESEA_WMSClient/src/views/Home.vue` - éå为空ç½çé¦é¡µï¼æ·»å 仪表çå¾è¡¨ |
| | | |
| | | **å¾è¡¨ç»ä»¶ï¼** |
| | | - å¤ç¨ `src/views/charts/bigdata.vue` ä¸ç ECharts ä½¿ç¨æ¨¡å¼ |
| | | - ä½¿ç¨ ECharts 5.0.2 |
| | | |
| | | **页é¢ç»æï¼** |
| | | ``` |
| | | Home.vue |
| | | âââ æ¬æè¶å¿å¾ï¼æçº¿+æ±ç¶ç»åï¼ |
| | | âââ 仿¥/æ¬å¨/æ¬æåºå
¥åºå¯¹æ¯ï¼æ±ç¶å¾ï¼ |
| | | âââ å½ååºåæ»éï¼æ°åå¡çï¼ |
| | | âââ åºååºé¾åå¸ï¼æ±ç¶å¾ï¼ |
| | | âââ åä»åºåºååå¸ï¼æ±ç¶å¾ï¼ |
| | | ``` |
| | | |
| | | **API è°ç¨ï¼** |
| | | - é¦é¡µå è½½æ¶è°ç¨ Overview æ¥å£è·åæ±æ»æ°æ® |
| | | - åå¾è¡¨ç»ä»¶ mounted æ¶è°ç¨å¯¹åºæ¥å£è·åè¯¦ç»æ°æ® |
| | | |
| | | ## 5. å®ç°ä»»å¡ |
| | | |
| | | ### å端 |
| | | 1. å建 DashboardController |
| | | 2. å®ç° Overview æ¥å£ |
| | | 3. å®ç° DailyStats æ¥å£ |
| | | 4. å®ç° WeeklyStats æ¥å£ |
| | | 5. å®ç° MonthlyStats æ¥å£ |
| | | 6. å®ç° StockAgeDistribution æ¥å£ |
| | | 7. å®ç° StockByWarehouse æ¥å£ |
| | | |
| | | ### å端 |
| | | 1. éå Home.vueï¼ä½¿ç¨ ECharts å®ç°ä»ªè¡¨çå¸å± |
| | | 2. å®ç° Overview æ¥å£è°ç¨ |
| | | 3. å®ç°åå¾è¡¨ç»ä»¶ |
| | | 4. è°æ´å¾è¡¨æ ·å¼åååºå¼å¸å± |
| | | |
| | | ## 6. é¢è®¡æä»¶åæ´ |
| | | |
| | | **æ°å¢ï¼** |
| | | - `WIDESEA_WMSServer/Controllers/Dashboard/DashboardController.cs` |
| | | |
| | | **ä¿®æ¹ï¼** |
| | | - `WIDESEA_WMSClient/src/views/Home.vue` |
| | | |
| | | **åèï¼** |
| | | - `WIDESEA_WMSClient/src/views/charts/bigdata.vue` - ECharts 使ç¨ç¤ºä¾ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # åºå3Dæ¥çå¨ (stockChat) è®¾è®¡ææ¡£ |
| | | |
| | | ## 1. æ¦è¿° |
| | | |
| | | - **åè½åç§°**ï¼åºå3Dä»åºæ¥çå¨ (stockChat) |
| | | - **æä»¶è·¯å¾**ï¼`WIDESEA_WMSClient/src/views/stock/stockChat.vue` |
| | | - **æ ¸å¿åè½**ï¼ä½¿ç¨ Three.js + WebGL å®ç°ä»åº3Då¯è§åï¼ç¨æ·å¯å¨3Dåºæ¯ä¸å·¡è§ä»åºãæ¥çè´§ä½ç¶æãç¹å»è´§ä½æ¥çåºå详æ
|
| | | - **ç®æ ç¨æ·**ï¼ä»åºç®¡çåãè°åº¦äººåãè´¨æ£äººå |
| | | |
| | | ## 2. ææ¯éå |
| | | |
| | | | ææ¯ | éå | 说æ | |
| | | |------|------|------| |
| | | | 3D弿 | Three.js | WebGL 主æµåºï¼Vue 3 å好 | |
| | | | 渲æçç¥ | InstancedMesh | æ¹é渲æå¤§éè´§ä½ï¼å次 drawcall | |
| | | | ç¶æç®¡ç | Vue 3 Composition API | `ref/reactive` | |
| | | | UIç»ä»¶ | Element Plus | ä¸é¡¹ç®ç°æææ¯æ ä¸è´ | |
| | | | 宿¶éä¿¡ | SignalR | ä¸å端 WebSocket é
åå®ç°åºåå¨ææ´æ° | |
| | | |
| | | ## 3. åè½éæ± |
| | | |
| | | ### 3.1 æ ¸å¿åè½ |
| | | |
| | | 1. **ä»åº3Då¸å±å±ç¤º** |
| | | - å
¨é¨è´§ä½å渲æï¼æç¶æçè² |
| | | - è´§æ¶å¼æåï¼å
å«å··é |
| | | - å°é¢ç½æ ¼èæ¯ |
| | | |
| | | 2. **å¤ä»åº Tab 忢** |
| | | - Element Plus Tabs ç»ä»¶ |
| | | - æ¯ä¸ªä»åºç¬ç«å è½½æ°æ® |
| | | - Tab 忢æ¶éç½®3Dåºæ¯ |
| | | |
| | | 3. **è´§ä½ç¹å»è¯¦æ
å¼¹çª** |
| | | - å
¨å± Dialog å±ç¤º |
| | | - æ¾ç¤ºï¼è´§ä½ä¿¡æ¯ãåºåç¶æãæçç¼å·ãç©ææç»å表 |
| | | - æ¯ææ¥çæ¹æ¬¡ãä¿è´¨æé¢è¦ |
| | | |
| | | 4. **3Dåºæ¯äº¤äº** |
| | | - é¼ æ æè½¬ï¼OrbitControlsï¼ |
| | | - æ»è½®ç¼©æ¾ |
| | | - ä¸é®å¹³ç§» |
| | | - ç¹å»è´§ä½é«äº® + ç¸æºèç¦ |
| | | |
| | | 5. **宿¶åºåç¶ææ´æ°** |
| | | - SignalR çå¬åºååå |
| | | - è´§ä½é¢è²å¨ææ´æ° |
| | | |
| | | 6. **çéè¿æ»¤** |
| | | - æç©æç±»å/æ¹æ¬¡/åºåç¶æè¿æ»¤ |
| | | - é«äº®æ¾ç¤ºå¹é
è´§ä½ |
| | | |
| | | 7. **ç¶æå¾ä¾** |
| | | - å³ä¸è§/åºé¨é¢è²å¾ä¾ |
| | | - è¯´ææ¯ç§é¢è²ä»£è¡¨çè´§ä½ç¶æ |
| | | |
| | | ### 3.2 è´§ä½ç¶æé¢è²ç¼ç ï¼é¢è²ä¼å
级ï¼locationStatus > stockStatusï¼ |
| | | |
| | | **é¢è²å¤å®è§åï¼æä¼å
级顺åºï¼ï¼** |
| | | |
| | | 1. **`locationStatus = 3`ï¼ç¦ç¨ï¼** â æ·±ç° `#2d2d2d`ï¼æé«ä¼å
çº§ï¼ |
| | | 2. **`locationStatus = 2`ï¼éå®ï¼** â çº¢è² `#F56C6C` |
| | | 3. **`locationStatus = 1`ï¼å ç¨ï¼ä¸ `stockStatus = 0`ï¼æ è´§ï¼** â æç° `#4a4a4a` |
| | | 4. **`locationStatus = 1`ï¼å ç¨ï¼ä¸ `stockStatus = 1`ï¼æè´§ï¼** â èè² `#409EFF` |
| | | 5. **`locationStatus = 1`ï¼å ç¨ï¼ä¸ `stockStatus = 2`ï¼åºåç´§å¼ <10%ï¼** â æ©è² `#E6A23C` |
| | | 6. **`locationStatus = 1`ï¼å ç¨ï¼ä¸ `stockStatus = 3`ï¼å·²æ»¡ â¥90%ï¼** â ç»¿è² `#67C23A` |
| | | 7. **`locationStatus = 0`ï¼ç©ºï¼** â æç° `#4a4a4a` |
| | | |
| | | **éå¼å®ä¹ï¼** |
| | | - åºåç´§å¼ ï¼`stockQuantity / maxCapacity < 10%` |
| | | - 已满ï¼`stockQuantity / maxCapacity ⥠90%` |
| | | |
| | | ## 4. å端 API |
| | | |
| | | ### 4.1 æ°å¢æ¥å£ |
| | | |
| | | ``` |
| | | GET /api/StockInfo/Get3DLayout?warehouseId={id} |
| | | ``` |
| | | |
| | | **ååºç»æ**ï¼ |
| | | ```json |
| | | { |
| | | "status": true, |
| | | "data": { |
| | | "warehouseId": 1, |
| | | "warehouseName": "主ä»åº", |
| | | "maxRow": 10, |
| | | "maxColumn": 20, |
| | | "maxLayer": 5, |
| | | "materielCodeList": ["M001", "M002", "M003"], |
| | | "batchNoList": ["B20260301", "B20260302"], |
| | | "locations": [ |
| | | { |
| | | "locationId": 1, |
| | | "locationCode": "A-01-02-03", |
| | | "row": 1, |
| | | "column": 2, |
| | | "layer": 3, |
| | | "locationStatus": 0, |
| | | "stockStatus": 2, |
| | | "stockQuantity": 50, |
| | | "maxCapacity": 100, |
| | | "palletCode": "PLT-001", |
| | | "materielCode": "M001", |
| | | "materielName": "ç©æA", |
| | | "batchNo": "B20260301" |
| | | } |
| | | ] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | **说æ**ï¼ |
| | | - `locationStatus`: 0=空, 1=å ç¨, 2=éå®, 3=ç¦ç¨ |
| | | - `stockStatus`: 0=æ è´§, 1=æè´§, 2=åºåç´§å¼ , 3=已满 |
| | | - `maxCapacity`: è´§ä½æå¤§å®¹éï¼ç¨äºè®¡ç®å¡«å
çï¼ |
| | | - `materielCodeList`: å½åä»åºææç©æç¼å·å表ï¼ç¨äºçéä¸æï¼ |
| | | - `batchNoList`: å½åä»åºæææ¹æ¬¡å·å表ï¼ç¨äºçéä¸æï¼ |
| | | - **é¢è²å¤å®å¨å端å®ç°**ï¼å端è¿å `stockQuantity` å `maxCapacity`ï¼å端æ 3.2 è§å计ç®é¢è² |
| | | |
| | | ### 4.2 SignalR 宿¶æ¨é |
| | | |
| | | **Hub è·¯å¾**ï¼`/stockHub` |
| | | |
| | | **æ¨éäºä»¶**ï¼ |
| | | ```javascript |
| | | // åºåååäºä»¶ |
| | | stockUpdated: { locationId, warehouseId, stockQuantity, stockStatus } |
| | | ``` |
| | | |
| | | ## 5. å端æä»¶ç»æ |
| | | |
| | | ``` |
| | | WIDESEA_WMSClient/src/ |
| | | âââ views/stock/ |
| | | â âââ stockChat.vue # 主页é¢ç»ä»¶ |
| | | âââ extension/stock/ |
| | | â âââ stockChat.js # ViewGrid æ©å±é
ç½® |
| | | âââ api/ |
| | | âââ http.js # å¤ç¨ç°æ http å°è£
|
| | | ``` |
| | | |
| | | ## 6. ç»ä»¶ç»æ (stockChat.vue) |
| | | |
| | | ```vue |
| | | <template> |
| | | <div class="stock-chat-container"> |
| | | <!-- ä»åº Tabs --> |
| | | <el-tabs v-model="activeWarehouse" @tab-change="onWarehouseChange"> |
| | | <el-tab-pane |
| | | v-for="wh in warehouseList" |
| | | :key="wh.warehouseId" |
| | | :label="wh.warehouseName" |
| | | :name="wh.warehouseId" |
| | | /> |
| | | </el-tabs> |
| | | |
| | | <!-- å·¥å
·æ --> |
| | | <div class="toolbar"> |
| | | <el-select v-model="filterStockStatus" placeholder="åºåç¶æçé" clearable> |
| | | <el-option label="æè´§" :value="1" /> |
| | | <el-option label="åºåç´§å¼ " :value="2" /> |
| | | <el-option label="已满" :value="3" /> |
| | | </el-select> |
| | | <el-select v-model="filterMaterielCode" placeholder="ç©æçé" clearable> |
| | | <el-option v-for="code in materielCodeList" :key="code" :label="code" :value="code" /> |
| | | </el-select> |
| | | <el-select v-model="filterBatchNo" placeholder="æ¹æ¬¡çé" clearable> |
| | | <el-option v-for="batch in batchNoList" :key="batch" :label="batch" :value="batch" /> |
| | | </el-select> |
| | | <el-button @click="resetCamera">éç½®è§è§</el-button> |
| | | </div> |
| | | |
| | | <!-- 3D Canvas --> |
| | | <div ref="canvasContainer" class="canvas-container" /> |
| | | |
| | | <!-- ç¶æå¾ä¾ --> |
| | | <div class="legend"> |
| | | <div v-for="item in legendItems" :key="item.status" class="legend-item"> |
| | | <span class="color-box" :style="{ background: item.color }" /> |
| | | <span>{{ item.label }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 详æ
å¼¹çª --> |
| | | <el-dialog v-model="detailDialogVisible" title="åºå详æ
" fullscreen> |
| | | <!-- 详æ
å
容 --> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | ``` |
| | | |
| | | ## 7. Three.js åºæ¯è®¾è®¡ |
| | | |
| | | ### 7.1 åå§åæµç¨ |
| | | |
| | | 1. å建 `WebGLRenderer`ï¼æè½½å° `canvasContainer` |
| | | 2. å建 `PerspectiveCamera`ï¼éè§ç¸æºï¼ |
| | | 3. å建 `Scene` åºæ¯ |
| | | 4. æ·»å å
ç
§ï¼ç¯å¢å
+ å®åå
ï¼ |
| | | 5. å建å°é¢ï¼`PlaneGeometry` + ç½æ ¼æè´¨ï¼ |
| | | 6. å建货æ¶è´§ä½ï¼`InstancedMesh`ï¼ |
| | | 7. æ·»å `OrbitControls` æ§å¶å¨ |
| | | 8. å¯å¨æ¸²æå¾ªç¯ |
| | | |
| | | ### 7.2 è´§ä½å®ä½ç®æ³ |
| | | |
| | | ``` |
| | | x = (column - maxColumn/2) * CELL_SIZE_X |
| | | y = layer * CELL_SIZE_Y |
| | | z = (row - maxRow/2) * CELL_SIZE_Z |
| | | ``` |
| | | |
| | | ### 7.3 ç¹å»æ¾å |
| | | |
| | | - ä½¿ç¨ `Raycaster` è¿è¡å°çº¿æ£æµ |
| | | - éè¿ `instanceId` è¯å«è¢«ç¹å»çè´§ä½å®ä¾ |
| | | - é«äº®ï¼ä¸´æ¶æ¿æ¢æè´¨é¢è² |
| | | |
| | | ### 7.4 ç¸æºèç¦å¨ç» |
| | | |
| | | - 使ç¨ç®åçº¿æ§æå¼ï¼lerpï¼å¹³æ»ç§»å¨ç¸æº |
| | | - ç®æ ä½ç½®ï¼è´§ä½åæ + åç§»é |
| | | - æå¼å
¬å¼ï¼`camera.position.lerp(target, 0.05)` æ¯å¸§æ§è¡ |
| | | |
| | | ## 8. æ§è½ä¼å |
| | | |
| | | | çç¥ | 说æ | |
| | | |------|------| |
| | | | InstancedMesh | 忬¡ drawcall æ¸²æææè´§ä½ | |
| | | | è§é¥ä½åé¤ | ç¸æºå¤çè´§ä½ä¸æ¸²æ | |
| | | | é¢è²ç¼å | æè´¨å¤ç¨ï¼é¿å
é¢ç¹å建 | |
| | | | requestAnimationFrame | 渲æå¾ªç¯ä½¿ç¨ RAF | |
| | | | æ°æ®å页 | 大ä»åºå¯èèæåºååçå è½½ | |
| | | |
| | | ## 9. è·¯ç±æ³¨å |
| | | |
| | | å¨ `viewGird.js` 䏿³¨åè·¯ç±ï¼ |
| | | ```javascript |
| | | { |
| | | path: '/stockChat', |
| | | name: 'stockChat', |
| | | component: () => import('@/views/stock/stockChat.vue') |
| | | } |
| | | ``` |
| | | |
| | | ## 10. å®ç°æ³¨æäºé¡¹ |
| | | |
| | | ### 10.1 å端交ä»ç© |
| | | - `Get3DLayout` API å®ç°ï¼è¿åç»æè§ 4.1 |
| | | - SignalR Hub é
ç½®ï¼`/stockHub`ï¼ï¼æ¨é `stockUpdated` äºä»¶ |
| | | - è´§ä½é¢è²å¤å®é»è¾æ 3.2 è§åå¨å端æå端å®ç°åå¯ |
| | | |
| | | ### 10.2 å端çéèå¨ |
| | | - `filterStockStatus`ã`filterMaterielCode`ã`filterBatchNo` ä¸è
èå¨ |
| | | - çéç»æé«äº®æ¾ç¤ºï¼éå¹é
è´§ä½åæï¼opacity: 0.3ï¼ |
| | | - çéä¸ºç©ºæ¶æ¾ç¤ºå
¨é¨è´§ä½æ£å¸¸é¢è² |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ä»»å¡åºååå²è®°å½è®¾è®¡ |
| | | |
| | | ## æ¦è¿° |
| | | |
| | | å¨ `TaskService` 䏿³¨å
¥ä»»å¡åå²ååºåå岿å¡ï¼å¨ææä»»å¡å®ææ¹æ³ä¸ä¿ååå²è®°å½ã |
| | | |
| | | ## æ¶åæä»¶ |
| | | |
| | | - `WIDESEA_TaskInfoService/TaskService.cs` |
| | | |
| | | ## 详ç»è®¾è®¡ |
| | | |
| | | ### 1. 注å
¥æå¡ |
| | | |
| | | å¨ `TaskService` 䏿·»å ï¼ |
| | | ```csharp |
| | | private readonly ITask_HtyService _task_HtyService; |
| | | private readonly IStockInfo_HtyService _stockInfo_HtyService; |
| | | ``` |
| | | |
| | | æé 彿°æ·»å åæ°å¹¶èµå¼ã |
| | | |
| | | ### 2. ä¿®æ¹ CompleteTaskAsync |
| | | |
| | | `InboundFinishTaskAsync`ã`OutboundFinishTaskAsync`ã`RelocationFinishTaskAsync` é½è°ç¨äº `CompleteTaskAsync`ï¼å æ¤åªéä¿®æ¹ `CompleteTaskAsync` å³å¯ã |
| | | |
| | | ç±äº `Dt_Task` 没æ `OperateType` åæ®µï¼éè¦ç» `CompleteTaskAsync` æ·»å åæ°ï¼ |
| | | |
| | | **ä¿®æ¹æ¹æ³ç¾åï¼** |
| | | ```csharp |
| | | private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType) |
| | | ``` |
| | | |
| | | **ä¿®æ¹å代ç ï¼** |
| | | ```csharp |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = operateType; |
| | | var saveResult = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0; |
| | | if (!saveResult) return WebResponseContent.Instance.Error("ä»»å¡åå²ä¿å失败"); |
| | | return WebResponseContent.Instance.OK("ä»»å¡å®æ"); |
| | | ``` |
| | | |
| | | **è°ç¨æ¹ä¿®æ¹ï¼3å¤ï¼ï¼** |
| | | - `InboundFinishTaskAsync`ï¼è°ç¨ `await CompleteTaskAsync(task, "å
¥åºå®æ")` |
| | | - `OutboundFinishTaskAsync`ï¼è°ç¨ `await CompleteTaskAsync(task, "åºåºå®æ")` |
| | | - `RelocationFinishTaskAsync`ï¼è°ç¨ `await CompleteTaskAsync(task, "ç§»åºå®æ")` |
| | | |
| | | ### 3. InboundFinishTaskTrayAsync å OutboundFinishTaskTrayAsync |
| | | |
| | | è¿ä¸¤ä¸ªæ¹æ³**ä¸è°ç¨** `CompleteTaskAsync`ï¼éè¦å¨äºå¡å
ãå é¤ä»»å¡åæ·»å ä»»å¡åå²ååºååå²ä¿åï¼ |
| | | |
| | | ```csharp |
| | | // ä»»å¡åå² |
| | | var historyTask = _mapper.Map<Dt_Task_Hty>(task); |
| | | historyTask.InsertTime = DateTime.Now; |
| | | historyTask.OperateType = "空æçå
¥åºå®æ"; // æ"空æçåºåºå®æ" |
| | | if (await _task_HtyService.Repository.AddDataAsync(historyTask) <= 0) |
| | | return content.Error("ä»»å¡åå²ä¿å失败"); |
| | | |
| | | // åºååå² |
| | | var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo); |
| | | historyStock.InsertTime = DateTime.Now; |
| | | historyStock.OperateType = "空æçå
¥åºå®æ"; // æ"空æçåºåºå®æ" |
| | | if (await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) <= 0) |
| | | return content.Error("åºååå²ä¿å失败"); |
| | | ``` |
| | | |
| | | ### 4. æä½ç±»åæä¸¾ |
| | | |
| | | | æ¹æ³ | OperateType | |
| | | |------|-------------| |
| | | | `InboundFinishTaskAsync` | "å
¥åºå®æ" | |
| | | | `OutboundFinishTaskAsync` | "åºåºå®æ" | |
| | | | `RelocationFinishTaskAsync` | "ç§»åºå®æ" | |
| | | | `InboundFinishTaskTrayAsync` | "空æçå
¥åºå®æ" | |
| | | | `OutboundFinishTaskTrayAsync` | "空æçåºåºå®æ" | |
| | | |
| | | ## å
³é®ç¹ |
| | | |
| | | - `Dt_Task_Hty` ç»§æ¿èª `Dt_Task`ï¼å
å« `SourceId`ï¼å表主é®ï¼ã`OperateType`ï¼æä½ç±»åï¼ã`InsertTime`ï¼æå
¥æ¶é´ï¼ |
| | | - ä½¿ç¨ `MapsterMapper` ç `_mapper.Map<T>()` è¿è¡å¯¹è±¡æ å° |
| | | - åå²ä¿åå¿
é¡»å¨å é¤åè®°å½**ä¹å**æ§è¡ |