Merge branch 'dev_tide' into dev_tide_cbsglxt
# Conflicts:
# .env.production
# index.html
# src/store/modules/user.js
# src/views/contractor/evaluateTemplate/index.vue
# src/views/environmentAccess/accessManagement/index.vue
# src/views/environmentAccess/intelligentInspectionManagement/index.vue
# src/views/environmentAccess/remoteMonitoringOfEquipment/index.vue
# src/views/environmentAccess/vehicleInformationCollection/index.vue
# src/views/financialManagement/expenseManagement/index.vue
# src/views/financialManagement/financialStatements/index.vue
# src/views/index.vue
# src/views/inventoryManagement/dispatchLog/index.vue
# src/views/inventoryManagement/issueManagement/index.vue
# src/views/inventoryManagement/receiptManagement/index.vue
# src/views/inventoryManagement/stockManagement/index.vue
# src/views/inventoryManagement/stockWarning/index.vue
# src/views/procurementManagement/paymentEntry/index.vue
# src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
# src/views/procurementManagement/procurementLedger/index.vue
# src/views/productionManagement/operationScheduling/components/formDia.vue
# src/views/productionManagement/operationScheduling/index.vue
# src/views/productionManagement/productionCosting/index.vue
# src/views/productionManagement/productionDispatching/components/formDia.vue
# src/views/productionManagement/productionDispatching/index.vue
# src/views/productionManagement/productionOrder/index.vue
# src/views/productionManagement/productionReporting/index.vue
# src/views/salesManagement/receiptPayment/index.vue
# src/views/salesManagement/receiptPaymentHistory/index.vue
# src/views/salesManagement/salesLedger/index.vue
# src/views/tideLogin.vue
# vite.config.js
| | |
| | | <meta name="renderer" content="webkit">
|
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
| | | <link rel="icon" href="/favicon.ico">
|
| | | <title>æ¿å
å管çç³»ç»</title>
|
| | | <title>%VITE_APP_TITLE%</title>
|
| | | <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
| | | <style>
|
| | | html,
|
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢ç产å
¥åºä¿¡æ¯å表 |
| | | export const getStockInPageByProduction = (params) => { |
| | | return request({ |
| | | url: "/stockin/listPageByProduction", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // åºåºå°è´¦-æ¥è¯¢èªå®ä¹å
¥åºä¿¡æ¯å表 |
| | | export const getStockInPageByCustom = (params) => { |
| | | return request({ |
| | | url: "/stockmanagement/listPageByCustom", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | // å
¥åºç®¡ç-æ¥è¯¢èªå®ä¹å
¥åºä¿¡æ¯å表 |
| | | export const getInPageByCustom = (params) => { |
| | | return request({ |
| | | url: "/stockin/listPageByCustom", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // åºåºå°è´¦-æ¥è¯¢ç产åºåºä¿¡æ¯å表 |
| | | export const getStockInPageByProduct = (params) => { |
| | | return request({ |
| | | url: "/stockmanagement/listPageByProduct", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // ä¿®æ¹å
¥åºåä¿¡æ¯ |
| | | export const updateStockIn = (data) => { |
| | | return request({ |
| | |
| | | data, |
| | | }); |
| | | }; |
| | | // ä¿®æ¹ææåºåä¿¡æ¯ |
| | | export const updateManagementByCustom = (data) => { |
| | | return request({ |
| | | url: "/stockin/updateManagementByCustom ", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | // æ°å¢ååå
¥åºä¿¡æ¯ |
| | | export function addSutockIn(data) { |
| | |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢èªå®ä¹å
¥åºä¿¡æ¯ |
| | | export function addStockInCustom(data) { |
| | | return request({ |
| | | url: '/stockin/addCustom', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // ç¼è¾èªå®ä¹å
¥åºä¿¡æ¯ |
| | | export function updateStockInCustom(data) { |
| | | return request({ |
| | | url: '/stockin/updateCustom', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | // ç¼è¾æåå
¥åºä¿¡æ¯ |
| | | export function updateProduct(data) { |
| | | return request({ |
| | | url: '/stockin/update', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // å é¤å
¥åºä¿¡æ¯ |
| | | export function delStockIn(ids) { |
| | | return request({ |
| | |
| | | }) |
| | | } |
| | | |
| | | // å é¤èªå®ä¹å
¥åºä¿¡æ¯ |
| | | export function delStockInCustom(ids) { |
| | | return request({ |
| | | url: '/stockin/delteCustom', |
| | | method: 'post', |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | // 导åºå
¥åºä¿¡æ¯ |
| | | export function exportStockIn(query) { |
| | | return request({ |
| | |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢ç产å
¥åºåºåä¿¡æ¯å表 |
| | | export const getStockManagePageByProduction = (params) => { |
| | | return request({ |
| | | url: "/stockin/listPageCopyByProduction", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢èªå®ä¹å
¥åºåºåä¿¡æ¯å表 |
| | | export const getStockManagePageByCustom = (params) => { |
| | | return request({ |
| | | url: "/stockin/listPageCopyByCustom", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | |
| | | // ä¿®æ¹åºåä¿¡æ¯ |
| | | export const updateStockManage = (data) => { |
| | |
| | | }) |
| | | } |
| | | |
| | | //åºåºæ¥å£ |
| | | // åºåºç®¡ç-é¢ç¨æ¥å£ |
| | | export const stockOut = (data) => { |
| | | return request({ |
| | | url: '/stockmanagement/stockout', |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | //æ¥è¯¢åºåºå表 |
| | | // åºåºå°è´¦-éè´åºåºæ¥è¯¢åºåºå表 |
| | | export const getStockOutPage = (params) => { |
| | | return request({ |
| | | url: "/stockmanagement/listPage", |
| | |
| | | data: ids |
| | | }) |
| | | } |
| | | |
| | | //导åºåºåºä¿¡æ¯ |
| | | export const exportStockOut = (query) => { |
| | | return request({ |
| | | url: '/stockmanagement/export', |
| | | method: 'get', |
| | | params: query, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // è·ååºåæ¥æ¥ç»è®¡ |
| | | export const getStockDailyReport = (params) => { |
| | | return request({ |
| | | url: "/stockin/getReportList", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | // è·ååºåææ¥ç»è®¡ |
| | | export const getStockMonthlyReport = (params) => { |
| | | return request({ |
| | | url: "/stockin/getReportList", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // è·åä½ä¸æ¥è¡¨ç»è®¡ |
| | | export const getWorkReport = (params) => { |
| | | return request({ |
| | | url: "/stockin/getReportList", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // è·ååºåè¿åºåç»è®¡ |
| | | export const getStockInOutReport = (params) => { |
| | | return request({ |
| | | url: "/stockin/getReportList", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // 导åºåºåæ¥è¡¨ |
| | | export const exportStockReport = (params) => { |
| | | return request({ |
| | | url: "/stockin/exportCopy", |
| | | method: "post", |
| | | params, |
| | | responseType: 'blob' |
| | | }); |
| | | }; |
| | | |
| | | // è·ååºåè¶å¿æ°æ® |
| | | export const getStockTrendData = (params) => { |
| | | return request({ |
| | | url: "/stockreport/trend", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢å¨æ°ç½é¢è¦å表 |
| | | export const getStockWarningPage = (params) => { |
| | | export const getStockWarningPage = (page, params) => { |
| | | return request({ |
| | | url: "/gasTankWarning/listPage", |
| | | method: "get", |
| | | params, |
| | | params: { |
| | | ...page, |
| | | ...params |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | |
| | | return request({ |
| | | url: "/gasTankWarning/add", |
| | | method: "post", |
| | | data, |
| | | data: data, |
| | | }); |
| | | }; |
| | | |
| | |
| | | export const updateStockWarning = (data) => { |
| | | return request({ |
| | | url: "/gasTankWarning/update", |
| | | method: "put", |
| | | data, |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | }; |
| | | |
| | |
| | | return request({ |
| | | url: "/gasTankWarning/delete", |
| | | method: "delete", |
| | | data: { ids }, |
| | | data: ids, |
| | | }); |
| | | }; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function listPage(query) { |
| | | return request({ |
| | | url: '/lavorIssue/listPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function statistics(params) { |
| | | return request({ |
| | | url: '/lavorIssue/statistics', |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | | |
| | | export function statisticsList(params) { |
| | | return request({ |
| | | url: '/lavorIssue/statisticsList', |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | | |
| | | // æ·»å |
| | | export function add(data) { |
| | | return request({ |
| | | url: '/lavorIssue/add', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹ |
| | | export function update(data) { |
| | | return request({ |
| | | url: '/lavorIssue/update', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | // å é¤ |
| | | export function deleteLedger(data) { |
| | | return request({ |
| | | url: '/lavorIssue/delete', |
| | | method: 'delete', |
| | | data |
| | | }) |
| | | } |
| | | |
| | |
| | | // å页æ¥è¯¢ |
| | | export function schedulingListPage(query) { |
| | | return request({ |
| | | url: "/salesLedger/scheduling/listPage", |
| | | url: "/productionOrder/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢ç产订å |
| | | export function addProductionOrder(query) { |
| | | return request({ |
| | | url: "/productionOrder/addProductionOrder", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹ç产订å |
| | | export function updateProductionOrder(query) { |
| | | return request({ |
| | | url: "/productionOrder/updateProductionOrder", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // å é¤ç产订å |
| | | export function deleteProductionOrder(query) { |
| | | return request({ |
| | | url: "/productionOrder/deleteProductionOrder", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
| | |
| | | export function isEqual(obj1, obj2) {
|
| | | return JSON.stringify(obj1) === JSON.stringify(obj2);
|
| | | }
|
| | |
|
| | | /**
|
| | | * è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD
|
| | | * @returns {string} æ ¼å¼åçæ¥æå符串
|
| | | */
|
| | | export function getCurrentDate() {
|
| | | const today = new Date();
|
| | | const year = today.getFullYear();
|
| | | const month = String(today.getMonth() + 1).padStart(2, '0'); // æä»½ä»0å¼å§
|
| | | const day = String(today.getDate()).padStart(2, '0');
|
| | | return `${year}-${month}-${day}`;
|
| | | }
|
| | |
|
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="é¨ç¦åç§°"> |
| | | <el-input |
| | | v-model="filters.name" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥é¨ç¦åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="é¨ç¦ç¶æ"> |
| | | <el-select |
| | | v-model="filters.status" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©é¨ç¦ç¶æ" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="æ£å¸¸" value="1"></el-option> |
| | | <el-option label="å¼å¸¸" value="0"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åºå"> |
| | | <el-input |
| | | v-model="filters.area" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥åºå" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus"> æ°å¢ </el-button> |
| | | <el-button type="danger" icon="Delete" :disabled="multipleList.length <= 0" @click="batchDelete">æ¹éå é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | </PIMTable> |
| | | </div> |
| | | <!-- æ°å¢ç¼è¾å¼¹çª --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px"> |
| | | <el-form :model="formData" label-width="100px"> |
| | | <el-form-item label="é¨ç¦åç§°" required> |
| | | <el-input v-model="formData.name" placeholder="请è¾å
¥é¨ç¦åç§°"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="åºå" required> |
| | | <el-input v-model="formData.area" placeholder="请è¾å
¥åºå"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ä½ç½®" required> |
| | | <el-input v-model="formData.location" placeholder="请è¾å
¥å
·ä½ä½ç½®"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" required> |
| | | <el-select v-model="formData.status" placeholder="è¯·éæ©ç¶æ"> |
| | | <el-option label="æ£å¸¸" value="1"></el-option> |
| | | <el-option label="å¼å¸¸" value="0"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æè¿°"> |
| | | <el-input v-model="formData.description" type="textarea" rows="3" placeholder="请è¾å
¥æè¿°ä¿¡æ¯"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveData">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from 'vue'; |
| | | import { Search, Plus, Delete } from '@element-plus/icons-vue'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | // å®ä¹åæ°æ® |
| | | const mockData = [ |
| | | { id: 1, name: 'ä¸é¨é¨ç¦', area: 'ç产åº', location: 'ä¸é¨å
¥å£', status: '1', lastUpdate: '2025-12-30 08:30:00', description: '主è¦ç¨äºåå·¥ä¸ä¸çéè¡' }, |
| | | { id: 2, name: '西é¨é¨ç¦', area: 'ä»å¨åº', location: '西é¨å
¥å£', status: '1', lastUpdate: '2025-12-30 09:15:00', description: '主è¦ç¨äºç©æµè½¦è¾éè¡' }, |
| | | { id: 3, name: 'åé¨é¨ç¦', area: 'åå
¬åº', location: 'åé¨å
¥å£', status: '0', lastUpdate: '2025-12-30 10:20:00', description: '主è¦ç¨äºè®¿å®¢éè¡ï¼å½åæ
é' }, |
| | | { id: 4, name: 'åé¨é¨ç¦', area: 'ç产åº', location: 'åé¨å
¥å£', status: '1', lastUpdate: '2025-12-30 11:45:00', description: '主è¦ç¨äºåææè¿è¾è½¦è¾éè¡' }, |
| | | { id: 5, name: '䏿§å®¤é¨ç¦', area: '䏿§åº', location: '䏿§å®¤é¨å£', status: '1', lastUpdate: '2025-12-30 13:20:00', description: 'ä»
éææäººåè¿å
¥' }, |
| | | ]; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const filters = reactive({ |
| | | name: '', |
| | | status: '', |
| | | area: '' |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0 |
| | | }); |
| | | |
| | | const multipleList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref('æ°å¢é¨ç¦'); |
| | | const formData = reactive({ |
| | | id: '', |
| | | name: '', |
| | | area: '', |
| | | location: '', |
| | | status: '1', |
| | | description: '' |
| | | }); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const columns = [ |
| | | { |
| | | label: 'é¨ç¦åç§°', |
| | | align: 'center', |
| | | prop: 'name', |
| | | }, |
| | | { |
| | | label: 'åºå', |
| | | align: 'center', |
| | | prop: 'area', |
| | | }, |
| | | { |
| | | label: 'ä½ç½®', |
| | | align: 'center', |
| | | prop: 'location', |
| | | }, |
| | | { |
| | | label: 'ç¶æ', |
| | | align: 'center', |
| | | prop: 'status', |
| | | formatter: (row) => { |
| | | return row.status === '1' ? '<el-tag type="success">æ£å¸¸</el-tag>' : '<el-tag type="danger">å¼å¸¸</el-tag>'; |
| | | } |
| | | }, |
| | | { |
| | | label: 'æåæ´æ°', |
| | | align: 'center', |
| | | prop: 'lastUpdate', |
| | | }, |
| | | { |
| | | label: 'æè¿°', |
| | | align: 'center', |
| | | prop: 'description', |
| | | }, |
| | | { |
| | | dataType: 'action', |
| | | label: 'æä½', |
| | | align: 'center', |
| | | fixed: 'right', |
| | | width: 140, |
| | | operation: [ |
| | | { |
| | | name: 'ç¼è¾', |
| | | type: 'text', |
| | | clickFun: (row) => { |
| | | edit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: 'å é¤', |
| | | type: 'text', |
| | | clickFun: (row) => { |
| | | deleteRow(row.id); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // è¿æ»¤åçæ°æ® |
| | | const filteredData = computed(() => { |
| | | return mockData.filter(item => { |
| | | const nameMatch = !filters.name || item.name.includes(filters.name); |
| | | const statusMatch = !filters.status || item.status === filters.status; |
| | | const areaMatch = !filters.area || item.area.includes(filters.area); |
| | | return nameMatch && statusMatch && areaMatch; |
| | | }); |
| | | }); |
| | | |
| | | // è·åè¡¨æ ¼æ°æ® |
| | | const getTableData = () => { |
| | | pagination.total = filteredData.value.length; |
| | | const start = (pagination.currentPage - 1) * pagination.pageSize; |
| | | const end = start + pagination.pageSize; |
| | | dataList.value = filteredData.value.slice(start, end); |
| | | }; |
| | | |
| | | // éç½®è¿æ»¤å¨ |
| | | const resetFilters = () => { |
| | | filters.name = ''; |
| | | filters.status = ''; |
| | | filters.area = ''; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å页åå |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å¤éå¤ç |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | // æ°å¢ |
| | | const add = () => { |
| | | dialogTitle.value = 'æ°å¢é¨ç¦'; |
| | | formData.id = ''; |
| | | formData.name = ''; |
| | | formData.area = ''; |
| | | formData.location = ''; |
| | | formData.status = '1'; |
| | | formData.description = ''; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // ç¼è¾ |
| | | const edit = (row) => { |
| | | dialogTitle.value = 'ç¼è¾é¨ç¦'; |
| | | formData.id = row.id; |
| | | formData.name = row.name; |
| | | formData.area = row.area; |
| | | formData.location = row.location; |
| | | formData.status = row.status; |
| | | formData.description = row.description; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // ä¿åæ°æ® |
| | | const saveData = () => { |
| | | if (!formData.name || !formData.area || !formData.location) { |
| | | ElMessage.warning('请填åå¿
å¡«åæ®µ'); |
| | | return; |
| | | } |
| | | |
| | | const currentTime = dayjs().format('YYYY-MM-DD HH:mm:ss'); |
| | | if (formData.id) { |
| | | // ç¼è¾ |
| | | const index = mockData.findIndex(item => item.id === formData.id); |
| | | if (index !== -1) { |
| | | mockData[index] = { |
| | | ...formData, |
| | | lastUpdate: currentTime |
| | | }; |
| | | ElMessage.success('ç¼è¾æå'); |
| | | } |
| | | } else { |
| | | // æ°å¢ |
| | | const newId = Math.max(...mockData.map(item => item.id), 0) + 1; |
| | | mockData.unshift({ |
| | | ...formData, |
| | | id: newId, |
| | | lastUpdate: currentTime |
| | | }); |
| | | ElMessage.success('æ°å¢æå'); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å é¤ |
| | | const deleteRow = (id) => { |
| | | ElMessageBox.confirm('æ¤æä½å°æ°¸ä¹
å é¤è¯¥é¨ç¦è®°å½, æ¯å¦ç»§ç»?', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }).then(() => { |
| | | const index = mockData.findIndex(item => item.id === id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | ElMessage.success('å 餿å'); |
| | | getTableData(); |
| | | } |
| | | }).catch(() => {}); |
| | | }; |
| | | |
| | | // æ¹éå é¤ |
| | | const batchDelete = () => { |
| | | if (multipleList.value.length === 0) { |
| | | ElMessage.warning('è¯·éæ©è¦å é¤çæ°æ®'); |
| | | return; |
| | | } |
| | | |
| | | ElMessageBox.confirm('æ¤æä½å°æ°¸ä¹
å 餿éé¨ç¦è®°å½, æ¯å¦ç»§ç»?', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }).then(() => { |
| | | const ids = multipleList.value.map(item => item.id); |
| | | mockData.forEach((item, index) => { |
| | | if (ids.includes(item.id)) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | }); |
| | | ElMessage.success('å 餿å'); |
| | | getTableData(); |
| | | multipleList.value = []; |
| | | }).catch(() => {}); |
| | | }; |
| | | |
| | | // åå§åæ°æ® |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="å·¡æ£åç§°"> |
| | | <el-input |
| | | v-model="filters.name" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å·¡æ£åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="å·¡æ£ç¶æ"> |
| | | <el-select |
| | | v-model="filters.status" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©å·¡æ£ç¶æ" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="å¾
æ§è¡" value="0"></el-option> |
| | | <el-option label="æ§è¡ä¸" value="1"></el-option> |
| | | <el-option label="已宿" value="2"></el-option> |
| | | <el-option label="已忶" value="3"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å·¡æ£ç±»å"> |
| | | <el-select |
| | | v-model="filters.type" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©å·¡æ£ç±»å" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="宿巡æ£" value="0"></el-option> |
| | | <el-option label="临æ¶å·¡æ£" value="1"></el-option> |
| | | <el-option label="æ
éå·¡æ£" value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus"> æ°å¢ </el-button> |
| | | <el-button |
| | | type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0" |
| | | @click="batchDelete" |
| | | >æ¹éå é¤</el-button |
| | | > |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | </PIMTable> |
| | | </div> |
| | | <!-- æ°å¢ç¼è¾å¼¹çª --> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px"> |
| | | <el-form :model="formData" label-width="100px"> |
| | | <el-form-item label="å·¡æ£åç§°" required> |
| | | <el-input |
| | | v-model="formData.name" |
| | | placeholder="请è¾å
¥å·¡æ£åç§°" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å·¡æ£ç±»å" required> |
| | | <el-select v-model="formData.type" placeholder="è¯·éæ©å·¡æ£ç±»å"> |
| | | <el-option label="宿巡æ£" value="0"></el-option> |
| | | <el-option label="临æ¶å·¡æ£" value="1"></el-option> |
| | | <el-option label="æ
éå·¡æ£" value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å·¡æ£åºå" required> |
| | | <el-input |
| | | v-model="formData.area" |
| | | placeholder="请è¾å
¥å·¡æ£åºå" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å·¡æ£äººå" required> |
| | | <el-input |
| | | v-model="formData.inspector" |
| | | placeholder="请è¾å
¥å·¡æ£äººå" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="计åå¼å§æ¶é´" required> |
| | | <el-date-picker |
| | | v-model="formData.planStartTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©è®¡åå¼å§æ¶é´" |
| | | style="width: 100%" |
| | | ></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="计åç»ææ¶é´" required> |
| | | <el-date-picker |
| | | v-model="formData.planEndTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©è®¡åç»ææ¶é´" |
| | | style="width: 100%" |
| | | ></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="å·¡æ£å
容"> |
| | | <el-input |
| | | v-model="formData.content" |
| | | type="textarea" |
| | | rows="3" |
| | | placeholder="请è¾å
¥å·¡æ£å
容" |
| | | ></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveData">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { Search, Plus, Delete } from "@element-plus/icons-vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // å®ä¹åæ°æ® |
| | | const mockData = [ |
| | | { |
| | | id: 1, |
| | | name: "ä¸é¨åºåæ¥å¸¸å·¡æ£", |
| | | type: "0", |
| | | status: "2", |
| | | planStartTime: "2025-12-30 08:00:00", |
| | | planEndTime: "2025-12-30 09:00:00", |
| | | actualStartTime: "2025-12-30 08:05:00", |
| | | actualEndTime: "2025-12-30 08:55:00", |
| | | inspector: "å¼ ä¸", |
| | | area: "ä¸é¨åºå", |
| | | content: "æ£æ¥é¨ç¦è®¾å¤è¿è¡ç¶æãç¯å¢çæµè®¾å¤æ°æ®", |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: "西é¨åºå临æ¶å·¡æ£", |
| | | type: "1", |
| | | status: "2", |
| | | planStartTime: "2025-12-30 10:00:00", |
| | | planEndTime: "2025-12-30 11:00:00", |
| | | actualStartTime: "2025-12-30 10:00:00", |
| | | actualEndTime: "2025-12-30 10:45:00", |
| | | inspector: "æå", |
| | | area: "西é¨åºå", |
| | | content: "æ£æ¥å¼å¸¸æ¥è¦è®¾å¤", |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: "åé¨é¨ç¦æ
éå·¡æ£", |
| | | type: "2", |
| | | status: "1", |
| | | planStartTime: "2025-12-30 13:00:00", |
| | | planEndTime: "2025-12-30 14:00:00", |
| | | actualStartTime: "2025-12-30 13:10:00", |
| | | actualEndTime: "", |
| | | inspector: "çäº", |
| | | area: "åé¨åºå", |
| | | content: "ä¿®å¤é¨ç¦æ
é", |
| | | }, |
| | | { |
| | | id: 4, |
| | | name: "䏿§å®¤å®æå·¡æ£", |
| | | type: "0", |
| | | status: "0", |
| | | planStartTime: "2025-12-31 09:00:00", |
| | | planEndTime: "2025-12-31 10:00:00", |
| | | actualStartTime: "", |
| | | actualEndTime: "", |
| | | inspector: "èµµå
", |
| | | area: "䏿§åºå", |
| | | content: "æ£æ¥çæ§è®¾å¤ãæå¡å¨è¿è¡ç¶æ", |
| | | }, |
| | | { |
| | | id: 5, |
| | | name: "åé¨åºåæ¥å¸¸å·¡æ£", |
| | | type: "0", |
| | | status: "2", |
| | | planStartTime: "2025-12-30 15:00:00", |
| | | planEndTime: "2025-12-30 16:00:00", |
| | | actualStartTime: "2025-12-30 15:00:00", |
| | | actualEndTime: "2025-12-30 15:50:00", |
| | | inspector: "å¼ ä¸", |
| | | area: "åé¨åºå", |
| | | content: "æ£æ¥è½¦è¾è¯å«è®¾å¤ãéé¸è¿è¡ç¶æ", |
| | | }, |
| | | ]; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const filters = reactive({ |
| | | name: "", |
| | | status: "", |
| | | type: "", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const multipleList = ref([]); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref("æ°å¢å·¡æ£"); |
| | | const formData = reactive({ |
| | | id: "", |
| | | name: "", |
| | | type: "0", |
| | | status: "0", |
| | | planStartTime: "", |
| | | planEndTime: "", |
| | | actualStartTime: "", |
| | | actualEndTime: "", |
| | | inspector: "", |
| | | area: "", |
| | | content: "", |
| | | }); |
| | | |
| | | // ç¶ææ å° |
| | | const statusMap = { |
| | | '0': '<el-tag type="warning">å¾
æ§è¡</el-tag>', |
| | | '1': '<el-tag type="primary">æ§è¡ä¸</el-tag>', |
| | | '2': '<el-tag type="success">已宿</el-tag>', |
| | | '3': '<el-tag type="danger">已忶</el-tag>', |
| | | }; |
| | | |
| | | // ç±»åæ å° |
| | | const typeMap = { |
| | | 0: "宿巡æ£", |
| | | 1: "临æ¶å·¡æ£", |
| | | 2: "æ
éå·¡æ£", |
| | | }; |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const columns = [ |
| | | { |
| | | label: "å·¡æ£åç§°", |
| | | align: "center", |
| | | prop: "name", |
| | | }, |
| | | { |
| | | label: "å·¡æ£ç±»å", |
| | | align: "center", |
| | | prop: "type", |
| | | formatter: (row) => { |
| | | return typeMap[row.type]; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å·¡æ£ç¶æ", |
| | | align: "center", |
| | | prop: "status", |
| | | formatter: (row) => { |
| | | return statusMap[row.status]; |
| | | }, |
| | | }, |
| | | { |
| | | label: "计åå¼å§æ¶é´", |
| | | align: "center", |
| | | prop: "planStartTime", |
| | | }, |
| | | { |
| | | label: "计åç»ææ¶é´", |
| | | align: "center", |
| | | prop: "planEndTime", |
| | | }, |
| | | { |
| | | label: "å®é
å¼å§æ¶é´", |
| | | align: "center", |
| | | prop: "actualStartTime", |
| | | formatter: (row) => { |
| | | return row.actualStartTime || "-"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å®é
ç»ææ¶é´", |
| | | align: "center", |
| | | prop: "actualEndTime", |
| | | formatter: (row) => { |
| | | return row.actualEndTime || "-"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å·¡æ£äººå", |
| | | align: "center", |
| | | prop: "inspector", |
| | | }, |
| | | { |
| | | label: "å·¡æ£åºå", |
| | | align: "center", |
| | | prop: "area", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 180, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | edit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å¼å§æ§è¡", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | startInspection(row); |
| | | }, |
| | | visible: (row) => { |
| | | return row.status === "0"; |
| | | }, |
| | | }, |
| | | { |
| | | name: "宿巡æ£", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | completeInspection(row); |
| | | }, |
| | | visible: (row) => { |
| | | return row.status === "1"; |
| | | }, |
| | | }, |
| | | { |
| | | name: "åæ¶å·¡æ£", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | cancelInspection(row); |
| | | }, |
| | | visible: (row) => { |
| | | return ["0", "1"].includes(row.status); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | deleteRow(row.id); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // è¿æ»¤åçæ°æ® |
| | | const filteredData = computed(() => { |
| | | return mockData.filter((item) => { |
| | | const nameMatch = !filters.name || item.name.includes(filters.name); |
| | | const statusMatch = !filters.status || item.status === filters.status; |
| | | const typeMatch = !filters.type || item.type === filters.type; |
| | | return nameMatch && statusMatch && typeMatch; |
| | | }); |
| | | }); |
| | | |
| | | // è·åè¡¨æ ¼æ°æ® |
| | | const getTableData = () => { |
| | | pagination.total = filteredData.value.length; |
| | | const start = (pagination.currentPage - 1) * pagination.pageSize; |
| | | const end = start + pagination.pageSize; |
| | | dataList.value = filteredData.value.slice(start, end); |
| | | }; |
| | | |
| | | // éç½®è¿æ»¤å¨ |
| | | const resetFilters = () => { |
| | | filters.name = ""; |
| | | filters.status = ""; |
| | | filters.type = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å页åå |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å¤éå¤ç |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | // æ°å¢ |
| | | const add = () => { |
| | | dialogTitle.value = "æ°å¢å·¡æ£"; |
| | | formData.id = ""; |
| | | formData.name = ""; |
| | | formData.type = "0"; |
| | | formData.status = "0"; |
| | | formData.planStartTime = ""; |
| | | formData.planEndTime = ""; |
| | | formData.actualStartTime = ""; |
| | | formData.actualEndTime = ""; |
| | | formData.inspector = ""; |
| | | formData.area = ""; |
| | | formData.content = ""; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // ç¼è¾ |
| | | const edit = (row) => { |
| | | dialogTitle.value = "ç¼è¾å·¡æ£"; |
| | | formData.id = row.id; |
| | | formData.name = row.name; |
| | | formData.type = row.type; |
| | | formData.status = row.status; |
| | | formData.planStartTime = row.planStartTime; |
| | | formData.planEndTime = row.planEndTime; |
| | | formData.actualStartTime = row.actualStartTime; |
| | | formData.actualEndTime = row.actualEndTime; |
| | | formData.inspector = row.inspector; |
| | | formData.area = row.area; |
| | | formData.content = row.content; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // ä¿åæ°æ® |
| | | const saveData = () => { |
| | | if ( |
| | | !formData.name || |
| | | !formData.planStartTime || |
| | | !formData.planEndTime || |
| | | !formData.inspector || |
| | | !formData.area |
| | | ) { |
| | | ElMessage.warning("请填åå¿
å¡«åæ®µ"); |
| | | return; |
| | | } |
| | | |
| | | if (formData.id) { |
| | | // ç¼è¾ |
| | | const index = mockData.findIndex((item) => item.id === formData.id); |
| | | if (index !== -1) { |
| | | mockData[index] = { |
| | | ...mockData[index], |
| | | ...formData, |
| | | }; |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } |
| | | } else { |
| | | // æ°å¢ |
| | | const newId = Math.max(...mockData.map((item) => item.id), 0) + 1; |
| | | mockData.unshift({ |
| | | ...formData, |
| | | id: newId, |
| | | }); |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogVisible.value = false; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å¼å§å·¡æ£ |
| | | const startInspection = (row) => { |
| | | const index = mockData.findIndex((item) => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "1"; |
| | | mockData[index].actualStartTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | ElMessage.success("å·¡æ£å·²å¼å§"); |
| | | getTableData(); |
| | | } |
| | | }; |
| | | |
| | | // å®æå·¡æ£ |
| | | const completeInspection = (row) => { |
| | | const index = mockData.findIndex((item) => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "2"; |
| | | mockData[index].actualEndTime = dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | ElMessage.success("å·¡æ£å·²å®æ"); |
| | | getTableData(); |
| | | } |
| | | }; |
| | | |
| | | // åæ¶å·¡æ£ |
| | | const cancelInspection = (row) => { |
| | | ElMessageBox.confirm("ç¡®å®è¦åæ¶è¯¥å·¡æ£å?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | const index = mockData.findIndex((item) => item.id === row.id); |
| | | if (index !== -1) { |
| | | mockData[index].status = "3"; |
| | | ElMessage.success("å·¡æ£å·²åæ¶"); |
| | | getTableData(); |
| | | } |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // å é¤ |
| | | const deleteRow = (id) => { |
| | | ElMessageBox.confirm("æ¤æä½å°æ°¸ä¹
å é¤è¯¥å·¡æ£è®°å½, æ¯å¦ç»§ç»?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | const index = mockData.findIndex((item) => item.id === id); |
| | | if (index !== -1) { |
| | | mockData.splice(index, 1); |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | } |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // æ¹éå é¤ |
| | | const batchDelete = () => { |
| | | if (multipleList.value.length === 0) { |
| | | ElMessage.warning("è¯·éæ©è¦å é¤çæ°æ®"); |
| | | return; |
| | | } |
| | | |
| | | ElMessageBox.confirm("æ¤æä½å°æ°¸ä¹
å 餿éå·¡æ£è®°å½, æ¯å¦ç»§ç»?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | const ids = multipleList.value.map((item) => item.id); |
| | | mockData.forEach((item, index) => { |
| | | if (ids.includes(item.id)) { |
| | | mockData.splice(index, 1); |
| | | } |
| | | }); |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | multipleList.value = []; |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // åå§åæ°æ® |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="设å¤åç§°"> |
| | | <el-input |
| | | v-model="filters.name" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥è®¾å¤åç§°" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤ç¶æ"> |
| | | <el-select |
| | | v-model="filters.status" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©è®¾å¤ç¶æ" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="å¨çº¿" value="1"></el-option> |
| | | <el-option label="离线" value="0"></el-option> |
| | | <el-option label="æ
é" value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤ç±»å"> |
| | | <el-select |
| | | v-model="filters.type" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©è®¾å¤ç±»å" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="é¨ç¦è®¾å¤" value="0"></el-option> |
| | | <el-option label="ç¯å¢çæµè®¾å¤" value="1"></el-option> |
| | | <el-option label="è§é¢çæ§è®¾å¤" value="2"></el-option> |
| | | <el-option label="æºè½éé¸" value="3"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="monitor-container"> |
| | | <div class="monitor-stats"> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">å¨çº¿è®¾å¤</div> |
| | | <div class="stat-value online">{{ onlineCount }}</div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">离线设å¤</div> |
| | | <div class="stat-value offline">{{ offlineCount }}</div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">æ
é设å¤</div> |
| | | <div class="stat-value error">{{ errorCount }}</div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">è®¾å¤æ»æ°</div> |
| | | <div class="stat-value total">{{ totalCount }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | </PIMTable> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设å¤è¯¦æ
å¼¹çª --> |
| | | <el-dialog |
| | | v-model="detailVisible" |
| | | :title="`设å¤è¯¦æ
- ${selectedDevice.name}`" |
| | | width="800px" |
| | | > |
| | | <div v-if="selectedDevice" class="device-detail"> |
| | | <div class="detail-section"> |
| | | <h3>åºæ¬ä¿¡æ¯</h3> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="设å¤åç§°">{{ |
| | | selectedDevice.name |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ç±»å">{{ |
| | | typeMap[selectedDevice.type] |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ç¶æ">{{ |
| | | statusMap[selectedDevice.status] |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ç¼å·">{{ |
| | | selectedDevice.code |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®è£
ä½ç½®">{{ |
| | | selectedDevice.location |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®è£
æ¶é´">{{ |
| | | selectedDevice.installTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æåç»´æ¤æ¶é´">{{ |
| | | selectedDevice.maintainTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="责任人">{{ |
| | | selectedDevice.responsiblePerson |
| | | }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | |
| | | <div class="detail-section"> |
| | | <h3>宿¶æ°æ®</h3> |
| | | <div class="realtime-data"> |
| | | <div |
| | | v-for="data in selectedDevice.realtimeData" |
| | | :key="data.name" |
| | | class="data-item" |
| | | > |
| | | <div class="data-name">{{ data.name }}</div> |
| | | <div class="data-value">{{ data.value }}{{ data.unit }}</div> |
| | | <div class="data-status" :class="data.status"> |
| | | {{ data.status === "normal" ? "æ£å¸¸" : "å¼å¸¸" }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="detail-section"> |
| | | <h3>æ§å¶æä½</h3> |
| | | <div class="control-buttons"> |
| | | <el-button |
| | | type="primary" |
| | | @click="controlDevice('restart')" |
| | | :disabled="selectedDevice.status !== '1'" |
| | | >éå¯è®¾å¤</el-button |
| | | > |
| | | <el-button |
| | | type="warning" |
| | | @click="controlDevice('reset')" |
| | | :disabled="selectedDevice.status !== '1'" |
| | | >é置设å¤</el-button |
| | | > |
| | | <el-button |
| | | type="danger" |
| | | @click="controlDevice('shutdown')" |
| | | :disabled="selectedDevice.status !== '1'" |
| | | >å
³é设å¤</el-button |
| | | > |
| | | <el-button |
| | | type="success" |
| | | @click="controlDevice('update')" |
| | | :disabled="selectedDevice.status !== '1'" |
| | | >åºä»¶æ´æ°</el-button |
| | | > |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="detailVisible = false">å
³é</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { |
| | | Search |
| | | } from "@element-plus/icons-vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | |
| | | // å®ä¹åæ°æ® |
| | | const mockDevices = [ |
| | | { |
| | | id: 1, |
| | | name: "ä¸é¨é¨ç¦æ§å¶å¨", |
| | | code: "DEV-20250001", |
| | | type: "0", |
| | | status: "1", |
| | | location: "ä¸é¨å
¥å£", |
| | | installTime: "2025-01-15", |
| | | maintainTime: "2025-12-20", |
| | | responsiblePerson: "å¼ ä¸", |
| | | realtimeData: [ |
| | | { name: "温度", value: 23.5, unit: "â", status: "normal" }, |
| | | { name: "湿度", value: 45, unit: "%", status: "normal" }, |
| | | { name: "è¿è¡æ¶é¿", value: 12560, unit: "h", status: "normal" }, |
| | | { name: "é¨ç¦å¼å
³æ¬¡æ°", value: 234, unit: "次/天", status: "normal" }, |
| | | ], |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: "西é¨è§é¢çæ§", |
| | | code: "DEV-20250002", |
| | | type: "2", |
| | | status: "1", |
| | | location: "西é¨åºå£", |
| | | installTime: "2025-02-20", |
| | | maintainTime: "2025-11-15", |
| | | responsiblePerson: "æå", |
| | | realtimeData: [ |
| | | { name: "è§é¢æ¸
æ°åº¦", value: 1080, unit: "P", status: "normal" }, |
| | | { name: "帧ç", value: 25, unit: "fps", status: "normal" }, |
| | | { name: "åå¨ç©ºé´", value: 78, unit: "%", status: "warning" }, |
| | | { name: "ç½ç»å»¶è¿", value: 12, unit: "ms", status: "normal" }, |
| | | ], |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: "åé¨ç¯å¢çæµä»ª", |
| | | code: "DEV-20250003", |
| | | type: "1", |
| | | status: "2", |
| | | location: "åé¨å¹¿åº", |
| | | installTime: "2025-03-10", |
| | | maintainTime: "2025-10-05", |
| | | responsiblePerson: "çäº", |
| | | realtimeData: [ |
| | | { name: "PM2.5", value: 85, unit: "μg/m³", status: "error" }, |
| | | { name: "PM10", value: 120, unit: "μg/m³", status: "error" }, |
| | | { name: "åªå£°", value: 65, unit: "dB", status: "normal" }, |
| | | { name: "é£é", value: 1.2, unit: "m/s", status: "normal" }, |
| | | ], |
| | | }, |
| | | { |
| | | id: 4, |
| | | name: "å鍿ºè½éé¸", |
| | | code: "DEV-20250004", |
| | | type: "3", |
| | | status: "0", |
| | | location: "åé¨å
¥å£", |
| | | installTime: "2025-04-05", |
| | | maintainTime: "2025-09-20", |
| | | responsiblePerson: "èµµå
", |
| | | realtimeData: [ |
| | | { name: "å¼å
³æ¬¡æ°", value: 156, unit: "次/天", status: "normal" }, |
| | | { name: "è¿è¡çµå", value: 220, unit: "V", status: "normal" }, |
| | | { name: "è¿è¡çµæµ", value: 5.2, unit: "A", status: "normal" }, |
| | | { name: "æ
é代ç ", value: "E001", unit: "", status: "error" }, |
| | | ], |
| | | }, |
| | | { |
| | | id: 5, |
| | | name: "䏿§å®¤æå¡å¨", |
| | | code: "DEV-20250005", |
| | | type: "1", |
| | | status: "1", |
| | | location: "䏿§å®¤", |
| | | installTime: "2025-05-15", |
| | | maintainTime: "2025-12-01", |
| | | responsiblePerson: "åä¸", |
| | | realtimeData: [ |
| | | { name: "CPU使ç¨ç", value: 45, unit: "%", status: "normal" }, |
| | | { name: "å
å使ç¨ç", value: 68, unit: "%", status: "warning" }, |
| | | { name: "ç£ç使ç¨ç", value: 82, unit: "%", status: "warning" }, |
| | | { name: "ç½ç»æµé", value: 125, unit: "Mbps", status: "normal" }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const filters = reactive({ |
| | | name: "", |
| | | status: "", |
| | | type: "", |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const detailVisible = ref(false); |
| | | const selectedDevice = ref({}); |
| | | |
| | | // ç¶ææ å° |
| | | const statusMap = { |
| | | 0: '<el-tag type="warning">离线</el-tag>', |
| | | 1: '<el-tag type="success">å¨çº¿</el-tag>', |
| | | 2: '<el-tag type="danger">æ
é</el-tag>', |
| | | }; |
| | | |
| | | // ç±»åæ å° |
| | | const typeMap = { |
| | | 0: "é¨ç¦è®¾å¤", |
| | | 1: "ç¯å¢çæµè®¾å¤", |
| | | 2: "è§é¢çæ§è®¾å¤", |
| | | 3: "æºè½éé¸", |
| | | }; |
| | | |
| | | // è®¾å¤æ°éç»è®¡ |
| | | const onlineCount = computed(() => { |
| | | return mockDevices.filter((device) => device.status === "1").length; |
| | | }); |
| | | |
| | | const offlineCount = computed(() => { |
| | | return mockDevices.filter((device) => device.status === "0").length; |
| | | }); |
| | | |
| | | const errorCount = computed(() => { |
| | | return mockDevices.filter((device) => device.status === "2").length; |
| | | }); |
| | | |
| | | const totalCount = computed(() => { |
| | | return mockDevices.length; |
| | | }); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const columns = [ |
| | | { |
| | | label: "设å¤åç§°", |
| | | align: "center", |
| | | prop: "name", |
| | | }, |
| | | { |
| | | label: "设å¤ç¼å·", |
| | | align: "center", |
| | | prop: "code", |
| | | }, |
| | | { |
| | | label: "设å¤ç±»å", |
| | | align: "center", |
| | | prop: "type", |
| | | formatter: (row) => { |
| | | return typeMap[row.type]; |
| | | }, |
| | | }, |
| | | { |
| | | label: "设å¤ç¶æ", |
| | | align: "center", |
| | | prop: "status", |
| | | formatter: (row) => { |
| | | return statusMap[row.status]; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å®è£
ä½ç½®", |
| | | align: "center", |
| | | prop: "location", |
| | | }, |
| | | { |
| | | label: "责任人", |
| | | align: "center", |
| | | prop: "responsiblePerson", |
| | | }, |
| | | { |
| | | label: "æåç»´æ¤æ¶é´", |
| | | align: "center", |
| | | prop: "maintainTime", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 140, |
| | | operation: [ |
| | | { |
| | | name: "æ¥ç详æ
", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | viewDetail(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "宿¶çæ§", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | realtimeMonitor(row); |
| | | }, |
| | | visible: (row) => { |
| | | return row.status === "1"; |
| | | }, |
| | | }, |
| | | { |
| | | name: "éå¯è®¾å¤", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | controlDevice("restart", row); |
| | | }, |
| | | visible: (row) => { |
| | | return row.status === "1"; |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // è¿æ»¤åçæ°æ® |
| | | const filteredData = computed(() => { |
| | | return mockDevices.filter((item) => { |
| | | const nameMatch = !filters.name || item.name.includes(filters.name); |
| | | const statusMatch = !filters.status || item.status === filters.status; |
| | | const typeMatch = !filters.type || item.type === filters.type; |
| | | return nameMatch && statusMatch && typeMatch; |
| | | }); |
| | | }); |
| | | |
| | | // è·åè¡¨æ ¼æ°æ® |
| | | const getTableData = () => { |
| | | pagination.total = filteredData.value.length; |
| | | const start = (pagination.currentPage - 1) * pagination.pageSize; |
| | | const end = start + pagination.pageSize; |
| | | dataList.value = filteredData.value.slice(start, end); |
| | | }; |
| | | |
| | | // éç½®è¿æ»¤å¨ |
| | | const resetFilters = () => { |
| | | filters.name = ""; |
| | | filters.status = ""; |
| | | filters.type = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å页åå |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // æ¥ç设å¤è¯¦æ
|
| | | const viewDetail = (row) => { |
| | | selectedDevice.value = row; |
| | | detailVisible.value = true; |
| | | }; |
| | | |
| | | // 宿¶çæ§ |
| | | const realtimeMonitor = (row) => { |
| | | ElMessage.info(`æ£å¨è·³è½¬å°${row.name}ç宿¶çæ§é¡µé¢...`); |
| | | // è¿éå¯ä»¥è·³è½¬å°å®æ¶çæ§é¡µé¢ææå¼çæ§çªå£ |
| | | }; |
| | | |
| | | // è®¾å¤æ§å¶ |
| | | const controlDevice = (action, row = selectedDevice.value) => { |
| | | const actionMap = { |
| | | restart: "éå¯", |
| | | reset: "éç½®", |
| | | shutdown: "å
³é", |
| | | update: "åºä»¶æ´æ°", |
| | | }; |
| | | |
| | | ElMessageBox.confirm( |
| | | `ç¡®å®è¦å¯¹è®¾å¤ ${row.name} æ§è¡${actionMap[action]}æä½å?`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ) |
| | | .then(() => { |
| | | ElMessage.success(`设å¤${actionMap[action]}æä½å·²åé`); |
| | | // 模æè®¾å¤ååº |
| | | setTimeout(() => { |
| | | ElMessage.success(`设å¤${actionMap[action]}æä½æ§è¡æå`); |
| | | }, 1000); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 宿¶æ´æ°è®¾å¤ç¶æï¼æ¨¡æå®æ¶ååï¼ |
| | | const updateDeviceStatus = () => { |
| | | // éæºæ´æ°ä¸äºè®¾å¤çç¶æå宿¶æ°æ® |
| | | mockDevices.forEach((device) => { |
| | | if (Math.random() > 0.8) { |
| | | // 80%æ¦ç䏿´æ° |
| | | return; |
| | | } |
| | | |
| | | // éæºæ´æ°ç¶æ |
| | | const statuses = ["0", "1", "2"]; |
| | | device.status = statuses[Math.floor(Math.random() * statuses.length)]; |
| | | |
| | | // æ´æ°å®æ¶æ°æ® |
| | | device.realtimeData.forEach((data) => { |
| | | if (data.name === "温度") { |
| | | data.value = (20 + Math.random() * 10).toFixed(1); |
| | | data.status = data.value > 30 ? "error" : "normal"; |
| | | } else if (data.name === "湿度") { |
| | | data.value = Math.floor(30 + Math.random() * 50); |
| | | data.status = data.value > 80 ? "error" : "normal"; |
| | | } else if (data.name === "CPU使ç¨ç") { |
| | | data.value = Math.floor(20 + Math.random() * 60); |
| | | data.status = |
| | | data.value > 80 ? "error" : data.value > 60 ? "warning" : "normal"; |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | // å·æ°è¡¨æ ¼æ°æ® |
| | | getTableData(); |
| | | }; |
| | | |
| | | // åå§åæ°æ® |
| | | onMounted(() => { |
| | | getTableData(); |
| | | // æ¯5ç§æ´æ°ä¸æ¬¡è®¾å¤ç¶æ |
| | | const interval = setInterval(updateDeviceStatus, 5000); |
| | | |
| | | // æ¸
ç宿¶å¨ |
| | | return () => clearInterval(interval); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | |
| | | .monitor-container { |
| | | .monitor-stats { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | |
| | | .stat-card { |
| | | flex: 1; |
| | | background: #fff; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | text-align: center; |
| | | |
| | | .stat-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | |
| | | &.online { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | &.offline { |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | &.error { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | &.total { |
| | | color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .device-detail { |
| | | .detail-section { |
| | | margin-bottom: 20px; |
| | | |
| | | h3 { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | margin-bottom: 10px; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | .realtime-data { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 15px; |
| | | |
| | | .data-item { |
| | | background: #f5f7fa; |
| | | padding: 15px; |
| | | border-radius: 8px; |
| | | |
| | | .data-name { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .data-status { |
| | | font-size: 12px; |
| | | padding: 2px 8px; |
| | | border-radius: 10px; |
| | | |
| | | &.normal { |
| | | background: #f0f9eb; |
| | | color: #67c23a; |
| | | } |
| | | |
| | | &.warning { |
| | | background: #fdf6ec; |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | &.error { |
| | | background: #fef0f0; |
| | | color: #f56c6c; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .control-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | margin-top: 10px; |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="车çå·ç "> |
| | | <el-input |
| | | v-model="filters.plateNumber" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥è½¦çå·ç " |
| | | clearable |
| | | :prefix-icon="Search" |
| | | @change="getTableData" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="车è¾ç±»å"> |
| | | <el-select |
| | | v-model="filters.vehicleType" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©è½¦è¾ç±»å" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="å°å车" value="0"></el-option> |
| | | <el-option label="ä¸å车" value="1"></el-option> |
| | | <el-option label="大å车" value="2"></el-option> |
| | | <el-option label="ç¹ç§è½¦" value="3"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="è¿åºæ¹å"> |
| | | <el-select |
| | | v-model="filters.direction" |
| | | style="width: 240px" |
| | | placeholder="è¯·éæ©è¿åºæ¹å" |
| | | clearable |
| | | @change="getTableData" |
| | | > |
| | | <el-option label="è¿" value="0"></el-option> |
| | | <el-option label="åº" value="1"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="è¿åºæ¶é´"> |
| | | <el-date-picker |
| | | v-model="filters.timeRange" |
| | | type="daterange" |
| | | start-placeholder="å¼å§æ¶é´" |
| | | end-placeholder="ç»ææ¶é´" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | format="YYYY-MM-DD HH:mm" |
| | | clearable |
| | | @change="changeTimeRange" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <div class="vehicle-stats"> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">仿¥è¿è½¦æ°</div> |
| | | <div class="stat-value in">{{ todayInCount }}</div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">仿¥åºè½¦æ°</div> |
| | | <div class="stat-value out">{{ todayOutCount }}</div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">å½åå¨åºè½¦è¾</div> |
| | | <div class="stat-value present">{{ presentCount }}</div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <div class="stat-title">仿¥æ»æµé</div> |
| | | <div class="stat-value total">{{ todayTotalCount }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <!-- 车è¾è¯¦æ
å¼¹çª --> |
| | | <el-dialog |
| | | v-model="detailVisible" |
| | | :title="`车è¾è¯¦æ
- ${selectedVehicle.plateNumber}`" |
| | | width="700px" |
| | | > |
| | | <div v-if="selectedVehicle" class="vehicle-detail"> |
| | | <div class="detail-section"> |
| | | <h3>åºæ¬ä¿¡æ¯</h3> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="车çå·ç ">{{ |
| | | selectedVehicle.plateNumber |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="车è¾ç±»å">{{ |
| | | typeMap[selectedVehicle.vehicleType] |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="车è¾é¢è²">{{ |
| | | selectedVehicle.color |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="è¿åºæ¹å">{{ |
| | | selectedVehicle.direction === "0" ? "è¿" : "åº" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="è¿åºæ¶é´">{{ |
| | | selectedVehicle.timestamp |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="ééåç§°">{{ |
| | | selectedVehicle.channelName |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="è¯å«æ¹å¼">{{ |
| | | selectedVehicle.identifyMethod |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="车è¾ç¶æ">{{ |
| | | statusMap[selectedVehicle.status] |
| | | }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | |
| | | <div class="detail-section"> |
| | | <h3>车è¾å¾ç</h3> |
| | | <div class="vehicle-images"> |
| | | <div class="image-item"> |
| | | <div class="image-title">æ£é¢å¾ç</div> |
| | | <el-image |
| | | :src="selectedVehicle.frontImage" |
| | | fit="cover" |
| | | style="width: 100%; height: 200px" |
| | | :preview-src-list="[ |
| | | selectedVehicle.frontImage, |
| | | selectedVehicle.sideImage, |
| | | ]" |
| | | ></el-image> |
| | | </div> |
| | | <div class="image-item"> |
| | | <div class="image-title">ä¾§é¢å¾ç</div> |
| | | <el-image |
| | | :src="selectedVehicle.sideImage" |
| | | fit="cover" |
| | | style="width: 100%; height: 200px" |
| | | :preview-src-list="[ |
| | | selectedVehicle.sideImage, |
| | | selectedVehicle.frontImage, |
| | | ]" |
| | | ></el-image> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="detailVisible = false">å
³é</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // å®ä¹åæ°æ® |
| | | const mockVehicles = [ |
| | | { |
| | | id: 1, |
| | | plateNumber: "京A12345", |
| | | vehicleType: "0", |
| | | color: "ç½è²", |
| | | direction: "0", |
| | | timestamp: "2025-12-31 08:15:30", |
| | | channelName: "ä¸é¨éé", |
| | | identifyMethod: "车çè¯å«", |
| | | status: "0", |
| | | frontImage: "https://picsum.photos/id/1024/800/600", |
| | | sideImage: "https://picsum.photos/id/1025/800/600", |
| | | duration: "00:00:00", |
| | | present: true, |
| | | }, |
| | | { |
| | | id: 2, |
| | | plateNumber: "沪B67890", |
| | | vehicleType: "1", |
| | | color: "é»è²", |
| | | direction: "1", |
| | | timestamp: "2025-12-31 08:10:20", |
| | | channelName: "西é¨éé", |
| | | identifyMethod: "车çè¯å«", |
| | | status: "1", |
| | | frontImage: "https://picsum.photos/id/1026/800/600", |
| | | sideImage: "https://picsum.photos/id/1027/800/600", |
| | | duration: "01:25:30", |
| | | present: false, |
| | | }, |
| | | { |
| | | id: 3, |
| | | plateNumber: "粤C54321", |
| | | vehicleType: "0", |
| | | color: "红è²", |
| | | direction: "0", |
| | | timestamp: "2025-12-31 08:05:15", |
| | | channelName: "åé¨éé", |
| | | identifyMethod: "ICå¡è¯å«", |
| | | status: "0", |
| | | frontImage: "https://picsum.photos/id/1028/800/600", |
| | | sideImage: "https://picsum.photos/id/1029/800/600", |
| | | duration: "00:05:45", |
| | | present: true, |
| | | }, |
| | | { |
| | | id: 4, |
| | | plateNumber: "å·D98765", |
| | | vehicleType: "2", |
| | | color: "èè²", |
| | | direction: "0", |
| | | timestamp: "2025-12-31 08:00:00", |
| | | channelName: "åé¨éé", |
| | | identifyMethod: "车çè¯å«", |
| | | status: "0", |
| | | frontImage: "https://picsum.photos/id/1030/800/600", |
| | | sideImage: "https://picsum.photos/id/1031/800/600", |
| | | duration: "00:10:30", |
| | | present: true, |
| | | }, |
| | | { |
| | | id: 5, |
| | | plateNumber: "èE13579", |
| | | vehicleType: "0", |
| | | color: "é¶è²", |
| | | direction: "1", |
| | | timestamp: "2025-12-31 07:55:45", |
| | | channelName: "ä¸é¨éé", |
| | | identifyMethod: "车çè¯å«", |
| | | status: "1", |
| | | frontImage: "https://picsum.photos/id/1032/800/600", |
| | | sideImage: "https://picsum.photos/id/1033/800/600", |
| | | duration: "02:30:15", |
| | | present: false, |
| | | }, |
| | | { |
| | | id: 6, |
| | | plateNumber: "æµF24680", |
| | | vehicleType: "3", |
| | | color: "é»è²", |
| | | direction: "0", |
| | | timestamp: "2025-12-31 07:50:30", |
| | | channelName: "åé¨éé", |
| | | identifyMethod: "人工ç»è®°", |
| | | status: "0", |
| | | frontImage: "https://picsum.photos/id/1034/800/600", |
| | | sideImage: "https://picsum.photos/id/1035/800/600", |
| | | duration: "00:15:30", |
| | | present: true, |
| | | }, |
| | | { |
| | | id: 7, |
| | | plateNumber: "é²G97531", |
| | | vehicleType: "1", |
| | | color: "绿è²", |
| | | direction: "1", |
| | | timestamp: "2025-12-31 07:45:15", |
| | | channelName: "西é¨éé", |
| | | identifyMethod: "车çè¯å«", |
| | | status: "1", |
| | | frontImage: "https://picsum.photos/id/1036/800/600", |
| | | sideImage: "https://picsum.photos/id/1037/800/600", |
| | | duration: "01:45:30", |
| | | present: false, |
| | | }, |
| | | { |
| | | id: 8, |
| | | plateNumber: "豫H86420", |
| | | vehicleType: "0", |
| | | color: "ç°è²", |
| | | direction: "0", |
| | | timestamp: "2025-12-31 07:40:00", |
| | | channelName: "åé¨éé", |
| | | identifyMethod: "ICå¡è¯å«", |
| | | status: "0", |
| | | frontImage: "https://picsum.photos/id/1038/800/600", |
| | | sideImage: "https://picsum.photos/id/1039/800/600", |
| | | duration: "00:20:15", |
| | | present: true, |
| | | }, |
| | | ]; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const filters = reactive({ |
| | | plateNumber: "", |
| | | vehicleType: "", |
| | | direction: "", |
| | | timeRange: [], |
| | | }); |
| | | |
| | | const dataList = ref([]); |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const detailVisible = ref(false); |
| | | const selectedVehicle = ref({}); |
| | | |
| | | // æ¶é´èå´ |
| | | const timeRange = reactive({ |
| | | startTime: "", |
| | | endTime: "", |
| | | }); |
| | | |
| | | // ç¶ææ å° |
| | | const statusMap = { |
| | | '0': '<el-tag type="success">æ£å¸¸</el-tag>', |
| | | '1': '<el-tag type="info">已离å¼</el-tag>', |
| | | '2': '<el-tag type="warning">å¼å¸¸</el-tag>', |
| | | }; |
| | | |
| | | // ç±»åæ å° |
| | | const typeMap = { |
| | | 0: "å°å车", |
| | | 1: "ä¸å车", |
| | | 2: "大å车", |
| | | 3: "ç¹ç§è½¦", |
| | | }; |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const todayInCount = computed(() => { |
| | | const today = dayjs().format("YYYY-MM-DD"); |
| | | return mockVehicles.filter((vehicle) => { |
| | | return vehicle.direction === "0" && vehicle.timestamp.startsWith(today); |
| | | }).length; |
| | | }); |
| | | |
| | | const todayOutCount = computed(() => { |
| | | const today = dayjs().format("YYYY-MM-DD"); |
| | | return mockVehicles.filter((vehicle) => { |
| | | return vehicle.direction === "1" && vehicle.timestamp.startsWith(today); |
| | | }).length; |
| | | }); |
| | | |
| | | const presentCount = computed(() => { |
| | | return mockVehicles.filter((vehicle) => vehicle.present).length; |
| | | }); |
| | | |
| | | const todayTotalCount = computed(() => { |
| | | const today = dayjs().format("YYYY-MM-DD"); |
| | | return mockVehicles.filter((vehicle) => { |
| | | return vehicle.timestamp.startsWith(today); |
| | | }).length; |
| | | }); |
| | | |
| | | // è¡¨æ ¼åé
ç½® |
| | | const columns = [ |
| | | { |
| | | label: "车çå·ç ", |
| | | align: "center", |
| | | prop: "plateNumber", |
| | | }, |
| | | { |
| | | label: "车è¾ç±»å", |
| | | align: "center", |
| | | prop: "vehicleType", |
| | | formatter: (row) => { |
| | | return typeMap[row.vehicleType]; |
| | | }, |
| | | }, |
| | | { |
| | | label: "车è¾é¢è²", |
| | | align: "center", |
| | | prop: "color", |
| | | }, |
| | | { |
| | | label: "è¿åºæ¹å", |
| | | align: "center", |
| | | prop: "direction", |
| | | formatter: (row) => { |
| | | return row.direction === "0" ? '<el-tag type="success">è¿</el-tag>' : '<el-tag type="info">åº</el-tag>'; |
| | | }, |
| | | }, |
| | | { |
| | | label: "è¿åºæ¶é´", |
| | | align: "center", |
| | | prop: "timestamp", |
| | | }, |
| | | { |
| | | label: "ééåç§°", |
| | | align: "center", |
| | | prop: "channelName", |
| | | }, |
| | | { |
| | | label: "è¯å«æ¹å¼", |
| | | align: "center", |
| | | prop: "identifyMethod", |
| | | }, |
| | | { |
| | | label: "车è¾ç¶æ", |
| | | align: "center", |
| | | prop: "status", |
| | | formatter: (row) => { |
| | | return statusMap[row.status]; |
| | | }, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 140, |
| | | operation: [ |
| | | { |
| | | name: "æ¥ç详æ
", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | viewDetail(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // è¿æ»¤åçæ°æ® |
| | | const filteredData = computed(() => { |
| | | return mockVehicles.filter((item) => { |
| | | const plateMatch = |
| | | !filters.plateNumber || item.plateNumber.includes(filters.plateNumber); |
| | | const typeMatch = |
| | | !filters.vehicleType || item.vehicleType === filters.vehicleType; |
| | | const directionMatch = |
| | | !filters.direction || item.direction === filters.direction; |
| | | const timeMatch = |
| | | !timeRange.startTime || |
| | | (item.timestamp >= timeRange.startTime && |
| | | item.timestamp <= timeRange.endTime); |
| | | return plateMatch && typeMatch && directionMatch && timeMatch; |
| | | }); |
| | | }); |
| | | |
| | | // è·åè¡¨æ ¼æ°æ® |
| | | const getTableData = () => { |
| | | pagination.total = filteredData.value.length; |
| | | const start = (pagination.currentPage - 1) * pagination.pageSize; |
| | | const end = start + pagination.pageSize; |
| | | dataList.value = filteredData.value.slice(start, end); |
| | | }; |
| | | |
| | | // éç½®è¿æ»¤å¨ |
| | | const resetFilters = () => { |
| | | filters.plateNumber = ""; |
| | | filters.vehicleType = ""; |
| | | filters.direction = ""; |
| | | filters.timeRange = []; |
| | | timeRange.startTime = ""; |
| | | timeRange.endTime = ""; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // å页åå |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | // æ¶é´èå´åå |
| | | const changeTimeRange = (value) => { |
| | | if (value && value.length === 2) { |
| | | timeRange.startTime = value[0]; |
| | | timeRange.endTime = value[1]; |
| | | } else { |
| | | timeRange.startTime = ""; |
| | | timeRange.endTime = ""; |
| | | } |
| | | getTableData(); |
| | | }; |
| | | |
| | | // æ¥ç车è¾è¯¦æ
|
| | | const viewDetail = (row) => { |
| | | selectedVehicle.value = row; |
| | | detailVisible.value = true; |
| | | }; |
| | | |
| | | // åå§åæ°æ® |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | |
| | | .vehicle-stats { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | |
| | | .stat-card { |
| | | flex: 1; |
| | | background: #fff; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | text-align: center; |
| | | |
| | | .stat-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | |
| | | &.in { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | &.out { |
| | | color: #409eff; |
| | | } |
| | | |
| | | &.present { |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | &.total { |
| | | color: #909399; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .vehicle-detail { |
| | | .detail-section { |
| | | margin-bottom: 20px; |
| | | |
| | | h3 { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | margin-bottom: 10px; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | .vehicle-images { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 15px; |
| | | |
| | | .image-item { |
| | | background: #f5f7fa; |
| | | padding: 15px; |
| | | border-radius: 8px; |
| | | |
| | | .image-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div style="padding: 20px;"> |
| | | <!-- 页颿 é¢åç鿡件 --> |
| | | <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;"> |
| | | <el-button |
| | | type="primary" |
| | | icon="Refresh" |
| | | @click="resetFilters" |
| | | size="default" |
| | | > |
| | | æ¥è¯¢ |
| | | </el-button> |
| | | </div> |
| | | |
| | | <main class="container mx-auto px-4 pb-10"> |
| | | <!-- åºå®èµäº§ææ å¡ç --> |
| | | <div class="grid-container"> |
| | | <!-- è®¾å¤æ»æ° --> |
| | | <el-card class="bg2"> |
| | | <p>è®¾å¤æ»æ°</p> |
| | | <h3> |
| | | {{ assetInfo.totalEquipment }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- èµäº§åå¼ --> |
| | | <el-card class="bg3"> |
| | | <p>èµäº§åå¼</p> |
| | | <h3> |
| | | ¥{{ assetInfo.totalOriginalValue }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- ç´¯è®¡ææ§ --> |
| | | <el-card class="bg4"> |
| | | <p>ç´¯è®¡ææ§</p> |
| | | <h3> |
| | | ¥{{ assetInfo.totalDepreciation }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- åå¼ --> |
| | | <el-card class="bg5"> |
| | | <p>åå¼</p> |
| | | <h3> |
| | | ¥{{ assetInfo.totalNetValue }} |
| | | </h3> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- åºå®èµäº§ç»è®¡å¾è¡¨ --> |
| | | <div class="grid-layout"> |
| | | <!-- æè®¾å¤ç±»åç»è®¡ --> |
| | | <el-card style="margin-bottom: 20px;"> |
| | | <h2 class="section-title">设å¤ç±»ååå¸</h2> |
| | | <div class="echarts"> |
| | | <Echarts |
| | | :legend="typeDistributionLegend" |
| | | :chartStyle="chartStylePie" |
| | | :series="typeDistributionSeries" |
| | | :tooltip="pieTooltip" |
| | | style="height: 260px; width: 35%;"> |
| | | <div class="chart-num"> |
| | | <span style="font-size: 22px;">设å¤ç±»å</span> |
| | | <span style="font-size: 36px; font-weight: 500; font-family: 'MyCustomFont', sans-serif;">{{ assetInfo.totalEquipment }}</span> |
| | | </div> |
| | | </Echarts> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="typeDistributionLineSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 260px; width: 64%;"></Echarts> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | <!-- 设å¤å°è´¦è¡¨æ ¼ --> |
| | | <el-card style="margin-bottom: 20px;"> |
| | | <el-table |
| | | :data="equipmentList" |
| | | stripe |
| | | style="width: 100%" |
| | | :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" |
| | | > |
| | | <el-table-column prop="id" label="èµäº§ç¼å·" width="120" /> |
| | | <el-table-column prop="deviceName" label="设å¤åç§°" width="250" /> |
| | | <el-table-column prop="deviceModel" label="åå·è§æ ¼" min-width="150" /> |
| | | <el-table-column prop="supplierName" label="ä¾åºå" min-width="120" /> |
| | | <el-table-column prop="unit" label="åä½" width="120" /> |
| | | <el-table-column prop="number" label="æ°é" width="120" /> |
| | | <el-table-column prop="originalValue" label="åå¼(å
)" width="120"> |
| | | <template #default="{ row }"> |
| | | ¥{{ formatCurrency(row.taxIncludingPriceTotal) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="depreciation" label="ç´¯è®¡ææ§(å
)" width="140"> |
| | | <template #default="{ row }"> |
| | | ¥{{ formatCurrency(row.taxIncludingPriceTotal-row.unTaxIncludingPriceTotal) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="netValue" label="åå¼(å
)" width="120"> |
| | | <template #default="{ row }"> |
| | | ¥{{ formatCurrency(row.unTaxIncludingPriceTotal) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag |
| | | :type="getStatusTagType(row.status)" |
| | | size="small" |
| | | > |
| | | {{ row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | :current-page="pagination.currentPage" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | :page-size="pagination.pageSize" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="pagination.total" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | </main> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, reactive } from 'vue'; |
| | | import 'element-plus/dist/index.css'; |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { getLedgerPage } from "@/api/equipmentManagement/ledger"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // ç鿡件 |
| | | const dateRange = ref(null); |
| | | const equipmentType = ref(''); |
| | | |
| | | |
| | | // åºå®èµäº§ä¿¡æ¯ |
| | | const assetInfo = ref({ |
| | | totalEquipment: 0, |
| | | totalOriginalValue: 0, |
| | | totalDepreciation: 0, |
| | | totalNetValue: 0 |
| | | }); |
| | | |
| | | // 设å¤å表 |
| | | const equipmentList = ref([]); |
| | | const pagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0 |
| | | }); |
| | | |
| | | // å¾è¡¨é
ç½® |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%', |
| | | position: 'relative', |
| | | }; |
| | | |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }; |
| | | |
| | | const lineLegend = { |
| | | show: false, |
| | | }; |
| | | |
| | | // æçº¿å¾æç¤ºæ¡ |
| | | const tooltip = reactive({ |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'line', |
| | | lineStyle: { color: '#aaa' } |
| | | }, |
| | | // èªå®ä¹å
容 |
| | | formatter: function (params) { |
| | | if (!params || !params.length) return ''; |
| | | const axisLabel = params[0].axisValueLabel || params[0].axisValue || ''; |
| | | const rows = params |
| | | .map(p => { |
| | | const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>`; |
| | | return `${colorDot}${p.seriesName}: ${p.value}`; |
| | | }) |
| | | .join('<br/>'); |
| | | return `<div>${axisLabel}</div><div>${rows}</div>`; |
| | | } |
| | | }); |
| | | |
| | | const xAxis = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: true, alignWithLabel: true }, |
| | | data: [], |
| | | }, |
| | | ]); |
| | | |
| | | const yAxis = [ |
| | | { |
| | | type: 'value', |
| | | name: 'æ°é/éé¢', // 左侧yè½´ |
| | | position: 'left', |
| | | min: 0, |
| | | // åæ è½´åç§°æ ·å¼ |
| | | nameTextStyle: { |
| | | color: '#000', |
| | | fontSize: 14, |
| | | }, |
| | | } |
| | | ]; |
| | | |
| | | const chartStylePie = { |
| | | width: '100%', |
| | | height: '100%' // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | }; |
| | | |
| | | const pieColors = ['#F04864', '#FACC14', '#8543E0', '#1890FF', '#13C2C2', '#2FC25B']; // 坿 ¹æ®å®é
è°æ´ |
| | | |
| | | // 饼徿°æ® |
| | | const typeDistributionData = ref([]); |
| | | const departmentDistributionData = ref([]); |
| | | |
| | | // 饼å¾å¾ä¾ |
| | | const typeDistributionLegend = computed(() => ({ |
| | | show: true, |
| | | top: 'center', |
| | | left: '60%', |
| | | orient: 'vertical', |
| | | icon: 'circle', |
| | | data: typeDistributionData.value.map(item => item.name), |
| | | formatter: function(name) { |
| | | const item = typeDistributionData.value.find(i => i.name === name); |
| | | if (!item) return name; |
| | | return `${name} | ${item.count} å° | ${item.amount}`; |
| | | }, |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 14, |
| | | lineHeight: 26, |
| | | } |
| | | })); |
| | | |
| | | |
| | | // 饼å¾ç³»å |
| | | const typeDistributionSeries = computed(() => [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['50%', '65%'], |
| | | center: ['25%', '50%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: typeDistributionData.value, |
| | | color: pieColors |
| | | } |
| | | ]); |
| | | |
| | | // æçº¿å¾æ°æ® |
| | | const typeDistributionLineSeries = ref([]); |
| | | |
| | | |
| | | // é¥¼å¾æç¤ºæ¡ |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function(params) { |
| | | // æ£æ¥æ°æ®æ¯å¦åå¨ |
| | | if (!params.data) return params.name; |
| | | // æ¼æ¥å®æ´å
容 |
| | | return ` |
| | | <div> |
| | | <div style="color:${params.color};font-size:16px;">â</div> |
| | | <div>${params.name}</div> |
| | | <div>æ°éï¼${params.data.count} å°</div> |
| | | <div>éé¢ï¼${params.data.amount}</div> |
| | | </div> |
| | | `; |
| | | } |
| | | }); |
| | | |
| | | // éé¡¹æ°æ® |
| | | const equipmentTypeOptions = ref([]); |
| | | |
| | | // è·åæ°æ® |
| | | const fetchData = async () => { |
| | | try { |
| | | // è·ååºå®èµäº§æ±æ»ä¿¡æ¯ |
| | | const assetInfoRes = await getAssetInfo({ |
| | | startDate: dateRange.value ? dateRange.value[0] : null, |
| | | endDate: dateRange.value ? dateRange.value[1] : null, |
| | | equipmentType: equipmentType.value |
| | | }); |
| | | |
| | | if (assetInfoRes.code === 200) { |
| | | assetInfo.value = assetInfoRes.data; |
| | | } |
| | | |
| | | // è·å设å¤å表 |
| | | const equipmentListRes = await getLedgerPage({ |
| | | current: pagination.value.currentPage, |
| | | size: pagination.value.pageSize, |
| | | startDate: dateRange.value ? dateRange.value[0] : null, |
| | | endDate: dateRange.value ? dateRange.value[1] : null, |
| | | equipmentType: equipmentType.value |
| | | }); |
| | | |
| | | if (equipmentListRes.code === 200) { |
| | | equipmentList.value = equipmentListRes.data.records; |
| | | pagination.value.total = equipmentListRes.data.total; |
| | | |
| | | // æ ¹æ® equipmentList æ deviceName è¿è¡åç±»ç»è®¡ |
| | | const deviceNameMap = {}; |
| | | equipmentList.value.forEach(item => { |
| | | const deviceName = item.deviceName; |
| | | if (!deviceNameMap[deviceName]) { |
| | | deviceNameMap[deviceName] = { |
| | | name: deviceName, |
| | | count: 0, |
| | | totalValue: 0 |
| | | }; |
| | | } |
| | | deviceNameMap[deviceName].count += item.number || 1; // å设 number ä¸ºè®¾å¤æ°é |
| | | deviceNameMap[deviceName].totalValue += item.taxIncludingPriceTotal || 0; // ç´¯å å«ç¨æ»ä»· |
| | | }); |
| | | |
| | | // 转æ¢ä¸º typeDistributionData æ ¼å¼ |
| | | typeDistributionData.value = Object.values(deviceNameMap).map(item => ({ |
| | | name: item.name, |
| | | value: item.count, |
| | | count: item.count, |
| | | amount: `Â¥${formatCurrency(item.totalValue)}` |
| | | })); |
| | | |
| | | // æ´æ°xè½´æ°æ® |
| | | xAxis.value[0].data = typeDistributionData.value.map(item => item.name); |
| | | |
| | | // æå»ºæçº¿å¾æ°æ® |
| | | typeDistributionLineSeries.value = [ |
| | | { |
| | | name: 'è®¾å¤æ°é', |
| | | type: 'line', |
| | | data: typeDistributionData.value.map(item => item.count) |
| | | } |
| | | ]; |
| | | } |
| | | } catch (error) { |
| | | console.error('è·ååºå®èµäº§æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | }; |
| | | |
| | | // åå§å |
| | | onMounted(() => { |
| | | // è·ååè¡¨æ°æ® |
| | | fetchData(); |
| | | }); |
| | | |
| | | // æ ¼å¼åè´§å¸ |
| | | const formatCurrency = (value) => { |
| | | if (!value) return '0.00'; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ','); |
| | | }; |
| | | |
| | | // è·åç¶ææ ç¾ç±»å |
| | | const getStatusTagType = (status) => { |
| | | switch (status) { |
| | | case 'å¨ç¨': |
| | | return 'success'; |
| | | case 'é²ç½®': |
| | | return 'info'; |
| | | case 'ç»´ä¿®ä¸': |
| | | return 'warning'; |
| | | case 'æ¥åº': |
| | | return 'danger'; |
| | | default: |
| | | return 'info'; |
| | | } |
| | | }; |
| | | |
| | | // éç½®ç鿡件 |
| | | const resetFilters = () => { |
| | | dateRange.value = null; |
| | | equipmentType.value = ''; |
| | | fetchData(); |
| | | }; |
| | | |
| | | // å页å¤ç |
| | | const handleSizeChange = (size) => { |
| | | pagination.value.pageSize = size; |
| | | fetchData(); |
| | | }; |
| | | |
| | | const handleCurrentChange = (page) => { |
| | | pagination.value.currentPage = page; |
| | | fetchData(); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | /* åºç¡æ ·å¼è¡¥å
*/ |
| | | :root { |
| | | --el-color-primary: #4f46e5; |
| | | } |
| | | |
| | | .el-card { |
| | | position: relative; |
| | | border-radius: 12px; |
| | | padding: 14px 10px 10px 10px; |
| | | box-shadow: 0 2px 8px #eee; |
| | | |
| | | :deep(.el-card__body) { |
| | | padding: 10px 20px !important; |
| | | } |
| | | |
| | | &.bg1 { |
| | | background: url(@/assets/icons/png/1.png) no-repeat 100% 100% !important; |
| | | } |
| | | |
| | | &.bg2 { |
| | | background: url(@/assets/icons/png/2.png) no-repeat 100% 100% !important; |
| | | } |
| | | |
| | | &.bg3 { |
| | | background: url(@/assets/icons/png/3.png) no-repeat 100% 100% !important; |
| | | } |
| | | |
| | | &.bg4 { |
| | | background: url(@/assets/icons/png/4.png) no-repeat 100% 100% !important; |
| | | } |
| | | |
| | | &.bg5 { |
| | | background: url(@/assets/icons/png/5.png) no-repeat 100% 100% !important; |
| | | } |
| | | } |
| | | |
| | | .grid-container { |
| | | /* grid 容å¨åºç¡æ ·å¼ */ |
| | | display: grid; |
| | | gap: 1rem; /* gap-4 å¯¹åº 1rem (16px) */ |
| | | margin-bottom: 2rem; /* mb-8 å¯¹åº 2rem (32px) */ |
| | | |
| | | p { |
| | | font-size: 22px; |
| | | margin-top: 0px; |
| | | color: #fff; |
| | | } |
| | | |
| | | h3 { |
| | | font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif; |
| | | margin: 10px 0; |
| | | color: #fff; |
| | | } |
| | | } |
| | | |
| | | /* ç§»å¨ç«¯é»è®¤æ ·å¼ (grid-cols-1) */ |
| | | .grid-container { |
| | | grid-template-columns: repeat(1, minmax(0, 1fr)); |
| | | } |
| | | |
| | | /* å°å±å¹åä»¥ä¸ (sm:grid-cols-2) */ |
| | | @media (min-width: 640px) { |
| | | .grid-container { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | /* 大å±å¹åä»¥ä¸ (lg:grid-cols-5) */ |
| | | @media (min-width: 1024px) { |
| | | .grid-container { |
| | | grid-template-columns: repeat(5, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | /* å¡çæ¬åææå¢å¼º */ |
| | | .el-card:hover { |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | .echarts { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | /* å¾è¡¨å®¹å¨æ ·å¼ */ |
| | | .el-chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .section-title { |
| | | position: relative; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 10px; |
| | | margin-bottom: 10px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .section-title::before { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0px; |
| | | content: ''; |
| | | width: 4px; |
| | | height: 18px; |
| | | background-color: #002FA7; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .chart-num { |
| | | position: absolute; |
| | | z-index: 3; |
| | | top: 92px; |
| | | left: 92px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .pagination-container { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="æ¯åºæ¥æ:"> |
| | | <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item label="仿¬¾æ¹å¼:"> |
| | | <el-select |
| | | v-model="filters.expenseMethod" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | style="width: 200px;" |
| | | > |
| | | <el-option |
| | | v-for="item in checkout_payment" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus"> æ°å¢ </el-button> |
| | | <el-button @click="handleOut" icon="download">导åº</el-button> |
| | | <el-button |
| | | type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0" |
| | | @click="deleteRow(multipleList.map((item) => item.id))" |
| | | > |
| | | æ¹éå é¤ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | isSelection |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" text @click="edit(row.id)" icon="editPen"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button |
| | | type="primary" |
| | | text |
| | | @click="openFilesFormDia(row)" |
| | | > |
| | | éä»¶ |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <Modal ref="modalRef" @success="getTableData"></Modal> |
| | | <files-dia ref="filesDia"></files-dia> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { usePaginationApi } from "@/hooks/usePaginationApi"; |
| | | import { listPage, delAccountExpense } from "@/api/financialManagement/expenseManagement"; |
| | | import { onMounted, getCurrentInstance } from "vue"; |
| | | import Modal from "./Modal.vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import FilesDia from "../revenueManagement/filesDia.vue"; |
| | | |
| | | defineOptions({ |
| | | name: "æ¯åºç®¡ç", |
| | | }); |
| | | |
| | | // è¡¨æ ¼å¤éæ¡éä¸é¡¹ |
| | | const multipleList = ref([]); |
| | | const { proxy } = getCurrentInstance(); |
| | | const modalRef = ref(); |
| | | const { checkout_payment } = proxy.useDict("checkout_payment"); |
| | | const { expense_types } = proxy.useDict("expense_types"); |
| | | const filesDia = ref() |
| | | |
| | | const { |
| | | filters, |
| | | columns, |
| | | dataList, |
| | | pagination, |
| | | getTableData, |
| | | resetFilters, |
| | | onCurrentChange, |
| | | } = usePaginationApi( |
| | | listPage, |
| | | { |
| | | expenseMethod: undefined, |
| | | entryDate: undefined, |
| | | }, |
| | | [ |
| | | { |
| | | label: "æ¯åºæ¥æ", |
| | | align: "center", |
| | | prop: "expenseDate", |
| | | }, |
| | | { |
| | | label: "æ¯åºç±»å", |
| | | align: "center", |
| | | prop: "expenseType", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | if (expense_types.value.find((m) => m.value == params)) { |
| | | return expense_types.value.find((m) => m.value == params).label; |
| | | } else { |
| | | return null |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "ä¾åºååç§°", |
| | | align: "center", |
| | | prop: "supplierName", |
| | | |
| | | }, |
| | | { |
| | | label: "æ¯åºéé¢", |
| | | align: "center", |
| | | prop: "expenseMoney", |
| | | |
| | | }, |
| | | { |
| | | label: "æ¯åºæè¿°", |
| | | align: "center", |
| | | prop: "expenseDescribed", |
| | | |
| | | }, |
| | | { |
| | | label: "仿¬¾æ¹å¼", |
| | | align: "center", |
| | | prop: "expenseMethod", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | if (checkout_payment.value.find((m) => m.value == params)) { |
| | | return checkout_payment.value.find((m) => m.value == params).label; |
| | | } else { |
| | | return null |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "å票å·ç ", |
| | | align: "center", |
| | | prop: "invoiceNumber", |
| | | |
| | | }, |
| | | { |
| | | label: "夿³¨", |
| | | align: "center", |
| | | prop: "note", |
| | | |
| | | }, |
| | | { |
| | | label: "å½å
¥äºº", |
| | | align: "center", |
| | | prop: "inputUser", |
| | | }, |
| | | { |
| | | label: "å½å
¥æ¥æ", |
| | | align: "center", |
| | | prop: "inputTime", |
| | | |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "æä½", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | }, |
| | | ] |
| | | ); |
| | | |
| | | // å¤éååä»ä¹ |
| | | const handleSelectionChange = (selectionList) => { |
| | | multipleList.value = selectionList; |
| | | }; |
| | | |
| | | const add = () => { |
| | | modalRef.value.openModal(); |
| | | }; |
| | | const edit = (id) => { |
| | | modalRef.value.loadForm(id); |
| | | }; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | onCurrentChange(page); |
| | | }; |
| | | const deleteRow = (id) => { |
| | | ElMessageBox.confirm("æ¤æä½å°æ°¸ä¹
å é¤è¯¥æ°æ®, æ¯å¦ç»§ç»?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(async () => { |
| | | const { code } = await delAccountExpense(id); |
| | | if (code == 200) { |
| | | ElMessage({ |
| | | type: "success", |
| | | message: "å 餿å", |
| | | }); |
| | | getTableData(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | filters.entryDate = value; |
| | | filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | filters.entryDate = null; |
| | | filters.entryDateStart = undefined; |
| | | filters.entryDateEnd = undefined; |
| | | } |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download(`/account/accountExpense/export`, {}, "æ¯åºå°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | const openFilesFormDia = (row) => { |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog( row,'æ¯åº') |
| | | }) |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div style="padding: 20px;"> |
| | | <!-- 页颿 é¢åæ¥æçé --> |
| | | <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;"> |
| | | <el-date-picker |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | @change="handleDateChange" |
| | | class="w-full md:w-auto" |
| | | style="margin-right: 30px;" |
| | | /> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | icon="Refresh" |
| | | @click="resetDateRange" |
| | | size="default" |
| | | > |
| | | éç½® |
| | | </el-button> |
| | | </div> |
| | | |
| | | <main class="container mx-auto px-4 pb-10"> |
| | | <!-- è´¢å¡ææ å¡ç --> |
| | | <div class="grid-container"> |
| | | <!-- æ»æ¶å
¥ --> |
| | | <el-card class="bg1"> |
| | | <p>æ»æ¶å
¥</p> |
| | | <h3> |
| | | ¥{{ pageInfo.totalIncome }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- æ¶å
¥ç¬æ° --> |
| | | <el-card class="bg2"> |
| | | <p>æ¶å
¥ç¬æ°</p> |
| | | <h3> |
| | | {{ pageInfo.incomeNumber }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- æ»æ¯åº --> |
| | | <el-card class="bg3"> |
| | | <p>æ»æ¯åº</p> |
| | | <h3> |
| | | ¥{{ pageInfo.totalExpense }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- æ¯åºç¬æ° --> |
| | | <el-card class="bg4"> |
| | | <p>æ¯åºç¬æ°</p> |
| | | <h3> |
| | | {{ pageInfo.expenseNumber }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- åæ¶å
¥ --> |
| | | <el-card class="bg5"> |
| | | <p>åæ¶å
¥</p> |
| | | <h3> |
| | | ¥{{ pageInfo.netRevenue }} |
| | | </h3> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- æ¶å
¥ç»è®¡å¾è¡¨ --> |
| | | <div class="grid-layout"> |
| | | <el-card style="margin-bottom: 20px;"> |
| | | <h2 class="section-title">æ¶å
¥ç»è®¡(å
)</h2> |
| | | <div class="echarts"> |
| | | <Echarts :legend="pieLegend0" :chartStyle="chartStylePie" |
| | | :series="materialPieSeries0" |
| | | :tooltip="pieTooltip" style="height: 260px;width: 35%;"> |
| | | <div class="chart-num"> |
| | | <span style="font-size: 22px;">æ¶å
¥</span> |
| | | <span style="font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalIncome }}</span> |
| | | </div> |
| | | </Echarts> |
| | | <Echarts ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries0" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis0" |
| | | :yAxis="yAxis0" |
| | | style="height: 260px;width: 64%;"></Echarts> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ¯åºç»è®¡å¾è¡¨ --> |
| | | <el-card> |
| | | <h2 class="section-title">æ¯åºç»è®¡(å
)</h2> |
| | | <div class="echarts"> |
| | | <Echarts ref="chart" |
| | | :legend="pieLegend1" |
| | | :chartStyle="chartStylePie" |
| | | :series="materialPieSeries1" |
| | | :tooltip="pieTooltip" |
| | | style="height: 260px;width: 35%;"> |
| | | <div class="chart-num"> |
| | | <span style="font-size: 22px;">æ¯åº</span> |
| | | <span style="font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalExpense }}</span> |
| | | </div></Echarts> |
| | | <Echarts ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries1" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | style="height: 260px;width: 64%;"></Echarts> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </main> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed, onMounted, reactive } from 'vue'; |
| | | import 'element-plus/dist/index.css'; |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { reportForms,reportIncome,reportExpense } from "@/api/financialManagement/financialStatements"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // æ¥æèå´ |
| | | const dateRange = ref(null); |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%', // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | position:'relative', |
| | | } |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | } |
| | | const lineLegend = { |
| | | show: false, |
| | | } |
| | | // æçº¿å¾æç¤ºæ¡ |
| | | const tooltip = reactive({ |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'line', |
| | | lineStyle: { color: '#aaa' } |
| | | }, |
| | | // èªå®ä¹å
容 |
| | | formatter: function (params) { |
| | | if (!params || !params.length) return '' |
| | | const axisLabel = params[0].axisValueLabel || params[0].axisValue || '' |
| | | const rows = params |
| | | .map(p => { |
| | | const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>` |
| | | return `${colorDot}${p.seriesName}: ${p.value}` |
| | | }) |
| | | .join('<br/>') |
| | | return `<div>${axisLabel}</div><div>${rows}</div>` |
| | | } |
| | | }) |
| | | const months = ['1æ','2æ','3æ','4æ','5æ','6æ','7æ','8æ','9æ','10æ','11æ','12æ']; |
| | | const lineSeries0 = ref([]) |
| | | const lineSeries1 = ref([]) |
| | | |
| | | const xAxis0 = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: true, alignWithLabel: true }, |
| | | data: months, |
| | | }, |
| | | ]); |
| | | const xAxis1 = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: true, alignWithLabel: true }, |
| | | data: months, |
| | | }, |
| | | ]); |
| | | const yAxis0 = [ |
| | | { |
| | | type: 'value', |
| | | name: 'æ¶å
¥ç»è®¡', // 左侧yè½´ |
| | | position: 'left', |
| | | min: 0, |
| | | // åæ è½´åç§°æ ·å¼ |
| | | nameTextStyle: { |
| | | color: '#000', |
| | | fontSize: 14, |
| | | }, |
| | | } |
| | | ] |
| | | |
| | | const yAxis1 = [ |
| | | { |
| | | type: 'value', |
| | | name: 'æ¯åºç»è®¡', // 左侧yè½´ |
| | | position: 'left', |
| | | min: 0, |
| | | // åæ è½´åç§°æ ·å¼ |
| | | nameTextStyle: { |
| | | color: '#000', |
| | | fontSize: 14, |
| | | }, |
| | | } |
| | | ] |
| | | |
| | | const chartStylePie = { |
| | | width: '100%', |
| | | height: '100%' // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | } |
| | | const pieColors = ['#F04864','#FACC14', '#8543E0', '#1890FF', '#13C2C2','#2FC25B']; // 坿 ¹æ®å®é
è°æ´ |
| | | const pieData0 = ref([]); |
| | | const pieData1 = ref([]); |
| | | |
| | | const pieLegend0 = computed(() => ({ |
| | | show: true, |
| | | top: 'center', |
| | | left: '60%', |
| | | orient: 'vertical', |
| | | icon: 'circle', |
| | | data: pieData0.value.map(item => item.name), |
| | | formatter: function(name) { |
| | | const item = pieData0.value.find(i => i.name === name); |
| | | if (!item) return name; |
| | | return `${name} | ${item.percent} ${item.amount}`; |
| | | }, |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 14, |
| | | lineHeight: 26, |
| | | } |
| | | })); |
| | | const pieLegend1 = computed(() => ({ |
| | | show: true, |
| | | top: 'center', |
| | | left: '60%', |
| | | orient: 'vertical', |
| | | icon: 'circle', |
| | | data: pieData1.value.map(item => item.name), |
| | | formatter: function(name) { |
| | | const item = pieData1.value.find(i => i.name === name); |
| | | if (!item) return name; |
| | | return `${name} | ${item.percent} ${item.amount}`; |
| | | }, |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 14, |
| | | lineHeight: 26, |
| | | } |
| | | })); |
| | | |
| | | const materialPieSeries0 = computed(() => [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['50%', '65%'], |
| | | center: ['25%', '50%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: pieData0.value, |
| | | color: pieColors |
| | | } |
| | | ]); |
| | | const materialPieSeries1 = computed(() => [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['50%', '65%'], |
| | | center: ['25%', '50%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: pieData1.value, |
| | | color: pieColors |
| | | } |
| | | ]); |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function(params) { |
| | | // æ£æ¥æ°æ®æ¯å¦åå¨ |
| | | if (!params.data) return params.name; |
| | | // æ¼æ¥å®æ´å
容 |
| | | return ` |
| | | <div> |
| | | <div style="color:${params.color};font-size:16px;">â</div> |
| | | <div>${params.name}</div> |
| | | <div>å æ¯ï¼${params.data.percent}</div> |
| | | <div>éé¢ï¼${params.data.amount}</div> |
| | | </div> |
| | | `; |
| | | } |
| | | }) |
| | | |
| | | |
| | | const pageInfo = ref({ |
| | | }) |
| | | |
| | | const getData = async () => { |
| | | if (!dateRange.value || !dateRange.value.length) { |
| | | return; |
| | | } |
| | | try { |
| | | const {code,data} = await reportForms({entryDateStart:dateRange.value[0], entryDateEnd:dateRange.value[1]}); |
| | | if(code === 200) { |
| | | pageInfo.value = data |
| | | pieData0.value = data.incomeType.map(item=>({ |
| | | name:item.typeName, |
| | | value:item.account, |
| | | percent:`${item.proportion*100}%`, |
| | | amount:`Â¥${item.account}` |
| | | })) |
| | | pieData1.value = data.expenseType.map(item=>({ |
| | | name:item.typeName, |
| | | value:item.account, |
| | | percent:`${item.proportion*100}%`, |
| | | amount:`Â¥${item.account}` |
| | | })) |
| | | |
| | | } |
| | | } catch (error) { |
| | | console.error('è·åè´¢å¡ææ æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | try{ |
| | | const {code,data} = await reportIncome(); |
| | | if(code==200){ |
| | | lineSeries0.value = data.map(item=>({ |
| | | name:item.typeName, |
| | | type: 'line', |
| | | data:item.account.map(item=>Number(item)) |
| | | })) |
| | | |
| | | } |
| | | }catch (error) { |
| | | console.error('è·åè´¢å¡ææ æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | try{ |
| | | const {code,data} = await reportExpense(); |
| | | if(code==200){ |
| | | lineSeries1.value = data.map(item=>({ |
| | | name:item.typeName, |
| | | type: 'line', |
| | | data:item.account.map(item=>Number(item)) |
| | | })) |
| | | |
| | | } |
| | | }catch (error) { |
| | | console.error('è·åè´¢å¡ææ æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | }; |
| | | |
| | | |
| | | // åå§å |
| | | onMounted(() => { |
| | | // ä¸è®¾ç½®é»è®¤æ¥æï¼ç±ç¨æ·æå¨éæ© |
| | | }); |
| | | |
| | | // å¤çæ¥æèå´åå |
| | | const handleDateChange = (newRange) => { |
| | | dateRange.value = newRange; |
| | | if (newRange && newRange.length === 2) { |
| | | getData() |
| | | } |
| | | }; |
| | | |
| | | // éç½®æ¥æèå´ |
| | | const resetDateRange = () => { |
| | | dateRange.value = null; |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | /* åºç¡æ ·å¼è¡¥å
*/ |
| | | :root { |
| | | --el-color-primary: #4f46e5; |
| | | } |
| | | .el-card{ |
| | | position: relative; |
| | | border-radius: 12px; |
| | | padding: 14px 10px 10px 10px; |
| | | box-shadow: 0 2px 8px #eee; |
| | | :deep(.el-card__body){ |
| | | padding: 10px 20px !important; |
| | | } |
| | | &.bg1{ |
| | | background: url(@/assets/icons/png/1.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg2{ |
| | | background: url(@/assets/icons/png/2.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg3{ |
| | | background: url(@/assets/icons/png/3.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg4{ |
| | | background: url(@/assets/icons/png/4.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg5{ |
| | | background: url(@/assets/icons/png/5.png) no-repeat 100% 100% !important; |
| | | } |
| | | } |
| | | |
| | | .grid-container { |
| | | /* grid 容å¨åºç¡æ ·å¼ */ |
| | | display: grid; |
| | | gap: 1rem; /* gap-4 å¯¹åº 1rem (16px) */ |
| | | margin-bottom: 2rem; /* mb-8 å¯¹åº 2rem (32px) */ |
| | | |
| | | p{ |
| | | font-size: 22px; |
| | | margin-top: 0px; |
| | | color: #fff; |
| | | } |
| | | h3{ |
| | | font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif; |
| | | margin: 10px 0; |
| | | color: #fff; |
| | | } |
| | | |
| | | } |
| | | |
| | | /* ç§»å¨ç«¯é»è®¤æ ·å¼ (grid-cols-1) */ |
| | | .grid-container { |
| | | grid-template-columns: repeat(1, minmax(0, 1fr)); |
| | | } |
| | | |
| | | /* å°å±å¹åä»¥ä¸ (sm:grid-cols-2) */ |
| | | @media (min-width: 640px) { |
| | | .grid-container { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | /* 大å±å¹åä»¥ä¸ (lg:grid-cols-5) */ |
| | | @media (min-width: 1024px) { |
| | | .grid-container { |
| | | grid-template-columns: repeat(5, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | /* å¡çæ¬åææå¢å¼º */ |
| | | .el-card:hover { |
| | | transform: translateY(-2px); |
| | | } |
| | | .echarts{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | /* å¾è¡¨å®¹å¨æ ·å¼ */ |
| | | .el-chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | .section-title { |
| | | position: relative; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 10px; |
| | | margin-bottom: 10px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .section-title::before { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0px; |
| | | content: ''; |
| | | width: 4px; |
| | | height: 18px; |
| | | background-color: #002FA7; |
| | | border-radius: 2px; |
| | | } |
| | | .chart-num{ |
| | | position: absolute; |
| | | z-index: 3; |
| | | top: 92px; |
| | | left: 92px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="inventory-statistics"> |
| | | <!-- çé表å --> |
| | | <div class="filter-form"> |
| | | <el-form :model="filterForm" inline> |
| | | <!-- <el-form-item label="æ¶é´èå´">--> |
| | | <!-- <el-date-picker--> |
| | | <!-- v-model="filterForm.dateRange"--> |
| | | <!-- type="daterange"--> |
| | | <!-- range-separator="è³"--> |
| | | <!-- start-placeholder="å¼å§æ¥æ"--> |
| | | <!-- end-placeholder="ç»ææ¥æ"--> |
| | | <!-- />--> |
| | | <!-- </el-form-item>--> |
| | | <!-- <el-form-item label="ä¾åºååç§°">--> |
| | | <!-- <el-input v-model="filterForm.supplierName" style="width: 240px" placeholder="请è¾å
¥" clearable prefix-icon="Search" />--> |
| | | <!-- </el-form-item>--> |
| | | <!-- <el-form-item label="产ååç§°">--> |
| | | <!-- <el-input v-model="filterForm.productCategory" style="width: 240px" placeholder="请è¾å
¥" clearable prefix-icon="Search" />--> |
| | | <!-- </el-form-item>--> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æ¥è¯¢</el-button> |
| | | <!-- <el-button @click="handleReset">éç½®</el-button>--> |
| | | <!-- <el-button type="success" @click="handleExport">导åº</el-button>--> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- ç»è®¡æ±æ»å¡ç --> |
| | | <div class="summary-cards"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-card class="summary-card"> |
| | | <div class="summary-item"> |
| | | <p class="summary-title">æ»åºåæ°é</p> |
| | | <p class="summary-value">{{ summaryData.totalInventoryCount }}</p> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="summary-card"> |
| | | <div class="summary-item"> |
| | | <p class="summary-title">æ»åºåéé¢</p> |
| | | <p class="summary-value">Â¥{{ summaryData.totalInventoryValue }}</p> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="summary-card"> |
| | | <div class="summary-item"> |
| | | <p class="summary-title">åºåå卿°é</p> |
| | | <p class="summary-value">{{ summaryData.inventoryChangeCount }}</p> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="summary-card"> |
| | | <div class="summary-item"> |
| | | <p class="summary-title">åºååå¨éé¢</p> |
| | | <p class="summary-value">Â¥{{ summaryData.inventoryChangeValue }}</p> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- å¾è¡¨åºå --> |
| | | <div class="chart-section"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-card class="chart-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>åºååç±»å æ¯</span> |
| | | </div> |
| | | </template> |
| | | <div id="category-pie-chart" style="height: 400px;"></div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card class="chart-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>åºåéé¢è¶å¿</span> |
| | | </div> |
| | | </template> |
| | | <div id="amount-trend-chart" style="height: 400px;"></div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="tableData" |
| | | v-loading="loading" |
| | | border |
| | | style="width: 100%" |
| | | :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="ä¾åºååç§°" prop="supplierName" width="240" show-overflow-tooltip /> |
| | | <el-table-column label="产å" prop="productCategory" min-width="100" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" min-width="200" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" width="70" show-overflow-tooltip /> |
| | | <el-table-column label="å
¥åºæ°é" prop="inboundNum" width="90" show-overflow-tooltip /> |
| | | <el-table-column label="åºåæ°é" prop="inboundNum0" width="90" show-overflow-tooltip /> |
| | | <el-table-column label="å«ç¨åä»·" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="å«ç¨æ»ä»·" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" width="80" show-overflow-tooltip /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="å
¥åºäºº" prop="createBy" width="100" show-overflow-tooltip /> |
| | | </el-table> |
| | | <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, nextTick } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | import {getStockInPage} from "@/api/inventoryManagement/stockIn.js"; |
| | | |
| | | // ç¶æåé |
| | | const loading = ref(false) |
| | | const total = ref(0) |
| | | const tableData = ref([]) |
| | | const summaryData = ref({}) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | |
| | | // å¾è¡¨å®ä¾ |
| | | const categoryPieChart = ref(null) |
| | | const amountTrendChart = ref(null) |
| | | |
| | | // çé表å |
| | | const filterForm = reactive({ |
| | | dateRange: [], |
| | | supplierName: '', |
| | | productCategory: '' |
| | | }) |
| | | |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | loadData() |
| | | } |
| | | |
| | | // åå§åæ°æ® |
| | | onMounted(() => { |
| | | loadSummaryData() |
| | | loadData() |
| | | }) |
| | | |
| | | // å è½½ç»è®¡æ±æ»æ°æ® |
| | | const loadSummaryData = () => { |
| | | getStockInChartData().then(res => { |
| | | summaryData.value = res.data |
| | | }) |
| | | } |
| | | |
| | | // å è½½åºåæ°æ® |
| | | const loadData = () => { |
| | | loading.value = true |
| | | getStockInPage({ ...filterForm, ...page }).then(res => { |
| | | loading.value = false |
| | | tableData.value = res.data.records |
| | | total.value = res.data.total |
| | | console.log('res', res.data.records) |
| | | |
| | | // æ°æ®å è½½å®æåæ¸²æå¾è¡¨ |
| | | nextTick(() => { |
| | | renderCategoryPieChart() |
| | | renderAmountTrendChart() |
| | | }) |
| | | }).catch(() => { |
| | | loading.value = false |
| | | }) |
| | | } |
| | | |
| | | // 渲æåç±»å æ¯é¥¼å¾ |
| | | const renderCategoryPieChart = () => { |
| | | if (!categoryPieChart.value) { |
| | | categoryPieChart.value = echarts.init(document.getElementById('category-pie-chart')) |
| | | } |
| | | // æ ¹æ® tableData æ productCategory åç±»å¹¶è®¡ç® inboundNum0 æ°éæ»å |
| | | const categoryMap = tableData.value.reduce((acc, cur) => { |
| | | acc[cur.productCategory] = (acc[cur.productCategory] || 0) + cur.inboundNum0 |
| | | return acc |
| | | }, {}) |
| | | |
| | | // å°åç±»ç»æè½¬æ¢ä¸º ECharts 饼徿éçæ°æ®æ ¼å¼ |
| | | const categoryData = Object.entries(categoryMap).map(([name, value]) => ({ |
| | | name: name, |
| | | value: value |
| | | })) |
| | | const option = { |
| | | title: { |
| | | text: 'åºååç±»å æ¯', |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item', |
| | | formatter: '{a} <br/>{b}: {c} ({d}%)' |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'åºååç±»', |
| | | type: 'pie', |
| | | radius: ['40%', '70%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderRadius: 10, |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: true, |
| | | formatter: '{b}: {d}%' |
| | | }, |
| | | emphasis: { |
| | | label: { |
| | | show: true, |
| | | fontSize: '16', |
| | | fontWeight: 'bold' |
| | | } |
| | | }, |
| | | data: categoryData |
| | | } |
| | | ] |
| | | } |
| | | |
| | | categoryPieChart.value.setOption(option) |
| | | } |
| | | // 渲æéé¢è¶å¿æçº¿å¾ |
| | | const renderAmountTrendChart = () => { |
| | | if (!amountTrendChart.value) { |
| | | amountTrendChart.value = echarts.init(document.getElementById('amount-trend-chart')) |
| | | } |
| | | // ææä»½åç»å¹¶è®¡ç®taxInclusiveTotalPriceæ»å |
| | | const monthlyAmounts = tableData.value.reduce((acc, cur) => { |
| | | const date = new Date(cur.createTime); |
| | | const month = date.getMonth() + 1; |
| | | |
| | | // ç¡®ä¿monthå¨1-12èå´å
|
| | | if (month >= 1 && month <= 12) { |
| | | acc[month] = (acc[month] || 0) + cur.taxInclusiveTotalPrice; |
| | | } |
| | | return acc; |
| | | }, {}); |
| | | |
| | | // çæ12个æçæ°æ®ï¼ç¼ºå¤±æä»½ç¨0ä»£æ¿ |
| | | const amounts = []; |
| | | for (let i = 1; i <= 12; i++) { |
| | | amounts.push(monthlyAmounts[i] || 0); |
| | | } |
| | | const dates = ['1æ', '2æ', '3æ', '4æ', '5æ', '6æ', '7æ', '8æ', '9æ', '10æ', '11æ', '12æ'] |
| | | |
| | | const option = { |
| | | title: { |
| | | text: 'åºåéé¢è¶å¿', |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: '{b}: ¥{c}' |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: dates |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | axisLabel: { |
| | | formatter: 'Â¥{value}' |
| | | } |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'åºåéé¢', |
| | | type: 'line', |
| | | data: amounts, |
| | | smooth: true, |
| | | areaStyle: {} |
| | | } |
| | | ] |
| | | } |
| | | |
| | | amountTrendChart.value.setOption(option) |
| | | } |
| | | |
| | | // æ¥è¯¢æä½ |
| | | const handleSearch = () => { |
| | | loadData() |
| | | } |
| | | |
| | | // éç½®æä½ |
| | | const handleReset = () => { |
| | | filterForm.dateRange = [] |
| | | filterForm.supplierName = '' |
| | | filterForm.productCategory = '' |
| | | loadData() |
| | | } |
| | | |
| | | // å¯¼åºæä½ |
| | | const handleExport = () => { |
| | | console.log('å¯¼åºæ°æ®') |
| | | } |
| | | |
| | | // çªå£å¤§å°æ¹åæ¶ï¼éæ°è°æ´å¾è¡¨å¤§å° |
| | | window.addEventListener('resize', () => { |
| | | if (categoryPieChart.value) categoryPieChart.value.resize() |
| | | if (amountTrendChart.value) amountTrendChart.value.resize() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .inventory-statistics { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .filter-form { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .summary-cards { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .summary-card { |
| | | text-align: center; |
| | | height: 100px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .summary-item { |
| | | width: 100%; |
| | | } |
| | | |
| | | .summary-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .summary-value.warning { |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | .summary-value.danger { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .chart-section { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .chart-card { |
| | | height: 460px; |
| | | } |
| | | |
| | | .card-header { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .table_list { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .pagination { |
| | | text-align: right; |
| | | margin-top: 20px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="dashboard"> |
| | | <!-- 顶鍿¨ªå两æ --> |
| | | <div class="dashboard-top"> |
| | | <!-- å·¦ï¼ç³»ç»æ¦è§+æ°æ®å¡ç --> |
| | | <div class="top-left"> |
| | | <div class="system-info"> |
| | | <div class="section-title">æ¿å
å管çç³»ç»</div> |
| | | <div style="display: flex; align-items: center; gap: 20px"> |
| | | <div class="system-card"> |
| | | <div class="system-name">æ¿å
å管çç³»ç»</div> |
| | | <div class="system-meta">èµè´¨å®¡æ ¸ · åå管ç · 绩æè¯ä¼°</div> |
| | | </div> |
| | | <div style="display: flex; align-items: center; gap: 8px"> |
| | | <el-icon color="#5053B5" size="22"><Clock /></el-icon> |
| | | <span>å½åæ¶é´ï¼{{ currentTime }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="data-cards"> |
| | | <div class="data-card total"> |
| | | <div class="data-title">æ»æ¿å
åæ°</div> |
| | | <div class="data-value">{{ contractorStats.total }}</div> |
| | | <div class="data-desc"> |
| | | å·²å®¡æ ¸ {{ contractorStats.approved }} | å¾
å®¡æ ¸ |
| | | {{ contractorStats.pending }} |
| | | </div> |
| | | </div> |
| | | <div class="data-card pending"> |
| | | <div class="data-title">å¾
å®¡æ ¸æ¿å
å</div> |
| | | <div class="data-value">{{ contractorStats.pending }}</div> |
| | | <div class="data-desc"> |
| | | A级 {{ contractorStats.aPending }} | B级 |
| | | {{ contractorStats.bPending }} | C级 |
| | | {{ contractorStats.cPending }} |
| | | </div> |
| | | </div> |
| | | <div class="data-card today"> |
| | | <div class="data-title">æ¬ææ°å¢</div> |
| | | <div class="data-value">{{ contractorStats.monthly }}</div> |
| | | <div class="data-desc"> |
| | | 忝 {{ contractorStats.monthly忝 }}% | ç¯æ¯ |
| | | {{ contractorStats.monthlyç¯æ¯ }}% |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- å³ï¼å¾
å¤çæ¥è¦å表 --> |
| | | <div class="alarm-panel"> |
| | | <div class="section-title">å¾
å®¡æ ¸æ¿å
å</div> |
| | | <ul class="alarm-list" v-if="pendingContractors.length > 0"> |
| | | <li v-for="item in pendingContractors" :key="item.id"> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | gap: 10px; |
| | | " |
| | | > |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | " |
| | | > |
| | | <div class="alarm-title">{{ item.name }} - {{ item.type }}</div> |
| | | <el-tag :type="getContractorLevelType(item.level)" |
| | | >{{ item.level }}级</el-tag |
| | | > |
| | | </div> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | " |
| | | > |
| | | <div class="alarm-value"> |
| | | è系人: {{ item.contact }} | æ³¨åæ¶é´: {{ item.registerDate }} |
| | | </div> |
| | | <div class="alarm-time">{{ item.applyDate }}</div> |
| | | </div> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | <div v-else style="text-align: center; color: #909399; padding: 20px"> |
| | | ææ å¾
å®¡æ ¸æ¿å
å |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ä¸é¨æ¨ªå两æ --> |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åå¢é¿è¶å¿</div> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries" |
| | | :tooltip="tooltipLine" |
| | | :xAxis="xAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 300px" |
| | | ></Echarts> |
| | | </div> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åèµè´¨åå¸</div> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | justify-content: space-evenly; |
| | | height: 300px; |
| | | " |
| | | > |
| | | <div style="width: 50%"> |
| | | <Echarts |
| | | ref="chart" |
| | | :legend="pieLegend" |
| | | :chartStyle="chartStylePie" |
| | | :series="levelPieSeries" |
| | | :tooltip="pieTooltip" |
| | | ></Echarts> |
| | | </div> |
| | | <ul class="level-list" style="width: 50%"> |
| | | <li v-for="item in levelPieSeries[0].data" :key="item.name"> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | " |
| | | > |
| | | <div class="line" :style="{ color: item.itemStyle.color }"> |
| | | â{{ item.name }} |
| | | </div> |
| | | <div style="width: 60px">{{ item.value }}å®¶</div> |
| | | <div style="width: 60px">{{ item.rate }}%</div> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- åºé¨æ¨ªå两æ --> |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åç±»ååå¸</div> |
| | | <Echarts |
| | | ref="chart" |
| | | :color="barColors" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :series="equipmentBarSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="equipmentXAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 300px" |
| | | ></Echarts> |
| | | </div> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åå®¡æ ¸æ¶æ</div> |
| | | <Echarts |
| | | ref="chart" |
| | | :color="barColors" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :series="handlingTimeSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="handlingTimeXAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 300px" |
| | | ></Echarts> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="dashboard"> |
| | | <!-- 顶鍿¨ªå两æ --> |
| | | <div class="dashboard-top"> |
| | | <!-- å·¦ï¼ä¼ä¸ä¿¡æ¯+ä¸å¤§æ°æ®å¡çï¼ä¸ä¸æåï¼ --> |
| | | <div class="top-left"> |
| | | <div class="company-info"> |
| | | <div class="section-title">ç»éä¿¡æ¯</div> |
| | | <div style="display: flex;align-items: center;gap: 20px"> |
| | | <img :src="userStore.avatar" class="avatar" alt=""/> |
| | | <div class="company-card"> |
| | | <div class="company-name">{{userStore.name}}</div> |
| | | <div class="company-meta">{{userStore.roleName}}</div> |
| | | </div> |
| | | <div style="display: flex;align-items: center;gap: 8px"> |
| | | <el-icon color="#5053B5" size="22"><Clock /></el-icon> |
| | | <span>ç»éæ¥æï¼{{userStore.currentLoginTime}}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- <div class="data-cards">--> |
| | | <!-- <div class="data-card sales">--> |
| | | <!-- <div class="data-title">é宿°æ®</div>--> |
| | | <!-- <div class="data-num">--> |
| | | <!-- <div>--> |
| | | <!-- <div class="data-desc">æ¬æéå®é¢/å
</div>--> |
| | | <!-- <div class="data-value">{{businessInfo.monthSaleMoney}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- <div>--> |
| | | <!-- <div class="data-desc">æªå¼ç¥¨éé¢/å
</div>--> |
| | | <!-- <div class="data-value">{{businessInfo.monthSaleHaveMoney}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- --> |
| | | <!-- </div>--> |
| | | <!-- <div class="data-card purchase">--> |
| | | <!-- <div class="data-title">éè´æ°æ®</div>--> |
| | | <!-- <div class="data-num">--> |
| | | <!-- <div>--> |
| | | <!-- <div class="data-desc">æ¬æéè´é¢/å
</div>--> |
| | | <!-- <div class="data-value">{{businessInfo.monthPurchaseMoney}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- <div>--> |
| | | <!-- <div class="data-desc">å¾
仿¬¾éé¢/å
</div>--> |
| | | <!-- <div class="data-value">{{businessInfo.monthPurchaseHaveMoney}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="data-card inventory">--> |
| | | <!-- <div class="data-title">åºåæ°æ®</div>--> |
| | | <!-- <div class="data-num">--> |
| | | <!-- <div>--> |
| | | <!-- <div class="data-desc">å½ååºåæ»é/ä»¶</div>--> |
| | | <!-- <div class="data-value">{{businessInfo.inventoryNum}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- <div>--> |
| | | <!-- <div class="data-desc">仿¥å
¥åº/ä»¶</div>--> |
| | | <!-- <div class="data-value">{{businessInfo.todayInventoryNum}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | </div> |
| | | <!-- å³ï¼å¾
åäºé¡¹ --> |
| | | <!-- <div class="todo-panel">--> |
| | | <!-- <div class="section-title">å¾
åäºé¡¹</div>--> |
| | | <!-- <ul class="todo-list" v-if="todoList.length > 0">--> |
| | | <!-- <li v-for="item in todoList" :key="item.id">--> |
| | | <!-- <div style="display: flex;flex-direction: column;justify-content: space-between;width: 100%;gap: 20px">--> |
| | | <!-- <div style="display: flex;justify-content: space-between;align-items: center;">--> |
| | | <!-- <div class="todo-title">æµç¨ç¼å·ï¼{{item.approveId}}</div>--> |
| | | <!-- <div class="todo-division">ç³è¯·é¨é¨ï¼{{item.approveDeptName}}</div>--> |
| | | <!-- <div class="todo-time">{{item.approveTime}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="todo-division">审æ¹äºç±ï¼{{item.approveReason}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- </li>--> |
| | | <!-- </ul>--> |
| | | <!-- <div v-else style="text-align: center">--> |
| | | <!-- ææ æ°æ®--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- --> |
| | | <!-- <!– ä¸é¨æ¨ªåä¸¤æ –>--> |
| | | <!-- <div class="dashboard-row">--> |
| | | <!-- <div class="main-panel">--> |
| | | <!-- <div class="section-title">客æ·ååéé¢åæ</div>--> |
| | | <!-- <div class="contract-summary">--> |
| | | <!-- <div class="contract-info">--> |
| | | <!-- <img src="../assets/images/khtitle.png" alt="" style="width: 42px"/>--> |
| | | <!-- <div class="contract-card">--> |
| | | <!-- <div class="contract-name">æ»ååéé¢(å
)</div>--> |
| | | <!-- <div class="contract-meta">--> |
| | | <!-- <div class="main-amount">{{sum}}</div>--> |
| | | <!-- <div>å¨åæ¯: <span class="up">{{yny}}% </span> æ¥ç¯æ¯: <span class="up">{{chain}}% </span></div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px">--> |
| | | <!-- <div>--> |
| | | <!-- <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie"--> |
| | | <!-- :series="materialPieSeries"--> |
| | | <!-- :tooltip="pieTooltip"></Echarts>--> |
| | | <!-- </div>--> |
| | | <!-- <ul class="contract-list">--> |
| | | <!-- <li v-for="item in materialPieSeries[0].data" :key="item.name">--> |
| | | <!-- <div style="display: flex;align-items: center;justify-content: space-between;width: 100%">--> |
| | | <!-- <div class="line" :style="{color: item.itemStyle.color}">â{{item.name}}</div>--> |
| | | <!-- <div style="width: 70px">{{item.rate}}%</div>--> |
| | | <!-- <div>ï¿¥{{item.value}}</div>--> |
| | | <!-- </div>--> |
| | | <!-- </li>--> |
| | | <!-- </ul>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="main-panel">--> |
| | | <!-- <div style="display: flex;justify-content: space-between;">--> |
| | | <!-- <div class="section-title">åºæ¶åºä»ç»è®¡</div>--> |
| | | <!--<!– <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">–>--> |
| | | <!--<!– <el-radio-button label="æå¨" :value="1" />–>--> |
| | | <!--<!– <el-radio-button label="ææ" :value="2" />–>--> |
| | | <!--<!– <el-radio-button label="æå£åº¦" :value="3" />–>--> |
| | | <!--<!– </el-radio-group>–>--> |
| | | <!-- </div>--> |
| | | <!-- <Echarts ref="chart"--> |
| | | <!-- :color="barColors2"--> |
| | | <!-- :chartStyle="chartStyle"--> |
| | | <!-- :grid="grid"--> |
| | | <!-- :series="barSeries"--> |
| | | <!-- :tooltip="tooltip"--> |
| | | <!-- :xAxis="xAxis"--> |
| | | <!-- :yAxis="yAxis"--> |
| | | <!-- style="height: 260px"></Echarts>--> |
| | | <!-- </div>--> |
| | | <!-- </div>--> |
| | | |
| | | <!-- åºé¨æ¨ªå两æ --> |
| | | <!-- <div class="dashboard-row">--> |
| | | <!-- <div class="main-panel">--> |
| | | <!-- <div class="section-title">è´¨éç»è®¡</div>--> |
| | | <!-- <div class="quality-cards">--> |
| | | <!-- <div class="quality-card one">åææå·²æ£æµæ° <span>{{qualityStatisticsObject.supplierNum}}ä»¶</span></div>--> |
| | | <!-- <div class="quality-card two">è¿ç¨æ£éªæ°é <span>{{qualityStatisticsObject.processNum}}ä»¶</span></div>--> |
| | | <!-- <div class="quality-card three">åºåå·²æ£æ°é <span>{{qualityStatisticsObject.factoryNum}}ä»¶</span></div>--> |
| | | <!-- </div>--> |
| | | <!-- <Echarts ref="chart"--> |
| | | <!-- :chartStyle="chartStyle"--> |
| | | <!-- :grid="grid"--> |
| | | <!-- :legend="barLegend"--> |
| | | <!-- :series="barSeries1"--> |
| | | <!-- :tooltip="tooltip"--> |
| | | <!-- :xAxis="xAxis1"--> |
| | | <!-- :yAxis="yAxis1"--> |
| | | <!-- style="height: 260px"></Echarts>--> |
| | | <!-- </div>--> |
| | | <!-- <div class="main-panel">--> |
| | | <!-- <div class="section-title">忬¾ä¸å¼ç¥¨åæ</div>--> |
| | | <!-- <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries"--> |
| | | <!-- :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 270px;"></Echarts>--> |
| | | <!-- </div>--> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, reactive, computed } from "vue"; |
| | | import { ref, onMounted } from 'vue' |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { Clock } from "@element-plus/icons-vue"; |
| | | import * as echarts from 'echarts'; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { |
| | | analysisCustomerContractAmounts, getAmountHalfYear, |
| | | getBusiness, |
| | | homeTodos, |
| | | qualityStatistics, |
| | | statisticsReceivablePayable |
| | | } from "@/api/viewIndex.js"; |
| | | |
| | | // å½åæ¶é´ |
| | | const currentTime = computed(() => { |
| | | const now = new Date(); |
| | | return now.toLocaleString("zh-CN"); |
| | | }); |
| | | const userStore = useUserStore() |
| | | |
| | | // æ¿å
åç»è®¡æ°æ® |
| | | const contractorStats = reactive({ |
| | | total: 156, |
| | | approved: 132, |
| | | pending: 24, |
| | | aPending: 8, |
| | | bPending: 12, |
| | | cPending: 4, |
| | | monthly: 28, |
| | | monthly忝: "+18.5", |
| | | monthlyç¯æ¯: "+5.2", |
| | | }); |
| | | const businessInfo = ref({ |
| | | inventoryNum: 0, |
| | | monthPurchaseHaveMoney: 0, |
| | | monthPurchaseMoney: 0, |
| | | monthSaleHaveMoney: 0, |
| | | monthSaleMoney: 0, |
| | | todayInventoryNum: 0, |
| | | }) |
| | | const qualityStatisticsObject = ref({ |
| | | supplierNum: 0, |
| | | processNum: 0, |
| | | factoryNum: 0, |
| | | }) |
| | | const sum = ref(0) |
| | | const yny = ref(0) |
| | | const chain = ref(0) |
| | | |
| | | // å¾
å®¡æ ¸æ¿å
åå表 |
| | | const pendingContractors = ref([ |
| | | { |
| | | id: 1, |
| | | name: "建çå·¥ç¨æéå
¬å¸", |
| | | type: "å»ºçæ½å·¥", |
| | | contact: "å¼ ä¸", |
| | | registerDate: "2025-01-15", |
| | | applyDate: "2025-12-16 14:30:23", |
| | | level: "A", |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: "æºçµå®è£
å·¥ç¨å
¬å¸", |
| | | type: "æºçµå®è£
", |
| | | contact: "æå", |
| | | registerDate: "2025-03-20", |
| | | applyDate: "2025-12-16 14:28:15", |
| | | level: "B", |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: "è£
饰è£
修工ç¨å
¬å¸", |
| | | type: "è£
饰è£
ä¿®", |
| | | contact: "çäº", |
| | | registerDate: "2025-05-10", |
| | | applyDate: "2025-12-16 14:22:18", |
| | | level: "B", |
| | | }, |
| | | { |
| | | id: 4, |
| | | name: "绿åå·¥ç¨æéå
¬å¸", |
| | | type: "åæç»¿å", |
| | | contact: "èµµå
", |
| | | registerDate: "2025-08-05", |
| | | applyDate: "2025-12-16 14:18:55", |
| | | level: "C", |
| | | }, |
| | | ]); |
| | | |
| | | // å¾è¡¨æ ·å¼ |
| | | const pieLegend = reactive({ |
| | | show: false, |
| | | }) |
| | | const barSeries = ref([ |
| | | { |
| | | type: 'bar', |
| | | data: [], |
| | | label: { |
| | | show: true, |
| | | } |
| | | }, |
| | | ]) |
| | | const barSeries1 = ref([ |
| | | { |
| | | name: 'åææä¸åæ ¼æ°', |
| | | type: 'bar', |
| | | barGap: 0, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: 'è¿ç¨ä¸åæ ¼æ°', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: 'åºåä¸åæ ¼æ°', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [] |
| | | }, |
| | | ]) |
| | | const chartStyle = { |
| | | width: "100%", |
| | | height: "100%", |
| | | }; |
| | | |
| | | width: '100%', |
| | | height: '100%' // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | } |
| | | const chartStylePie = { |
| | | width: "100%", |
| | | height: "100%", |
| | | }; |
| | | |
| | | width: '140%', |
| | | height: '140%' // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | } |
| | | const grid = { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }; |
| | | |
| | | // æ¿å
åå¢é¿è¶å¿ - æçº¿å¾ |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | } |
| | | const barLegend = { |
| | | show: true, |
| | | data: ['åææä¸åæ ¼æ°', 'è¿ç¨ä¸åæ ¼æ°', 'åºåä¸åæ ¼æ°'] |
| | | } |
| | | const barLegend1 = { |
| | | show: true, |
| | | data: ['é¢ä»è´¦æ¬¾', 'åºä»è´¦æ¬¾', '颿¶è´¦æ¬¾', 'åºæ¶è´¦æ¬¾'] |
| | | } |
| | | const lineLegend = { |
| | | show: true, |
| | | data: ["A级", "B级", "C级"], |
| | | }; |
| | | |
| | | const tooltipLine = { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "cross", |
| | | }, |
| | | }; |
| | | |
| | | const xAxis = ref({ |
| | | type: "category", |
| | | data: ["12-01", "12-02", "12-03", "12-04", "12-05", "12-06", "12-07"], |
| | | }); |
| | | |
| | | const yAxis = ref({ |
| | | type: "value", |
| | | name: "æ¿å
åæ°é", |
| | | }); |
| | | |
| | | const lineSeries = ref([ |
| | | { |
| | | name: "A级", |
| | | type: "line", |
| | | data: [15, 18, 20, 22, 25, 28, 30], |
| | | itemStyle: { |
| | | color: "#f56c6c", |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | { |
| | | name: "B级", |
| | | type: "line", |
| | | data: [45, 52, 58, 65, 70, 75, 80], |
| | | itemStyle: { |
| | | color: "#e6a23c", |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | { |
| | | name: "C级", |
| | | type: "line", |
| | | data: [30, 35, 40, 45, 50, 55, 60], |
| | | itemStyle: { |
| | | color: "#67c23a", |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | ]); |
| | | |
| | | // æ¿å
åèµè´¨åå¸ - é¥¼å¾ |
| | | const pieLegend = { |
| | | show: false, |
| | | }; |
| | | |
| | | const pieTooltip = { |
| | | trigger: "item", |
| | | formatter: "{b}: {c}å®¶ ({d}%)", |
| | | }; |
| | | |
| | | const levelPieSeries = ref([ |
| | | { |
| | | type: "pie", |
| | | radius: ["60%", "80%"], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: "#fff", |
| | | borderWidth: 2, |
| | | }, |
| | | label: { |
| | | show: false, |
| | | }, |
| | | data: [ |
| | | { |
| | | name: "A级", |
| | | value: 45, |
| | | rate: 28.85, |
| | | itemStyle: { color: "#f56c6c" }, |
| | | }, |
| | | { |
| | | name: "B级", |
| | | value: 70, |
| | | rate: 44.87, |
| | | itemStyle: { color: "#e6a23c" }, |
| | | }, |
| | | { |
| | | name: "C级", |
| | | value: 41, |
| | | rate: 26.28, |
| | | itemStyle: { color: "#67c23a" }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | // æ¿å
åç±»ååå¸ - æ±ç¶å¾ |
| | | const barColors = ["#409eff", "#67c23a", "#e6a23c", "#f56c6c", "#909399"]; |
| | | |
| | | show: true, |
| | | data: ['å¼ç¥¨', '忬¾'] |
| | | } |
| | | const tooltip = { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | }; |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | } |
| | | const xAxis = [{ |
| | | type: 'value', |
| | | }] |
| | | const xAxis1 = ref([{ |
| | | type: 'category', |
| | | axisTick: { show: false }, |
| | | data: [] |
| | | }]) |
| | | const yAxis = [{ |
| | | type: 'category', |
| | | data: [ 'åºä»è´¦æ¬¾', 'åºæ¶è´¦æ¬¾',] |
| | | }] |
| | | const yAxis1 = [{ |
| | | type: 'value' |
| | | }] |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function (params) { |
| | | // å¨æçææç¤ºä¿¡æ¯ï¼åºäºæ°æ®é¡¹ç name 屿§ |
| | | const description = params.name === 'æ¬æåæ¬¾éé¢' ? 'æ¬æåæ¬¾éé¢' : 'åºæ¶æ¬¾éé¢'; |
| | | return `${description} ${formatNumber(params.value)}å
${params.percent}%`; |
| | | }, |
| | | position: 'right' |
| | | }) |
| | | const materialPieSeries = ref([ |
| | | { |
| | | type: 'pie', |
| | | radius: ['66%', '90%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: [] |
| | | } |
| | | ]) |
| | | const lineSeries = ref([ |
| | | { |
| | | type: 'line', |
| | | data: [], |
| | | label: { |
| | | show: true |
| | | }, |
| | | showSymbol: true, // æ¾ç¤ºåç¹ |
| | | }, |
| | | ]) |
| | | const tooltipLine = { |
| | | trigger: 'axis', |
| | | } |
| | | const yAxis2 = ref([ |
| | | { |
| | | type: 'value', |
| | | } |
| | | ]) |
| | | const xAxis2 = ref([ |
| | | { |
| | | type: 'category', |
| | | data: [], |
| | | axisLabel: { |
| | | interval: 0, |
| | | formatter: function(value) { |
| | | return value.replace(/~/g, '\n'); |
| | | }, |
| | | } |
| | | } |
| | | ]) |
| | | |
| | | const equipmentXAxis = ref({ |
| | | type: "category", |
| | | data: ["å»ºçæ½å·¥", "æºçµå®è£
", "è£
饰è£
ä¿®", "åæç»¿å", "å
¶ä»"], |
| | | }); |
| | | // å¾
åäºé¡¹ |
| | | const todoList = ref([]) |
| | | const radio1 = ref(1) |
| | | |
| | | const equipmentBarSeries = ref([ |
| | | { |
| | | name: "æ¿å
åæ°é", |
| | | type: "bar", |
| | | data: [45, 35, 25, 20, 31], |
| | | itemStyle: { |
| | | color: function (params) { |
| | | return barColors[params.dataIndex % barColors.length]; |
| | | }, |
| | | }, |
| | | label: { |
| | | show: true, |
| | | position: "top", |
| | | }, |
| | | }, |
| | | ]); |
| | | // å¾è¡¨å¼ç¨ |
| | | const barChart = ref(null) |
| | | const lineChart = ref(null) |
| | | const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8'] |
| | | |
| | | // æ¿å
åå®¡æ ¸æ¶æåæ - æ±ç¶å¾ |
| | | const handlingTimeXAxis = ref({ |
| | | type: "category", |
| | | data: ["0-1天", "1-3天", "3-7天", "7天以ä¸"], |
| | | }); |
| | | |
| | | const handlingTimeSeries = ref([ |
| | | { |
| | | name: "å®¡æ ¸æ°é", |
| | | type: "bar", |
| | | data: [85, 45, 20, 10], |
| | | itemStyle: { |
| | | color: function (params) { |
| | | return barColors[params.dataIndex % barColors.length]; |
| | | }, |
| | | }, |
| | | label: { |
| | | show: true, |
| | | position: "top", |
| | | }, |
| | | }, |
| | | ]); |
| | | |
| | | // è·åæ¿å
åçº§å«æ ·å¼ |
| | | const getContractorLevelType = (level) => { |
| | | switch (level) { |
| | | case "A": |
| | | return "danger"; |
| | | case "B": |
| | | return "warning"; |
| | | case "C": |
| | | return "info"; |
| | | default: |
| | | return "info"; |
| | | } |
| | | }; |
| | | // éæºé¢è²çæå½æ° |
| | | const getRandomColor = () => { |
| | | return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0'); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½å®æåçåå§åæä½ |
| | | }); |
| | | getBusinessData() |
| | | analysisCustomer() |
| | | todoInfoS() |
| | | statisticsReceivable() |
| | | qualityStatisticsInfo() |
| | | getAmountHalfYearNum() |
| | | }) |
| | | // æ°æ®ç»è®¡ |
| | | const getBusinessData = () => { |
| | | getBusiness().then((res) => { |
| | | businessInfo.value = {...res.data} |
| | | }) |
| | | } |
| | | // ååéé¢ |
| | | const analysisCustomer = () => { |
| | | analysisCustomerContractAmounts().then((res) => { |
| | | sum.value = res.data.sum |
| | | yny.value = res.data.yny |
| | | chain.value = res.data.chain |
| | | // 为æ¯ä¸ªæ°æ®é¡¹åé
éæºé¢è² |
| | | materialPieSeries.value[0].data = res.data.item.map(item => ({ |
| | | ...item, |
| | | itemStyle: { color: getRandomColor() } |
| | | })) |
| | | }) |
| | | } |
| | | // å¾
åäºé¡¹ |
| | | const todoInfoS = () => { |
| | | homeTodos().then((res) => { |
| | | todoList.value = res.data |
| | | }) |
| | | } |
| | | // åºä»åºæ¶ç»è®¡ |
| | | const statisticsReceivable = (type) => { |
| | | console.log(type) |
| | | statisticsReceivablePayable({type: radio1.value}).then((res) => { |
| | | barSeries.value[0].data = [ |
| | | // { value: res.data.prepayMoney, itemStyle: { color: barColors2[0] } }, |
| | | { value: res.data.payableMoney, itemStyle: { color: barColors2[0] } }, |
| | | // { value: res.data.advanceMoney, itemStyle: { color: barColors2[2] } }, |
| | | { value: res.data.receivableMoney, itemStyle: { color: barColors2[1] } } |
| | | ] |
| | | }) |
| | | } |
| | | // è´¨æ£ç»è®¡ |
| | | const qualityStatisticsInfo = () => { |
| | | qualityStatistics().then((res) => { |
| | | res.data.item.forEach(item => { |
| | | xAxis1.value[0].data.push(item.date) |
| | | barSeries1.value[0].data.push(item.supplierNum) |
| | | barSeries1.value[1].data.push(item.processNum) |
| | | barSeries1.value[2].data.push(item.factoryNum) |
| | | }) |
| | | qualityStatisticsObject.value.supplierNum = res.data.supplierNum |
| | | qualityStatisticsObject.value.processNum = res.data.processNum |
| | | qualityStatisticsObject.value.factoryNum = res.data.factoryNum |
| | | }) |
| | | } |
| | | const getAmountHalfYearNum = async () => { |
| | | const res = await getAmountHalfYear() |
| | | console.log(res) |
| | | const monthName = [] |
| | | const receiptAmount = [] |
| | | const invoiceAmount = [] |
| | | res.data.forEach(item => { |
| | | monthName.push(item.month) |
| | | receiptAmount.push(item.receiptAmount) |
| | | invoiceAmount.push(item.invoiceAmount) |
| | | }) |
| | | // æ£ç¡®ååºå¼èµå¼ï¼å建æ°ç xAxis å series 对象 |
| | | xAxis2.value[0].data = monthName |
| | | xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~')); |
| | | lineSeries.value = [ |
| | | { |
| | | name: 'å¼ç¥¨', |
| | | type: 'line', |
| | | data: receiptAmount, |
| | | stack: 'Total', |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(131, 207, 255, 1)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(186, 228, 255, 1)' |
| | | } |
| | | ]) |
| | | }, |
| | | itemStyle: { |
| | | color: '#2D99FF', |
| | | borderColor: '#2D99FF' |
| | | }, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | lineStyle: { |
| | | width: 0 |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | { |
| | | name: '忬¾', |
| | | type: 'line', |
| | | data: invoiceAmount, |
| | | stack: 'Total', |
| | | lineStyle: { |
| | | width: 0 |
| | | }, |
| | | itemStyle: { |
| | | color: '#83CFFF', |
| | | borderColor: '#83CFFF' |
| | | }, |
| | | showSymbol: true, |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(54, 153, 255, 1)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(89, 169, 254, 1)' |
| | | } |
| | | ]) |
| | | }, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | } |
| | | ] |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dashboard { |
| | | background: #f5f7fa; |
| | | min-height: 100vh; |
| | | padding: 20px; |
| | | box-sizing: border-box; |
| | | background: #f5f7fa; |
| | | min-height: 100vh; |
| | | padding: 20px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .dashboard-top { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | | .company-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | padding: 20px; |
| | | min-width: 0; |
| | | background-color: #EFF2FB; /* ä½¿ç¨æå®çèæ¯é¢è² */ |
| | | background-image: url("../assets/images/denglu.png"); |
| | | background-size: 100% 260%; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | border-radius: 12px; |
| | | height: 138px; |
| | | } |
| | | .avatar { |
| | | width: 60px; |
| | | height: 60px; |
| | | border-radius: 50%; |
| | | object-fit: contain; |
| | | background: #fff; |
| | | border: 1px solid #eee; |
| | | } |
| | | .company-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | position: relative; |
| | | padding-right: 15px; |
| | | } |
| | | |
| | | .system-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | padding: 20px; |
| | | min-width: 0; |
| | | background-color: #eff2fb; |
| | | border-radius: 12px; |
| | | height: 138px; |
| | | .company-card::after { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | width: 1px; |
| | | background-color: #C9C5C5; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .system-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | position: relative; |
| | | padding-right: 15px; |
| | | .company-name { |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #161A9A; |
| | | } |
| | | |
| | | .system-name { |
| | | font-weight: 600; |
| | | font-size: 18px; |
| | | color: #161a9a; |
| | | .company-meta { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #818185; |
| | | } |
| | | |
| | | .system-meta { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #818185; |
| | | } |
| | | |
| | | .data-cards { |
| | | display: flex; |
| | | gap: 16px; |
| | | justify-content: flex-start; |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | display: flex; |
| | | gap: 16px; |
| | | justify-content: flex-start; |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .data-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | min-width: 160px; |
| | | box-shadow: 0 2px 8px #eee; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 32%; |
| | | height: 140px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .data-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .data-card.total { |
| | | background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | .data-card.pending { |
| | | background: linear-gradient(135deg, #f56c6c 0%, #f78989 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | .data-card.today { |
| | | background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | .data-title { |
| | | font-weight: 600; |
| | | font-size: 16px; |
| | | margin-bottom: 12px; |
| | | font-weight: 700; |
| | | font-size: 26px; |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | margin-bottom: 8px; |
| | | .data-num { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .data-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 14px 10px 10px 10px; |
| | | min-width: 160px; |
| | | box-shadow: 0 2px 8px #eee; |
| | | display: flex; |
| | | flex-direction: column; |
| | | width: 32%; |
| | | height: 140px; |
| | | } |
| | | .data-card.sales { |
| | | background-image: url("../assets/images/xioashoushuju.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | .data-card.purchase { |
| | | background-image: url("../assets/images/caigou.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | .data-card.inventory { |
| | | background-image: url("../assets/images/kucun.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | .data-desc { |
| | | font-size: 12px; |
| | | opacity: 0.9; |
| | | font-weight: 500; |
| | | font-size: 13px; |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 18px; |
| | | font-weight: 500; |
| | | margin: 10px 0; |
| | | color: #FFFFFF; |
| | | } |
| | | .top-left { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | width: 60%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | width: 50%; |
| | | } |
| | | |
| | | .alarm-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | width: 40%; |
| | | .todo-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | width: 50%; |
| | | } |
| | | |
| | | .alarm-list { |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | font-size: 14px; |
| | | overflow-y: auto; |
| | | height: 260px; |
| | | .todo-list { |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | font-size: 15px; |
| | | overflow-y: auto; |
| | | height: 260px; |
| | | } |
| | | |
| | | .alarm-list li { |
| | | border-radius: 8px; |
| | | margin-bottom: 12px; |
| | | padding: 12px 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background: #f8f9fa; |
| | | border-left: 4px solid #409eff; |
| | | transition: all 0.3s ease; |
| | | .todo-list li { |
| | | border-radius: 8px; |
| | | margin-bottom: 12px; |
| | | padding: 8px 20px; |
| | | height: 74px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background: rgba(225,227,250,0.62); |
| | | } |
| | | |
| | | .alarm-list li:hover { |
| | | background: #e9ecef; |
| | | transform: translateX(4px); |
| | | .todo-title { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #000000; |
| | | position: relative; |
| | | } |
| | | |
| | | .alarm-title { |
| | | font-weight: 600; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | .todo-title::before { |
| | | content: ''; /* å¿
éï¼è¡¨ç¤ºè¿éæä¸ä¸ªå
容 */ |
| | | position: absolute; |
| | | left: -10px; /* å®ä½å°å·¦ä¾§ */ |
| | | top: 50%; /* åç´å±
ä¸ */ |
| | | transform: translateY(-50%); /* å¾®è°åç´å±
ä¸ */ |
| | | width: 6px; /* åçç´å¾ */ |
| | | height: 6px; /* åçç´å¾ */ |
| | | background: #498CEB; |
| | | border-radius: 50%; /* 让å
¶åæåå½¢ */ |
| | | } |
| | | |
| | | .alarm-value { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | .todo-division { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #000000; |
| | | } |
| | | |
| | | .alarm-time { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | .todo-time { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #000000; |
| | | } |
| | | |
| | | .todo-meta { |
| | | color: #888; |
| | | font-size: 13px; |
| | | } |
| | | .dashboard-row { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .main-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .section-title { |
| | | position: relative; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 10px; |
| | | margin-bottom: 20px; |
| | | font-weight: 700; |
| | | position: relative; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 10px; |
| | | margin-bottom: 10px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .section-title::before { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 4px; |
| | | content: ""; |
| | | width: 4px; |
| | | height: 18px; |
| | | background-color: #409eff; |
| | | border-radius: 2px; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 4px; |
| | | content: ''; |
| | | width: 4px; |
| | | height: 18px; |
| | | background-color: #002FA7; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .level-list { |
| | | margin: 0; |
| | | padding: 0; |
| | | list-style: none; |
| | | height: 200px; |
| | | overflow-y: auto; |
| | | width: 100%; |
| | | .contract-info { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | height: 90px; |
| | | background: rgba(245,245,245,0.59); |
| | | width: 100%; |
| | | border-radius: 10px; |
| | | padding: 10px 30px; |
| | | } |
| | | |
| | | .level-list li { |
| | | margin-bottom: 15px; |
| | | padding: 10px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | transition: all 0.3s ease; |
| | | .contract-summary { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 30px; |
| | | } |
| | | |
| | | .level-list li:hover { |
| | | background: #e9ecef; |
| | | .contract-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .contract-name { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #050505; |
| | | } |
| | | .contract-meta { |
| | | display: flex; |
| | | align-items: center; |
| | | width: 100%; |
| | | gap: 80px; |
| | | } |
| | | .main-amount { |
| | | font-size: 24px; |
| | | color: rgba(51,50,50,0.85); |
| | | } |
| | | .up { color: #e57373; } |
| | | .contract-list { |
| | | margin-top: 16px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | list-style: none; |
| | | padding: 0; |
| | | height: 190px; |
| | | overflow-y: auto; |
| | | width: 460px; |
| | | } |
| | | .line { |
| | | position: relative; |
| | | width: 80px; |
| | | font-weight: 500; |
| | | position: relative; |
| | | width: 230px; |
| | | } |
| | | </style> |
| | | .line::after { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 2px; |
| | | top: 0; |
| | | bottom: 0; |
| | | width: 1px; |
| | | background-color: #C9C5C5; |
| | | border-radius: 2px; |
| | | } |
| | | .contract-list li { |
| | | margin-top: 10px; |
| | | } |
| | | .quality-cards { |
| | | display: flex; |
| | | gap: 12px; |
| | | margin-bottom: 12px; |
| | | } |
| | | .quality-card { |
| | | border-radius: 8px; |
| | | padding: 15px 10px 10px 50px; |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: rgba(0,0,0,0.67); |
| | | width: 236px; |
| | | height: 49px; |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | .quality-card.one { |
| | | background-image: url("../assets/images/yuancailiao.png"); |
| | | } |
| | | .quality-card.two { |
| | | background-image: url("../assets/images/guocheng.png"); |
| | | } |
| | | .quality-card.three { |
| | | background-image: url("../assets/images/chuchang.png"); |
| | | |
| | | } |
| | | .quality-card span { |
| | | color: #4fc3f7; |
| | | font-weight: bold; |
| | | margin-left: 6px; |
| | | } |
| | | .chart { |
| | | width: 100%; |
| | | height: 220px; |
| | | margin-top: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">åææ¥æï¼</span> |
| | | <el-date-picker |
| | | v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | @change="handleQuery" |
| | | /> |
| | | <span class="search_title ml10">产å大类ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.productCategory" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | style="width: 100%" |
| | | :summary-method="summarizeMainTable" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="åææ¥æ" prop="createTime" show-overflow-tooltip width="130"/> |
| | | <el-table-column label="æ¹æ¬¡å·" prop="code" width="130" show-overflow-tooltip /> |
| | | <el-table-column label="产å大类" prop="productCategory" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" show-overflow-tooltip /> |
| | | <el-table-column label="ç©åç±»å" prop="itemType" show-overflow-tooltip /> |
| | | <el-table-column label="åææ°é" prop="inboundNum" show-overflow-tooltip /> |
| | | <el-table-column label="åä»·(å
)" prop="taxInclusiveUnitPrice" width="150"></el-table-column> |
| | | <el-table-column label="æ»ä»·(å
)" prop="taxInclusiveTotalPrice" width="150"></el-table-column> |
| | | <el-table-column label="åæäºº" prop="createBy" show-overflow-tooltip /> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | delStockOut, |
| | | } from "@/api/inventoryManagement/stockOut.js"; |
| | | import { |
| | | getStockInPageByCustom, |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | |
| | | // æ¥è¯¢è¡¨åæ°æ® |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", |
| | | customerName: "", |
| | | productCategory:'', |
| | | timeStr: getCurrentDate(), |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...page } |
| | | params.supplierName = searchForm.value.supplierName |
| | | params.timeStr = searchForm.value.timeStr |
| | | params.productCategory = searchForm.value.productCategory |
| | | |
| | | // ææåºåºï¼è°ç¨èªå®ä¹åºåºè®°å½æ¥å£ |
| | | const apiCall = getStockInPageByCustom(params) |
| | | |
| | | apiCall |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value.map((item) => { |
| | | item.children = []; |
| | | // åç«¯è®¡ç®æ»ä»· |
| | | const inboundNum = Number(item.inboundNum) || 0; |
| | | // ææåºåºï¼æ»ä»· = taxInclusiveUnitPrice à inboundNum |
| | | const taxInclusiveUnitPrice = Number(item.taxInclusiveUnitPrice) || 0; |
| | | item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * inboundNum).toFixed(2); |
| | | }); |
| | | total.value = res.data.total; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter((item) => item.id); |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "contractAmount", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | // æ ¹æ®ä¸åç tab ç±»åè°ç¨ä¸åçå¯¼åºæ¥å£ |
| | | const exportUrl = "/stockmanagement/exportTwo" |
| | | proxy.download(exportUrl, {}, "åºåºå°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delStockOut({ids:ids}).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .print-preview-dialog { |
| | | .el-dialog__body { |
| | | padding: 0; |
| | | max-height: 80vh; |
| | | overflow-y: auto; |
| | | } |
| | | } |
| | | |
| | | .print-preview-container { |
| | | .print-preview-header { |
| | | padding: 15px; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | text-align: center; |
| | | |
| | | .el-button { |
| | | margin: 0 10px; |
| | | } |
| | | } |
| | | |
| | | .print-preview-content { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 400px; |
| | | } |
| | | } |
| | | |
| | | .print-page { |
| | | width: 220mm; |
| | | height: 90mm; |
| | | padding: 10mm; |
| | | margin: 0 auto; |
| | | background: white; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | margin-bottom: 10px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .delivery-note { |
| | | width: 100%; |
| | | height: 100%; |
| | | font-family: "SimSun", serif; |
| | | font-size: 10px; |
| | | line-height: 1.2; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .header { |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | |
| | | .company-name { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .document-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .info-section { |
| | | margin-bottom: 8px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .info-row { |
| | | line-height: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 60px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table-section { |
| | | margin-bottom: 4px; |
| | | flex: 1; |
| | | |
| | | .product-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | border: 1px solid #000; |
| | | |
| | | th, td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | | text-align: center; |
| | | font-size: 14px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | th { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-label { |
| | | text-align: right; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .total-value { |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .footer-section { |
| | | .footer-row { |
| | | display: flex; |
| | | margin-bottom: 3px; |
| | | line-height: 20px; |
| | | justify-content: space-between; |
| | | |
| | | .footer-item { |
| | | display: flex; |
| | | margin-right: 20px; |
| | | |
| | | .label { |
| | | font-weight: bold; |
| | | width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | &.address-item { |
| | | .address-value { |
| | | min-width: 200px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media print { |
| | | .app-container { |
| | | display: none; |
| | | } |
| | | |
| | | .print-page { |
| | | box-shadow: none; |
| | | margin: 0; |
| | | padding: 10mm; |
| | | padding-left: 20mm; |
| | | page-break-inside: avoid; |
| | | page-break-after: always; |
| | | } |
| | | .print-page:last-child { |
| | | page-break-after: avoid; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">åæ¾å£åº¦ï¼</span> |
| | | <el-select |
| | | style="width: 200px;" |
| | | @change="handleQuery" |
| | | v-model="searchForm.season" |
| | | placeholder="è¯·éæ©" |
| | | :clearable="false" |
| | | > |
| | | <el-option :label="item.label" :value="item.value" v-for="(item,index) in jidu" :key="item.value" /> |
| | | </el-select> |
| | | <span class="search_title ml10">åå·¥åç§°ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus"> æ°å¢ </el-button> |
| | | <el-button @click="handleOut" icon="download">导åº</el-button> |
| | | <el-button |
| | | type="danger" |
| | | icon="Delete" |
| | | :disabled="multipleList.length <= 0" |
| | | @click="deleteRow(multipleList.map((item) => item.id))" |
| | | > |
| | | æ¹éå é¤ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table |
| | | ref="tableRef" |
| | | v-loading="tableLoading" |
| | | :data="tableData" |
| | | border |
| | | height="calc(100vh - 21em)" |
| | | :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" |
| | | style="width: 100%" |
| | | @selection-change="handleSelectionChange" |
| | | > |
| | | <!-- éæ©å --> |
| | | <el-table-column |
| | | align="center" |
| | | type="selection" |
| | | width="55" |
| | | fixed="left" |
| | | /> |
| | | |
| | | <!-- åºå·å --> |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | fixed="left" |
| | | /> |
| | | |
| | | <!-- åºå®åï¼å§å --> |
| | | <el-table-column |
| | | label="å§å" |
| | | prop="staffName" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | align="center" |
| | | fixed="left" |
| | | /> |
| | | |
| | | <!-- åºå®åï¼å·¥å· --> |
| | | <el-table-column |
| | | label="å·¥å·" |
| | | prop="staffNo" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | align="center" |
| | | fixed="left" |
| | | /> |
| | | |
| | | <!-- 卿åï¼æ ¹æ®åå
¸æ¸²æ --> |
| | | <el-table-column |
| | | v-for="(dictItem, index) in sys_lavor_issue" |
| | | :key="dictItem.value" |
| | | :label="dictItem.label" |
| | | :prop="dictItem.value" |
| | | show-overflow-tooltip |
| | | > |
| | | </el-table-column> |
| | | |
| | | <!-- æä½å --> |
| | | <el-table-column |
| | | label="æä½" |
| | | width="150" |
| | | align="center" |
| | | fixed="right" |
| | | > |
| | | <template #default="scope"> |
| | | <el-button |
| | | type="primary" |
| | | link |
| | | size="small" |
| | | @click="edit(scope.row)" |
| | | > |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | link |
| | | size="small" |
| | | :disabled="!!scope.row.adoptedDate" |
| | | @click="adopted(scope.row)" |
| | | > |
| | | é¢ç¨ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination :total="total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | </div> |
| | | <!-- <Modal ref="modalRef" @success="handleQuery"></Modal> --> |
| | | <!-- <files-dia ref="filesDia"></files-dia> --> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, reactive, toRefs, nextTick, getCurrentInstance } from 'vue' |
| | | import dayjs from "dayjs"; |
| | | // import Modal from "./Modal.vue"; |
| | | // import FilesDia from "./filesDia.vue"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import {listPage, deleteLedger, update} from "@/api/lavorissce/ledger.js"; |
| | | import {ElMessageBox, ElMessage} from "element-plus"; |
| | | const { proxy } = getCurrentInstance(); |
| | | import { getCurrentMonth } from "@/utils/util" |
| | | |
| | | const page = ref({ |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | const total = ref(0) |
| | | // ååºå¼æ°æ® |
| | | const tableRef = ref(null) |
| | | const tableData = ref([]) |
| | | const tableLoading = ref(false) |
| | | const { sys_lavor_issue } = proxy.useDict("sys_lavor_issue") |
| | | const data = reactive({ |
| | | searchForm: { |
| | | season: "", |
| | | staffName: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | const modalRef = ref(); |
| | | // const filesDia = ref(); |
| | | const multipleList = ref([]); |
| | | const jidu = ref([ |
| | | { |
| | | value: '1', |
| | | label: '第ä¸å£åº¦' |
| | | }, |
| | | { |
| | | value: '2', |
| | | label: '第äºå£åº¦' |
| | | }, |
| | | { |
| | | value: '3', |
| | | label: '第ä¸å£åº¦' |
| | | }, |
| | | { |
| | | value: '4', |
| | | label: '第åå£åº¦' |
| | | } |
| | | ]) |
| | | |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.value.current = 1; |
| | | getList(); |
| | | }; |
| | | // è·ååå
¸æ°æ® |
| | | const getList = async () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page.value }; |
| | | listPage(params).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | const add = () => { |
| | | modalRef.value.openModal(); |
| | | }; |
| | | const edit = (row) => { |
| | | modalRef.value.loadForm(row); |
| | | }; |
| | | const deleteRow = (id) => { |
| | | ElMessageBox.confirm("æ¤æä½å°æ°¸ä¹
å é¤è¯¥æ°æ®, æ¯å¦ç»§ç»?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(async () => { |
| | | const { code } = await deleteLedger(id); |
| | | if (code == 200) { |
| | | ElMessage({ |
| | | type: "success", |
| | | message: "å 餿å", |
| | | }); |
| | | await getList(); |
| | | } |
| | | }); |
| | | }; |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download(`/lavorIssue/exportCopy`, {season: searchForm.value.season}, "å³ä¿å°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info("已忶"); |
| | | }); |
| | | }; |
| | | const adopted = (row) => { |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤é¢ç¨?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(async () => { |
| | | const params = { |
| | | id: row.id, |
| | | adoptedDate: dayjs().format("YYYY-MM-DD") |
| | | } |
| | | const { code } = await update(params); |
| | | if (code == 200) { |
| | | ElMessage({ |
| | | type: "success", |
| | | message: "é¢ç¨æå", |
| | | }); |
| | | await getList(); |
| | | } |
| | | }) |
| | | } |
| | | // æå¼éä»¶å¼¹æ¡ |
| | | // const openFilesFormDia = (row) => { |
| | | // nextTick(() => { |
| | | // filesDia.value?.openDialog( row,'æ¶å
¥') |
| | | // }) |
| | | // }; |
| | | // äºä»¶å¤ç彿° |
| | | const handleSelectionChange = (selection) => { |
| | | multipleList.value = selection; |
| | | } |
| | | |
| | | const paginationChange = (pagination) => { |
| | | page.value.current = pagination.page; |
| | | page.value.size = pagination.limit; |
| | | getList(); |
| | | } |
| | | |
| | | // ç»ä»¶æè½½æ¶å è½½åå
¸æ°æ® |
| | | onMounted(() => { |
| | | handleQuery() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dynamic-table-container { |
| | | width: 100%; |
| | | } |
| | | |
| | | .pagination-container { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | :deep(.el-table .el-table__header-wrapper th) { |
| | | background-color: #F0F1F5 !important; |
| | | color: #333333; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | :deep(.el-table .el-table__body-wrapper td) { |
| | | padding: 8px 0; |
| | | } |
| | | |
| | | :deep(.el-select) { |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.el-input) { |
| | | width: 100%; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">产å大类ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.productCategory" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="row => row.id" |
| | | show-summary |
| | | style="width: 100%" |
| | | :summary-method="summarizeMainTable" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="æ¹æ¬¡å·" prop="code" width="130" show-overflow-tooltip /> |
| | | <el-table-column label="产å大类" prop="productCategory" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" width="70" show-overflow-tooltip /> |
| | | <el-table-column label="ç©åç±»å" prop="itemType" show-overflow-tooltip /> |
| | | <el-table-column label="å©ä½åºå" prop="inboundNum0" width="90" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="æä½" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openForm(scope.row);">åæ</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="40%" @close="closeDia" draggable> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <div>{{getAvailableQuantityText()}}:{{currentRowNum}}</div> |
| | | <el-form-item :label="getQuantityLabel()" prop="salesContractNo"> |
| | | <el-input-number :step="0.01" :min="0" :max="currentRowNum" style="width: 100%" v-model="form.inboundQuantity" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | <el-form-item :label="getDateLabel()" prop="projectName"> |
| | | <el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | <el-form-item :label="getPersonLabel()" prop="entryPerson"> |
| | | <el-select v-model="form.nickName" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" placeholder="è¯·éæ©" clearable> |
| | | <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from '@/store/modules/user' |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { |
| | | getInPageByCustom |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import { |
| | | delStockManage, |
| | | stockOut, |
| | | } from "@/api/inventoryManagement/stockManage.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const userStore = useUserStore() |
| | | const { proxy } = getCurrentInstance() |
| | | const tableData = ref([]) |
| | | const selectedRows = ref([]) |
| | | const userList = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | const total = ref(0) |
| | | const fileList = ref([]) |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const dialogFormVisible = ref(false) |
| | | const data = reactive({ |
| | | searchForm: { |
| | | inboundQuantity:'', |
| | | inboundTime:'', |
| | | nickName: '', |
| | | userId: '', |
| | | productCategory:'', |
| | | // timeStr: getCurrentDate(), |
| | | }, |
| | | form: { |
| | | productrecordId: '', |
| | | }, |
| | | rules: { |
| | | inboundTime: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | inboundQuantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | nickname: [{ required: true, message: "è¯·éæ©", trigger: "change" }] |
| | | } |
| | | }) |
| | | const { searchForm, form, rules } = toRefs(data) |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getList() |
| | | } |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList() |
| | | } |
| | | const getList = () => { |
| | | tableLoading.value = true |
| | | const params = { ...page } |
| | | params.productCategory = searchForm.value.productCategory |
| | | params.flag = true |
| | | const apiCall = getInPageByCustom(params) |
| | | apiCall.then(res => { |
| | | tableLoading.value = false |
| | | tableData.value = res.data.records |
| | | total.value = res.data.total |
| | | }).catch(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode.label; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | console.log('selection', selectedRows.value) |
| | | } |
| | | const expandedRowKeys = ref([]) |
| | | |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']); |
| | | }; |
| | | const currentRowId = ref(null) // æ°å¢ï¼åå¨å½åæä½çè¡ID |
| | | |
| | | const currentRowNum = ref(0) |
| | | const salesLedgerProductId = ref(null); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (row) => { |
| | | dialogFormVisible.value = true |
| | | currentRowId.value = row.id |
| | | currentRowNum.value = row.inboundNum0 |
| | | salesLedgerProductId.value = row.salesLedgerProductId |
| | | form.value = {} |
| | | |
| | | // å è½½ç¨æ·å表 |
| | | try { |
| | | const userLists = await userListNoPageByTenantId() |
| | | userList.value = userLists.data |
| | | |
| | | // ææåºåºé»è®¤åæäºº |
| | | const defaultPersonName = 'å´çæ¢
' |
| | | const defaultPerson = userList.value.find(user => user.nickName === defaultPersonName) |
| | | const defaultUserId = defaultPerson ? defaultPerson.userId : '' |
| | | |
| | | // åå§åè¡¨åæ°æ® |
| | | form.value = { |
| | | productrecordId: '', |
| | | inboundQuantity: currentRowNum.value, // 使ç¨currentRowNumä½ä¸ºé»è®¤å¼ |
| | | inboundTime: getCurrentDate(), // é»è®¤å½åæ¥æ |
| | | nickName: defaultUserId, // æ ¹æ®tabç±»å设置é»è®¤å货人 |
| | | } |
| | | console.log('form',form.value) |
| | | } catch (error) { |
| | | console.error('å è½½ç¨æ·å表失败:', error) |
| | | // 妿å 载失败ï¼ä½¿ç¨ç©ºå¼åå§å |
| | | form.value = { |
| | | productrecordId: '', |
| | | inboundQuantity: currentRowNum.value, |
| | | inboundTime: getCurrentDate(), |
| | | nickName: '', |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | let num = Number(form.value.inboundQuantity) |
| | | if(num <= 0 || num > currentRowNum.value){ |
| | | return proxy.$modal.msgWarning("请填å
¥æææ°å") |
| | | } |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid && currentRowId.value) { |
| | | const outData = { |
| | | id: currentRowId.value, // åå§è®°å½ID |
| | | salesLedgerProductId: 0, |
| | | quantity: form.value.inboundQuantity, // åºåºæ°é |
| | | time: form.value.inboundTime, // åºåºæ¶é´ |
| | | userId: form.value.nickName, // æä½äºº |
| | | type: 3 // åºåºç±»åï¼èªå®ä¹/ææ |
| | | } |
| | | console.log(outData) |
| | | |
| | | stockOut(outData).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå") |
| | | closeDia() |
| | | getList() |
| | | }).catch(err => { |
| | | proxy.$modal.msgError("åºåºå¤±è´¥") |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef") |
| | | dialogFormVisible.value = false |
| | | } |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm( |
| | | 'æ¯å¦ç¡®è®¤å¯¼åºï¼', |
| | | '导åº', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | } |
| | | ).then(() => { |
| | | const exportUrl = "/stockin/exportTwo" |
| | | proxy.download(exportUrl, {}, 'åºåºå°è´¦.xlsx') |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶") |
| | | }) |
| | | } |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = [] |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning('è¯·éæ©æ°æ®') |
| | | return |
| | | } |
| | | ElMessageBox.confirm( |
| | | 'éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼', |
| | | '导åº', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | } |
| | | ).then(() => { |
| | | delStockManage(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å") |
| | | getList() |
| | | }) |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶") |
| | | }) |
| | | } |
| | | |
| | | // æ ¹æ®tabç±»åè·åå¼¹æ¡æ é¢ |
| | | const getDialogTitle = () => { |
| | | return 'æ°å¢åæ'; |
| | | }; |
| | | |
| | | // æ ¹æ®tabç±»åè·åå¯åºåºæ°éææ¬ |
| | | const getAvailableQuantityText = () => { |
| | | return 'å¯åææ°é'; |
| | | }; |
| | | |
| | | // æ ¹æ®tabç±»åè·åæ°éåæ®µæ ç¾ |
| | | const getQuantityLabel = () => { |
| | | return 'åææ°éï¼'; |
| | | }; |
| | | |
| | | // æ ¹æ®tabç±»åè·åæ¥æåæ®µæ ç¾ |
| | | const getDateLabel = () => { |
| | | return 'åææ¥æï¼'; |
| | | }; |
| | | |
| | | // æ ¹æ®tabç±»åè·å人ååæ®µæ ç¾ |
| | | const getPersonLabel = () => { |
| | | return 'åæäººï¼'; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| | | |
| | | |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-form-item label="éè´è®¢åå·" prop="purchaseContractNumber"> |
| | | <el-select |
| | | v-model="form.purchaseContractNumber" |
| | | placeholder="è¯·éæ©éè´è®¢åå·" |
| | | clearable |
| | | filterable |
| | | :loading="loadingPurchaseOptions" |
| | | @change="handlePurchaseChange" |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in purchaseOptions" |
| | | :key="item.purchaseContractNumber" |
| | | :label="formatPurchaseOption(item)" |
| | | :value="item.purchaseContractNumber" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-table |
| | | :data="productList" |
| | | border |
| | | v-loading="loadingProducts" |
| | | @selection-change="handleSelectionChange" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" width="70" /> |
| | | <!-- <el-table-column label="ä¾åºå" prop="supplierName" width="100" /> --> |
| | | <el-table-column label="éè´æ°é" prop="quantity" width="100" /> |
| | | <el-table-column label="å¾
å
¥åºæ°é" prop="quantity0" width="100" /> |
| | | <el-table-column label="æ¬æ¬¡å
¥åºæ°é" prop="quantityStock" width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" @change="() => calculateTotalPrice(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" width="120" /> |
| | | <el-table-column label="åä»·(å
)" prop="taxInclusiveUnitPrice" width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" :disabled="operationType === 'edit'"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="æ»ä»·(å
)" |
| | | :formatter="formattedNumber" |
| | | prop="taxInclusiveTotalPrice" |
| | | width="150" |
| | | > |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from 'vue' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { |
| | | updateStockIn, |
| | | addSutockIn, |
| | | selectProductRecordListByPuechaserId |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const userStore = useUserStore() |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close', 'success']) |
| | | |
| | | const operationType = ref('')// æä½ç±»å: 'add' æ 'edit' |
| | | const dialogFormVisible = ref(false)// å¼¹æ¡æ¾ç¤ºç¶æ |
| | | const productList = ref([]);// 产ååè¡¨æ°æ® |
| | | const loadingProducts = ref(false);// 产åå è½½ç¶æ |
| | | const selectedRows = ref([]) // 产åè¡¨æ ¼éä¸è¡ |
| | | const purchaseOptions = ref([]) |
| | | const loadingPurchaseOptions = ref(false) |
| | | const loading = ref(false); |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | id: null, |
| | | purchaseContractNumber: '', // éè´è®¢åå· |
| | | supplierId: null, // ä¾åºåID |
| | | supplierName: '', // ä¾åºååç§° |
| | | inboundTime: '', // å
¥åºæ¶é´ |
| | | inboundBatch: '', // å
¥åºæ¹æ¬¡ |
| | | recorderId: userStore.userId, // å½å
¥äººID |
| | | recorderName: userStore.name, // å½å
¥äººå§å |
| | | entryDate: getCurrentDate(), // å½å
¥æ¥æ |
| | | remark: '', // 夿³¨ |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [{ required: true, message: "请è¾å
¥éè´ååå·", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" }], |
| | | inboundTime: [{ required: true, message: "è¯·éæ©å
¥åºæ¶é´", trigger: "change" }], |
| | | inboundBatch: [{ required: true, message: "请è¾å
¥å
¥åºæ¹æ¬¡", trigger: "blur" }] |
| | | } |
| | | }) |
| | | const { form, rules } = toRefs(data) |
| | | |
| | | // å¨æè®¡ç®å¯¹è¯æ¡æ é¢ |
| | | const getDialogTitle = () => { |
| | | return operationType.value === 'add' ? 'æ°å¢å
¥åº' : 'ç¼è¾å
¥åº' |
| | | } |
| | | |
| | | const formatPurchaseOption = (item = {}) => { |
| | | const contract = item.purchaseContractNumber || '--'; |
| | | const supplier = item.supplierName ? ` · ${item.supplierName}` : ''; |
| | | return `${contract}${supplier}`; |
| | | }; |
| | | |
| | | const loadPurchaseOptions = async (keyword = '') => { |
| | | try { |
| | | loadingPurchaseOptions.value = true; |
| | | const res = await purchaseListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | purchaseContractNumber: keyword, |
| | | }); |
| | | const records = res.data?.records || []; |
| | | purchaseOptions.value = records; |
| | | if ( |
| | | form.value.purchaseContractNumber && |
| | | !purchaseOptions.value.find( |
| | | (item) => item.purchaseContractNumber === form.value.purchaseContractNumber |
| | | ) |
| | | ) { |
| | | purchaseOptions.value.push({ |
| | | purchaseContractNumber: form.value.purchaseContractNumber, |
| | | supplierName: form.value.supplierName, |
| | | supplierId: form.value.supplierId, |
| | | }); |
| | | } |
| | | } finally { |
| | | loadingPurchaseOptions.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handlePurchaseChange = (value) => { |
| | | form.value.purchaseContractNumber = value || ''; |
| | | const matched = purchaseOptions.value.find( |
| | | (item) => item.purchaseContractNumber === value |
| | | ); |
| | | if (matched) { |
| | | form.value.supplierName = matched.supplierName || form.value.supplierName; |
| | | form.value.supplierId = matched.supplierId || form.value.supplierId; |
| | | } |
| | | if (!value) { |
| | | productList.value = []; |
| | | return; |
| | | } |
| | | fetchProductsByContract(); |
| | | }; |
| | | |
| | | const exceedsAddLimit = (product) => { |
| | | const stock = Number(product?.quantityStock ?? 0); |
| | | const waiting = Number(product?.quantity0 ?? 0); |
| | | if (!Number.isFinite(stock) || !Number.isFinite(waiting)) { |
| | | return false; |
| | | } |
| | | return stock > waiting; |
| | | }; |
| | | |
| | | const exceedsEditLimit = (product) => { |
| | | const stock = Number(product?.quantityStock ?? 0); |
| | | const waiting = Number(product?.quantity0 ?? 0); |
| | | const original = Number(product?.originalQuantityStock ?? 0); |
| | | if (!Number.isFinite(stock) || !Number.isFinite(waiting) || !Number.isFinite(original)) { |
| | | return false; |
| | | } |
| | | return stock > waiting + original; |
| | | }; |
| | | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | |
| | | // è®¡ç®æ»ä»· |
| | | const calculateTotalPrice = (row) => { |
| | | const quantityStock = Number(row?.quantityStock ?? 0); |
| | | const taxInclusiveUnitPrice = Number(row?.taxInclusiveUnitPrice ?? 0); |
| | | |
| | | if (Number.isFinite(quantityStock) && Number.isFinite(taxInclusiveUnitPrice)) { |
| | | row.taxInclusiveTotalPrice = quantityStock * taxInclusiveUnitPrice; |
| | | } else { |
| | | row.taxInclusiveTotalPrice = 0; |
| | | } |
| | | }; |
| | | |
| | | const fetchProductsByContract = async () => { |
| | | if (!form.value.purchaseContractNumber) { |
| | | proxy.$modal.msgWarning('è¯·éæ©ååå·') |
| | | return |
| | | } |
| | | try { |
| | | loadingProducts.value = true |
| | | const productRes = await selectProductRecordListByPuechaserId({ |
| | | purchaseContractNumber: form.value.purchaseContractNumber |
| | | }); |
| | | if (!productRes.data || productRes.data.length === 0) { |
| | | proxy.$modal.msgWarning('该åå䏿²¡æäº§åè®°å½') |
| | | productList.value = []; |
| | | return |
| | | } |
| | | productList.value = productRes.data.map(item => { |
| | | const quantityStock = Number(item?.quantity0 ?? 0); |
| | | const taxInclusiveUnitPrice = Number(item?.taxInclusiveUnitPrice ?? 0); |
| | | return { |
| | | ...item, |
| | | quantityStock, |
| | | taxInclusiveUnitPrice, |
| | | taxInclusiveTotalPrice: quantityStock * taxInclusiveUnitPrice, |
| | | originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? 0), |
| | | }; |
| | | }) |
| | | } catch (error) { |
| | | console.error('æ¥è¯¢äº§åè®°å½å¤±è´¥:', error) |
| | | proxy.$modal.msgError('æ¥è¯¢äº§åè®°å½å¤±è´¥') |
| | | productList.value = []; |
| | | } finally { |
| | | loadingProducts.value = false |
| | | } |
| | | } |
| | | |
| | | const updatePro = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning('请å
éæ©äº§å'); |
| | | return; |
| | | } |
| | | const target = selectedRows.value[0]; |
| | | const stock = Number(target?.quantityStock ?? 0); |
| | | if (!Number.isFinite(stock) || stock <= 0) { |
| | | proxy.$modal.msgWarning('è¯·å¡«åææçå
¥åºæ°é'); |
| | | return; |
| | | } |
| | | if (exceedsEditLimit(target)) { |
| | | proxy.$modal.msgError('æ¬æ¬¡å
¥åºæ°éä¸è½è¶
è¿åå
¥åºæ°éä¸å¾
å
¥åºæ°éä¹å'); |
| | | return; |
| | | } |
| | | const stockInData = { |
| | | id: selectedRows.value[0].recordId, |
| | | quantityStock: Number(selectedRows.value[0].quantityStock), |
| | | }; |
| | | await updateStockIn(stockInData) |
| | | proxy.$modal.msgSuccess('ä¿®æ¹å
¥åºæå') |
| | | closeDia() |
| | | emit('success') |
| | | } |
| | | |
| | | const submitForm = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning('请å
éæ©éè´ååå¹¶éæ©äº§å') |
| | | return |
| | | } |
| | | if(operationType.value !== 'add'){ |
| | | await updatePro() |
| | | return |
| | | } |
| | | try { |
| | | await proxy.$refs.formRef.validate() |
| | | const invalidProducts = selectedRows.value.filter((product) => { |
| | | const stock = Number(product?.quantityStock ?? 0); |
| | | if (!Number.isFinite(stock) || stock <= 0) { |
| | | return true; |
| | | } |
| | | return exceedsAddLimit(product); |
| | | }) |
| | | |
| | | if (invalidProducts.length > 0) { |
| | | proxy.$modal.msgError('æ¬æ¬¡å
¥åºæ°éé大äº0ï¼ä¸ä¸è½è¶
è¿å¾
å
¥åºæ°é') |
| | | return |
| | | } |
| | | |
| | | const stockInData = { |
| | | ...form.value, |
| | | inboundTime: formatDateTime(form.value.inboundTime), |
| | | nickName: userStore.nickName, |
| | | details: selectedRows.value.map(product => ({ |
| | | id: product.id, |
| | | inboundQuantity: Number(product.quantityStock), |
| | | taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice), |
| | | taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice) |
| | | })), |
| | | }; |
| | | loading.value = true |
| | | await addSutockIn(stockInData) |
| | | |
| | | proxy.$modal.msgSuccess('æ°å¢å
¥åºæå') |
| | | closeDia() |
| | | emit('success') |
| | | |
| | | } catch (error) { |
| | | console.error('æäº¤å¤±è´¥:', error) |
| | | if (!error.errors) { |
| | | proxy.$modal.msgError('æä½å¤±è´¥ï¼è¯·éè¯') |
| | | } |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const closeDia = () => { |
| | | proxy.$refs.formRef.resetFields() |
| | | dialogFormVisible.value = false |
| | | emit('close') |
| | | } |
| | | |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | } |
| | | |
| | | function formatDateTime(date = new Date(), includeTime = true) { |
| | | const d = new Date(date); |
| | | const year = d.getFullYear(); |
| | | const month = String(d.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(d.getDate()).padStart(2, '0'); |
| | | |
| | | if (!includeTime) { |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | |
| | | const hours = String(d.getHours()).padStart(2, '0'); |
| | | const minutes = String(d.getMinutes()).padStart(2, '0'); |
| | | const seconds = String(d.getSeconds()).padStart(2, '0'); |
| | | |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type |
| | | dialogFormVisible.value = true |
| | | selectedRows.value = [] |
| | | await loadPurchaseOptions(); |
| | | |
| | | if (type === 'add') { |
| | | form.value = { |
| | | id: null, |
| | | purchaseContractNumber: '', |
| | | supplierId: null, |
| | | supplierName: '', |
| | | inboundTime: '', |
| | | inboundBatch: '', |
| | | recorderId: userStore.userId, |
| | | recorderName: userStore.name, |
| | | entryDate: getCurrentDate(), |
| | | remark: '' |
| | | } |
| | | productList.value = [] |
| | | } else { |
| | | form.value = JSON.parse(JSON.stringify(row)) |
| | | try { |
| | | loadingProducts.value = true |
| | | const res = await selectProductRecordListByPuechaserId({ |
| | | purchaseContractNumber: form.value.purchaseContractNumber, |
| | | id: row.id |
| | | }); |
| | | productList.value = res.data.map(item => ({ |
| | | ...item, |
| | | quantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0), |
| | | taxInclusiveUnitPrice: Number(item?.taxInclusiveUnitPrice ?? 0), |
| | | taxInclusiveTotalPrice: Number(item?.quantityStock ?? 0) * Number(item?.taxInclusiveUnitPrice ?? 0), |
| | | originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0), |
| | | })) |
| | | selectedRows.value = productList.value |
| | | } catch (error) { |
| | | console.error('å 载产å失败:', error) |
| | | proxy.$modal.msgError('å 载产å失败') |
| | | productList.value = [] |
| | | } finally { |
| | | loadingProducts.value = false |
| | | } |
| | | } |
| | | } |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| | | |
| | | |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢èªå®ä¹å
¥åº' : 'ç¼è¾èªå®ä¹å
¥åº'" width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <div style="margin-bottom: 10px;" v-if="operationType === 'add'"> |
| | | <el-button type="primary" @click="addProductRow">æ°å¢</el-button> |
| | | </div> |
| | | <el-table |
| | | :data="productList" |
| | | border |
| | | v-loading="loadingProducts" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="产å大类" prop="productCategory" width="200"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.productCategory" placeholder="请è¾å
¥äº§å大类" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" width="200"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.specificationModel" placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä½" prop="unit" width="100"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.unit" placeholder="请è¾å
¥åä½" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ä¾åºå" prop="supplierName" width="200"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.supplierName" placeholder="请è¾å
¥ä¾åºå" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç©åç±»å" prop="itemType" width="150"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.itemType" placeholder="请è¾å
¥ç©åç±»å" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºæ°é" prop="inboundNum" width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.inboundNum" @change="() => calculateTotalPrice(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºæ¥æ" prop="inboundDate" width="180"> |
| | | <template #default="scope"> |
| | | <el-date-picker |
| | | v-model="scope.row.inboundDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©å
¥åºæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä»·(å
)" prop="taxInclusiveUnitPrice" width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | width="150" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80" v-if="operationType === 'add'"> |
| | | <template #default="scope"> |
| | | <el-button type="danger" size="small" @click="removeProductRow(scope.$index)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from 'vue' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { |
| | | addStockInCustom, |
| | | updateStockInCustom, |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const userStore = useUserStore() |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close', 'success']) |
| | | |
| | | const operationType = ref('')// æä½ç±»å: 'add' æ 'edit' |
| | | const dialogFormVisible = ref(false)// å¼¹æ¡æ¾ç¤ºç¶æ |
| | | const productList = ref([]);// 产ååè¡¨æ°æ® |
| | | const loadingProducts = ref(false);// 产åå è½½ç¶æ |
| | | const loading = ref(false); |
| | | |
| | | function formatDateTime(date = new Date(), includeTime = true) { |
| | | const d = new Date(date); |
| | | const year = d.getFullYear(); |
| | | const month = String(d.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(d.getDate()).padStart(2, '0'); |
| | | |
| | | if (!includeTime) { |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | |
| | | const hours = String(d.getHours()).padStart(2, '0'); |
| | | const minutes = String(d.getMinutes()).padStart(2, '0'); |
| | | const seconds = String(d.getSeconds()).padStart(2, '0'); |
| | | |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | |
| | | const taxRateOptions = [ |
| | | { label: '1', value: 1 }, |
| | | { label: '6', value: 6 }, |
| | | { label: '13', value: 13 }, |
| | | ] |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | id: null, |
| | | supplierId: null, // ä¾åºåID |
| | | supplierName: '', // ä¾åºååç§° |
| | | recorderId: userStore.userId, // å½å
¥äººID |
| | | recorderName: userStore.name, // å½å
¥äººå§å |
| | | entryDate: getCurrentDate(), // å½å
¥æ¥æ |
| | | remark: '', // 夿³¨ |
| | | }, |
| | | rules: { |
| | | supplierName: [{ required: true, message: "请è¾å
¥ä¾åºååç§°", trigger: "blur" }] |
| | | } |
| | | }) |
| | | const { form, rules } = toRefs(data) |
| | | |
| | | // æ°å¢äº§åè¡ |
| | | const addProductRow = () => { |
| | | productList.value.push({ |
| | | id: null, |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | unit: '', |
| | | supplierName: form.value.supplierName || '', |
| | | itemType: '', |
| | | inboundNum: 0, |
| | | inboundDate: getCurrentDate(), // é»è®¤å½å¤©æ¥æ |
| | | // quantityStock: 0, |
| | | taxInclusiveUnitPrice: 0, |
| | | taxInclusiveTotalPrice: 0, |
| | | taxRate: null, |
| | | taxExclusiveTotalPrice: 0, |
| | | }); |
| | | }; |
| | | |
| | | // å é¤äº§åè¡ |
| | | const removeProductRow = (index) => { |
| | | productList.value.splice(index, 1); |
| | | }; |
| | | |
| | | // è®¡ç®æ»ä»·ï¼æ ¹æ®æ°éãåä»·åå«ç¨åä»·ï¼ |
| | | const calculateTotalPrice = (row) => { |
| | | // è®¡ç®æ®éæ»ä»·ï¼inboundNum * taxInclusiveUnitPrice |
| | | const quantity = Number(row.inboundNum || 0); |
| | | const taxInclusiveUnitPrice = Number(row.taxInclusiveUnitPrice || 0); |
| | | row.taxInclusiveTotalPrice = quantity * taxInclusiveUnitPrice; |
| | | calculateExclusivePrice(row); |
| | | }; |
| | | |
| | | // 计ç®ä¸å«ç¨æ»ä»·ï¼æ ¹æ®å«ç¨æ»ä»·åç¨çï¼ |
| | | const calculateExclusivePrice = (row) => { |
| | | const taxInclusiveTotalPrice = Number(row.taxInclusiveTotalPrice || 0); |
| | | const taxRate = Number(row.taxRate || 0); |
| | | row.taxExclusiveTotalPrice = taxInclusiveTotalPrice / (1 + taxRate / 100); |
| | | }; |
| | | |
| | | const submitForm = async () => { |
| | | try { |
| | | await proxy.$refs.formRef.validate() |
| | | |
| | | if (!productList.value.length) { |
| | | proxy.$modal.msgError('请è³å°æ·»å 䏿¡äº§åæ°æ®') |
| | | return |
| | | } |
| | | |
| | | // éªè¯èªå®ä¹æ·»å çæ°æ®å¿
å¡«åæ®µ |
| | | for (let i = 0; i < productList.value.length; i++) { |
| | | const product = productList.value[i]; |
| | | if (!product.productCategory || !product.specificationModel || !product.unit) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡äº§åæ°æ®æªå¡«å宿´ï¼äº§å大类ãè§æ ¼åå·ãåä½ä¸ºå¿
å¡«ï¼`) |
| | | return |
| | | } |
| | | if (!product.itemType) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡è¯·éæ©ç©åç±»å`) |
| | | return |
| | | } |
| | | if (!product.inboundDate) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡è¯·éæ©å
¥åºæ¥æ`) |
| | | return |
| | | } |
| | | const stock = Number(product?.inboundNum ?? 0); |
| | | if (!Number.isFinite(stock) || stock <= 0) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡æ¬æ¬¡å
¥åºæ°éé大äº0`) |
| | | return |
| | | } |
| | | } |
| | | |
| | | const payloadList = productList.value.map(product => ({ |
| | | id: product.id ?? null, |
| | | inboundNum: Number(product.inboundNum), |
| | | productCategory: product.productCategory, |
| | | specificationModel: product.specificationModel, |
| | | unit: product.unit, |
| | | supplierName: product.supplierName || form.value.supplierName, |
| | | itemType: product.itemType, |
| | | inboundDate: formatDateTime(product.inboundDate, false), |
| | | taxRate: Number(product.taxRate || 0), |
| | | taxExclusiveTotalPrice: Number(product.taxExclusiveTotalPrice || 0), |
| | | taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice || 0), |
| | | taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice || 0), |
| | | })); |
| | | loading.value = true |
| | | if (operationType.value === 'edit') { |
| | | const editPayload = payloadList[0] |
| | | await updateStockInCustom(editPayload) |
| | | } else { |
| | | await addStockInCustom(payloadList) |
| | | } |
| | | |
| | | proxy.$modal.msgSuccess(operationType.value === 'edit' ? 'ç¼è¾èªå®ä¹å
¥åºæå' : 'æ°å¢èªå®ä¹å
¥åºæå') |
| | | closeDia() |
| | | emit('success') |
| | | |
| | | } catch (error) { |
| | | console.error('æäº¤å¤±è´¥:', error) |
| | | if (!error.errors) { |
| | | proxy.$modal.msgError('æä½å¤±è´¥ï¼è¯·éè¯') |
| | | } |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const closeDia = () => { |
| | | proxy.$refs.formRef.resetFields() |
| | | dialogFormVisible.value = false |
| | | productList.value = [] |
| | | emit('close') |
| | | } |
| | | |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type |
| | | dialogFormVisible.value = true |
| | | |
| | | if (type === 'add') { |
| | | form.value = { |
| | | id: null, |
| | | supplierId: null, |
| | | supplierName: '', |
| | | recorderId: userStore.userId, |
| | | recorderName: userStore.name, |
| | | entryDate: getCurrentDate(), |
| | | remark: '' |
| | | } |
| | | productList.value = [] |
| | | } else { |
| | | // ç¼è¾æ¨¡å¼ï¼å°è¡æ°æ®å¡«å
å°è¡¨æ ¼ä¸ä»¥æ¯æä¿®æ¹ |
| | | form.value = { |
| | | id: row?.id ?? null, |
| | | supplierId: row?.supplierId ?? null, |
| | | supplierName: row?.supplierName ?? '', |
| | | recorderId: userStore.userId, |
| | | recorderName: userStore.name, |
| | | entryDate: getCurrentDate(), |
| | | remark: row?.remark ?? '' |
| | | } |
| | | productList.value = [{ |
| | | id: row?.id ?? null, |
| | | productCategory: row?.productCategory ?? '', |
| | | specificationModel: row?.specificationModel ?? '', |
| | | unit: row?.unit ?? '', |
| | | supplierName: row?.supplierName ?? '', |
| | | itemType: row?.itemType ?? '', |
| | | inboundNum: Number(row?.inboundNum ?? row?.inboundQuantity ?? 0), |
| | | inboundDate: row?.inboundDate ?? row?.createTime ?? '', |
| | | taxRate: Number(row?.taxRate ?? 0), |
| | | taxInclusiveUnitPrice: Number(row?.taxInclusiveUnitPrice ?? 0), |
| | | taxInclusiveTotalPrice: Number(row?.taxInclusiveTotalPrice ?? 0), |
| | | taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0), |
| | | }] |
| | | } |
| | | } |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢èªå®ä¹å
¥åº' : 'ç¼è¾èªå®ä¹å
¥åº'" width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <div style="margin-bottom: 10px;" v-if="operationType === 'add'"> |
| | | <el-button type="primary" @click="addProductRow">æ°å¢</el-button> |
| | | </div> |
| | | <el-table |
| | | :data="productList" |
| | | border |
| | | v-loading="loadingProducts" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="产å大类" prop="productCategory" width="200"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.productCategory" placeholder="请è¾å
¥äº§å大类" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" width="200"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.specificationModel" placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä½" prop="unit" width="100"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.unit" placeholder="请è¾å
¥åä½" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºæ°é" prop="inboundNum" width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.inboundNum" @change="() => calculateTotalPrice(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¥åºæ¥æ" prop="inboundDate" width="180"> |
| | | <template #default="scope"> |
| | | <el-date-picker |
| | | v-model="scope.row.inboundDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©å
¥åºæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä»·(å
)" prop="unitPrice" width="150"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.unitPrice" @change="() => calculateTotalPrice(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | label="æ»ä»·(å
)" |
| | | prop="totalPrice" |
| | | width="150" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80" v-if="operationType === 'add'"> |
| | | <template #default="scope"> |
| | | <el-button type="danger" size="small" @click="removeProductRow(scope.$index)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, getCurrentInstance } from 'vue' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { |
| | | addStockInCustom, updateProduct |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const userStore = useUserStore() |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close', 'success']) |
| | | |
| | | const operationType = ref('')// æä½ç±»å: 'add' æ 'edit' |
| | | const dialogFormVisible = ref(false)// å¼¹æ¡æ¾ç¤ºç¶æ |
| | | const productList = ref([]);// 产ååè¡¨æ°æ® |
| | | const loadingProducts = ref(false);// 产åå è½½ç¶æ |
| | | const loading = ref(false); |
| | | |
| | | function formatDateTime(date = new Date(), includeTime = true) { |
| | | const d = new Date(date); |
| | | const year = d.getFullYear(); |
| | | const month = String(d.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(d.getDate()).padStart(2, '0'); |
| | | |
| | | if (!includeTime) { |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | |
| | | const hours = String(d.getHours()).padStart(2, '0'); |
| | | const minutes = String(d.getMinutes()).padStart(2, '0'); |
| | | const seconds = String(d.getSeconds()).padStart(2, '0'); |
| | | |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | |
| | | const itemTypeOptions = [ |
| | | { label: 'ç©æ', value: 'ç©æ' }, |
| | | { label: 'åæ', value: 'åæ' }, |
| | | { label: 'æå', value: 'æå' }, |
| | | { label: 'å
¶ä»', value: 'å
¶ä»' }, |
| | | ] |
| | | |
| | | const taxRateOptions = [ |
| | | { label: '1', value: 1 }, |
| | | { label: '6', value: 6 }, |
| | | { label: '13', value: 13 }, |
| | | ] |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | id: null, |
| | | supplierId: null, // ä¾åºåID |
| | | supplierName: '', // ä¾åºååç§° |
| | | recorderId: userStore.userId, // å½å
¥äººID |
| | | recorderName: userStore.name, // å½å
¥äººå§å |
| | | entryDate: getCurrentDate(), // å½å
¥æ¥æ |
| | | remark: '', // 夿³¨ |
| | | }, |
| | | rules: { |
| | | supplierName: [{ required: true, message: "请è¾å
¥ä¾åºååç§°", trigger: "blur" }] |
| | | } |
| | | }) |
| | | const { form, rules } = toRefs(data) |
| | | |
| | | // æ°å¢äº§åè¡ |
| | | const addProductRow = () => { |
| | | productList.value.push({ |
| | | id: null, |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | unit: '', |
| | | supplierName: form.value.supplierName || '', |
| | | itemType: '', |
| | | inboundNum: 0, |
| | | inboundDate: '', |
| | | quantityStock: 0, |
| | | unitPrice: 0, |
| | | totalPrice: 0, |
| | | taxRate: null, |
| | | taxExclusiveTotalPrice: 0, |
| | | }); |
| | | }; |
| | | |
| | | // å é¤äº§åè¡ |
| | | const removeProductRow = (index) => { |
| | | productList.value.splice(index, 1); |
| | | }; |
| | | |
| | | // è®¡ç®æ»ä»·ï¼æ ¹æ®æ°éãåä»·åå«ç¨åä»·ï¼ |
| | | const calculateTotalPrice = (row) => { |
| | | // è®¡ç®æ®éæ»ä»·ï¼inboundNum * unitPrice |
| | | const quantity = Number(row.inboundNum || 0); |
| | | const unitPrice = Number(row.unitPrice || 0); |
| | | row.totalPrice = quantity * unitPrice; |
| | | calculateExclusivePrice(row); |
| | | }; |
| | | |
| | | // 计ç®ä¸å«ç¨æ»ä»·ï¼æ ¹æ®å«ç¨æ»ä»·åç¨çï¼ |
| | | const calculateExclusivePrice = (row) => { |
| | | const totalPrice = Number(row.totalPrice || 0); |
| | | const taxRate = Number(row.taxRate || 0); |
| | | row.taxExclusiveTotalPrice = totalPrice / (1 + taxRate / 100); |
| | | }; |
| | | |
| | | const submitForm = async () => { |
| | | try { |
| | | await proxy.$refs.formRef.validate() |
| | | |
| | | if (!productList.value.length) { |
| | | proxy.$modal.msgError('请è³å°æ·»å 䏿¡äº§åæ°æ®') |
| | | return |
| | | } |
| | | |
| | | // éªè¯èªå®ä¹æ·»å çæ°æ®å¿
å¡«åæ®µ |
| | | for (let i = 0; i < productList.value.length; i++) { |
| | | const product = productList.value[i]; |
| | | if (!product.productCategory || !product.specificationModel || !product.unit) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡äº§åæ°æ®æªå¡«å宿´ï¼äº§å大类ãè§æ ¼åå·ãåä½ä¸ºå¿
å¡«ï¼`) |
| | | return |
| | | } |
| | | if (!product.inboundDate) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡è¯·éæ©å
¥åºæ¥æ`) |
| | | return |
| | | } |
| | | const stock = Number(product?.inboundNum ?? 0); |
| | | if (!Number.isFinite(stock) || stock <= 0) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡æ¬æ¬¡å
¥åºæ°éé大äº0`) |
| | | return |
| | | } |
| | | } |
| | | |
| | | const payloadList = productList.value.map(product => ({ |
| | | id: product.id ?? null, |
| | | inboundNum: Number(product.inboundNum), |
| | | productCategory: product.productCategory, |
| | | specificationModel: product.specificationModel, |
| | | unit: product.unit, |
| | | supplierName: product.supplierName || form.value.supplierName, |
| | | itemType: product.itemType, |
| | | inboundDate: formatDateTime(product.inboundDate, false), |
| | | taxRate: Number(product.taxRate || 0), |
| | | taxExclusiveTotalPrice: Number(product.taxExclusiveTotalPrice || 0), |
| | | unitPrice: Number(product.unitPrice || 0), |
| | | })); |
| | | loading.value = true |
| | | if (operationType.value === 'edit') { |
| | | const editPayload = payloadList[0] |
| | | await updateProduct(editPayload) |
| | | } else { |
| | | await addStockInCustom(payloadList) |
| | | } |
| | | |
| | | proxy.$modal.msgSuccess(operationType.value === 'edit' ? 'ç¼è¾èªå®ä¹å
¥åºæå' : 'æ°å¢èªå®ä¹å
¥åºæå') |
| | | closeDia() |
| | | emit('success') |
| | | |
| | | } catch (error) { |
| | | console.error('æäº¤å¤±è´¥:', error) |
| | | if (!error.errors) { |
| | | proxy.$modal.msgError('æä½å¤±è´¥ï¼è¯·éè¯') |
| | | } |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | const closeDia = () => { |
| | | proxy.$refs.formRef.resetFields() |
| | | dialogFormVisible.value = false |
| | | productList.value = [] |
| | | emit('close') |
| | | } |
| | | |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type |
| | | dialogFormVisible.value = true |
| | | |
| | | if (type === 'add') { |
| | | form.value = { |
| | | id: null, |
| | | supplierId: null, |
| | | supplierName: '', |
| | | recorderId: userStore.userId, |
| | | recorderName: userStore.name, |
| | | entryDate: getCurrentDate(), |
| | | remark: '' |
| | | } |
| | | productList.value = [] |
| | | } else { |
| | | // ç¼è¾æ¨¡å¼ï¼å°è¡æ°æ®å¡«å
å°è¡¨æ ¼ä¸ä»¥æ¯æä¿®æ¹ |
| | | form.value = { |
| | | id: row?.id ?? null, |
| | | supplierId: row?.supplierId ?? null, |
| | | supplierName: row?.supplierName ?? '', |
| | | recorderId: userStore.userId, |
| | | recorderName: userStore.name, |
| | | entryDate: getCurrentDate(), |
| | | remark: row?.remark ?? '' |
| | | } |
| | | productList.value = [{ |
| | | id: row?.id ?? null, |
| | | productCategory: row?.productCategory ?? '', |
| | | specificationModel: row?.specificationModel ?? '', |
| | | unit: row?.unit ?? '', |
| | | supplierName: row?.supplierName ?? '', |
| | | itemType: row?.itemType ?? '', |
| | | inboundNum: Number(row?.inboundNum ?? row?.inboundQuantity ?? 0), |
| | | inboundDate: row?.inboundDate ?? row?.createTime ?? '', |
| | | taxRate: Number(row?.taxRate ?? 0), |
| | | unitPrice: Number(row?.unitPrice ?? 0), |
| | | taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0), |
| | | }] |
| | | } |
| | | } |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">å
¥åºæ¥æï¼</span> |
| | | <el-date-picker |
| | | v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | @change="handleQuery" |
| | | /> |
| | | <span class="search_title ml10">产å大类ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.productCategory" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢å
¥åº</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="row => row.id" |
| | | show-summary |
| | | style="width: 100%" |
| | | :summary-method="summarizeMainTable" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="å
¥åºæ¶é´" prop="inboundDate" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="æ¹æ¬¡å·" prop="code" width="130" show-overflow-tooltip /> |
| | | <el-table-column label="产å大类" prop="productCategory" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" width="70" show-overflow-tooltip /> |
| | | <el-table-column label="ç©åç±»å" prop="itemType" show-overflow-tooltip /> |
| | | <el-table-column label="å
¥åºæ°é" prop="inboundNum" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="å©ä½åºå" prop="inboundNum0" show-overflow-tooltip /> |
| | | <el-table-column label="åä»·(å
)" prop="taxInclusiveUnitPrice" width="150" /> |
| | | <el-table-column label="æ»ä»·(å
)" prop="taxInclusiveTotalPrice" width="150" /> |
| | | <el-table-column label="å
¥åºäºº" prop="createBy" width="80" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="æä½" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | |
| | | <form-dia-manual ref="formDiaManual" @close="handleQuery" @success="handleQuery"></form-dia-manual> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance, nextTick } from 'vue' |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from '@/store/modules/user' |
| | | import dayjs from 'dayjs' |
| | | import { |
| | | delStockInCustom, |
| | | getInPageByCustom, |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import FormDiaManual from './components/formDiaManual.vue' |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const tableData = ref([]) |
| | | const selectedRows = ref([]) |
| | | const tableLoading = ref(false) |
| | | const formDiaManual = ref() |
| | | |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | const total = ref(0) |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productCategory:'', |
| | | timeStr: getCurrentDate(), |
| | | }, |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getList() |
| | | } |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList() |
| | | } |
| | | const getList = () => { |
| | | tableLoading.value = true |
| | | const params = { ...page } |
| | | params.timeStr = searchForm.value.timeStr |
| | | params.productCategory = searchForm.value.productCategory |
| | | params.flag = false |
| | | const apiCall = getInPageByCustom(params) |
| | | |
| | | apiCall.then(res => { |
| | | tableLoading.value = false |
| | | tableData.value = res.data.records |
| | | |
| | | // å端计ç®å«ç¨æ»ä»·ï¼å«ç¨æ»ä»· = taxInclusiveUnitPrice * å
¥åºæ°é |
| | | tableData.value = tableData.value.map(item => { |
| | | const inboundNum = Number(item.inboundNum) || 0 |
| | | const taxInclusiveUnitPrice = Number(item.taxInclusiveUnitPrice) || 0 |
| | | item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * inboundNum).toFixed(2) |
| | | return item |
| | | }) |
| | | |
| | | total.value = res.data.total |
| | | }).catch(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (type, row) => { |
| | | await nextTick(() => { |
| | | formDiaManual.value?.openDialog(type, row) |
| | | }) |
| | | } |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection.filter(item => item.id) |
| | | } |
| | | |
| | | const expandedRowKeys = ref([]) |
| | | |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']) |
| | | } |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm('æ¯å¦ç¡®è®¤å¯¼åºï¼', '导åº', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }).then(() => { |
| | | const exportUrl = "/stockin/exportTwo" |
| | | proxy.download(exportUrl, {}, 'å
¥åºå°è´¦.xlsx') |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶") |
| | | }) |
| | | } |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning('è¯·éæ©æ°æ®') |
| | | return |
| | | } |
| | | const ids = selectedRows.value.map(item => item.id) |
| | | |
| | | ElMessageBox.confirm('éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼', 'å é¤', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | }).then(() => { |
| | | // ææå
¥åºå é¤ |
| | | delStockInCustom(ids).then(() => { |
| | | proxy.$modal.msgSuccess("å 餿å") |
| | | getList() |
| | | }).catch(() => { |
| | | proxy.$modal.msgError("å é¤å¤±è´¥") |
| | | }) |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶") |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| | | |
| | | |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog :model-value="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢ææåºå' : 'ç¼è¾ææåºå'" width="70%" |
| | | @update:model-value="$emit('update:dialogFormVisible', $event)" @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类ï¼" prop="productCategory"> |
| | | <el-input disabled v-model="form.productCategory" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="specificationModel"> |
| | | <el-input disabled v-model="form.specificationModel" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input disabled v-model="form.unit" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç©åç±»åï¼" prop="itemType"> |
| | | <el-input disabled v-model="form.itemType" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºæ¶é´ï¼" prop="createTime"> |
| | | <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåæ°éï¼" prop="inboundNum"> |
| | | <el-input v-model="form.inboundNum" placeholder="请è¾å
¥" clearable @input="calculateTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å·²åºåºæ°éï¼" prop="totalInboundNum"> |
| | | <el-input disabled v-model="form.totalInboundNum" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¾
åºåºæ°éï¼" prop="inboundNum0"> |
| | | <el-input disabled v-model="form.inboundNum0" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä»·(å
)ï¼" prop="taxInclusiveUnitPrice"> |
| | | <el-input v-model="form.taxInclusiveUnitPrice" placeholder="请è¾å
¥" clearable @input="calculateTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ»ä»·(å
)ï¼" prop="taxInclusiveTotalPrice"> |
| | | <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="èªå¨è®¡ç®" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, watch } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | dialogFormVisible: Boolean, |
| | | operationType: String, |
| | | formData: Object |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:dialogFormVisible', 'submit', 'close']) |
| | | |
| | | const formRef = ref() |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | unit: '', |
| | | itemType: '', |
| | | createTime: '', |
| | | inboundNum: '', |
| | | totalInboundNum: '', |
| | | inboundNum0: '', |
| | | taxInclusiveUnitPrice: '', |
| | | taxInclusiveTotalPrice: '' |
| | | }, |
| | | rules: { |
| | | productCategory: [{ required: true, message: '请è¾å
¥äº§å大类', trigger: 'blur' }], |
| | | specificationModel: [{ required: true, message: '请è¾å
¥è§æ ¼åå·', trigger: 'blur' }], |
| | | unit: [{ required: true, message: '请è¾å
¥åä½', trigger: 'blur' }], |
| | | itemType: [{ required: true, message: '请è¾å
¥ç©åç±»å', trigger: 'blur' }], |
| | | createTime: [{ required: true, message: 'è¯·éæ©å
¥åºæ¶é´', trigger: 'change' }], |
| | | inboundNum: [{ required: true, message: '请è¾å
¥åºåæ°é', trigger: 'blur' }], |
| | | taxInclusiveUnitPrice: [{ required: true, message: '请è¾å
¥åä»·', trigger: 'blur' }] |
| | | } |
| | | }) |
| | | |
| | | const { form, rules } = toRefs(data) |
| | | |
| | | // è®¡ç®æ»ä»·ï¼æ»ä»· = åä»· à å©ä½åºå |
| | | const calculateTotalPrice = () => { |
| | | const unitPrice = parseFloat(form.value.taxInclusiveUnitPrice) || 0 |
| | | const stockQuantity = parseFloat(form.value.inboundNum) || 0 // åºåæ°é |
| | | const outboundQuantity = parseFloat(form.value.totalInboundNum) || 0 // å·²åºåºæ°é |
| | | const remainingStock = stockQuantity - outboundQuantity // å©ä½åºå |
| | | form.value.taxInclusiveTotalPrice = (unitPrice * remainingStock).toFixed(2) |
| | | } |
| | | |
| | | // çå¬formDataåå |
| | | watch(() => props.formData, (newVal) => { |
| | | if (newVal) { |
| | | form.value = { ...newVal } |
| | | // æ°æ®åååéæ°è®¡ç®æ»ä»· |
| | | calculateTotalPrice() |
| | | } |
| | | }, { immediate: true }) |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | emit('submit', form.value) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | emit('close') |
| | | emit('update:dialogFormVisible', false) |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dialog-footer { |
| | | text-align: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog :model-value="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢æååºå' : 'ç¼è¾æååºå'" width="70%" |
| | | @update:model-value="$emit('update:dialogFormVisible', $event)" @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类ï¼" prop="productCategory"> |
| | | <el-input disabled v-model="form.productCategory" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="specificationModel"> |
| | | <el-input disabled v-model="form.specificationModel" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input disabled v-model="form.unit" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºæ¶é´ï¼" prop="createTime"> |
| | | <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåæ°éï¼" prop="inboundNum"> |
| | | <el-input v-model="form.inboundNum" placeholder="请è¾å
¥" clearable @input="calculateTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å·²åºåºæ°éï¼" prop="totalInboundNum"> |
| | | <el-input disabled v-model="form.totalInboundNum" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¾
åºåºæ°éï¼" prop="inboundNum0"> |
| | | <el-input disabled v-model="form.inboundNum0" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä»·(å
)ï¼" prop="unitPrice"> |
| | | <el-input v-model="form.unitPrice" placeholder="请è¾å
¥" clearable @input="calculateTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ»ä»·(å
)ï¼" prop="totalPrice"> |
| | | <el-input disabled v-model="form.totalPrice" placeholder="èªå¨è®¡ç®" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, watch } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | dialogFormVisible: Boolean, |
| | | operationType: String, |
| | | formData: Object |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:dialogFormVisible', 'submit', 'close']) |
| | | |
| | | const formRef = ref() |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | unit: '', |
| | | createTime: '', |
| | | inboundNum: '', |
| | | totalInboundNum: '', |
| | | inboundNum0: '', |
| | | unitPrice: '', |
| | | totalPrice: '' |
| | | }, |
| | | rules: { |
| | | productCategory: [{ required: true, message: '请è¾å
¥äº§å大类', trigger: 'blur' }], |
| | | specificationModel: [{ required: true, message: '请è¾å
¥è§æ ¼åå·', trigger: 'blur' }], |
| | | unit: [{ required: true, message: '请è¾å
¥åä½', trigger: 'blur' }], |
| | | createTime: [{ required: true, message: 'è¯·éæ©å
¥åºæ¶é´', trigger: 'change' }], |
| | | inboundNum: [{ required: true, message: '请è¾å
¥åºåæ°é', trigger: 'blur' }], |
| | | unitPrice: [{ required: true, message: '请è¾å
¥åä»·', trigger: 'blur' }] |
| | | } |
| | | }) |
| | | |
| | | const { form, rules } = toRefs(data) |
| | | |
| | | // è®¡ç®æ»ä»·ï¼æ»ä»· = åä»· à å©ä½åºå |
| | | const calculateTotalPrice = () => { |
| | | const unitPrice = parseFloat(form.value.unitPrice) || 0 |
| | | const stockQuantity = parseFloat(form.value.inboundNum) || 0 // åºåæ°é |
| | | const outboundQuantity = parseFloat(form.value.totalInboundNum) || 0 // å·²åºåºæ°é |
| | | const remainingStock = stockQuantity - outboundQuantity // å©ä½åºå |
| | | form.value.totalPrice = (unitPrice * remainingStock).toFixed(2) |
| | | } |
| | | |
| | | // çå¬formDataåå |
| | | watch(() => props.formData, (newVal) => { |
| | | if (newVal) { |
| | | form.value = { ...newVal } |
| | | // æ°æ®åååéæ°è®¡ç®æ»ä»· |
| | | calculateTotalPrice() |
| | | } |
| | | }, { immediate: true }) |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | emit('submit', form.value) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | emit('close') |
| | | emit('update:dialogFormVisible', false) |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dialog-footer { |
| | | text-align: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog :model-value="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢åæåºå' : 'ç¼è¾åæåºå'" width="70%" |
| | | @update:model-value="$emit('update:dialogFormVisible', $event)" @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类ï¼" prop="productCategory"> |
| | | <el-input disabled v-model="form.productCategory" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="specificationModel"> |
| | | <el-input disabled v-model="form.specificationModel" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input disabled v-model="form.unit" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥åºæ¶é´ï¼" prop="createTime"> |
| | | <el-date-picker style="width: 100%" v-model="form.createTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="date" placeholder="è¯·éæ©" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåæ°éï¼" prop="inboundNum"> |
| | | <el-input v-model="form.inboundNum" placeholder="请è¾å
¥" clearable @input="calculateTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å·²åºåºæ°éï¼" prop="totalInboundNum"> |
| | | <el-input disabled v-model="form.totalInboundNum" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¾
åºåºæ°éï¼" prop="inboundNum0"> |
| | | <el-input disabled v-model="form.inboundNum0" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨åä»·(å
)ï¼" prop="taxInclusiveUnitPrice"> |
| | | <el-input v-model="form.taxInclusiveUnitPrice" placeholder="请è¾å
¥" clearable @input="calculateTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨æ»ä»·(å
)ï¼" prop="taxInclusiveTotalPrice"> |
| | | <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="èªå¨è®¡ç®" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, watch } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | dialogFormVisible: Boolean, |
| | | operationType: String, |
| | | formData: Object |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:dialogFormVisible', 'submit', 'close']) |
| | | |
| | | const formRef = ref() |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | productCategory: '', |
| | | specificationModel: '', |
| | | unit: '', |
| | | createTime: '', |
| | | inboundNum: '', |
| | | totalInboundNum: '', |
| | | inboundNum0: '', |
| | | taxInclusiveUnitPrice: '', |
| | | taxInclusiveTotalPrice: '' |
| | | }, |
| | | rules: { |
| | | productCategory: [{ required: true, message: '请è¾å
¥äº§å大类', trigger: 'blur' }], |
| | | specificationModel: [{ required: true, message: '请è¾å
¥è§æ ¼åå·', trigger: 'blur' }], |
| | | unit: [{ required: true, message: '请è¾å
¥åä½', trigger: 'blur' }], |
| | | createTime: [{ required: true, message: 'è¯·éæ©å
¥åºæ¶é´', trigger: 'change' }], |
| | | inboundNum: [{ required: true, message: '请è¾å
¥åºåæ°é', trigger: 'blur' }], |
| | | taxInclusiveUnitPrice: [{ required: true, message: '请è¾å
¥å«ç¨åä»·', trigger: 'blur' }] |
| | | } |
| | | }) |
| | | |
| | | const { form, rules } = toRefs(data) |
| | | |
| | | // è®¡ç®æ»ä»·ï¼å«ç¨æ»ä»· = å«ç¨åä»· à å©ä½åºå |
| | | const calculateTotalPrice = () => { |
| | | const unitPrice = parseFloat(form.value.taxInclusiveUnitPrice) || 0 |
| | | const stockQuantity = parseFloat(form.value.inboundNum) || 0 // åºåæ°é |
| | | const outboundQuantity = parseFloat(form.value.totalInboundNum) || 0 // å·²åºåºæ°é |
| | | const remainingStock = stockQuantity - outboundQuantity // å©ä½åºå |
| | | form.value.taxInclusiveTotalPrice = (unitPrice * remainingStock).toFixed(2) |
| | | } |
| | | |
| | | // çå¬formDataåå |
| | | watch(() => props.formData, (newVal) => { |
| | | if (newVal) { |
| | | form.value = { ...newVal } |
| | | // æ°æ®åååéæ°è®¡ç®æ»ä»· |
| | | calculateTotalPrice() |
| | | } |
| | | }, { immediate: true }) |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | emit('submit', form.value) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | emit('close') |
| | | emit('update:dialogFormVisible', false) |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dialog-footer { |
| | | text-align: center; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">å
¥åºæ¥æï¼</span> |
| | | <el-date-picker |
| | | v-model="searchForm.timeStr" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | @change="handleQuery" |
| | | /> |
| | | <span class="search_title ml10">产å大类ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.productCategory" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="row => row.id" |
| | | show-summary |
| | | style="width: 100%" |
| | | :row-class-name="tableRowClassName" |
| | | :summary-method="summarizeMainTable" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="å
¥åºæ¥æ" prop="inboundDate" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="æ¹æ¬¡å·" prop="code" width="130" show-overflow-tooltip /> |
| | | <el-table-column label="产å大类" prop="productCategory" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" show-overflow-tooltip /> |
| | | <el-table-column label="åä½" prop="unit" width="80" show-overflow-tooltip /> |
| | | <el-table-column label="ç©åç±»å" prop="itemType" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="已忿°é" prop="totalInboundNum" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="å©ä½åºå" prop="inboundNum0" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="åä»·(å
)" prop="taxInclusiveUnitPrice" width="150" /> |
| | | <el-table-column label="æ»ä»·(å
)" prop="taxInclusiveTotalPrice" width="150" /> |
| | | <el-table-column fixed="right" label="æä½" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">ç¼è¾</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | |
| | | <!-- ææåºåå¼¹æ¡ --> |
| | | <FormDiaManual |
| | | v-model:dialogFormVisible="manualDialogVisible" |
| | | :operationType="operationType" |
| | | :formData="form" |
| | | @submit="submitForm" |
| | | @close="closeDia" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from '@/store/modules/user' |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { productTreeList,modelList } from "@/api/basicData/product.js" |
| | | import { |
| | | getStockManagePageByCustom, |
| | | delStockManage, |
| | | } from "@/api/inventoryManagement/stockManage.js"; |
| | | import { |
| | | updateManagement, updateManagementByCustom, updateStockIn |
| | | } from "@/api/inventoryManagement/stockIn.js"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | | |
| | | // 导å
¥ææåºåå¼¹æ¡ç»ä»¶ |
| | | import FormDiaManual from './components/FormDiaManual.vue' |
| | | |
| | | const userStore = useUserStore() |
| | | const { proxy } = getCurrentInstance() |
| | | const tableData = ref([]) |
| | | const productData = ref([]) |
| | | const selectedRows = ref([]) |
| | | const userList = ref([]) |
| | | const productList = ref([]) |
| | | const productModelList = ref([]) |
| | | // const customerOption = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }) |
| | | const total = ref(0) |
| | | const fileList = ref([]) |
| | | const loading = ref(false); |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref('') |
| | | |
| | | // ææåºåå¼¹æ¡æ¾ç¤ºç¶æ |
| | | const manualDialogVisible = ref(false) |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | // supplierName: '', |
| | | productCategory:'', |
| | | customerName: '', |
| | | timeStr: getCurrentDate(), |
| | | }, |
| | | form: { |
| | | supplierId: null, |
| | | productId: null, |
| | | productName: '', |
| | | userId: userStore.userId, |
| | | nickName: '', |
| | | productModelId: null, |
| | | model: '', |
| | | unit: '', |
| | | productrecordId: null, |
| | | taxInclusiveUnitPrice: '', |
| | | taxInclusiveTotalPrice: '', |
| | | taxRate: '', |
| | | taxExclusiveTotalPrice: '', |
| | | inboundTime: '', |
| | | inboundBatch: '', |
| | | stockQuantity: '', |
| | | boundTime: '', |
| | | warnNum: '', // æ°å¢æä½åºååæ®µ |
| | | salesLedgerProductId: null, |
| | | }, |
| | | rules: { |
| | | // supplierName: [{ required: true, message: '请è¾å
¥ä¾åºååç§°', trigger: 'blur' }], |
| | | productCategory: [{ required: true, message: 'è¯·éæ©äº§å大类', trigger: 'change' }], |
| | | specificationModel: [{ required: true, message: '请è¾å
¥è§æ ¼åå·', trigger: 'blur' }], |
| | | unit: [{ required: true, message: '请è¾å
¥åä½', trigger: 'blur' }], |
| | | stockQuantity: [{ required: true, message: '请è¾å
¥åºåºæ°é', trigger: 'blur' }], |
| | | unitPrice: [{ required: true, message: '请è¾å
¥åä»·', trigger: 'blur' }], // æ·»å æååºååä»·çéªè¯è§å |
| | | taxInclusiveUnitPrice: [{ required: true, message: '请è¾å
¥å«ç¨åä»·', trigger: 'blur' }], |
| | | taxInclusiveTotalPrice: [{ required: true, message: '请è¾å
¥å«ç¨æ»ä»·', trigger: 'blur' }], |
| | | taxRate: [{ required: true, message: '请è¾å
¥ç¨ç', trigger: 'blur' }], |
| | | taxExclusiveTotalPrice: [{ required: true, message: '请è¾å
¥ä¸å«ç¨æ»ä»·', trigger: 'blur' }], |
| | | boundTime: [{ required: true, message: 'è¯·éæ©åºåæ¶é´', trigger: 'change' }], |
| | | inboundTime: [{ required: true, message: 'è¯·éæ©å
¥åºæ¶é´', trigger: 'change' }], |
| | | inboundPerson: [{ required: true, message: 'è¯·éæ©åºåºäºº', trigger: 'change' }], |
| | | warnNum: [{ required: true, message: '请è¾å
¥æä½åºå', trigger: 'blur' }], |
| | | } |
| | | }) |
| | | const { searchForm, form, rules } = toRefs(data) |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getList() |
| | | } |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList() |
| | | } |
| | | const buildQueryParams = () => { |
| | | const params = { |
| | | ...page, |
| | | timeStr: searchForm.value.timeStr, |
| | | } |
| | | params.productCategory = searchForm.value.productCategory |
| | | return params |
| | | } |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true |
| | | const params = buildQueryParams() |
| | | getStockManagePageByCustom(params).then(res => { |
| | | tableLoading.value = false |
| | | tableData.value = res.data.records |
| | | |
| | | // ä¸ºè¡¨æ ¼æ°æ®èªå¨è®¡ç®æ»ä»· |
| | | tableData.value = tableData.value.map(item => { |
| | | // 计ç®å©ä½åºå |
| | | const stockQuantity = parseFloat(item.inboundNum) || 0 |
| | | const outboundQuantity = parseFloat(item.totalInboundNum) || 0 |
| | | const remainingStock = Math.max(stockQuantity - outboundQuantity, 0) |
| | | |
| | | // ææåºåï¼å«ç¨æ»ä»· = å«ç¨åä»· à å©ä½åºå |
| | | const taxInclusiveUnitPrice = parseFloat(item.taxInclusiveUnitPrice) || 0 |
| | | item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * remainingStock).toFixed(2) |
| | | |
| | | return item |
| | | }) |
| | | |
| | | total.value = res.data.total |
| | | // æ°æ®å è½½å®æåæ£æ¥åºå |
| | | // checkStockAndCreatePurchase(); |
| | | }).catch(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter(item => item.id); |
| | | console.log('selection', selectedRows.value) |
| | | } |
| | | const expandedRowKeys = ref([]) |
| | | |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']); |
| | | }; |
| | | |
| | | // è¡¨æ ¼è¡ç±»å |
| | | const tableRowClassName = ({ row }) => { |
| | | const stock = Number(row?.inboundNum0 ?? 0); |
| | | const warn = Number(row?.warnNum ?? 0); |
| | | if (!Number.isFinite(stock) || !Number.isFinite(warn)) { |
| | | return ''; |
| | | } |
| | | return stock < warn ? 'row-low-stock' : ''; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (type, row) => { |
| | | operationType.value = type |
| | | form.value = {} |
| | | productData.value = [] |
| | | let userLists = await userListNoPageByTenantId() |
| | | userList.value = userLists.data |
| | | if (type === 'edit') { |
| | | form.value = { ...row } |
| | | productTreeList().then(res =>{ |
| | | productList.value = res |
| | | productList.value.forEach(i =>{ |
| | | if (i.label === row.productCategory) { |
| | | modelList({ id: i.id }).then((res) => { |
| | | productModelList.value = res; |
| | | }); |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | form.value.entryDate = getCurrentDate() // 设置é»è®¤å½å
¥æ¥æä¸ºå½åæ¥æ |
| | | |
| | | // ä»
ææåºåå¼¹æ¡ |
| | | manualDialogVisible.value = true |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = (submittedData) => { |
| | | console.log('åç»ä»¶æäº¤çæ°æ®:', submittedData) |
| | | |
| | | // 使ç¨åç»ä»¶æäº¤çæ°æ®ï¼è䏿¯ç¶ç»ä»¶çform对象 |
| | | const submitData = { ...submittedData } |
| | | |
| | | // ææåºåï¼ç§»é¤å«ç¨æ»ä»·å段 |
| | | delete submitData.taxInclusiveTotalPrice |
| | | // ç§»é¤å
¶ä»å¯è½çæ»ä»·å段 |
| | | delete submitData.taxExclusiveTotalPrice |
| | | |
| | | console.log('æäº¤ç»åç«¯çæ°æ®ï¼å·²ç§»é¤æ»ä»·å段ï¼:', submitData) |
| | | |
| | | // ææåºåä½¿ç¨ updateManagementByCustom æ¥å£ |
| | | updateManagementByCustom(submitData).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå") |
| | | closeDia() |
| | | getList() |
| | | // æäº¤åæ£æ¥åºåå¹¶å°è¯å建请è´å |
| | | // checkStockAndCreatePurchase(); |
| | | }).catch(error => { |
| | | console.error('æäº¤å¤±è´¥:', error) |
| | | proxy.$modal.msgError("æäº¤å¤±è´¥ï¼è¯·éè¯") |
| | | }) |
| | | } |
| | | // æ£æ¥åºåå¹¶å建请è´å |
| | | // const checkStockAndCreatePurchase = async () => { |
| | | // const stockList = tableData.value; |
| | | // // handList() |
| | | // for (const item of stockList) { |
| | | // if (item.inboundNum0 < item.warnNum) { |
| | | // try { |
| | | // const stockInData = { |
| | | // id: item.id, |
| | | // quantityStock: item.warnNum + item.totalInboundNum,// ä½¿ç¨æ°æ ¼å¼å彿° |
| | | // }; |
| | | // loading.value = true |
| | | // await updateStockIn(stockInData) |
| | | // proxy.$modal.msgSuccess(`产å ${item.productCategory} ä¿®æ¹å
¥åºæå`) |
| | | // loading.value = false |
| | | // } catch (error) { |
| | | // proxy.$modal.msgError(`产å ${item.productCategory} çæè¯·è´å失败ï¼è¯·æå¨å¤ç`); |
| | | // |
| | | // } |
| | | // } |
| | | // } |
| | | // }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef") |
| | | manualDialogVisible.value = false |
| | | } |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm( |
| | | 'æ¯å¦ç¡®è®¤å¯¼åºï¼', |
| | | '导åº', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | } |
| | | ).then(() => { |
| | | const exportParams = buildQueryParams() |
| | | const exportUrl = "/stockin/exportCopyTwo" |
| | | proxy.download(exportUrl, exportParams, 'åºåä¿¡æ¯.xlsx') |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶") |
| | | }) |
| | | } |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = [] |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning('è¯·éæ©æ°æ®') |
| | | return |
| | | } |
| | | ElMessageBox.confirm( |
| | | 'éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼', |
| | | '导åº', { |
| | | confirmButtonText: '确认', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning', |
| | | } |
| | | ).then(() => { |
| | | delStockManage({ids:ids}).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å") |
| | | getList() |
| | | }) |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶") |
| | | }) |
| | | } |
| | | onMounted(() => { |
| | | getList() |
| | | // checkStockAndCreatePurchase(); |
| | | // æ¯å°æ¶æ£æ¥ä¸æ¬¡åºå |
| | | // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000); |
| | | |
| | | // onUnmounted(() => { |
| | | // // ç»ä»¶å¸è½½æ¶æ¸
é¤å®æ¶å¨ |
| | | // clearInterval(intervalId); |
| | | // }); |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | :deep(.row-low-stock td) { |
| | | background-color: #fde2e2; |
| | | color: #c45656; |
| | | } |
| | | |
| | | :deep(.row-low-stock:hover > td) { |
| | | background-color: #fcd4d4; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢è¡¨å --> |
| | | <div class="search_form"> |
| | | <div class="search_left"> |
| | | <span class="search_title">æ¥è¡¨ç±»åï¼</span> |
| | | <el-select |
| | | v-model="searchForm.reportType" |
| | | style="width: 150px;" |
| | | placeholder="è¯·éæ©" |
| | | @change="handleReportTypeChange" |
| | | > |
| | | <el-option label="æ¥æ¥" value="daily" /> |
| | | <el-option label="ææ¥" value="monthly" /> |
| | | <el-option label="ä½ä¸æ¥è¡¨" value="work" /> |
| | | <el-option label="è¿åºåæ¥è¡¨" value="inout" /> |
| | | </el-select> |
| | | |
| | | <span class="search_title ml10">æ¶é´èå´ï¼</span> |
| | | <el-date-picker |
| | | v-if="searchForm.reportType === 'daily'" |
| | | v-model="searchForm.singleDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 200px;" |
| | | /> |
| | | <el-date-picker |
| | | v-else-if="searchForm.reportType === 'monthly'" |
| | | v-model="searchForm.monthRange" |
| | | type="monthrange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æä»½" |
| | | end-placeholder="ç»ææä»½" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 240px;" |
| | | /> |
| | | <el-date-picker |
| | | v-else |
| | | v-model="searchForm.dateRange" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 240px;" |
| | | /> |
| | | |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px"> |
| | | æ¥è¯¢ |
| | | </el-button> |
| | | <el-button @click="handleReset">éç½®</el-button> |
| | | </div> |
| | | |
| | | <div class="search_right"> |
| | | <el-button type="success" @click="handleExport" icon="Download"> |
| | | å¯¼åºæ¥è¡¨ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ç»è®¡å¡ç --> |
| | | <div class="stats_cards" v-if="reportData.summary"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-card class="stats_card"> |
| | | <div class="stats_content"> |
| | | <div class="stats_icon in"> |
| | | <el-icon><TrendCharts /></el-icon> |
| | | </div> |
| | | <div class="stats_info"> |
| | | <div class="stats_value">{{ reportData.summary.totalIn || 0 }}</div> |
| | | <div class="stats_label">æ»å
¥åºé</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="stats_card"> |
| | | <div class="stats_content"> |
| | | <div class="stats_icon out"> |
| | | <el-icon><TrendCharts /></el-icon> |
| | | </div> |
| | | <div class="stats_info"> |
| | | <div class="stats_value">{{ reportData.summary.totalOut || 0 }}</div> |
| | | <div class="stats_label">æ»åºåºé</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="stats_card"> |
| | | <div class="stats_content"> |
| | | <div class="stats_icon stock"> |
| | | <el-icon><Box /></el-icon> |
| | | </div> |
| | | <div class="stats_info"> |
| | | <div class="stats_value">{{ reportData.summary.currentStock || 0 }}</div> |
| | | <div class="stats_label">å½ååºå</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="stats_card"> |
| | | <div class="stats_content"> |
| | | <div class="stats_icon turnover"> |
| | | <el-icon><Refresh /></el-icon> |
| | | </div> |
| | | <div class="stats_info"> |
| | | <div class="stats_value">{{ reportData.summary.turnoverRate || 0 }}%</div> |
| | | <div class="stats_label">å¨è½¬ç</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- å¾è¡¨åºå --> |
| | | <div class="chart_section" v-if="reportData.chartData"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-card> |
| | | <template #header> |
| | | <span>åºåè¶å¿å¾</span> |
| | | </template> |
| | | <div ref="trendChart" style="height: 300px;"></div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card> |
| | | <template #header> |
| | | <span>è¿åºåºå¯¹æ¯</span> |
| | | </template> |
| | | <div ref="comparisonChart" style="height: 300px;"></div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- è¯¦ç»æ°æ®è¡¨æ ¼ --> |
| | | <div class="table_section"> |
| | | <el-card> |
| | | <template #header> |
| | | <span>{{ getTableTitle() }}</span> |
| | | </template> |
| | | <el-table |
| | | v-loading="tableLoading" |
| | | :data="reportData.tableData" |
| | | border |
| | | height="400" |
| | | style="width: 100%" |
| | | :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column |
| | | v-if="searchForm.reportType === 'daily'" |
| | | label="æ¥æ" |
| | | prop="createTime" |
| | | width="100" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | v-if="searchForm.reportType === 'monthly'" |
| | | label="æä»½" |
| | | prop="createTime" |
| | | width="100" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="å
¥åºæ¶é´" |
| | | prop="createTime" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å
¥åºæ¹æ¬¡" |
| | | prop="inboundBatches" |
| | | width="160" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="产å大类" |
| | | prop="productCategory" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="è§æ ¼åå·" |
| | | prop="specificationModel" |
| | | min-width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="åä½" |
| | | prop="unit" |
| | | width="70" |
| | | show-overflow-tooltip |
| | | /> |
| | | <!-- <el-table-column |
| | | label="æååºå" |
| | | prop="beginStock" |
| | | width="100" |
| | | align="center" |
| | | /> --> |
| | | <el-table-column |
| | | label="å
¥åºæ°é" |
| | | prop="inboundNum" |
| | | width="100" |
| | | align="center" |
| | | /> |
| | | <!-- <el-table-column |
| | | label="åºåºæ°é" |
| | | prop="" |
| | | width="100" |
| | | align="center" |
| | | /> --> |
| | | <el-table-column |
| | | label="ç°å¨åºå" |
| | | prop="inboundNum0" |
| | | width="100" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | label="å«ç¨åä»·" |
| | | prop="taxInclusiveUnitPrice" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å«ç¨æ»ä»·" |
| | | prop="taxInclusiveTotalPrice" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ç¨ç(%)" |
| | | prop="taxRate" |
| | | width="80" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ä¸å«ç¨æ»ä»·" |
| | | prop="taxExclusiveTotalPrice" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å
¥åºäºº" |
| | | prop="createBy" |
| | | width="80" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | v-if="searchForm.reportType === 'work'" |
| | | label="æä½äººå" |
| | | prop="operator" |
| | | width="80" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | v-if="searchForm.reportType === 'work'" |
| | | label="æä½æ¶é´" |
| | | prop="operateTime" |
| | | width="150" |
| | | align="center" |
| | | /> |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, nextTick } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import * as echarts from 'echarts' |
| | | import { |
| | | getStockDailyReport, |
| | | getStockMonthlyReport, |
| | | getWorkReport, |
| | | getStockInOutReport, |
| | | exportStockReport |
| | | } from '@/api/inventoryManagement/stockReport' |
| | | |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | // ååºå¼æ°æ® |
| | | const tableLoading = ref(false) |
| | | const trendChart = ref(null) |
| | | const comparisonChart = ref(null) |
| | | |
| | | const searchForm = reactive({ |
| | | reportType: 'daily', |
| | | singleDate: '', |
| | | dateRange: [], |
| | | monthRange: [] |
| | | }) |
| | | |
| | | const reportData = ref({ |
| | | summary: null, |
| | | chartData: null, |
| | | tableData: [] |
| | | }) |
| | | |
| | | // è·åè¡¨æ ¼æ é¢ |
| | | const getTableTitle = () => { |
| | | const typeMap = { |
| | | daily: 'æ¥æ¥è¯¦ç»æ°æ®', |
| | | monthly: 'ææ¥è¯¦ç»æ°æ®', |
| | | work: 'ä½ä¸æ¥è¡¨è¯¦ç»æ°æ®', |
| | | inout: 'è¿åºåæ¥è¡¨è¯¦ç»æ°æ®' |
| | | } |
| | | return typeMap[searchForm.reportType] || 'æ¥è¡¨è¯¦ç»æ°æ®' |
| | | } |
| | | |
| | | // æ¥è¡¨ç±»åæ¹å |
| | | const handleReportTypeChange = () => { |
| | | reportData.value = { |
| | | summary: null, |
| | | chartData: null, |
| | | tableData: [] |
| | | } |
| | | } |
| | | |
| | | // æ¥è¯¢æ°æ® |
| | | const handleQuery = async () => { |
| | | if (!validateSearchForm()) { |
| | | return |
| | | } |
| | | |
| | | tableLoading.value = true |
| | | try { |
| | | const params = getQueryParams() |
| | | let response |
| | | |
| | | switch (searchForm.reportType) { |
| | | case 'daily': |
| | | response = await getStockDailyReport(params) |
| | | break |
| | | case 'monthly': |
| | | response = await getStockMonthlyReport(params) |
| | | break |
| | | case 'work': |
| | | response = await getWorkReport(params) |
| | | break |
| | | case 'inout': |
| | | response = await getStockInOutReport(params) |
| | | break |
| | | default: |
| | | throw new Error('æªç¥çæ¥è¡¨ç±»å') |
| | | } |
| | | |
| | | if (response.code === 200) { |
| | | // generateMockData() |
| | | reportData.value.tableData = response.data.tableData |
| | | reportData.value.summary = response.data.summary |
| | | reportData.value.chartData = response.data.chartData |
| | | nextTick(() => { |
| | | initCharts() |
| | | }) |
| | | |
| | | } |
| | | } catch (error) { |
| | | ElMessage.error('æ¥è¯¢å¤±è´¥ï¼' + error.message) |
| | | } finally { |
| | | tableLoading.value = false |
| | | } |
| | | } |
| | | // // çæåæ°æ® |
| | | // const generateMockData = () => { |
| | | // // çæç»è®¡å¡çåæ°æ® |
| | | // const summary = { |
| | | // totalIn: 1000, |
| | | // totalOut: 600, |
| | | // currentStock: 400, |
| | | // turnoverRate: 30 |
| | | // } |
| | | |
| | | // // çæå¾è¡¨åæ°æ® |
| | | // const trendDates = ['2025-09-15', '2025-09-16', '2025-09-17', '2025-09-18', '2025-09-19'] |
| | | // const trendValues = [300, 350, 400, 380, 420] |
| | | // const comparisonDates = ['2025-09-15', '2025-09-16', '2025-09-17'] |
| | | // const inValues = [100, 150, 200] |
| | | // const outValues = [80, 120, 100] |
| | | |
| | | // const chartData = { |
| | | // trendDates, |
| | | // trendValues, |
| | | // comparisonDates, |
| | | // inValues, |
| | | // outValues |
| | | // } |
| | | |
| | | // reportData.value = { |
| | | // summary, |
| | | // chartData, |
| | | // tableData: [] |
| | | // } |
| | | // } |
| | | // éªè¯æç´¢è¡¨å |
| | | const validateSearchForm = () => { |
| | | if (searchForm.reportType === 'daily') { |
| | | if (!searchForm.singleDate) { |
| | | ElMessage.warning('è¯·éæ©æ¥æ') |
| | | return false |
| | | } |
| | | } else if (searchForm.reportType === 'work' || searchForm.reportType === 'inout') { |
| | | if (!searchForm.dateRange || searchForm.dateRange.length !== 2) { |
| | | ElMessage.warning('è¯·éæ©æ¥æèå´') |
| | | return false |
| | | } |
| | | } else if (searchForm.reportType === 'monthly') { |
| | | if (!searchForm.monthRange || searchForm.monthRange.length !== 2) { |
| | | ElMessage.warning('è¯·éæ©æä»½èå´') |
| | | return false |
| | | } |
| | | } |
| | | return true |
| | | } |
| | | |
| | | // è·åæ¥è¯¢åæ° |
| | | const getQueryParams = () => { |
| | | const params = { |
| | | reportType: searchForm.reportType, |
| | | reportDate: "", |
| | | startMonth: "", |
| | | endMonth: "", |
| | | startDate: "", |
| | | endDate: "" |
| | | } |
| | | |
| | | if (searchForm.reportType === 'daily') { |
| | | params.reportDate = searchForm.singleDate |
| | | } else if (searchForm.reportType === 'monthly') { |
| | | params.startMonth = searchForm.monthRange[0] |
| | | params.endMonth = searchForm.monthRange[1] |
| | | } else { |
| | | params.startDate = searchForm.dateRange[0] |
| | | params.endDate = searchForm.dateRange[1] |
| | | } |
| | | |
| | | return params |
| | | } |
| | | |
| | | // éç½®æç´¢ |
| | | const handleReset = () => { |
| | | searchForm.reportType = 'daily' |
| | | searchForm.singleDate = '' |
| | | searchForm.dateRange = [] |
| | | searchForm.monthRange = [] |
| | | reportData.value = { |
| | | summary: null, |
| | | chartData: null, |
| | | tableData: [] |
| | | } |
| | | } |
| | | |
| | | // å¯¼åºæ¥è¡¨ |
| | | const handleExport = async () => { |
| | | if (!validateSearchForm()) { |
| | | return |
| | | } |
| | | |
| | | try { |
| | | const params = getQueryParams() |
| | | // const response = await exportStockReport(params) |
| | | proxy.download("/stockin/exportCopy", params, 'åºåæ¥è¡¨.xlsx') |
| | | // å建ä¸è½½é¾æ¥ |
| | | // const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) |
| | | // const url = window.URL.createObjectURL(blob) |
| | | // const link = document.createElement('a') |
| | | // link.href = url |
| | | // link.download = `${getTableTitle()}_${new Date().getTime()}.xlsx` |
| | | // document.body.appendChild(link) |
| | | // link.click() |
| | | // document.body.removeChild(link) |
| | | // window.URL.revokeObjectURL(url) |
| | | |
| | | // ElMessage.success('å¯¼åºæå') |
| | | } catch (error) { |
| | | ElMessage.error('导åºå¤±è´¥ï¼' + error.message) |
| | | } |
| | | } |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | if (!reportData.value.chartData) return |
| | | |
| | | initTrendChart() |
| | | initComparisonChart() |
| | | } |
| | | |
| | | // åå§åè¶å¿å¾ |
| | | const initTrendChart = () => { |
| | | if (!trendChart.value) return |
| | | |
| | | const chart = echarts.init(trendChart.value) |
| | | const option = { |
| | | title: { |
| | | text: 'åºåååè¶å¿', |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis' |
| | | }, |
| | | legend: { |
| | | data: ['åºåé'], |
| | | top: 30 |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: reportData.value.chartData.trendDates || [] |
| | | }, |
| | | yAxis: { |
| | | type: 'value' |
| | | }, |
| | | series: [{ |
| | | name: 'åºåé', |
| | | type: 'line', |
| | | data: reportData.value.chartData.trendValues || [], |
| | | smooth: true, |
| | | itemStyle: { |
| | | color: '#409EFF' |
| | | } |
| | | }] |
| | | } |
| | | chart.setOption(option) |
| | | } |
| | | |
| | | // åå§å对æ¯å¾ |
| | | const initComparisonChart = () => { |
| | | if (!comparisonChart.value) return |
| | | |
| | | const chart = echarts.init(comparisonChart.value) |
| | | const option = { |
| | | title: { |
| | | text: 'è¿åºåºå¯¹æ¯', |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis' |
| | | }, |
| | | legend: { |
| | | data: ['å
¥åº', 'åºåº'], |
| | | top: 30 |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: reportData.value.chartData.comparisonDates || [] |
| | | }, |
| | | yAxis: { |
| | | type: 'value' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'å
¥åº', |
| | | type: 'bar', |
| | | data: reportData.value.chartData.inValues || [], |
| | | itemStyle: { |
| | | color: '#67C23A' |
| | | } |
| | | }, |
| | | { |
| | | name: 'åºåº', |
| | | type: 'bar', |
| | | data: reportData.value.chartData.outValues || [], |
| | | itemStyle: { |
| | | color: '#F56C6C' |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | chart.setOption(option) |
| | | } |
| | | |
| | | // ç»ä»¶æè½½æ¶è®¾ç½®é»è®¤æ¶é´ |
| | | onMounted(() => { |
| | | const today = new Date() |
| | | searchForm.singleDate = today.toISOString().split('T')[0] |
| | | |
| | | const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000) |
| | | searchForm.dateRange = [ |
| | | yesterday.toISOString().split('T')[0], |
| | | today.toISOString().split('T')[0] |
| | | ] |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .search_form { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding: 20px; |
| | | background: #fff; |
| | | border-radius: 4px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .search_left { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .search_title { |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-right: 8px; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .stats_cards { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .stats_card { |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .stats_content { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .stats_icon { |
| | | width: 50px; |
| | | height: 50px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-right: 15px; |
| | | font-size: 24px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .stats_icon.in { |
| | | background: linear-gradient(135deg, #67C23A, #85CE61); |
| | | } |
| | | |
| | | .stats_icon.out { |
| | | background: linear-gradient(135deg, #F56C6C, #F78989); |
| | | } |
| | | |
| | | .stats_icon.stock { |
| | | background: linear-gradient(135deg, #409EFF, #66B1FF); |
| | | } |
| | | |
| | | .stats_icon.turnover { |
| | | background: linear-gradient(135deg, #E6A23C, #EEBE77); |
| | | } |
| | | |
| | | .stats_info { |
| | | flex: 1; |
| | | } |
| | | |
| | | .stats_value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #333; |
| | | line-height: 1; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .stats_label { |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .chart_section { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .table_section { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | :deep(.el-card__header) { |
| | | background: #f8f9fa; |
| | | border-bottom: 1px solid #e9ecef; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | :deep(.el-table .el-table__header-wrapper th) { |
| | | background-color: #F0F1F5 !important; |
| | | color: #333333; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | :deep(.el-table .el-table__body-wrapper td) { |
| | | padding: 8px 0; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- æç´¢è¡¨å --> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="卿°ç½åç§°ï¼"> |
| | | <el-input v-model="searchForm.tankName" placeholder="请è¾å
¥å¨æ°ç½åç§°" clearable style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="卿°ç½ç±»åï¼"> |
| | | <el-select v-model="searchForm.tankType" placeholder="è¯·éæ©å¨æ°ç½ç±»å" clearable style="width: 200px"> |
| | | <el-option label="æ¶²åæ°å¨ç½" value="æ¶²åæ°å¨ç½" /> |
| | | <el-option label="å缩æ°å¨ç½" value="å缩æ°å¨ç½" /> |
| | | <el-option label="å¤©ç¶æ°å¨ç½" value="å¤©ç¶æ°å¨ç½" /> |
| | | <el-option label="æ°§æ°å¨ç½" value="æ°§æ°å¨ç½" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="é¢è¦ç±»åï¼"> |
| | | <el-select v-model="searchForm.warningType" placeholder="è¯·éæ©é¢è¦ç±»å" clearable style="width: 200px"> |
| | | <el-option label="æ°ä½ä¸è¶³" value="æ°ä½ä¸è¶³" /> |
| | | <el-option label="ååå¼å¸¸" value="ååå¼å¸¸" /> |
| | | <el-option label="温度å¼å¸¸" value="温度å¼å¸¸" /> |
| | | <el-option label="æ³æ¼é¢è¦" value="æ³æ¼é¢è¦" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="é¢è¦çº§å«ï¼"> |
| | | <el-select v-model="searchForm.warningLevel" placeholder="è¯·éæ©é¢è¦çº§å«" clearable style="width: 200px"> |
| | | <el-option label="ç´§æ¥" value="ç´§æ¥" /> |
| | | <el-option label="éè¦" value="éè¦" /> |
| | | <el-option label="ä¸è¬" value="ä¸è¬" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">æç´¢</el-button> |
| | | <el-button @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <div class="table_list"> |
| | | <!-- æä½æé® --> |
| | | <div class="table-operations"> |
| | | <el-button type="primary" @click="handleAdd">æ°å¢é¢è¦è§å</el-button> |
| | | <!-- <el-button type="success" @click="handleBatchProcess">æ¹éå¤ç@</el-button> --> |
| | | <el-button @click="handleExport">导åº</el-button> |
| | | </div> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | style="width: 100%" |
| | | height="calc(100vh - 280px)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | |
| | | <!-- åºç¡ä¿¡æ¯å段 --> |
| | | <el-table-column label="卿°ç½ç¼ç " prop="tankCode" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="卿°ç½åç§°" prop="tankName" width="200" show-overflow-tooltip /> |
| | | <el-table-column label="卿°ç½ç±»å" prop="tankType" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" width="150" show-overflow-tooltip /> |
| | | <el-table-column label="容积(m³)" prop="volume" width="100" show-overflow-tooltip /> |
| | | |
| | | <!-- åºåç¸å
³å段 --> |
| | | <el-table-column label="å½åæ°ä½é" prop="currentGasLevel" width="120" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <span :class="getGasLevelClass(scope.row)">{{ scope.row.currentGasLevel }}%</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å®å
¨æ°ä½é" prop="safetyGasLevel" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="æä½æ°ä½é" prop="minGasLevel" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="æé«æ°ä½é" prop="maxGasLevel" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="å½ååå(MPa)" prop="currentPressure" width="140" show-overflow-tooltip /> |
| | | |
| | | <!-- é¢è¦è§ååæ®µ --> |
| | | <el-table-column label="é¢è¦ç±»å" prop="warningType" width="100" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag :type="getWarningTypeTag(scope.row.warningType)"> |
| | | {{ scope.row.warningType }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é¢è¦çº§å«" prop="warningLevel" width="100" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-tag :type="getWarningLevelTag(scope.row.warningLevel)"> |
| | | {{ scope.row.warningLevel }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é¢è¦éå¼" prop="warningThreshold" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="æ¯å¦å¯ç¨" prop="isEnabled" width="100" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <el-switch v-model="scope.row.isEnabled" @change="handleEnableChange(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <!-- æ¶é´ç¸å
³å段 --> |
| | | <el-table-column label="é¢è¦æ¶é´" prop="warningTime" width="150" show-overflow-tooltip /> |
| | | <el-table-column label="é¢è¦æç»å¤©æ°" prop="warningDuration" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="æåæ´æ°æ¶é´" prop="lastUpdateTime" width="150" show-overflow-tooltip /> |
| | | <el-table-column label="é¢è®¡å
è£
æ¶é´" prop="expectedRefillTime" width="150" show-overflow-tooltip /> |
| | | <el-table-column label="é¢è®¡ç¼ºæ°æ¶é´" prop="expectedShortageTime" width="150" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <div v-if="scope.row.expectedShortageTime"> |
| | | <div v-if="getCountdown(scope.row.expectedShortageTime).isExpired" class="countdown-expired"> |
| | | <el-tag type="danger">已缺æ°</el-tag> |
| | | </div> |
| | | <div v-else class="countdown-timer"> |
| | | <span :class="getCountdownClass(scope.row.expectedShortageTime)"> |
| | | {{ getCountdown(scope.row.expectedShortageTime).text }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <!-- æä½å --> |
| | | <el-table-column fixed="right" label="æä½" width="200" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="handleEdit(scope.row)">ç¼è¾</el-button> |
| | | <!-- <el-button link type="success" size="small" @click="handleProcess(scope.row)">å¤ç@</el-button>--> |
| | | <el-button link type="danger" size="small" @click="handleDelete(scope.row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | |
| | | <!-- æ°å¢/ç¼è¾é¢è¦è§åå¼¹çª --> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢é¢è¦è§å' : 'ç¼è¾é¢è¦è§å'" |
| | | width="50%" |
| | | @close="closeDialog" |
| | | > |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="140px"> |
| | | <el-row :gutter="20"> |
| | | <!-- åºç¡ä¿¡æ¯ --> |
| | | <el-col :span="12"> |
| | | <el-form-item label="卿°ç½ç¼ç ï¼" prop="tankCode"> |
| | | <el-input v-model="form.tankCode" placeholder="请è¾å
¥å¨æ°ç½ç¼ç " /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="卿°ç½åç§°ï¼" prop="tankName"> |
| | | <el-input v-model="form.tankName" placeholder="请è¾å
¥å¨æ°ç½åç§°" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="卿°ç½ç±»åï¼" prop="tankType"> |
| | | <el-select v-model="form.tankType" placeholder="è¯·éæ©å¨æ°ç½ç±»å" style="width: 100%"> |
| | | <el-option label="æ¶²åæ°å¨ç½" value="æ¶²åæ°å¨ç½" /> |
| | | <el-option label="å缩æ°å¨ç½" value="å缩æ°å¨ç½" /> |
| | | <el-option label="å¤©ç¶æ°å¨ç½" value="å¤©ç¶æ°å¨ç½" /> |
| | | <el-option label="æ°§æ°å¨ç½" value="æ°§æ°å¨ç½" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="specificationModel"> |
| | | <el-input v-model="form.specificationModel" placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="容积(m³)ï¼" prop="volume"> |
| | | <el-input-number v-model="form.volume" :min="0" :precision="2" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½åæ°ä½é(%)ï¼" prop="currentGasLevel"> |
| | | <el-input-number v-model="form.currentGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- åºåç¸å
³ --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å®å
¨æ°ä½é(%)ï¼" prop="safetyGasLevel"> |
| | | <el-input-number v-model="form.safetyGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æä½æ°ä½é(%)ï¼" prop="minGasLevel"> |
| | | <el-input-number v-model="form.minGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æé«æ°ä½é(%)ï¼" prop="maxGasLevel"> |
| | | <el-input-number v-model="form.maxGasLevel" :min="0" :max="100" :precision="1" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½ååå(MPa)ï¼" prop="currentPressure"> |
| | | <el-input-number v-model="form.currentPressure" :min="0" :precision="2" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- é¢è¦è§å --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è¦ç±»åï¼" prop="warningType"> |
| | | <el-select v-model="form.warningType" placeholder="è¯·éæ©é¢è¦ç±»å" style="width: 100%"> |
| | | <el-option label="æ°ä½ä¸è¶³" value="æ°ä½ä¸è¶³" /> |
| | | <el-option label="ååå¼å¸¸" value="ååå¼å¸¸" /> |
| | | <el-option label="温度å¼å¸¸" value="温度å¼å¸¸" /> |
| | | <el-option label="æ³æ¼é¢è¦" value="æ³æ¼é¢è¦" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è¦çº§å«ï¼" prop="warningLevel"> |
| | | <el-select v-model="form.warningLevel" placeholder="è¯·éæ©é¢è¦çº§å«" style="width: 100%"> |
| | | <el-option label="ç´§æ¥" value="ç´§æ¥" /> |
| | | <el-option label="éè¦" value="éè¦" /> |
| | | <el-option label="ä¸è¬" value="ä¸è¬" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è¦éå¼ï¼" prop="warningThreshold"> |
| | | <el-input-number v-model="form.warningThreshold" :min="0" :precision="2" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¯å¦å¯ç¨ï¼" prop="isEnabled"> |
| | | <el-switch v-model="form.isEnabled" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- æ¶é´ç¸å
³ --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è¦æ¶é´ï¼" prop="warningTime"> |
| | | <el-date-picker |
| | | v-model="form.warningTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©é¢è¦æ¶é´" |
| | | style="width: 100%" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è®¡å
è£
æ¶é´ï¼" prop="expectedRefillTime"> |
| | | <el-date-picker |
| | | v-model="form.expectedRefillTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©é¢è®¡å
è£
æ¶é´" |
| | | style="width: 100%" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è®¡ç¼ºæ°æ¶é´ï¼" prop="expectedShortageTime"> |
| | | <el-date-picker |
| | | v-model="form.expectedShortageTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©é¢è®¡ç¼ºæ°æ¶é´" |
| | | style="width: 100%" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¢è¦è§åæè¿°ï¼" prop="warningRule"> |
| | | <el-input |
| | | v-model="form.warningRule" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥é¢è¦è§åæè¿°" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDialog">åæ¶</el-button> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 缺æ°é¢è¦å¼¹æ¡ --> |
| | | <el-dialog |
| | | v-model="shortageWarningVisible" |
| | | title="â ï¸ ç¼ºæ°é¢è¦" |
| | | width="400px" |
| | | :close-on-click-modal="false" |
| | | :close-on-press-escape="false" |
| | | :show-close="false" |
| | | > |
| | | <div class="shortage-warning-content"> |
| | | <div class="warning-icon"> |
| | | <el-icon size="48" color="#f56c6c"><WarningFilled /></el-icon> |
| | | </div> |
| | | <div class="warning-message"> |
| | | <h3>{{ currentWarningTank.tankName }}</h3> |
| | | <p>卿°ç½å·²ç¼ºæ°ï¼è¯·åæ¶å¤çï¼</p> |
| | | <p class="warning-details"> |
| | | 卿°ç½ç¼ç ï¼{{ currentWarningTank.tankCode }}<br> |
| | | 卿°ç½ç±»åï¼{{ currentWarningTank.tankType }}<br> |
| | | å½åæ°ä½éï¼{{ currentWarningTank.currentGasLevel }}% |
| | | </p> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleShortageWarning">ç«å³å¤ç</el-button> |
| | | <el-button @click="closeShortageWarning">ç¨åå¤ç</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 缺æ°é¢è¦å¼¹æ¡ --> |
| | | <el-dialog |
| | | v-model="shortageWarningVisible" |
| | | title="â ï¸ ç¼ºæ°é¢è¦" |
| | | width="400px" |
| | | :close-on-click-modal="false" |
| | | :close-on-press-escape="false" |
| | | :show-close="false" |
| | | > |
| | | <div class="shortage-warning-content"> |
| | | <div class="warning-icon"> |
| | | <el-icon size="48" color="#f56c6c"><WarningFilled /></el-icon> |
| | | </div> |
| | | <div class="warning-message"> |
| | | <h3>{{ currentWarningTank.tankName }}</h3> |
| | | <p>卿°ç½å·²ç¼ºæ°ï¼è¯·åæ¶å¤çï¼</p> |
| | | <p class="warning-details"> |
| | | 卿°ç½ç¼ç ï¼{{ currentWarningTank.tankCode }}<br> |
| | | 卿°ç½ç±»åï¼{{ currentWarningTank.tankType }}<br> |
| | | å½åæ°ä½éï¼{{ currentWarningTank.currentGasLevel }}% |
| | | </p> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleShortageWarning">ç«å³å¤ç</el-button> |
| | | <el-button @click="closeShortageWarning">ç¨åå¤ç</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { WarningFilled } from '@element-plus/icons-vue' |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | // 注éæAPI导å
¥ï¼ä½¿ç¨åæ°æ® |
| | | import { |
| | | getStockWarningPage, |
| | | addStockWarning, |
| | | updateStockWarning, |
| | | deleteStockWarning, |
| | | batchProcessStockWarning, |
| | | exportStockWarning, |
| | | toggleStockWarningStatus |
| | | } from '@/api/inventoryManagement/stockWarning.js' |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | // ååºå¼æ°æ® |
| | | const tableData = ref([]) |
| | | const tableLoading = ref(false) |
| | | const selectedRows = ref([]) |
| | | const dialogFormVisible = ref(false) |
| | | const operationType = ref('add') |
| | | const total = ref(0) |
| | | |
| | | // 缺æ°é¢è¦ç¸å
³ |
| | | const shortageWarningVisible = ref(false) |
| | | const currentWarningTank = ref({}) |
| | | const countdownTimer = ref(null) |
| | | |
| | | // å页忰 |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0 |
| | | }) |
| | | |
| | | // æç´¢è¡¨å |
| | | const searchForm = reactive({ |
| | | tankName: '', |
| | | tankType: '', |
| | | warningType: '', |
| | | warningLevel: '' |
| | | }) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const form = reactive({ |
| | | id: null, |
| | | tankCode: '', |
| | | tankName: '', |
| | | tankType: '', |
| | | specificationModel: '', |
| | | volume: 0, |
| | | currentGasLevel: 0, |
| | | safetyGasLevel: 0, |
| | | minGasLevel: 0, |
| | | maxGasLevel: 0, |
| | | currentPressure: 0, |
| | | warningType: '', |
| | | warningLevel: '', |
| | | warningThreshold: 0, |
| | | isEnabled: true, |
| | | warningTime: '', |
| | | warningDuration: 0, |
| | | lastUpdateTime: '', |
| | | expectedRefillTime: '', |
| | | expectedShortageTime: '', |
| | | warningRule: '' |
| | | }) |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | tankCode: [{ required: true, message: '请è¾å
¥å¨æ°ç½ç¼ç ', trigger: 'blur' }], |
| | | tankName: [{ required: true, message: '请è¾å
¥å¨æ°ç½åç§°', trigger: 'blur' }], |
| | | tankType: [{ required: true, message: 'è¯·éæ©å¨æ°ç½ç±»å', trigger: 'change' }], |
| | | warningType: [{ required: true, message: 'è¯·éæ©é¢è¦ç±»å', trigger: 'change' }], |
| | | warningLevel: [{ required: true, message: 'è¯·éæ©é¢è¦çº§å«', trigger: 'change' }], |
| | | warningThreshold: [{ required: true, message: '请è¾å
¥é¢è¦éå¼', trigger: 'blur' }] |
| | | } |
| | | |
| | | // è·åå计æ¶ä¿¡æ¯ |
| | | const getCountdown = (expectedTime) => { |
| | | if (!expectedTime) return { text: '-', isExpired: false } |
| | | |
| | | const now = new Date().getTime() |
| | | const expected = new Date(expectedTime).getTime() |
| | | const diff = expected - now |
| | | |
| | | if (diff <= 0) { |
| | | return { text: '已缺æ°', isExpired: true } |
| | | } |
| | | |
| | | const days = Math.floor(diff / (1000 * 60 * 60 * 24)) |
| | | const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) |
| | | const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)) |
| | | |
| | | if (days > 0) { |
| | | return { text: `${days}天${hours}å°æ¶`, isExpired: false } |
| | | } else if (hours > 0) { |
| | | return { text: `${hours}å°æ¶${minutes}åé`, isExpired: false } |
| | | } else { |
| | | return { text: `${minutes}åé`, isExpired: false } |
| | | } |
| | | } |
| | | |
| | | // è·ååè®¡æ¶æ ·å¼ç±» |
| | | const getCountdownClass = (expectedTime) => { |
| | | if (!expectedTime) return '' |
| | | |
| | | const now = new Date().getTime() |
| | | const expected = new Date(expectedTime).getTime() |
| | | const diff = expected - now |
| | | |
| | | if (diff <= 0) { |
| | | return 'countdown-expired' |
| | | } else if (diff <= 24 * 60 * 60 * 1000) { // 24å°æ¶å
|
| | | return 'countdown-urgent' |
| | | } else if (diff <= 7 * 24 * 60 * 60 * 1000) { // 7天å
|
| | | return 'countdown-warning' |
| | | } else { |
| | | return 'countdown-normal' |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥ç¼ºæ°é¢è¦ |
| | | const checkShortageWarnings = () => { |
| | | tableData.value.forEach(tank => { |
| | | if (tank.expectedShortageTime) { |
| | | const countdown = getCountdown(tank.expectedShortageTime) |
| | | if (countdown.isExpired && !tank.warningShown) { |
| | | // æ è®°å·²æ¾ç¤ºé¢è¦ï¼é¿å
éå¤å¼¹æ¡ |
| | | tank.warningShown = true |
| | | showShortageWarning(tank) |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // æ¾ç¤ºç¼ºæ°é¢è¦å¼¹æ¡ |
| | | const showShortageWarning = (tank) => { |
| | | currentWarningTank.value = tank |
| | | shortageWarningVisible.value = true |
| | | |
| | | // ææ¾æç¤ºé³ï¼å¯éï¼ |
| | | // const audio = new Audio('/path/to/warning-sound.mp3') |
| | | // audio.play() |
| | | } |
| | | |
| | | // å¤ç缺æ°é¢è¦ |
| | | const handleShortageWarning = () => { |
| | | ElMessage.success(`æ£å¨å¤ç卿°ç½ ${currentWarningTank.value.tankName} ç缺æ°é®é¢`) |
| | | shortageWarningVisible.value = false |
| | | // è¿éå¯ä»¥è°ç¨å¤çAPI |
| | | } |
| | | // å¤ç缺æ°é¢è¦ |
| | | const closeShortageWarning = () => { |
| | | // ElMessage.success(`æ£å¨å¤ç卿°ç½ ${currentWarningTank.value.tankName} ç缺æ°é®é¢`) |
| | | shortageWarningVisible.value = false |
| | | // è¿éå¯ä»¥è°ç¨å¤çAPI |
| | | } |
| | | |
| | | // è·ååè¡¨æ°æ® |
| | | const getList = async () => { |
| | | tableLoading.value = true |
| | | getStockWarningPage(page, searchForm) |
| | | .then(res => { |
| | | |
| | | tableData.value = res.data.records |
| | | page.value.total = res.data.total; |
| | | tableLoading.value = false; |
| | | // æ£æ¥ç¼ºæ°é¢è¦ |
| | | checkShortageWarnings() |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | |
| | | // æç´¢ |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getList() |
| | | } |
| | | |
| | | // éç½®æç´¢ |
| | | const resetQuery = () => { |
| | | Object.keys(searchForm).forEach(key => { |
| | | searchForm[key] = '' |
| | | }) |
| | | handleQuery() |
| | | } |
| | | |
| | | // å页åå |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page |
| | | page.size = obj.limit |
| | | getList() |
| | | } |
| | | |
| | | // è¡¨æ ¼éæ©åå |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection |
| | | } |
| | | |
| | | // æ°å¢ |
| | | const handleAdd = () => { |
| | | operationType.value = 'add' |
| | | // resetForm() |
| | | dialogFormVisible.value = true |
| | | } |
| | | |
| | | // ç¼è¾ |
| | | const handleEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(form, row) |
| | | dialogFormVisible.value = true |
| | | } |
| | | |
| | | // å¤çé¢è¦ |
| | | const handleProcess = async (row) => { |
| | | try { |
| | | // 模æAPIè°ç¨å»¶è¿ |
| | | await new Promise(resolve => setTimeout(resolve, 300)) |
| | | ElMessage.success(`æ£å¨å¤çé¢è¦ï¼${row.tankName}`) |
| | | // getList() |
| | | } catch (error) { |
| | | ElMessage.error('å¤çé¢è¦å¤±è´¥') |
| | | } |
| | | } |
| | | |
| | | // å é¤ |
| | | const handleDelete = async (row) => { |
| | | try { |
| | | await ElMessageBox.confirm(`ç¡®å®è¦å é¤é¢è¦è§åï¼${row.tankName}åï¼`, 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }) |
| | | let ids = []; |
| | | ids.push(row.id); |
| | | deleteStockWarning(ids).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("å 餿å"); |
| | | ids.value = []; |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | // // 模æAPIè°ç¨å»¶è¿ |
| | | // await new Promise(resolve => setTimeout(resolve, 300)) |
| | | // ElMessage.success('å 餿å') |
| | | // getList() |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error('å é¤å¤±è´¥') |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æ¹éå¤ç |
| | | const handleBatchProcess = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | | ElMessage.warning('è¯·éæ©è¦å¤ççé¢è¦') |
| | | return |
| | | } |
| | | |
| | | try { |
| | | // 模æAPIè°ç¨å»¶è¿ |
| | | await new Promise(resolve => setTimeout(resolve, 500)) |
| | | ElMessage.success(`æ¹éå¤çäº ${selectedRows.value.length} æ¡é¢è¦`) |
| | | getList() |
| | | } catch (error) { |
| | | ElMessage.error('æ¹éå¤ç失败') |
| | | } |
| | | } |
| | | |
| | | // å¯¼åº |
| | | const handleExport = async () => { |
| | | // if (selectedRows.value.length === 0) { |
| | | // exportStockWarning().then(res => { |
| | | // // // å建ä¸è½½é¾æ¥ |
| | | // // const blob = new Blob([res.data], { type: 'text/csv;charset=utf-8;' }) |
| | | // // const url = window.URL.createObjectURL(blob) |
| | | // // const link = document.createElement('a') |
| | | // // link.href = url |
| | | // // link.download = `卿°ç½é¢è¦æ°æ®_${new Date().getTime()}.csv` |
| | | // // link.click() |
| | | // // window.URL.revokeObjectURL(url) |
| | | // }).catch(err => { |
| | | // ElMessage.error(err.msg); |
| | | // }) |
| | | // }else{ |
| | | // let ids = []; |
| | | // selectedRows.value.forEach(item => { |
| | | // ids.push(item.id); |
| | | // }) |
| | | // exportStockWarning(ids).then(res => { |
| | | // // // å建ä¸è½½é¾æ¥ |
| | | // // const blob = new Blob([res.data], { type: 'text/csv;charset=utf-8;' }) |
| | | // // const url = window.URL.createObjectURL(blob) |
| | | // // const link = document.createElement('a') |
| | | // // link.href = url |
| | | // // link.download = `卿°ç½é¢è¦æ°æ®_${new Date().getTime()}.csv` |
| | | // // link.click() |
| | | // // window.URL.revokeObjectURL(url) |
| | | // }).catch(err => { |
| | | // ElMessage.error(err.msg); |
| | | // }) |
| | | // } |
| | | |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/gasTankWarning/export", {ids: selectedRows.value.map(item => item.id)}, "卿°ç½é¢è¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | } |
| | | |
| | | |
| | | |
| | | // // å¯ç¨ç¶æåå |
| | | const handleEnableChange = async (row) => { |
| | | |
| | | try { |
| | | updateStockWarning(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success(`${row.tankName} çå¯ç¨ç¶æå·²æ´æ°`); |
| | | getList(); |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | } catch (error) { |
| | | ElMessage.error('ç¶ææ´æ°å¤±è´¥') |
| | | // æ¢å¤åç¶æ |
| | | row.isEnabled = !row.isEnabled |
| | | } |
| | | } |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = async () => { |
| | | try { |
| | | await proxy.$refs.formRef.validate() |
| | | |
| | | // 模æAPIè°ç¨å»¶è¿ |
| | | // await new Promise(resolve => setTimeout(resolve, 500)) |
| | | |
| | | if (operationType.value === 'add') { |
| | | addStockWarning(form).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("æ·»å æå"); |
| | | dialogFormVisible.value = false |
| | | getList() |
| | | resetForm() |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | |
| | | // ElMessage.success('æ°å¢æå') |
| | | } else { |
| | | updateStockWarning(form).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success("æ´æ°æå"); |
| | | dialogFormVisible.value = false |
| | | getList() |
| | | resetForm() |
| | | } |
| | | }).catch(err => { |
| | | ElMessage.error(err.msg); |
| | | }) |
| | | // ElMessage.success('ç¼è¾æå') |
| | | } |
| | | |
| | | // closeDialog() |
| | | // getList() |
| | | } catch (error) { |
| | | if (!error.errors) { |
| | | ElMessage.error(operationType.value === 'add' ? 'æ°å¢å¤±è´¥' : 'ç¼è¾å¤±è´¥') |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å
³éå¼¹çª |
| | | const closeDialog = () => { |
| | | dialogFormVisible.value = false |
| | | // resetForm() |
| | | } |
| | | |
| | | // é置表å |
| | | const resetForm = () => { |
| | | Object.keys(form).forEach(key => { |
| | | if (key === 'isEnabled') { |
| | | form[key] = true |
| | | } else if (typeof form[key] === 'number') { |
| | | form[key] = 0 |
| | | } else { |
| | | form[key] = '' |
| | | } |
| | | }) |
| | | proxy.$refs.formRef?.resetFields() |
| | | } |
| | | |
| | | // è·åæ°ä½éæ ·å¼ç±» |
| | | const getGasLevelClass = (row) => { |
| | | if (row.currentGasLevel < row.minGasLevel) { |
| | | return 'text-danger' |
| | | } else if (row.currentGasLevel > row.maxGasLevel) { |
| | | return 'text-warning' |
| | | } |
| | | return 'text-success' |
| | | } |
| | | |
| | | // è·åé¢è¦ç±»åæ ç¾æ ·å¼ |
| | | const getWarningTypeTag = (type) => { |
| | | const typeMap = { |
| | | 'æ°ä½ä¸è¶³': 'danger', |
| | | 'ååå¼å¸¸': 'warning', |
| | | '温度å¼å¸¸': 'info', |
| | | 'æ³æ¼é¢è¦': 'danger' |
| | | } |
| | | return typeMap[type] || 'info' |
| | | } |
| | | |
| | | // è·åé¢è¦çº§å«æ ç¾æ ·å¼ |
| | | const getWarningLevelTag = (level) => { |
| | | const levelMap = { |
| | | 'ç´§æ¥': 'danger', |
| | | 'éè¦': 'warning', |
| | | 'ä¸è¬': 'info' |
| | | } |
| | | return levelMap[level] || 'info' |
| | | } |
| | | |
| | | // å¯å¨å计æ¶å®æ¶å¨ |
| | | const startCountdownTimer = () => { |
| | | countdownTimer.value = setInterval(() => { |
| | | checkShortageWarnings() |
| | | }, 60000) // æ¯å鿣æ¥ä¸æ¬¡ |
| | | } |
| | | |
| | | // 忢å计æ¶å®æ¶å¨ |
| | | const stopCountdownTimer = () => { |
| | | if (countdownTimer.value) { |
| | | clearInterval(countdownTimer.value) |
| | | countdownTimer.value = null |
| | | } |
| | | } |
| | | |
| | | // 页é¢å è½½ |
| | | onMounted(() => { |
| | | getList() |
| | | startCountdownTimer() |
| | | }) |
| | | |
| | | // 页é¢å¸è½½ |
| | | onUnmounted(() => { |
| | | stopCountdownTimer() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | padding: 20px; |
| | | |
| | | .table-operations { |
| | | text-align: right; |
| | | margin-bottom: 20px; |
| | | |
| | | .el-button { |
| | | margin-right: 10px; |
| | | } |
| | | } |
| | | |
| | | .table_list { |
| | | background: #fff; |
| | | border-radius: 4px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-warning { |
| | | color: #e6a23c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | // åè®¡æ¶æ ·å¼ |
| | | .countdown-timer { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .countdown-normal { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .countdown-warning { |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | .countdown-urgent { |
| | | color: #f56c6c; |
| | | animation: blink 1s infinite; |
| | | } |
| | | |
| | | .countdown-expired { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | @keyframes blink { |
| | | 0%, 50% { opacity: 1; } |
| | | 51%, 100% { opacity: 0.5; } |
| | | } |
| | | |
| | | // 缺æ°é¢è¦å¼¹æ¡æ ·å¼ |
| | | .shortage-warning-content { |
| | | text-align: center; |
| | | padding: 20px 0; |
| | | |
| | | .warning-icon { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .warning-message { |
| | | h3 { |
| | | color: #f56c6c; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | p { |
| | | margin-bottom: 10px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .warning-details { |
| | | background: #f5f7fa; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | text-align: left; |
| | | font-size: 14px; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="searchForm" style="width: 100%"> |
| | | <el-row justify="space-between"> |
| | | <el-col :span="20"> |
| | | <el-form-item label="ä¾åºååç§°/ååå·"> |
| | | <el-input |
| | | v-model="searchForm.supplierNameOrContractNo" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥ä¾åºååç§°/ååå·æç´¢" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-checkbox |
| | | v-model="searchForm.status" |
| | | label="䏿¾ç¤ºå¾
仿¬¾ä¸º0" |
| | | @change="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item style="float: right; margin-right: unset"> |
| | | <el-button type="primary" @click="openForm('add')"> |
| | | æ°å¢ä»æ¬¾ |
| | | </el-button> |
| | | <!-- <el-button type="danger" plain @click="handleDelete">--> |
| | | <!-- å é¤--> |
| | | <!-- </el-button>--> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :expandRowKeys="expandedRowKeys" |
| | | :isSelection="true" |
| | | :isShowSummary="isShowSummarySon" |
| | | :summaryMethod="summarizeMainTable1" |
| | | @selection-change="handleSelectionChange" |
| | | @expand-change="expandChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | > |
| | | <template #expand="{ row }"> |
| | | <el-table |
| | | :data="expandData" |
| | | border |
| | | show-summary |
| | | v-loading="childrenLoading" |
| | | :summary-method="summarizeMainTable2" |
| | | stripe |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="仿¬¾æ¥æ" prop="paymentDate" /> |
| | | <el-table-column label="仿¬¾éé¢" prop="currentPaymentAmount"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" |
| | | v-model="scope.row.currentPaymentAmount" |
| | | :disabled="!scope.row.editType" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="仿¬¾æ¹å¼" prop="paymentMethod"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | :disabled="!scope.row.editType" |
| | | v-model="scope.row.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç»è®°äºº" prop="registrant" /> |
| | | <el-table-column label="ç»è®°æ¥æ" prop="registrationtDate" /> |
| | | <el-table-column label="æä½" width="150"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="changeEditType(scope.row)" |
| | | v-if="!scope.row.editType" |
| | | :disabled="scope.row.registrant !== userStore.nickName" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="saveReceiptPayment(scope.row)" |
| | | v-if="scope.row.editType" |
| | | :disabled="scope.row.registrant !== userStore.nickName" |
| | | >ä¿å</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="handleDelete(scope.row)" |
| | | :disabled="scope.row.registrant !== userStore.nickName" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢ä»æ¬¾ç»è®°' : 'ç¼è¾ä»æ¬¾ç»è®°'" |
| | | width="60%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form |
| | | :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ååå·ï¼" prop="purchaseContractNumber"> |
| | | <el-input |
| | | v-model="form.purchaseContractNumber" |
| | | placeholder="èªå¨å¡«å
" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" prop="salesContractNo"> |
| | | <el-input |
| | | v-model="form.salesContractNo" |
| | | placeholder="èªå¨å¡«å
" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼" prop="supplierName"> |
| | | <el-input |
| | | v-model="form.supplierName" |
| | | placeholder="èªå¨å¡«å
" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票å·ï¼" prop="invoiceNumber"> |
| | | <el-input |
| | | v-model="form.invoiceNumber" |
| | | placeholder="èªå¨å¡«å
" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票éé¢(å
)ï¼" prop="invoiceAmount"> |
| | | <el-input |
| | | v-model="form.invoiceAmount" |
| | | placeholder="èªå¨å¡«å
" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¬æ¬¡ä»æ¬¾éé¢ï¼" prop="currentPaymentAmount"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" |
| | | :precision="2" |
| | | v-model="form.currentPaymentAmount" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼ï¼" prop="paymentMethod"> |
| | | <el-input |
| | | v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¥æï¼" prop="paymentDate"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="form.paymentDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»è®°äººï¼" prop="registrant"> |
| | | <el-input |
| | | v-model="form.registrant" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»è®°æ¥æï¼" prop="registrationtDate"> |
| | | <el-input |
| | | v-model="form.registrationtDate" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { |
| | | byPurchaseId, |
| | | paymentRegistrationAdd, |
| | | paymentRegistrationDel, |
| | | paymentRegistrationEdit, |
| | | getTicketNo, |
| | | } from "@/api/procurementManagement/paymentEntry.js"; |
| | | import { |
| | | delPaymentRegistration, |
| | | invoiceListPage, |
| | | registrationListPageGetById, |
| | | updatePaymentRegistration |
| | | } from "@/api/procurementManagement/procurementInvoiceLedger.js"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableColumn = ref([ |
| | | { |
| | | type: "expand", |
| | | dataType: "slot", |
| | | slot: "expand", |
| | | }, |
| | | { |
| | | label: "éè´ååå·", |
| | | prop: "purchaseContractNumber", |
| | | }, |
| | | { |
| | | label: "éå®ååå·", |
| | | prop: "salesContractNo", |
| | | }, |
| | | { |
| | | label: "ä¾åºååç§°", |
| | | prop: "supplierName", |
| | | width:240 |
| | | }, |
| | | { |
| | | label: "å票å·", |
| | | prop: "invoiceNumber", |
| | | width:200 |
| | | }, |
| | | { |
| | | label: "å票éé¢(å
)", |
| | | prop: "invoiceAmount", |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "已仿¬¾éé¢(å
)", |
| | | prop: "paymentAmountTotal", |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å¾
仿¬¾éé¢(å
)", |
| | | prop: "unPaymentAmountTotal", |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å½å
¥äºº", |
| | | prop: "issUer", |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const expandData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const childrenLoading = ref(false); |
| | | const userStore = useUserStore(); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierNameOrContractNo: "", |
| | | status: false, |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | | purchaseLedgerId: "", |
| | | salesContractNo: "", |
| | | supplierName: "", |
| | | invoiceNumber: "", |
| | | invoiceAmount: "", |
| | | taxRate: "", |
| | | currentPaymentAmount: "", |
| | | paymentMethod: "", |
| | | registrant: "", |
| | | registrantId: "", |
| | | paymentDate: "", |
| | | registrationtDate: "", |
| | | }, |
| | | rules: { |
| | | purchaseLedgerId: [ |
| | | { required: true, message: "è¯·éæ©", trigger: "change" }, |
| | | ], |
| | | currentPaymentAmount: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | paymentMethod: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | invoiceNumber: [ |
| | | { required: true, message: "è¯·éæ©éè´ååå·", trigger: "change" }, |
| | | ], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm, resetForm } = useFormData(data.searchForm); |
| | | const isShowSummarySon = ref(true); |
| | | const expandedRowKeys = ref([]); |
| | | |
| | | |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = (param) => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["invoiceAmount", "paymentAmountTotal", "unPaymentAmountTotal"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable2 = (param) => { |
| | | return proxy.summarizeTable(param, ["currentPaymentAmount"], { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | }); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | invoiceListPage({ ...searchForm, ...page }).then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.records; |
| | | page.total = res.total; |
| | | if (expandedRowKeys.value.length > 0) { |
| | | const arr = [] |
| | | const index = tableData.value.findIndex(item => item.id === expandedRowKeys.value[0]); |
| | | if (index > -1) { |
| | | arr.push(tableData.value[index]); |
| | | expandChange(tableData.value[index], arr) |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | nextTick(() => { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | childrenLoading.value = true; |
| | | registrationListPageGetById({ id: row.id }).then((res) => { |
| | | childrenLoading.value = false; |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | expandData.value = res; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | childrenLoading.value = false; |
| | | console.log(error); |
| | | } |
| | | }) |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | // ç¼è¾ä¿®æ¹ç¶æ |
| | | const changeEditType = (row) => { |
| | | row.editType = !row.editType; |
| | | }; |
| | | // ä¿å忬¾è®°å½ |
| | | const saveReceiptPayment = (row) => { |
| | | let updateData = { |
| | | id: row.id, |
| | | currentPaymentAmount: row.currentPaymentAmount, |
| | | paymentMethod: row.paymentMethod, |
| | | }; |
| | | updatePaymentRegistration(updateData).then((res) => { |
| | | row.editType = !row.editType; |
| | | getList(); |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$message.error("è¯·éæ©ä¸æ¡åç¥¨æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].unPaymentAmountTotal == 0) { |
| | | proxy.$message.warning("æ éå仿¬¾"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | form.value = { ...selectedRows.value[0] }; |
| | | form.value.ticketRegistrationId = selectedRows.value[0].id; |
| | | form.value.id = null; |
| | | // æ¥è¯¢éè´ååå· |
| | | form.value.registrationtDate = getCurrentDate(); |
| | | form.value.paymentDate = getCurrentDate(); |
| | | form.value.registrant = userStore.name; |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitEdit(); |
| | | } else { |
| | | submitAdd(); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | // æäº¤æ°å¢ |
| | | const submitAdd = () => { |
| | | paymentRegistrationAdd(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | }; |
| | | // æäº¤ä¿®æ¹ |
| | | const submitEdit = () => { |
| | | paymentRegistrationEdit(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delPaymentRegistration(row.id) |
| | | .then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | getList(); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | ::v-deep(.el-checkbox__label) { |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-form :model="form"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ååå·ï¼"> |
| | | <el-tag size="large">{{ form.purchaseContractNumber }}</el-tag> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-text>{{ form.salesContractNo }}</el-text> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨åä»·(å
)ï¼"> |
| | | <el-text type="primary">{{ form.taxInclusiveUnitPrice }}</el-text> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å建æ¶é´ï¼"> |
| | | <el-text>{{ form.createdAt }}</el-text> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票å·ï¼"> |
| | | <el-input v-model="form.invoiceNumber" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¥ç¥¨æ°ï¼"> |
| | | <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsNum" @change="inputTicketsNum" :precision="2"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¬æ¬¡æ¥ç¥¨éé¢(å
)ï¼"> |
| | | <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsAmount" @change="inputTicketsAmount" :precision="2"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æªæ¥ç¥¨æ°ï¼"> |
| | | <el-text type="success">{{ form.futureTickets }}</el-text> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import { getProductRecordById } from "@/api/procurementManagement/procurementInvoiceLedger"; |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | defineOptions({ |
| | | name: "æ¥ç¥¨å°è´¦è¡¨å", |
| | | }); |
| | | const temFutureTickets = ref(0) // åå§æªæ¥ç¥¨æ° |
| | | const initialTicketsNum = ref(0) // åå§æ¥ç¥¨æ° |
| | | const initialTicketsAmount = ref(0) // åå§æ¥ç¥¨éé¢ |
| | | const quantity = ref(0) // æ»æ°é |
| | | const { form, resetForm } = useFormData({ |
| | | id: undefined, |
| | | purchaseContractNumber: undefined, // éè´ååå· |
| | | salesContractNo: undefined, // éå®ååå· |
| | | createdAt: undefined, // å建æ¶é´ |
| | | invoiceNumber: undefined, // åç¥¨å· |
| | | ticketsNum: undefined, // æ¥ç¥¨æ° |
| | | ticketsAmount: undefined, // æ¥ç¥¨éé¢ |
| | | taxInclusiveUnitPrice: undefined, // å«ç¨åä»· |
| | | ticketRegistrationId: undefined, // å«ç¨åä»· |
| | | }); |
| | | |
| | | const load = async (id) => { |
| | | const { code, data } = await getProductRecordById({ id }); |
| | | if (code === 200) { |
| | | form.id = data.id; |
| | | form.purchaseContractNumber = data.purchaseContractNumber; |
| | | form.salesContractNo = data.salesContractNo; |
| | | form.createdAt = data.createdAt; |
| | | form.invoiceNumber = data.invoiceNumber; |
| | | form.ticketsNum = data.ticketsNum; |
| | | form.ticketsAmount = data.ticketsAmount ? Number(data.ticketsAmount).toFixed(2) : 0; |
| | | form.taxInclusiveUnitPrice = data.taxInclusiveUnitPrice; |
| | | form.futureTickets = data.futureTickets; |
| | | temFutureTickets.value = data.futureTickets; |
| | | initialTicketsNum.value = data.ticketsNum || 0; |
| | | initialTicketsAmount.value = data.ticketsAmount || 0; |
| | | form.ticketRegistrationId = data.ticketRegistrationId; |
| | | // è·åæ»æ°éï¼å¦ææ°æ®ä¸æ quantity åæ®µå使ç¨ï¼å¦åä½¿ç¨æ¥ç¥¨æ°+æªæ¥ç¥¨æ° |
| | | quantity.value = data.quantity || (Number(data.ticketsNum || 0) + Number(data.futureTickets || 0)); |
| | | } |
| | | }; |
| | | |
| | | const inputTicketsNum = (val) => { |
| | | // ç¡®ä¿å«ç¨åä»·åå¨ä¸ä¸ä¸ºé¶ |
| | | if (!form.taxInclusiveUnitPrice || Number(form.taxInclusiveUnitPrice) === 0) { |
| | | proxy.$modal.msgWarning("å«ç¨åä»·ä¸è½ä¸ºé¶ææªå®ä¹"); |
| | | return; |
| | | } |
| | | |
| | | const newTicketsNum = Number(form.ticketsNum) || 0; |
| | | const currentTicketsNum = Number(initialTicketsNum.value) || 0; |
| | | |
| | | // è®¡ç®æ°å¢çæ¥ç¥¨æ° |
| | | const addedTicketsNum = newTicketsNum - currentTicketsNum; |
| | | |
| | | // è®¡ç®æ°çæªæ¥ç¥¨æ° = åå§æªæ¥ç¥¨æ° - æ°å¢çæ¥ç¥¨æ° |
| | | const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum; |
| | | |
| | | // éªè¯ï¼æ°çæ¥ç¥¨æ° + æ°çæªæ¥ç¥¨æ° ⤠quantity |
| | | if (newTicketsNum + newFutureTickets > Number(quantity.value)) { |
| | | proxy.$modal.msgWarning(`æ¥ç¥¨æ°+æªæ¥ç¥¨æ°ä¸è½å¤§äºæ»æ°é(${quantity.value})`); |
| | | // éå¶æ¥ç¥¨æ°ï¼ä½¿å
¶æ»¡è¶³ï¼æ¥ç¥¨æ° + æªæ¥ç¥¨æ° ⤠quantity |
| | | // æå¤§æ¥ç¥¨æ° = quantity - åå§æªæ¥ç¥¨æ° + åå§æ¥ç¥¨æ° |
| | | const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value); |
| | | form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum)); |
| | | // éæ°è®¡ç® |
| | | const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value); |
| | | const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum; |
| | | form.futureTickets = Number(recalculatedFutureTickets.toFixed(2)); |
| | | const ticketsAmount = Number(form.ticketsNum) * Number(form.taxInclusiveUnitPrice); |
| | | form.ticketsAmount = Number(ticketsAmount.toFixed(2)); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æ°å¢çæ¥ç¥¨æ°æ¯å¦å¤§äºåå§æªæ¥ç¥¨æ° |
| | | if (addedTicketsNum > Number(temFutureTickets.value)) { |
| | | proxy.$modal.msgWarning("æ°å¢å¼ç¥¨æ°ä¸å¾å¤§äºæªå¼ç¥¨æ°"); |
| | | form.ticketsNum = Number(initialTicketsNum.value) + Number(temFutureTickets.value); |
| | | } |
| | | |
| | | // ç¡®ä¿æææ°å¼é½è½¬æ¢ä¸ºæ°åç±»åè¿è¡è®¡ç® |
| | | const finalTicketsNum = Number(form.ticketsNum) || 0; |
| | | const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value); |
| | | const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum; |
| | | const ticketsAmount = finalTicketsNum * Number(form.taxInclusiveUnitPrice); |
| | | form.futureTickets = Number(finalFutureTickets.toFixed(2)); |
| | | form.ticketsAmount = Number(ticketsAmount.toFixed(2)); |
| | | }; |
| | | const inputTicketsAmount = (val) => { |
| | | // ç¡®ä¿å«ç¨åä»·åå¨ä¸ä¸ä¸ºé¶ |
| | | if (!form.taxInclusiveUnitPrice || Number(form.taxInclusiveUnitPrice) === 0) { |
| | | proxy.$modal.msgWarning("å«ç¨åä»·ä¸è½ä¸ºé¶ææªå®ä¹"); |
| | | return; |
| | | } |
| | | |
| | | const newTicketsAmount = Number(val) || 0; |
| | | |
| | | // è®¡ç®æ°çæ¥ç¥¨æ° |
| | | const newTicketsNum = newTicketsAmount / Number(form.taxInclusiveUnitPrice); |
| | | const currentTicketsNum = Number(initialTicketsNum.value) || 0; |
| | | |
| | | // è®¡ç®æ°å¢çæ¥ç¥¨æ° |
| | | const addedTicketsNum = newTicketsNum - currentTicketsNum; |
| | | |
| | | // è®¡ç®æ°çæªæ¥ç¥¨æ° = åå§æªæ¥ç¥¨æ° - æ°å¢çæ¥ç¥¨æ° |
| | | const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum; |
| | | |
| | | // éªè¯ï¼æ°çæ¥ç¥¨æ° + æ°çæªæ¥ç¥¨æ° ⤠quantity |
| | | if (newTicketsNum + newFutureTickets > Number(quantity.value)) { |
| | | proxy.$modal.msgWarning(`æ¥ç¥¨æ°+æªæ¥ç¥¨æ°ä¸è½å¤§äºæ»æ°é(${quantity.value})`); |
| | | // éå¶æ¥ç¥¨æ°ï¼ä½¿å
¶æ»¡è¶³ï¼æ¥ç¥¨æ° + æªæ¥ç¥¨æ° ⤠quantity |
| | | const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value); |
| | | form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum)); |
| | | form.ticketsAmount = Number((form.ticketsNum * Number(form.taxInclusiveUnitPrice)).toFixed(2)); |
| | | const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value); |
| | | const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum; |
| | | form.futureTickets = Number(recalculatedFutureTickets.toFixed(2)); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æ°å¢çæ¥ç¥¨é颿¯å¦å¤§äºåå§æªæ¥ç¥¨æ°å¯¹åºçéé¢ |
| | | const maxAddedAmount = Number(temFutureTickets.value * form.taxInclusiveUnitPrice); |
| | | if (addedTicketsNum > 0 && addedTicketsNum * Number(form.taxInclusiveUnitPrice) > maxAddedAmount) { |
| | | proxy.$modal.msgWarning("æ°å¢æ¥ç¥¨éé¢ä¸å¾å¤§äºæªå¼ç¥¨éé¢"); |
| | | form.ticketsAmount = Number((initialTicketsAmount.value + maxAddedAmount).toFixed(2)); |
| | | form.ticketsNum = Number((currentTicketsNum + Number(temFutureTickets.value)).toFixed(2)); |
| | | form.futureTickets = 0; |
| | | return; |
| | | } |
| | | |
| | | // ç¡®ä¿æææ°å¼é½è½¬æ¢ä¸ºæ°åç±»åè¿è¡è®¡ç® |
| | | const finalTicketsNum = Number(newTicketsNum.toFixed(2)); |
| | | const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value); |
| | | const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum; |
| | | form.ticketsNum = finalTicketsNum; |
| | | form.futureTickets = Number(finalFutureTickets.toFixed(2)); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | load, |
| | | form, |
| | | resetForm, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="ä¾åºååç§°ï¼"> |
| | | <el-input v-model="searchForm.supplierName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="éè´ååå·ï¼"> |
| | | <el-input |
| | | v-model="searchForm.purchaseContractNumber" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°ï¼"> |
| | | <el-input v-model="searchForm.projectName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="å½å
¥æ¥æï¼"> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;"> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢å°è´¦</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" |
| | | height="calc(100vh - 18.5em)" |
| | | stripe |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table |
| | | :data="props.row.children" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable" |
| | | stripe |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column |
| | | label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | /> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column |
| | | label="éè´ååå·" |
| | | prop="purchaseContractNumber" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ä¾åºååç§°" |
| | | width="240" |
| | | prop="supplierName" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="项ç®åç§°" |
| | | prop="projectName" |
| | | width="420" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="仿¬¾æ¹å¼" |
| | | width="100" |
| | | prop="paymentMethod" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="ååéé¢(å
)" |
| | | prop="contractAmount" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="å½å
¥äºº" |
| | | prop="recorderName" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="å½å
¥æ¥æ" |
| | | prop="entryDate" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | fixed="right" |
| | | label="æä½" |
| | | min-width="100" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="openForm('edit', scope.row)" |
| | | :disabled="scope.row.receiptPaymentAmount>0 || scope.row.recorderName !== userStore.nickName" |
| | | >ç¼è¾</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢éè´å°è´¦é¡µé¢' : 'ç¼è¾éè´å°è´¦é¡µé¢'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form |
| | | :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´ååå·ï¼" prop="purchaseContractNumber"> |
| | | <el-input |
| | | v-model="form.purchaseContractNumber" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" prop="salesContractNo"> |
| | | <el-input |
| | | v-model="form.salesContractNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºååç§°ï¼" prop="supplierId"> |
| | | <el-select |
| | | v-model="form.supplierId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项ç®åç§°ï¼" prop="projectName"> |
| | | <el-input |
| | | v-model="form.projectName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥äººï¼" prop="recorderId"> |
| | | <el-select |
| | | v-model="form.recorderId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | disabled |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥æ¥æï¼" prop="entryDate"> |
| | | <el-date-picker |
| | | disabled |
| | | style="width: 100%" |
| | | v-model="form.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-input |
| | | v-model="form.paymentMethod" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> |
| | | <el-button type="primary" @click="openProductForm('add')" |
| | | >æ·»å </el-button |
| | | > |
| | | <el-button plain type="danger" @click="deleteProduct" |
| | | >å é¤</el-button |
| | | > |
| | | </el-form-item> |
| | | </el-row> |
| | | <el-table |
| | | :data="productData" |
| | | border |
| | | @selection-change="productSelected" |
| | | show-summary |
| | | stripe |
| | | :summary-method="summarizeProTable" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" width="70" /> |
| | | <el-table-column label="æ°é" prop="quantity" width="70" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" width="80" /> |
| | | <el-table-column |
| | | label="å«ç¨åä»·(å
)" |
| | | prop="taxInclusiveUnitPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" |
| | | /> |
| | | <el-table-column |
| | | label="å«ç¨æ»ä»·(å
)" |
| | | prop="taxInclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" |
| | | /> |
| | | <el-table-column |
| | | label="ä¸å«ç¨æ»ä»·(å
)" |
| | | prop="taxExclusiveTotalPrice" |
| | | :formatter="formattedNumber" |
| | | width="150" |
| | | /> |
| | | <el-table-column |
| | | fixed="right" |
| | | label="æä½" |
| | | min-width="60" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="openProductForm('edit', scope.row, scope.$index)" |
| | | >ç¼è¾</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨Â·ï¼" prop="remark"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | type="textarea" |
| | | :rows="2" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼" prop="remark"> |
| | | <el-upload |
| | | v-model:file-list="fileList" |
| | | :action="upload.url" |
| | | multiple |
| | | ref="fileUpload" |
| | | auto-upload |
| | | :headers="upload.headers" |
| | | :before-upload="handleBeforeUpload" |
| | | :on-error="handleUploadError" |
| | | :on-success="handleUploadSuccess" |
| | | :on-remove="handleRemove" |
| | | > |
| | | <el-button type="primary">ä¸ä¼ </el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | æä»¶æ ¼å¼æ¯æ |
| | | docï¼docxï¼xlsï¼xlsxï¼pptï¼pptxï¼pdfï¼txtï¼xmlï¼jpgï¼jpegï¼pngï¼gifï¼bmpï¼rarï¼zipï¼7z |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog |
| | | v-model="productFormVisible" |
| | | :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" |
| | | width="40%" |
| | | @close="closeProductDia" |
| | | > |
| | | <el-form |
| | | :model="productForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="productRules" |
| | | ref="productFormRef" |
| | | > |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产å大类ï¼" prop="productId"> |
| | | <el-tree-select |
| | | v-model="productForm.productId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | check-strictly |
| | | @change="getModels" |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> |
| | | <el-select |
| | | v-model="productForm.productModelId" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="getProductModel" |
| | | > |
| | | <el-option |
| | | v-for="item in modelOptions" |
| | | :key="item.id" |
| | | :label="item.model" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input |
| | | v-model="productForm.unit" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç(%)ï¼" prop="taxRate"> |
| | | <el-select |
| | | v-model="productForm.taxRate" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | @change="mathNum" |
| | | > |
| | | <el-option label="1" value="1" /> |
| | | <el-option label="6" value="6" /> |
| | | <el-option label="13" value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨åä»·(å
)ï¼" prop="taxInclusiveUnitPrice"> |
| | | <el-input-number |
| | | v-model="productForm.taxInclusiveUnitPrice" |
| | | :precision="2" |
| | | :step="0.1" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="mathNum" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number |
| | | :step="0.1" |
| | | clearable |
| | | :precision="2" |
| | | style="width: 100%" |
| | | v-model="productForm.quantity" |
| | | placeholder="请è¾å
¥" |
| | | @change="mathNum" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨æ»ä»·(å
)ï¼" prop="taxInclusiveTotalPrice"> |
| | | <el-input-number |
| | | v-model="productForm.taxInclusiveTotalPrice" |
| | | :precision="2" |
| | | :step="0.1" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="reverseMathNum('taxInclusiveTotalPrice')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item |
| | | label="ä¸å«ç¨æ»ä»·(å
)ï¼" |
| | | prop="taxExclusiveTotalPrice" |
| | | > |
| | | <el-input |
| | | v-model="productForm.taxExclusiveTotalPrice" |
| | | @change="reverseMathNum('taxExclusiveTotalPrice')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类åï¼" prop="invoiceType"> |
| | | <el-select |
| | | v-model="productForm.invoiceType" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | > |
| | | <el-option label="墿®ç¥¨" value="墿®ç¥¨" /> |
| | | <el-option label="å¢ä¸ç¥¨" value="å¢ä¸ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitProduct">确认</el-button> |
| | | <el-button @click="closeProductDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | getSalesLedgerWithProducts, |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { |
| | | addOrEditPurchase, |
| | | delPurchase, |
| | | purchaseListPage, |
| | | productList, |
| | | getPurchaseById, |
| | | getOptions, |
| | | createPurchaseNo, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const productData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const productSelectedRows = ref([]); |
| | | const modelOptions = ref([]); |
| | | const userList = ref([]); |
| | | const productOptions = ref([]); |
| | | const supplierList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", // ä¾åºååç§° |
| | | purchaseContractNumber: "", // éè´ååç¼å· |
| | | salesContractNo: "", // éå®ååç¼å· |
| | | projectName: "", // 项ç®åç§° |
| | | entryDate: [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ], // å½å
¥æ¥æ |
| | | entryDateStart: dayjs().format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | | salesContractNo: "", |
| | | projectName: "", |
| | | recorderId: "", |
| | | entryDate: "", |
| | | productData: [], |
| | | supplierName: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | projectName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | | const productFormVisible = ref(false); |
| | | const productOperationType = ref(""); |
| | | const productOperationIndex = ref(""); |
| | | const currentId = ref(""); |
| | | const productFormData = reactive({ |
| | | productForm: { |
| | | productId: "", |
| | | productCategory: "", |
| | | productModelId: "", |
| | | specificationModel: "", |
| | | unit: "", |
| | | quantity: "", |
| | | taxInclusiveUnitPrice: "", |
| | | taxRate: "", |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | }, |
| | | productRules: { |
| | | productId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxExclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { productForm, productRules } = toRefs(productFormData); |
| | | const upload = reactive({ |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }); |
| | | |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.entryDateStart = undefined; |
| | | searchForm.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeChildrenTable = (param) => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | "ticketsNum", |
| | | "ticketsAmount", |
| | | "futureTickets", |
| | | "futureTicketsAmount", |
| | | ], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { entryDate, ...rest } = searchForm; |
| | | purchaseListPage({ ...rest, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value.map((item) => { |
| | | item.children = []; |
| | | }); |
| | | total.value = res.data.total; |
| | | expandedRowKeys.value = []; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const productSelected = (selectedRows) => { |
| | | productSelectedRows.value = selectedRows; |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | productList({ salesLedgerId: row.id, type: 2 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res.data; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, ["contractAmount"]); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeProTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | fileList.value = []; |
| | | if (operationType.value == "add") { |
| | | createPurchaseNo().then((res) => { |
| | | form.value.purchaseContractNumber = res.data; |
| | | }); |
| | | } |
| | | userListNoPage().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | form.value.recorderId = userStore.id; |
| | | form.value.entryDate = getCurrentDate(); |
| | | if (type === "edit") { |
| | | currentId.value = row.id; |
| | | getPurchaseById({ id: row.id, type: 2 }).then((res) => { |
| | | form.value = { ...res }; |
| | | productData.value = form.value.productData; |
| | | if (form.value.salesLedgerFiles) { |
| | | fileList.value = form.value.salesLedgerFiles; |
| | | } else { |
| | | fileList.value = []; |
| | | } |
| | | }); |
| | | } |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | if (file.size > 1024 * 1024 * 50) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿50MB!"); |
| | | return false; |
| | | } |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | file.tempId = res.data.tempId; |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | console.log("handleRemove", file.id); |
| | | if (file.size > 1024 * 1024 * 10) { |
| | | // ä»
å端æ¸
çï¼ä¸è°ç¨å 餿¥å£åæç¤º |
| | | return; |
| | | } |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | } |
| | | } |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | if (valid) { |
| | | if (productData.value.length > 0) { |
| | | form.value.productData = proxy.HaveJson(productData.value); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); |
| | | return; |
| | | } |
| | | let tempFileIds = []; |
| | | if (fileList.value.length > 0) { |
| | | tempFileIds = fileList.value.map((item) => item.tempId); |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 2; |
| | | addOrEditPurchase(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProductForm = (type, row, index) => { |
| | | productOperationType.value = type; |
| | | productOperationIndex.value = index; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | } |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | }; |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const getModels = (value) => { |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | }; |
| | | const getProductModel = (value) => { |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.unit = null; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode.label; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitProduct = () => { |
| | | proxy.$refs["productFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitProductEdit(); |
| | | } else { |
| | | if (productOperationType.value === "add") { |
| | | productData.value.push({ ...productForm.value }); |
| | | console.log("productData.value---", productData.value); |
| | | } else { |
| | | productData.value[productOperationIndex.value] = { |
| | | ...productForm.value, |
| | | }; |
| | | } |
| | | closeProductDia(); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | const submitProductEdit = () => { |
| | | productForm.value.salesLedgerId = currentId.value; |
| | | productForm.value.type = 2; |
| | | addOrUpdateSalesLedgerProduct(productForm.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeProductDia(); |
| | | getPurchaseById({ id: currentId.value, type: 2 }).then((res) => { |
| | | productData.value = res.productData; |
| | | }); |
| | | }); |
| | | }; |
| | | // å é¤äº§å |
| | | const deleteProduct = () => { |
| | | if (productSelectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | if (operationType.value === "add") { |
| | | productSelectedRows.value.forEach((selectedRow) => { |
| | | const index = productData.value.findIndex( |
| | | (product) => product.id === selectedRow.id |
| | | ); |
| | | if (index !== -1) { |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | } else { |
| | | let ids = []; |
| | | if (productSelectedRows.value.length > 0) { |
| | | ids = productSelectedRows.value.map((item) => item.id); |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delProduct(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | closeProductDia(); |
| | | getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then( |
| | | (res) => { |
| | | productData.value = res.productData; |
| | | } |
| | | ); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | } |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProductDia = () => { |
| | | proxy.resetForm("productFormRef"); |
| | | productFormVisible.value = false; |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/purchase/ledger/export", {}, "éè´å°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ£æ¥æ¯å¦æä»äººç»´æ¤çæ°æ® |
| | | const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("ä¸å¯å é¤ä»äººç»´æ¤çæ°æ®"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delPurchase(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | const mathNum = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | if (!productForm.value.taxInclusiveUnitPrice) { |
| | | return; |
| | | } |
| | | if (!productForm.value.quantity) { |
| | | return; |
| | | } |
| | | // å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | proxy.calculateTaxIncludeTotalPrice( |
| | | productForm.value.taxInclusiveUnitPrice, |
| | | productForm.value.quantity |
| | | ); |
| | | if (productForm.value.taxRate) { |
| | | // ä¸å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | productForm.value.taxInclusiveTotalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | }; |
| | | const reverseMathNum = (field) => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | const taxRate = Number(productForm.value.taxRate); |
| | | if (!taxRate) return; |
| | | if (field === 'taxInclusiveTotalPrice') { |
| | | // å·²ç¥å«ç¨æ»ä»·åæ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2); |
| | | } |
| | | // å·²ç¥å«ç¨æ»ä»·åå«ç¨åä»·ï¼åç®æ°é |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2); |
| | | } |
| | | // åç®ä¸å«ç¨æ»ä»· |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2); |
| | | } else if (field === 'taxExclusiveTotalPrice') { |
| | | // åç®å«ç¨æ»ä»· |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2); |
| | | // å·²ç¥æ°éï¼åç®å«ç¨åä»· |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2); |
| | | } |
| | | // å·²ç¥å«ç¨åä»·ï¼åç®æ°é |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | title="å·¥åºæäº§" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-button type="primary" @click="addRow" style="margin-bottom: 10px;">æ°å¢</el-button> |
| | | <span style="font-size: 18px;margin-left: 10px">å¾
æäº§æ°éï¼{{pendingNum}}</span> |
| | | <el-table :data="tableData" border style="width: 100%" :summary-method="summarizeMainTable" show-summary :row-key="row => row.id" stripe> |
| | | <el-table-column label="åºå·" width="60"> |
| | | <template #default="scope"> |
| | | {{ scope.$index + 1 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·¥åº" prop="process"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | v-model="scope.row.process" |
| | | placeholder="请è¾å
¥å·¥åº" |
| | | clearable |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä½" prop="unit"> |
| | | <template #default="scope"> |
| | | <el-input v-model="scope.row.unit" placeholder="请è¾å
¥åä½" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æäº§æ°é" width="200" prop="schedulingNum"> |
| | | <template #default="scope"> |
| | | <el-input-number |
| | | v-model="scope.row.schedulingNum" |
| | | placeholder="请è¾å
¥" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·¥æ¶å®é¢" width="200" prop="workHours"> |
| | | <template #default="scope"> |
| | | <el-input-number |
| | | v-model="scope.row.workHours" |
| | | placeholder="请è¾å
¥" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æäº§æ¥æ" prop="schedulingDate"> |
| | | <template #default="scope"> |
| | | <el-date-picker v-model="scope.row.schedulingDate" type="date" placeholder="éæ©æ¥æ" style="width: 100%;" value-format="YYYY-MM-DD" format="YYYY-MM-DD"/> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æäº§äºº" prop="schedulingUserId"> |
| | | <template #default="scope"> |
| | | <el-select |
| | | v-model="scope.row.schedulingUserId" |
| | | placeholder="éæ©äººå" |
| | | style="width: 100%;" |
| | | > |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" |
| | | /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="scope"> |
| | | <el-button type="danger" size="small" @click="removeRow(scope.$index)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | import {processScheduling} from "@/api/productionManagement/operationScheduling.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const tableData = ref([ |
| | | { process: '', schedulingDate: '', schedulingNum: '', schedulingUserId: '', workHours: '', unit: '' } |
| | | ]); |
| | | const unitFromRow = ref(''); |
| | | const idFromRow = ref(''); |
| | | const pendingNum = ref(''); |
| | | const userList = ref([]) |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | pendingNum.value = row.pendingNum |
| | | if (row && row.unit !== undefined) { |
| | | unitFromRow.value = row.unit; |
| | | idFromRow.value = row.id; |
| | | tableData.value.forEach(item => { |
| | | item.unit = row.unit; |
| | | item.id = row.id; |
| | | }); |
| | | } else { |
| | | unitFromRow.value = ''; |
| | | } |
| | | } |
| | | const submitForm = () => { |
| | | // 1. æ£æ¥æ¯ä¸è¡æ¯å¦å¡«å宿´ |
| | | for (let i = 0; i < tableData.value.length; i++) { |
| | | const row = tableData.value[i]; |
| | | if ( |
| | | !row.process || |
| | | !row.schedulingDate || |
| | | row.schedulingNum === '' || row.schedulingNum === null || |
| | | !row.schedulingUserId || |
| | | row.workHours === '' || row.workHours === null || |
| | | !row.unit |
| | | ) { |
| | | proxy.$modal.msgError(`第${i + 1}è¡æ°æ®æªå¡«å宿´`); |
| | | return; |
| | | } |
| | | } |
| | | // 2. å计æäº§æ°é |
| | | const totalSchedulingNum = tableData.value.reduce((sum, row) => { |
| | | return sum + Number(row.schedulingNum || 0); |
| | | }, 0); |
| | | if (totalSchedulingNum > Number(pendingNum.value)) { |
| | | proxy.$modal.msgError('æäº§æ°éå计ä¸è½è¶
è¿å¾
æäº§æ°é'); |
| | | return; |
| | | } |
| | | processScheduling(tableData.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, ['schedulingNum']); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | |
| | | const addRow = () => { |
| | | tableData.value.push({ id: idFromRow.value, process: '', unit: unitFromRow.value, schedulingNum: '', workHours: '', schedulingDate: '', schedulingUserId: '' }); |
| | | }; |
| | | const removeRow = (index) => { |
| | | tableData.value.splice(index, 1); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="æ´¾å·¥æ¥æ:"> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="searchForm.status" placeholder="è¯·éæ©ç¶æ" @change="handleQuery" style="width: 140px" clearable> |
| | | <el-option label="å¾
æäº§" :value="1"></el-option> |
| | | <el-option label="å·²æäº§" :value="3"></el-option> |
| | | <el-option label="æäº§ä¸" :value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">æç´¢</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="text-align: right" class="mb10"> |
| | | <el-button type="primary" @click="openForm">å·¥åºæäº§</el-button> |
| | | <el-button type="danger" @click="handleDelete" plain>åæ¶æäº§</el-button> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/productionManagement/operationScheduling/components/formDia.vue"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import {listPageProcess, productionDispatchDelete} from "@/api/productionManagement/operationScheduling.js"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | staffName: "", |
| | | status: 1, |
| | | entryDate: [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ], // å½å
¥æ¥æ |
| | | entryDateStart: dayjs().format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ç¶æ", |
| | | prop: "status", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | if (params == 3) { |
| | | return "å·²æäº§"; |
| | | } else if (params == 1) { |
| | | return "å¾
æäº§"; |
| | | } else { |
| | | return 'æäº§ä¸'; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | if (params == 3) { |
| | | return "success"; |
| | | } else if (params == 1) { |
| | | return "primary"; |
| | | } else { |
| | | return 'warning'; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "æ´¾å·¥æ¥æ", |
| | | prop: "schedulingDate", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "派工人", |
| | | prop: "schedulingUserName", |
| | | }, |
| | | { |
| | | label: "产å大类", |
| | | prop: "productCategory", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "specificationModel", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æäº§æ»æ°", |
| | | prop: "schedulingNum", |
| | | }, |
| | | { |
| | | label: "å·²æäº§æ°é", |
| | | prop: "successNum", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å¾
æäº§æ°é", |
| | | prop: "pendingNum", |
| | | width: 100, |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | listPageProcess(params).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records.map(item => ({ |
| | | ...item, |
| | | pendingNum: (Number(item.schedulingNum) || 0) - (Number(item.successNum) || 0) |
| | | })); |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$message.error("è¯·éæ©ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].pendingNum == 0) { |
| | | proxy.$message.warning("æ éåæäº§"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, selectedRows.value[0]) |
| | | }) |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // æ°å¢ï¼å¤ææ¯å¦æå·²æäº§çæ°æ® |
| | | const hasScheduled = selectedRows.value.some(item => item.status == 3); |
| | | if (hasScheduled) { |
| | | proxy.$modal.msgWarning("å·²æäº§æ°æ®ä¸è½åæ¶æäº§"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤åæ¶æäº§ï¼", "å é¤æç¤º", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | productionDispatchDelete(ids) |
| | | .then((res) => { |
| | | proxy.$modal.msgSuccess("åæ¶æäº§æå"); |
| | | getList(); |
| | | }) |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">çäº§æ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | <span class="search_title ml10">ç产人ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.schedulingUserName" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import { |
| | | listCustomer, |
| | | } from "@/api/basicData/customerFile.js"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import {productionAccountingListPage} from "@/api/productionManagement/productionCosting.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "çäº§æ¥æ", |
| | | prop: "schedulingDate", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "ç产人", |
| | | prop: "schedulingUserName", |
| | | width: 90, |
| | | }, |
| | | { |
| | | label: "产å大类", |
| | | prop: "productCategory", |
| | | width: 160, |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "specificationModel", |
| | | width: 160, |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "process", |
| | | }, |
| | | { |
| | | label: "ç产æ°é", |
| | | prop: "finishedNum", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å·¥æ¶å®é¢", |
| | | prop: "workHours", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å·¥èµ", |
| | | prop: "wages", |
| | | width: 100, |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | schedulingUserName: "", |
| | | entryDate: [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ], // å½å
¥æ¥æ |
| | | entryDateStart: dayjs().format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | productionAccountingListPage(params).then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/basic/customer/export", {}, "çäº§æ ¸ç®.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | title="ç产派工" |
| | | width="50%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="产å大类ï¼" prop="productCategory"> |
| | | <el-input v-model="form.productCategory" placeholder="请è¾å
¥" clearable disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ»æ°éï¼" prop="quantity"> |
| | | <el-input v-model="form.quantity" placeholder="请è¾å
¥" clearable disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¾
æäº§æ°éï¼" prop="pendingQuantity"> |
| | | <el-input v-model="form.pendingQuantity" placeholder="请è¾å
¥" clearable disabled/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¬æ¬¡æäº§æ°éï¼" prop="schedulingNum"> |
| | | <el-input-number |
| | | v-model="form.schedulingNum" |
| | | placeholder="请è¾å
¥" |
| | | :min="0" |
| | | :step="0.1" |
| | | :precision="2" |
| | | clearable |
| | | @change="changeNum" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="派工人ï¼" prop="schedulingUserId"> |
| | | <el-select |
| | | v-model="form.schedulingUserId" |
| | | placeholder="éæ©äººå" |
| | | style="width: 100%;" |
| | | > |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ´¾å·¥æ¥æï¼" prop="schedulingDate"> |
| | | <el-date-picker |
| | | v-model="form.schedulingDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | import {productionDispatch} from "@/api/productionManagement/productionOrder.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import dayjs from "dayjs"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const data = reactive({ |
| | | form: { |
| | | productCategory: "", |
| | | quantity: "", |
| | | schedulingNum: "", |
| | | schedulingUserId: "", |
| | | schedulingDate: "", |
| | | pendingQuantity: "", |
| | | salesLedgerProductId: "", |
| | | }, |
| | | rules: { |
| | | schedulingNum: [{ required: true, message: "请è¾å
¥", trigger: "blur" },], |
| | | schedulingUserId: [{ required: true, message: "è¯·éæ©", trigger: "change" },], |
| | | schedulingDate: [{ required: true, message: "è¯·éæ©", trigger: "change" },], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const userList = ref([]) |
| | | const userStore = useUserStore() |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | form.value = {...row} |
| | | // ç»å®å¤å±ä¼ å
¥ç产åIDå°å端éè¦ç salesLedgerProductId åæ®µ |
| | | form.value.salesLedgerProductId = row.id; |
| | | // ç¡®ä¿ä¸ä¼æåå§ id å½ä½æäº§è®°å½ä¸»é®ä¼ ç»å端 |
| | | delete form.value.id; |
| | | form.value.schedulingNum = 0 |
| | | form.value.schedulingUserId = userStore.id |
| | | form.value.schedulingDate = dayjs().format("YYYY-MM-DD"); |
| | | } |
| | | |
| | | // |
| | | const changeNum = (value) => { |
| | | if (value > form.value.pendingQuantity) { |
| | | form.value.schedulingNum = form.value.pendingQuantity; |
| | | proxy.$modal.msgWarning('æäº§æ°éä¸å¯å¤§äºå¾
æäº§æ°é') |
| | | } |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | productionDispatch(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">å½å
¥æ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">ç产派工</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/productionManagement/productionDispatching/components/formDia.vue"; |
| | | import dayjs from "dayjs"; |
| | | import {schedulingListPage} from "@/api/productionManagement/productionOrder.js"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | entryDate: [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ], // å½å
¥æ¥æ |
| | | entryDateStart: dayjs().format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "å½å
¥æ¥æ", |
| | | prop: "registerDate", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "产å大类", |
| | | prop: "productCategory", |
| | | width: 160, |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "specificationModel", |
| | | width: 220, |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | width:90 |
| | | }, |
| | | { |
| | | label: "æ°é", |
| | | prop: "quantity", |
| | | }, |
| | | { |
| | | label: "æäº§æ°é", |
| | | prop: "schedulingNum", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å¾
ææ°é", |
| | | prop: "pendingQuantity", |
| | | width: 100, |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = value[0]; |
| | | searchForm.value.entryDateEnd = value[1]; |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | // æé ä¸ä¸ªæ°ç对象ï¼ä¸å
å«entryDateåæ®µ |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | schedulingListPage(params).then((res) => { |
| | | tableLoading.value = false; |
| | | // å¤çæ¯æ¡æ°æ®ï¼å¢å pendingQuantityåæ®µ |
| | | tableData.value = res.data.records.map(item => ({ |
| | | ...item, |
| | | pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0) |
| | | })); |
| | | page.total = res.data.total; |
| | | }).catch(() => { |
| | | tableLoading.value = false; |
| | | }) |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type) => { |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$message.error("è¯·éæ©ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].pendingQuantity == 0) { |
| | | proxy.$message.warning("æ éåæ´¾å·¥"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, selectedRows.value[0]) |
| | | }) |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | searchForm.value.entryDate = [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ] |
| | | searchForm.value.entryDateStart = dayjs().format("YYYY-MM-DD") |
| | | searchForm.value.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD") |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">产å大类ï¼</span> |
| | | <el-tree-select |
| | | v-model="searchForm.productCategory" |
| | | :data="productOptions" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | check-strictly |
| | | :render-after-expand="false" |
| | | style="width: 240px" |
| | | @change="handleQuery" |
| | | /> |
| | | <span class="search_title ml10">å½å
¥æ¥æï¼</span> |
| | | <el-date-picker v-model="searchForm.registerDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openDialog('create')">æ°å¢è®¢å</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | > |
| | | <template #action="{ row }"> |
| | | <el-button type="primary" link @click="handleEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <el-dialog v-model="dialogVisible" :title="dialogTitle" width="40%" @close="closeDialog"> |
| | | <el-form ref="formRef" :model="form" :rules="formRules" label-width="100px"> |
| | | <el-form-item label="å½å
¥æ¥æ" prop="registerDate"> |
| | | <el-date-picker v-model="form.registerDate" type="date" value-format="YYYY-MM-DD" format="YYYY-MM-DD" placeholder="è¯·éæ©å½å
¥æ¥æ" style="width: 100%"/> |
| | | </el-form-item> |
| | | <el-form-item label="产å大类" prop="productCategory"> |
| | | <el-input |
| | | v-model="form.productCategory" |
| | | placeholder="请è¾å
¥äº§å大类" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·" prop="specificationModel"> |
| | | <el-input |
| | | v-model="form.specificationModel" |
| | | placeholder="请è¾å
¥è§æ ¼åå·" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="åä½" prop="unit"> |
| | | <el-input |
| | | v-model="form.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="æ°é" prop="quantity"> |
| | | <el-input-number v-model="form.quantity" :min="0" :step="0.1" style="width: 100%"/> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDialog">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref, reactive, toRefs, getCurrentInstance} from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import {schedulingListPage, addProductionOrder, updateProductionOrder, deleteProductionOrder} from "@/api/productionManagement/productionOrder.js"; |
| | | import {productTreeList} from "@/api/basicData/product.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "å½å
¥æ¥æ", |
| | | prop: "registerDate", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "ç产订åå·", |
| | | prop: "orderNo", |
| | | }, |
| | | { |
| | | label: "产å大类", |
| | | prop: "productCategory", |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "specificationModel", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "æ°é", |
| | | prop: "quantity", |
| | | }, |
| | | // { |
| | | // label: "æäº§æ°é", |
| | | // prop: "schedulingNum", |
| | | // width: 100, |
| | | // }, |
| | | // { |
| | | // label: "å®å·¥æ°é", |
| | | // prop: "successNum", |
| | | // width: 100, |
| | | // }, |
| | | { |
| | | label: "æä½", |
| | | prop: "action", |
| | | width: 120, |
| | | fixed: "right", |
| | | dataType: "slot", |
| | | align: "center", |
| | | slot: "action" |
| | | } |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = ref({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const dialogMode = ref(""); // 'create' æ 'edit' |
| | | const formRef = ref(); |
| | | const productOptions = ref([]); |
| | | const form = reactive({ |
| | | id: null, |
| | | registerDate: dayjs().format("YYYY-MM-DD"), |
| | | productCategory: "", |
| | | specificationModel: "", |
| | | unit: "", |
| | | quantity: null, |
| | | }); |
| | | const formRules = { |
| | | registerDate: [{ required: true, message: "è¯·éæ©å½å
¥æ¥æ", trigger: "change" }], |
| | | productCategory: [{ required: true, message: "请è¾å
¥äº§å大类", trigger: "blur" }], |
| | | specificationModel: [{ required: true, message: "请è¾å
¥è§æ ¼åå·", trigger: "blur" }], |
| | | unit: [{ required: true, message: "请è¾å
¥åä½", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥æ°é", trigger: "blur" }], |
| | | }; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productCategory: "", |
| | | registerDate: null, // å½å
¥æ¥æ |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | const openDialog = (mode, row = null) => { |
| | | dialogMode.value = mode; |
| | | if (mode === 'create') { |
| | | dialogTitle.value = "æ°å¢ç产订å"; |
| | | resetForm(); |
| | | } else if (mode === 'edit') { |
| | | dialogTitle.value = "ç¼è¾ç产订å"; |
| | | resetForm(); |
| | | |
| | | console.log('ç¼è¾æ°æ®:', row); |
| | | |
| | | // å¡«å
ç¼è¾æ°æ® |
| | | form.id = row.id; |
| | | form.registerDate = row.registerDate; |
| | | form.productCategory = row.productCategory; |
| | | form.specificationModel = row.specificationModel; |
| | | form.unit = row.unit; |
| | | form.quantity = row.quantity; |
| | | } |
| | | |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | const resetForm = () => { |
| | | form.id = null; |
| | | form.registerDate = dayjs().format("YYYY-MM-DD"); |
| | | form.productCategory = ""; |
| | | form.specificationModel = ""; |
| | | form.unit = ""; |
| | | form.quantity = null; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value?.validate(async (valid) => { |
| | | if (!valid) return; |
| | | try { |
| | | const payload = { |
| | | registerDate: form.registerDate, |
| | | productCategory: form.productCategory, |
| | | specificationModel: form.specificationModel, |
| | | unit: form.unit, |
| | | quantity: form.quantity, |
| | | }; |
| | | |
| | | if (dialogMode.value === 'create') { |
| | | await addProductionOrder(payload); |
| | | proxy.$modal.msgSuccess("æ°å¢æå"); |
| | | } else if (dialogMode.value === 'edit') { |
| | | payload.id = form.id; |
| | | await updateProductionOrder(payload); |
| | | proxy.$modal.msgSuccess("ç¼è¾æå"); |
| | | } |
| | | |
| | | closeDialog(); |
| | | getList(); |
| | | } catch (err) { |
| | | console.error(`${dialogMode.value === 'create' ? 'æ°å¢' : 'ç¼è¾'}失败`, err); |
| | | proxy.$modal.msgError(`${dialogMode.value === 'create' ? 'æ°å¢' : 'ç¼è¾'}失败ï¼è¯·éè¯`); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾æ¹æ³ |
| | | const handleEdit = (row) => { |
| | | openDialog('edit', row); |
| | | }; |
| | | |
| | | // å 餿¹æ³ |
| | | const handleDelete = (row) => { |
| | | proxy.$modal.confirm(`ç¡®å®è¦å é¤ç产订å"${row.orderNo}"åï¼`, "å é¤ç¡®è®¤", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(async () => { |
| | | try { |
| | | await deleteProductionOrder([row.id]); |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); // å·æ°å表 |
| | | } catch (err) { |
| | | console.error("å é¤å¤±è´¥", err); |
| | | proxy.$modal.msgError("å é¤å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | }).catch(() => { |
| | | proxy.$modal.msg("已忶å é¤"); |
| | | }); |
| | | }; |
| | | |
| | | const getProductOptions = () => { |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res || []); |
| | | }); |
| | | }; |
| | | |
| | | const convertIdToValue = (data) => { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | return newItem; |
| | | }); |
| | | }; |
| | | |
| | | const findNodeById = (nodes, value) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === value) { |
| | | return nodes[i].label; |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const label = findNodeById(nodes[i].children, value); |
| | | if (label) return label; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.value.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = value[0]; |
| | | searchForm.value.entryDateEnd = value[1]; |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | // æé ä¸ä¸ªæ°ç对象ï¼ä¸å
å«entryDateåæ®µå total åæ®µ |
| | | const { total, ...pageParams } = page.value; |
| | | const params = { ...searchForm.value, ...pageParams }; |
| | | params.registerDate = undefined; |
| | | if (params.productCategory) { |
| | | // 妿æ¯å¯¹è±¡ç±»åï¼è·åå
¶labelï¼åç§°ï¼è䏿¯valueï¼IDï¼ |
| | | if (typeof params.productCategory === "object") { |
| | | params.productCategory = findNodeById(productOptions.value, params.productCategory) || params.productCategory; |
| | | } |
| | | // 妿æ¯IDï¼è½¬æ¢ä¸ºåç§° |
| | | else if (typeof params.productCategory === "string" || typeof params.productCategory === "number") { |
| | | const categoryName = findNodeById(productOptions.value, params.productCategory); |
| | | if (categoryName) { |
| | | params.productCategory = categoryName; |
| | | } |
| | | } |
| | | } |
| | | schedulingListPage(params).then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | page.value.total = res.data.total; |
| | | }).catch(() => { |
| | | tableLoading.value = false; |
| | | }) |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/salesLedger/scheduling/export", {}, "ç产订å.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getProductOptions(); |
| | | // ä¸è®¾ç½®é»è®¤æ¥æï¼å
¨é¨æ¡ä»¶ä¸ºç©ºå è½½ |
| | | searchForm.value.registerDate = null; |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="æäº§æ¥æ:"> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ:"> |
| | | <el-select v-model="searchForm.status" placeholder="è¯·éæ©ç¶æ" style="width: 140px" clearable> |
| | | <el-option label="å¾
ç产" :value="1"></el-option> |
| | | <el-option label="å·²æ¥å·¥" :value="3"></el-option> |
| | | <el-option label="ç产ä¸" :value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">æç´¢</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="text-align: right" class="mb10"> |
| | | <el-button type="primary" @click="openForm('add')">ç产æ¥å·¥</el-button> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | :expandRowKeys="expandedRowKeys" |
| | | @expand-change="expandChange" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | > |
| | | <template #expand="{ row }"> |
| | | <el-table |
| | | :data="expandData" |
| | | border |
| | | show-summary |
| | | stripe |
| | | :summary-method="summarizeMainTable" |
| | | v-loading="childrenLoading" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="æ¬æ¬¡ç产æ°é" prop="finishedNum" align="center" width="400"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" |
| | | v-model="scope.row.finishedNum" |
| | | :disabled="!scope.row.editType" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | @change="changeNum(scope.row)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="å¾
ç产æ°é" prop="pendingNum" width="240" align="center"></el-table-column>--> |
| | | <el-table-column label="ç产人" prop="schedulingUserId" width="400"> |
| | | <template #default="scope"> |
| | | <el-select |
| | | v-model="scope.row.schedulingUserId" |
| | | placeholder="éæ©äººå" |
| | | :disabled="!scope.row.editType" |
| | | style="width: 100%;" |
| | | > |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" |
| | | /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çäº§æ¥æ" prop="schedulingDate" width="400"> |
| | | <template #default="scope"> |
| | | <el-date-picker |
| | | v-model="scope.row.schedulingDate" |
| | | type="date" |
| | | :disabled="!scope.row.editType" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="60"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="changeEditType(scope.row)" |
| | | v-if="!scope.row.editType" |
| | | :disabled="scope.row.parentStatus === 3" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="saveReceiptPayment(scope.row)" |
| | | v-if="scope.row.editType" |
| | | >ä¿å</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue"; |
| | | import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | productionReportUpdate, |
| | | workListPage, |
| | | workListPageById |
| | | } from "@/api/productionManagement/productionReporting.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | staffName: "", |
| | | entryDate: [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ], // å½å
¥æ¥æ |
| | | entryDateStart: dayjs().format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const expandedRowKeys = ref([]); |
| | | const expandData = ref([]); |
| | | const userList = ref([]) |
| | | const tableColumn = ref([ |
| | | { |
| | | type: "expand", |
| | | dataType: "slot", |
| | | slot: "expand", |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | | prop: "status", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | if (params == 3) { |
| | | return "å·²æ¥å·¥"; |
| | | } else if (params == 1) { |
| | | return "å¾
ç产"; |
| | | } else { |
| | | return 'ç产ä¸'; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | if (params == 3) { |
| | | return "success"; |
| | | } else if (params == 1) { |
| | | return "primary"; |
| | | } else { |
| | | return 'warning'; |
| | | } |
| | | }, |
| | | }, |
| | | { |
| | | label: "æäº§æ¥æ", |
| | | prop: "schedulingDate", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "æäº§äºº", |
| | | prop: "schedulingUserName", |
| | | }, |
| | | { |
| | | label: "产å大类", |
| | | prop: "productCategory", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "è§æ ¼åå·", |
| | | prop: "specificationModel", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "process", |
| | | }, |
| | | { |
| | | label: "æäº§æ°é", |
| | | prop: "schedulingNum", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "ç产æ°é", |
| | | prop: "finishedNum", |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "å¾
ç产æ°é", |
| | | prop: "pendingFinishNum", |
| | | width: 100, |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const childrenLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = value[0]; |
| | | searchForm.value.entryDateEnd = value[1]; |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | expandedRowKeys.value = [] |
| | | workListPage(params).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records.map(item => ({ |
| | | ...item, |
| | | pendingFinishNum: (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0) |
| | | })); |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | }; |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (expandedRows.length > 0) { |
| | | nextTick(() => { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | childrenLoading.value = true; |
| | | workListPageById({ id: row.id }).then((res) => { |
| | | childrenLoading.value = false; |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | expandData.value = res.data.map(item => ({ |
| | | ...item, |
| | | pendingNum: (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0), |
| | | parentStatus: row.status // æ°å¢ç¶è¡¨ç¶æ |
| | | })); |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | childrenLoading.value = false; |
| | | console.log(error); |
| | | } |
| | | }) |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | const changeNum = (row) => { |
| | | // æ¾å°ç¶è¡¨æ ¼æ°æ® |
| | | const parentRow = tableData.value.find(item => item.id === expandedRowKeys.value[0]); |
| | | // è®¡ç®ææåè¡¨æ ¼ finishedNum çæ»å |
| | | const totalFinishedNum = expandData.value.reduce((sum, item) => sum + (Number(item.finishedNum) || 0), 0); |
| | | // ç¶è¡¨æ ¼çæäº§æ°é |
| | | const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0; |
| | | |
| | | if (totalFinishedNum > schedulingNum) { |
| | | // åéæ¬æ¬¡è¾å
¥ |
| | | row.finishedNum = schedulingNum - (totalFinishedNum - Number(row.finishedNum)); |
| | | proxy.$modal.msgWarning('æææ¬æ¬¡ç产æ°éä¹åä¸å¯å¤§äºæäº§æ°é'); |
| | | } |
| | | row.pendingNum = row.schedulingNum - row.finishedNum; |
| | | } |
| | | // ç¼è¾ä¿®æ¹ç¶æ |
| | | const changeEditType = (row) => { |
| | | row.editType = !row.editType; |
| | | }; |
| | | // ä¿åè®°å½ |
| | | const saveReceiptPayment = (row) => { |
| | | productionReportUpdate(row).then((res) => { |
| | | row.editType = !row.editType; |
| | | getList(); |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "finishedNum" |
| | | ]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$message.error("è¯·éæ©ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].pendingFinishNum == 0) { |
| | | proxy.$message.warning("æ é忥工"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | const rowInfo = type === 'add' ? selectedRows.value[0] : row |
| | | formDia.value?.openDialog(type, rowInfo) |
| | | }) |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | staffJoinDel(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 1}, "人åå
¥è.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :inline="true" :model="searchForm" style="width: 100%"> |
| | | <el-row justify="space-between"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="客æ·åç§°"> |
| | | <el-input |
| | | v-model="searchForm.customerName" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·ååå·"> |
| | | <el-input |
| | | v-model="searchForm.customerContractNo" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°"> |
| | | <el-input |
| | | v-model="searchForm.projectName" |
| | | placeholder="请è¾å
¥" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-checkbox |
| | | v-model="searchForm.status" |
| | | label="䏿¾ç¤ºå¾
忬¾ä¸º0" |
| | | @change="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <br/> |
| | | <el-form-item label="å¼ç¥¨æ¥æ"> |
| | | <el-date-picker style="width: 240px" v-model="searchForm.commonDate" value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" type="daterange" start-placeholder="å¼å§æ¶é´" end-placeholder="ç»ææ¶é´" clearable |
| | | @change="changeDateRange" @clear="clearRange" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')"> |
| | | æ°å¢å款 |
| | | </el-button> |
| | | <el-button icon="Download" @click="handleOut"> å¯¼åº </el-button> |
| | | </div> |
| | | </div> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | @selection-change="handleSelectionChange" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | :expand-row-keys="expandedRowKeys" |
| | | @expand-change="expandChange" |
| | | stripe |
| | | height="calc(100vh - 21.5em)" |
| | | > |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table |
| | | :data="props.row.children" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeChildrenTable" |
| | | stripe |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="忬¾æ¥æ" prop="receiptPaymentDate" width="130"/> |
| | | <el-table-column label="忬¾éé¢" prop="receiptPaymentAmount"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" |
| | | v-model="scope.row.receiptPaymentAmount" |
| | | :disabled="!scope.row.editType" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="忬¾æ¹å¼" prop="receiptPaymentType"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | v-model="scope.row.receiptPaymentType" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :disabled="!scope.row.editType" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç»è®°äºº" prop="registrant" width="90"/> |
| | | <el-table-column label="ç»è®°æ¥æ" prop="createTime" width="130"/> |
| | | <el-table-column label="æä½" width="150" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="changeEditType(scope.row)" |
| | | v-if="!scope.row.editType" |
| | | :disabled="scope.row.registrant !== userStore.nickName" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="saveReceiptPayment(scope.row)" |
| | | v-if="scope.row.editType" |
| | | :disabled="scope.row.registrant !== userStore.nickName" |
| | | >ä¿å</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | size="small" |
| | | @click="delReceiptRecord(scope.row)" |
| | | :disabled="scope.row.registrant !== userStore.nickName" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column |
| | | label="å¼ç¥¨æ¥æ" |
| | | prop="invoiceDate" |
| | | show-overflow-tooltip |
| | | width="240" |
| | | /> |
| | | <el-table-column |
| | | label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | show-overflow-tooltip |
| | | width="240" |
| | | /> |
| | | <el-table-column |
| | | label="客æ·ååå·" |
| | | prop="customerContractNo" |
| | | show-overflow-tooltip |
| | | width="240" |
| | | |
| | | /> |
| | | <el-table-column |
| | | label="客æ·åç§°" |
| | | prop="customerName" |
| | | show-overflow-tooltip |
| | | width="240" |
| | | /> |
| | | <el-table-column |
| | | label="项ç®åç§°" |
| | | prop="projectName" |
| | | show-overflow-tooltip |
| | | width="340" |
| | | /> |
| | | <el-table-column |
| | | label="产å大类" |
| | | prop="productCategory" |
| | | show-overflow-tooltip |
| | | width="100" |
| | | /> |
| | | <el-table-column |
| | | label="å票å·" |
| | | prop="invoiceNo" |
| | | show-overflow-tooltip |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="å票éé¢(å
)" |
| | | prop="invoiceTotal" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" |
| | | /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" show-overflow-tooltip /> |
| | | <el-table-column |
| | | label="忬¾éé¢(å
)" |
| | | prop="receiptPaymentAmountTotal" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="å¾
忬¾éé¢(å
)" |
| | | prop="noReceiptAmount" |
| | | show-overflow-tooltip |
| | | width="200" |
| | | > |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.noReceiptAmount) }} |
| | | </el-text> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | title="æ°å¢å票å·é¡µé¢" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form |
| | | :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" prop="salesContractNo"> |
| | | <el-input |
| | | v-model="form.salesContractNo" |
| | | placeholder="èªå¨å¡«å
" |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·åç§°ï¼" prop="customerName"> |
| | | <el-input |
| | | v-model="form.customerName" |
| | | placeholder="èªå¨å¡«å
" |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票å·ï¼" prop="invoiceNo"> |
| | | <el-input |
| | | v-model="form.invoiceNo" |
| | | placeholder="èªå¨å¡«å
" |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票éé¢(å
)ï¼" prop="invoiceTotal"> |
| | | <el-input |
| | | type="number" |
| | | v-model="form.invoiceTotal" |
| | | placeholder="èªå¨å¡«å
" |
| | | :step="0.01" |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨çï¼" prop="taxRate"> |
| | | <el-input |
| | | type="number" |
| | | v-model="form.taxRate" |
| | | placeholder="èªå¨å¡«å
" |
| | | :step="0.01" |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¬æ¬¡å款éé¢ï¼" prop="receiptPaymentAmount"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" |
| | | :precision="2" |
| | | v-model="form.receiptPaymentAmount" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="忬¾å½¢å¼ï¼" prop="receiptPaymentType"> |
| | | <el-input |
| | | v-model="form.receiptPaymentType" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»è®°äººï¼" prop="registrant"> |
| | | <el-input |
| | | v-model="form.registrant" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¥æ¬¾æ¥æï¼" prop="receiptPaymentDate"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="form.receiptPaymentDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { onMounted, ref } from "vue"; |
| | | import { |
| | | receiptPaymentSaveOrUpdate, |
| | | bindInvoiceNoRegPage, |
| | | invoiceInfo, |
| | | receiptPaymentHistoryListNoPage, |
| | | receiptPaymentDel, |
| | | } from "../../../api/salesManagement/receiptPayment.js"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const expandedRowKeys = ref([]); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | searchText: "", |
| | | status: true, |
| | | customerName: "", |
| | | customerContractNo: "", |
| | | projectName: "", |
| | | }, |
| | | form: { |
| | | salesContractNo: "", |
| | | customerName: "", |
| | | invoiceNo: "", |
| | | invoiceTotal: "", |
| | | taxRate: "", |
| | | receiptPaymentAmount: "", |
| | | receiptPaymentType: "", |
| | | registrant: "", |
| | | receiptPaymentDate: "", |
| | | }, |
| | | rules: { |
| | | salesContractNo: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | customerName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | invoiceNo: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | invoiceTotal: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | receiptPaymentAmount: [ |
| | | { required: true, message: "è¯·éæ©", trigger: "change" }, |
| | | ], |
| | | receiptPaymentType: [ |
| | | { required: true, message: "è¯·éæ©", trigger: "change" }, |
| | | ], |
| | | registrant: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | receiptPaymentDate: [ |
| | | { required: true, message: "è¯·éæ©", trigger: "change" }, |
| | | ], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm, resetForm } = useFormData(data.searchForm); |
| | | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | |
| | | const changeDateRange = (date) => { |
| | | if (date) { |
| | | searchForm.invoiceDateStart = date[0]; |
| | | searchForm.invoiceDateEnd = date[1]; |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const clearRange = () => { |
| | | searchForm.commonDate = []; |
| | | searchForm.invoiceDateStart = undefined; |
| | | searchForm.invoiceDateEnd = undefined; |
| | | getList(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | bindInvoiceNoRegPage({ ...searchForm, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | if (expandedRowKeys.value.length > 0) { |
| | | const arr = [] |
| | | const index = tableData.value.findIndex(item => item.id === expandedRowKeys.value[0]); |
| | | if (index > -1) { |
| | | arr.push(tableData.value[index]); |
| | | expandChange(tableData.value[index], arr) |
| | | } |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | receiptPaymentHistoryListNoPage({ |
| | | invoiceLedgerId: row.id, |
| | | type: 1, |
| | | }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | if (res?.length > 0) { |
| | | res.forEach((item) => { |
| | | item.editType = false; |
| | | }); |
| | | } |
| | | tableData.value[index].children = res; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | console.log("selection", selection); |
| | | selectedRows.value = selection.filter( |
| | | (item) => item.customerContractNo !== null |
| | | ); |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["invoiceTotal", "receiptPaymentAmountTotal", "noReceiptAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeChildrenTable = (param) => { |
| | | return proxy.summarizeTable(param, ["receiptPaymentAmount"]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = () => { |
| | | form.value = {}; |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$modal.msgError("è¯·éæ©ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].noReceiptAmount == 0) { |
| | | proxy.$modal.msgWarning("æ éå忬¾"); |
| | | return; |
| | | } |
| | | invoiceInfo({ id: selectedRows.value[0].id }).then((res) => { |
| | | form.value = { ...res.data }; |
| | | form.value.invoiceLedgerId = form.value.id; |
| | | form.value.id = ""; |
| | | form.value.registrant = userStore.nickName; |
| | | }); |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | if (valid) { |
| | | receiptPaymentSaveOrUpdate(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | |
| | | // å é¤åæ¬¾è®°å½ |
| | | const delReceiptRecord = (row) => { |
| | | console.log("row", row); |
| | | ElMessageBox.confirm("确认å é¤è¯¥è®°å½åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(async () => { |
| | | try { |
| | | let ids = []; |
| | | ids.push(row.id); |
| | | await receiptPaymentDel(ids); |
| | | ElMessage.success("å 餿å"); |
| | | getList(); |
| | | } catch (error) { |
| | | console.error("å é¤å¤±è´¥:", error); |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.info("已忶å é¤"); |
| | | }); |
| | | }; |
| | | |
| | | // ç¼è¾ä¿®æ¹ç¶æ |
| | | const changeEditType = (row) => { |
| | | row.editType = !row.editType; |
| | | }; |
| | | |
| | | // ä¿å忬¾è®°å½ |
| | | const saveReceiptPayment = (row) => { |
| | | let updateData = { |
| | | id: row.id, |
| | | receiptPaymentType: row.receiptPaymentType, |
| | | receiptPaymentAmount: row.receiptPaymentAmount, |
| | | }; |
| | | receiptPaymentSaveOrUpdate(updateData).then((res) => { |
| | | row.editType = !row.editType; |
| | | getList(); |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | const ids = selectedRows.value.map((item) => item.id); |
| | | proxy.download( |
| | | `/receiptPayment/export`, |
| | | { ids: `${ids}` }, |
| | | "忬¾ç»è®°æ¡£æ¡.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | ::v-deep(.el-checkbox__label) { |
| | | font-weight: bold; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="客æ·åç§°"> |
| | | <el-input |
| | | v-model="searchForm.searchText" |
| | | placeholder="è¾å
¥å®¢æ·åç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·ååå·"> |
| | | <el-input |
| | | v-model="searchForm.customerContractNo" |
| | | placeholder="è¾å
¥å®¢æ·ååå·" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°"> |
| | | <el-input |
| | | v-model="searchForm.projectName" |
| | | placeholder="è¾å
¥é¡¹ç®åç§°" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="忬¾æ¥æ"> |
| | | <el-date-picker |
| | | v-model="searchForm.receiptPaymentDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | start-placeholder="å¼å§æ¶é´" |
| | | end-placeholder="ç»ææ¶é´" |
| | | clearable |
| | | style="width: 300px" |
| | | @change="changeDateRange" |
| | | @clear="clearRange" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | :isShowSummary="isShowSummarySon" |
| | | :summaryMethod="summarizeMainTable1" |
| | | :tableLoading="tableLoading" |
| | | :total="page.total" |
| | | @pagination="pagination" |
| | | @selection-change="handleSelectionChange" |
| | | ></PIMTable> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { receiptPaymentHistoryListPage } from "@/api/salesManagement/receiptPayment.js"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "éå®ååå·", |
| | | prop: "salesContractNo", |
| | | width:240 |
| | | }, |
| | | { |
| | | label: "客æ·ååå·", |
| | | prop: "customerContractNo", |
| | | width:240 |
| | | }, |
| | | { |
| | | label: "忬¾æ¥æ", |
| | | prop: "receiptPaymentDate", |
| | | width:100 |
| | | }, |
| | | { |
| | | label: "客æ·åç§°", |
| | | prop: "customerName", |
| | | width:240 |
| | | }, |
| | | { |
| | | label: "项ç®åç§°", |
| | | prop: "projectName", |
| | | width:200 |
| | | }, |
| | | { |
| | | label: "忬¾éé¢ï¼å
ï¼", |
| | | prop: "receiptPaymentAmount", |
| | | width:200, |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "忬¾æ¹å¼", |
| | | prop: "receiptPaymentType", |
| | | }, |
| | | { |
| | | label: "ç»è®°äºº", |
| | | prop: "registrant", |
| | | }, |
| | | { |
| | | label: "ç»è®°æ¥æ", |
| | | prop: "createTime", |
| | | width:100 |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const total = ref(0); |
| | | |
| | | const { form: searchForm } = useFormData({ |
| | | searchText: undefined, |
| | | receiptPaymentDate: [ |
| | | dayjs().startOf("month").format("YYYY-MM-DD"), |
| | | dayjs().endOf("month").format("YYYY-MM-DD"), |
| | | ], |
| | | receiptPaymentDateStart: dayjs() |
| | | .startOf("month") |
| | | .format("YYYY-MM-DD 00:00:00"), |
| | | receiptPaymentDateEnd: dayjs().endOf("month").format("YYYY-MM-DD 23:59:59"), |
| | | customerContractNo: undefined, |
| | | projectName: undefined, |
| | | }); |
| | | const { receipt_payment_type } = proxy.useDict("receipt_payment_type"); |
| | | const isShowSummarySon = ref(true); |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { receiptPaymentDate, ...rest } = searchForm; |
| | | receiptPaymentHistoryListPage({ ...rest, ...page }).then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.records; |
| | | page.total = res.total; |
| | | }); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = (param) => { |
| | | return proxy.summarizeTable(param, ["receiptPaymentAmount"], { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | const changeDateRange = (date) => { |
| | | if (date) { |
| | | searchForm.receiptPaymentDateStart = dayjs(date[0]).format( |
| | | "YYYY-MM-DD 00:00:00" |
| | | ); |
| | | searchForm.receiptPaymentDateEnd = dayjs(date[1]).format( |
| | | "YYYY-MM-DD 23:59:59" |
| | | ); |
| | | getList(); |
| | | } |
| | | }; |
| | | |
| | | const clearRange = () => { |
| | | searchForm.receiptPaymentDate = []; |
| | | searchForm.receiptPaymentDateStart = undefined; |
| | | searchForm.receiptPaymentDateEnd = undefined; |
| | | getList(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="客æ·åç§°ï¼"> |
| | | <el-input v-model="searchForm.customerName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·ååå·ï¼"> |
| | | <el-input v-model="searchForm.customerContractNo" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="éå®ååå·ï¼"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项ç®åç§°ï¼"> |
| | | <el-input v-model="searchForm.projectName" placeholder="请è¾å
¥" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="å½å
¥æ¥æï¼"> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="è¯·éæ©" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> æç´¢ </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')"> |
| | | æ°å¢å°è´¦ |
| | | </el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" |
| | | :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%" |
| | | :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)" stripe> |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable" stripe> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | </el-table> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="éå®ååå·" prop="salesContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="客æ·ååå·" prop="customerContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="客æ·åç§°" prop="customerName" width="300" show-overflow-tooltip /> |
| | | <el-table-column label="ä¸å¡å" prop="salesman" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="项ç®åç§°" prop="projectName" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="仿¬¾æ¹å¼" prop="paymentMethod" show-overflow-tooltip /> |
| | | <el-table-column label="ååéé¢(å
)" prop="contractAmount" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å·²å¼ç¥¨éé¢(å
)" prop="invoiceTotal" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="æªå¼ç¥¨éé¢(å
)" prop="noInvoiceAmountTotal" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="忬¾éé¢(å
)" prop="receiptPaymentAmountTotal" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å¾
忬¾éé¢(å
)" prop="noReceiptAmount" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="å½å
¥äºº" prop="entryPersonName" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="å½å
¥æ¥æ" prop="entryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="ç¾è®¢æ¥æ" prop="executionDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="æä½" min-width="140" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" :disabled="scope.row.invoiceTotal>0 || scope.row.entryPersonName !== userStore.nickName" @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | | <!-- <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详æ
</el-button>--> |
| | | <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">éä»¶</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | </div> |
| | | <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? 'æ°å¢éå®å°è´¦é¡µé¢' : 'ç¼è¾éå®å°è´¦é¡µé¢'" width="70%" |
| | | @close="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" prop="salesContractNo"> |
| | | <el-input v-model="form.salesContractNo" placeholder="èªå¨çæ" clearable disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸å¡åï¼" prop="salesman"> |
| | | <el-input |
| | | v-model="form.salesman" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :disabled="operationType === 'view'" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·ååå·ï¼" prop="customerContractNo"> |
| | | <el-input v-model="form.customerContractNo" placeholder="请è¾å
¥" clearable :disabled="operationType === 'view'"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·åç§°ï¼" prop="customerId"> |
| | | <el-select v-model="form.customerId" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'"> |
| | | <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"> |
| | | {{ |
| | | item.customerName + "ââ" + item.taxpayerIdentificationNumber |
| | | }} |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项ç®åç§°ï¼" prop="projectName"> |
| | | <el-input v-model="form.projectName" placeholder="请è¾å
¥" clearable :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¾è®¢æ¥æï¼" prop="executionDate"> |
| | | <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" type="date" placeholder="è¯·éæ©" clearable :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥äººï¼" prop="entryPerson"> |
| | | <el-select v-model="form.entryPerson" placeholder="è¯·éæ©" clearable @change="changs" disabled> |
| | | <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å½å
¥æ¥æï¼" prop="entryDate"> |
| | | <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" |
| | | type="date" placeholder="è¯·éæ©" clearable disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="仿¬¾æ¹å¼"> |
| | | <el-input v-model="form.paymentMethod" placeholder="请è¾å
¥" clearable :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-form-item label="产åä¿¡æ¯ï¼" prop="entryDate"> |
| | | <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">æ·»å </el-button> |
| | | <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >å é¤</el-button> |
| | | </el-form-item> |
| | | </el-row> |
| | | <el-table :data="productData" border @selection-change="productSelected" show-summary stripe |
| | | :summary-method="summarizeMainTable"> |
| | | <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" /> |
| | | <el-table-column align="center" label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="产å大类" prop="productCategory" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specificationModel" /> |
| | | <el-table-column label="åä½" prop="unit" /> |
| | | <el-table-column label="æ°é" prop="quantity" /> |
| | | <el-table-column label="ç¨ç(%)" prop="taxRate" /> |
| | | <el-table-column label="å«ç¨åä»·(å
)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="å«ç¨æ»ä»·(å
)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column label="ä¸å«ç¨æ»ä»·(å
)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> |
| | | <el-table-column fixed="right" label="æä½" min-width="60" align="center" v-if="operationType !== 'view'"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">ç¼è¾</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨Â·ï¼" prop="remark"> |
| | | <el-input v-model="form.remark" placeholder="请è¾å
¥" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="éä»¶ææï¼" prop="remark"> |
| | | <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload |
| | | :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError" |
| | | :on-success="handleUploadSuccess" :on-remove="handleRemove"> |
| | | <el-button type="primary" v-if="operationType !== 'view'">ä¸ä¼ </el-button> |
| | | <template #tip v-if="operationType !== 'view'"> |
| | | <div class="el-upload__tip"> |
| | | æä»¶æ ¼å¼æ¯æ |
| | | docï¼docxï¼xlsï¼xlsxï¼pptï¼pptxï¼pdfï¼txtï¼xmlï¼jpgï¼jpegï¼pngï¼gifï¼bmpï¼rarï¼zipï¼7z |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? 'æ°å¢äº§å' : 'ç¼è¾äº§å'" width="40%" |
| | | @close="closeProductDia"> |
| | | <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产å大类ï¼" prop="productCategory"> |
| | | <!-- <el-select v-model="productForm.productCategory" placeholder="è¯·éæ©" clearable> |
| | | <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/> |
| | | </el-select> --> |
| | | <el-tree-select v-model="productForm.productCategory" placeholder="è¯·éæ©" clearable check-strictly |
| | | @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="è§æ ¼åå·ï¼" prop="productModelId"> |
| | | <el-select v-model="productForm.productModelId" placeholder="è¯·éæ©" clearable @change="getProductModel"> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åä½ï¼" prop="unit"> |
| | | <el-input v-model="productForm.unit" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¨ç(%)ï¼" prop="taxRate"> |
| | | <el-select v-model="productForm.taxRate" placeholder="è¯·éæ©" clearable @change="calculateFromTaxRate"> |
| | | <el-option label="1" value="1" /> |
| | | <el-option label="6" value="6" /> |
| | | <el-option label="13" value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨åä»·(å
)ï¼" prop="taxInclusiveUnitPrice"> |
| | | <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥" clearable @change="calculateFromUnitPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ°éï¼" prop="quantity"> |
| | | <el-input-number :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请è¾å
¥" clearable |
| | | :precision="2" |
| | | @change="calculateFromQuantity" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å«ç¨æ»ä»·(å
)ï¼" prop="taxInclusiveTotalPrice"> |
| | | <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请è¾å
¥" clearable @change="calculateFromTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸å«ç¨æ»ä»·(å
)ï¼" prop="taxExclusiveTotalPrice"> |
| | | <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请è¾å
¥" clearable @change="calculateFromExclusiveTotalPrice" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å票类åï¼" prop="invoiceType"> |
| | | <el-select v-model="productForm.invoiceType" placeholder="è¯·éæ©" clearable> |
| | | <el-option label="墿®ç¥¨" value="墿®ç¥¨" /> |
| | | <el-option label="å¢ä¸ç¥¨" value="å¢ä¸ç¥¨" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitProduct">确认</el-button> |
| | | <el-button @click="closeProductDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <FileList ref="fileListRef" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import {onMounted, ref} from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileList from "./fileList.vue"; |
| | | import { |
| | | ledgerListPage, |
| | | productList, |
| | | customerList, |
| | | addOrUpdateSalesLedger, |
| | | getSalesLedgerWithProducts, |
| | | delLedger, |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const userStore = useUserStore(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const productData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const productSelectedRows = ref([]); |
| | | const userList = ref([]); |
| | | const customerOption = ref([]); |
| | | const productOptions = ref([]); |
| | | const modelOptions = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const fileList = ref([]); |
| | | |
| | | // ç¨æ·ä¿¡æ¯è¡¨åå¼¹æ¡æ°æ® |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | customerName: "", // 客æ·åç§° |
| | | customerContractNo: "", // 客æ·ååç¼å· |
| | | salesContractNo: "", // éå®ååç¼å· |
| | | projectName: "", // 项ç®åç§° |
| | | entryDate: [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | | dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | ], // å½å
¥æ¥æ |
| | | entryDateStart: dayjs().format("YYYY-MM-DD"), |
| | | entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"), |
| | | }, |
| | | form: { |
| | | salesContractNo: "", |
| | | salesman: "", |
| | | customerContractNo: "", |
| | | customerId: "", |
| | | projectName: "", |
| | | entryPerson: "", |
| | | entryDate: "", |
| | | maintenanceTime: "", |
| | | productData: [], |
| | | executionDate: "", |
| | | paymentMethod: "", |
| | | }, |
| | | rules: { |
| | | salesman: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | customerContractNo: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | customerId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | projectName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | entryPerson: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | entryDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | executionDate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | // 产å表åå¼¹æ¡æ°æ® |
| | | const productFormVisible = ref(false); |
| | | const productOperationType = ref(""); |
| | | const currentId = ref(""); |
| | | const productFormData = reactive({ |
| | | productForm: { |
| | | productCategory: "", |
| | | specificationModel: "", |
| | | unit: "", |
| | | quantity: "", |
| | | taxInclusiveUnitPrice: "", |
| | | taxRate: "", |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | }, |
| | | productRules: { |
| | | productCategory: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | productModelId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | specificationModel: [ |
| | | { required: true, message: "è¯·éæ©", trigger: "change" }, |
| | | ], |
| | | unit: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | quantity: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxInclusiveUnitPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | taxExclusiveTotalPrice: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | invoiceType: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { productForm, productRules } = toRefs(productFormData); |
| | | // 鲿¢å¾ªç¯è®¡ç®çæ å¿ |
| | | const isCalculating = ref(false); |
| | | const upload = reactive({ |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }); |
| | | |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.entryDateStart = undefined; |
| | | searchForm.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | expandedRowKeys.value = []; |
| | | getList(); |
| | | }; |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const { entryDate, ...rest } = searchForm; |
| | | ledgerListPage({ ...rest, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.records; |
| | | tableData.value.map((item) => { |
| | | item.children = []; |
| | | }); |
| | | total.value = res.total; |
| | | }) |
| | | .catch(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // è·å产å大类treeæ°æ® |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | // è·åtreeåæ°æ® |
| | | const getModels = (value) => { |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | }; |
| | | const getProductModel = (value) => { |
| | | console.log("value", value); |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.unit = null; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // æ¾å°èç¹ï¼è¿å该èç¹ |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode; // å¨åèç¹ä¸æ¾å°ï¼è¿å该èç¹ |
| | | } |
| | | } |
| | | } |
| | | return null; // æ²¡ææ¾å°èç¹ï¼è¿ånull |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // å° id æ¹ä¸º value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | } |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | // è¿æ»¤æåæ°æ® |
| | | selectedRows.value = selection.filter((item) => item.children !== undefined); |
| | | console.log("selection", selectedRows.value); |
| | | }; |
| | | const productSelected = (selectedRows) => { |
| | | productSelectedRows.value = selectedRows; |
| | | }; |
| | | const expandedRowKeys = ref([]); |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | if (expandedRows.length > 0) { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | productList({ salesLedgerId: row.id, type: 1 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res.data; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "contractAmount", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | 'invoiceTotal', |
| | | 'noInvoiceAmountTotal', |
| | | 'receiptPaymentAmountTotal', |
| | | 'noReceiptAmount', |
| | | ]); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeChildrenTable = (param) => { |
| | | return proxy.summarizeTable(param, [ |
| | | "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = async (type, row) => { |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then((res) => { |
| | | customerOption.value = res; |
| | | }); |
| | | form.value.entryPerson = userStore.id; |
| | | if (type !== "add") { |
| | | currentId.value = row.id; |
| | | getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { |
| | | form.value = { ...res }; |
| | | form.value.entryPerson = Number(res.entryPerson); |
| | | productData.value = form.value.productData; |
| | | fileList.value = form.value.salesLedgerFiles; |
| | | }); |
| | | } |
| | | // let userAll = await userStore.getInfo() |
| | | // userList.value.forEach(element => { |
| | | // if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) { |
| | | // form.value.entryPerson = userAll.user.userId // 设置é»è®¤ä¸å¡å为å½åç¨æ· |
| | | // } |
| | | // }); |
| | | form.value.entryDate = getCurrentDate(); // 设置é»è®¤å½å
¥æ¥æä¸ºå½åæ¥æ |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | function changs(val) { |
| | | console.log(val); |
| | | } |
| | | // ä¸ä¼ åæ ¡æ£ |
| | | function handleBeforeUpload(file) { |
| | | // æ ¡æ£æä»¶å¤§å° |
| | | // if (file.size > 1024 * 1024 * 10) { |
| | | // proxy.$modal.msgError("ä¸ä¼ æä»¶å¤§å°ä¸è½è¶
è¿10MB!"); |
| | | // return false; |
| | | // } |
| | | proxy.$modal.loading("æ£å¨ä¸ä¼ æä»¶ï¼è¯·ç¨å..."); |
| | | return true; |
| | | } |
| | | // ä¸ä¼ 失败 |
| | | function handleUploadError(err) { |
| | | proxy.$modal.msgError("ä¸ä¼ æä»¶å¤±è´¥"); |
| | | proxy.$modal.closeLoading(); |
| | | } |
| | | // ä¸ä¼ æååè° |
| | | function handleUploadSuccess(res, file, uploadFiles) { |
| | | proxy.$modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | file.tempId = res.data.tempId; |
| | | proxy.$modal.msgSuccess("ä¸ä¼ æå"); |
| | | } else { |
| | | proxy.$modal.msgError(res.msg); |
| | | proxy.$refs.fileUpload.handleRemove(file); |
| | | } |
| | | } |
| | | // ç§»é¤æä»¶ |
| | | function handleRemove(file) { |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | | delLedgerFile(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | }); |
| | | } |
| | | } |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | proxy.$refs["formRef"].validate((valid) => { |
| | | if (valid) { |
| | | console.log('productData.value--', productData.value) |
| | | if (productData.value !== null && productData.value.length > 0) { |
| | | form.value.productData = proxy.HaveJson(productData.value); |
| | | } else { |
| | | proxy.$modal.msgWarning("请添å 产åä¿¡æ¯"); |
| | | return; |
| | | } |
| | | let tempFileIds = []; |
| | | if (fileList.value !== null && fileList.value.length > 0) { |
| | | tempFileIds = fileList.value.map((item) => item.tempId); |
| | | } |
| | | form.value.tempFileIds = tempFileIds; |
| | | form.value.type = 1; |
| | | addOrUpdateSalesLedger(form.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | getList(); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | |
| | | const productIndex = ref(0); |
| | | // æå¼äº§åå¼¹æ¡ |
| | | const openProductForm = (type, row,index) => { |
| | | productOperationType.value = type; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | productIndex.value = index; |
| | | } |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | }; |
| | | // æäº¤äº§å表å |
| | | const submitProduct = () => { |
| | | proxy.$refs["productFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | if (operationType.value === "edit") { |
| | | submitProductEdit(); |
| | | } else { |
| | | if(productOperationType.value === "add"){ |
| | | productData.value.push({ ...productForm.value }); |
| | | }else{ |
| | | productData.value[productIndex.value] = { ...productForm.value } |
| | | } |
| | | closeProductDia(); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | const submitProductEdit = () => { |
| | | productForm.value.salesLedgerId = currentId.value; |
| | | productForm.value.type = 1 |
| | | addOrUpdateSalesLedgerProduct(productForm.value).then((res) => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeProductDia(); |
| | | getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => { |
| | | productData.value = res.productData; |
| | | }); |
| | | }); |
| | | }; |
| | | // å é¤äº§å |
| | | const deleteProduct = () => { |
| | | if (productSelectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | if (operationType.value === "add") { |
| | | productSelectedRows.value.forEach((selectedRow) => { |
| | | const index = productData.value.findIndex( |
| | | (product) => product.id === selectedRow.id |
| | | ); |
| | | if (index !== -1) { |
| | | productData.value.splice(index, 1); |
| | | } |
| | | }); |
| | | } else { |
| | | let ids = []; |
| | | if (productSelectedRows.value.length > 0) { |
| | | ids = productSelectedRows.value.map((item) => item.id); |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delProduct(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | closeProductDia(); |
| | | getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then( |
| | | (res) => { |
| | | productData.value = res.productData; |
| | | } |
| | | ); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | } |
| | | }; |
| | | // å
³é产åå¼¹æ¡ |
| | | const closeProductDia = () => { |
| | | proxy.resetForm("productFormRef"); |
| | | productFormVisible.value = false; |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/sales/ledger/export", {}, "éå®å°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delLedger(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // è·åå½åæ¥æå¹¶æ ¼å¼å为 YYYY-MM-DD |
| | | function getCurrentDate() { |
| | | const today = new Date(); |
| | | const year = today.getFullYear(); |
| | | const month = String(today.getMonth() + 1).padStart(2, "0"); // æä»½ä»0å¼å§ |
| | | const day = String(today.getDate()).padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | |
| | | const mathNum = () => { |
| | | console.log("productForm.value", productForm.value); |
| | | if (!productForm.value.taxInclusiveUnitPrice) { |
| | | return; |
| | | } |
| | | if (!productForm.value.quantity) { |
| | | return; |
| | | } |
| | | // å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | proxy.calculateTaxIncludeTotalPrice( |
| | | productForm.value.taxInclusiveUnitPrice, |
| | | productForm.value.quantity |
| | | ); |
| | | if (productForm.value.taxRate) { |
| | | // ä¸å«ç¨æ»ä»·è®¡ç® |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | productForm.value.taxInclusiveTotalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | }; |
| | | |
| | | // æ ¹æ®å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const calculateFromTotalPrice = () => { |
| | | if (isCalculating.value) return; |
| | | |
| | | const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | |
| | | if (!totalPrice || !quantity || quantity <= 0) { |
| | | return; |
| | | } |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productForm.value.taxRate) { |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | totalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | | // æ ¹æ®ä¸å«ç¨æ»ä»·è®¡ç®å«ç¨åä»·åæ°é |
| | | const calculateFromExclusiveTotalPrice = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice); |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | const taxRate = parseFloat(productForm.value.taxRate); |
| | | |
| | | if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) { |
| | | return; |
| | | } |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | // å
计ç®å«ç¨æ»ä»· = ä¸å«ç¨æ»ä»· / (1 - ç¨ç/100) |
| | | const taxRateDecimal = taxRate / 100; |
| | | const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal); |
| | | productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2); |
| | | |
| | | // 计ç®å«ç¨åä»· = å«ç¨æ»ä»· / æ°é |
| | | productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2); |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | | // æ ¹æ®æ°éååè®¡ç®æ»ä»· |
| | | const calculateFromQuantity = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productForm.value.taxRate) { |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | productForm.value.taxInclusiveTotalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | | // æ ¹æ®å«ç¨åä»·ååè®¡ç®æ»ä»· |
| | | const calculateFromUnitPrice = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | const quantity = parseFloat(productForm.value.quantity); |
| | | const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice); |
| | | |
| | | if (!quantity || quantity <= 0 || !unitPrice) { |
| | | return; |
| | | } |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | // 计ç®å«ç¨æ»ä»· |
| | | productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | |
| | | // 妿æç¨çï¼è®¡ç®ä¸å«ç¨æ»ä»· |
| | | if (productForm.value.taxRate) { |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | productForm.value.taxInclusiveTotalPrice, |
| | | productForm.value.taxRate |
| | | ); |
| | | } |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | |
| | | // æ ¹æ®ç¨çåå计ç®ä¸å«ç¨æ»ä»· |
| | | const calculateFromTaxRate = () => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请å
éæ©ç¨ç"); |
| | | return; |
| | | } |
| | | if (isCalculating.value) return; |
| | | |
| | | const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice); |
| | | const taxRate = parseFloat(productForm.value.taxRate); |
| | | |
| | | if (!inclusiveTotalPrice || !taxRate) { |
| | | return; |
| | | } |
| | | |
| | | isCalculating.value = true; |
| | | |
| | | // 计ç®ä¸å«ç¨æ»ä»· |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | proxy.calculateTaxExclusiveTotalPrice( |
| | | inclusiveTotalPrice, |
| | | taxRate |
| | | ); |
| | | |
| | | isCalculating.value = false; |
| | | }; |
| | | /** |
| | | * ä¸è½½æä»¶ |
| | | * |
| | | * @param row ä¸è½½æä»¶çç¸å
³ä¿¡æ¯å¯¹è±¡ |
| | | */ |
| | | const fileListRef = ref(null) |
| | | const downLoadFile = (row) => { |
| | | getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => { |
| | | fileListRef.value.open(res.salesLedgerFiles) |
| | | }); |
| | | |
| | | } |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .table_list { |
| | | margin-top: unset; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | </style> |