| | |
| | | import request from '@/utils/request'
|
| | | import request from '@/utils/request' |
| | | import { getToken } from '@/utils/auth' |
| | |
|
| | | // ç»å½æ¹æ³
|
| | | export function login(username, password, code, uuid) {
|
| | |
| | | }
|
| | |
|
| | | // è·åç¨æ·è¯¦ç»ä¿¡æ¯
|
| | | export function getInfo() {
|
| | | return request({
|
| | | url: '/getInfo',
|
| | | method: 'get'
|
| | | })
|
| | | }
|
| | | export function getInfo() { |
| | | const token = getToken() |
| | | return request({ |
| | | url: '/getInfo', |
| | | headers: token ? { Authorization: `Bearer ${token}` } : {}, |
| | | method: 'get' |
| | | }) |
| | | } |
| | |
|
| | | // éåºæ¹æ³
|
| | | export function logout() {
|
| | |
| | | // éè´å°è´¦é¡µé¢æ¥å£ |
| | | import request from "@/utils/request"; |
| | | |
| | | // å页æ¥è¯¢ |
| | | /** 仿¬¾å°è´¦ - ä¾åºå徿¥æ±æ» */ |
| | | export function paymentLedgerList(query) { |
| | | return request({ |
| | | url: "/purchase/paymentRegistration/supplierNameListPage", |
| | | url: "/purchase/report/supplierTransactions", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function paymentRecordList(supplierId) { |
| | | /** 仿¬¾å°è´¦ - ä¾åºå徿¥æç» */ |
| | | export function paymentRecordList(params) { |
| | | return request({ |
| | | url: "/purchase/paymentRegistration/supplierNameListPageDetails", |
| | | url: "/purchase/report/supplierTransactionsDetails", |
| | | method: "get", |
| | | params: supplierId, |
| | | params, |
| | | }); |
| | | } |
| | |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 客æ·å¾æ¥å表 |
| | | export function customewTransactions(query) { |
| | | return request({ |
| | | url: "/metricStatistics/customewTransactions", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 客æ·å¾æ¥æç» |
| | | export function customewTransactionsDetails(query) { |
| | | return request({ |
| | | url: "/metricStatistics/customewTransactionsDetails", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | |
| | | // å¼ç¥¨å°è´¦é¡µé¢æ¥å£ |
| | | import request from '@/utils/request' |
| | | |
| | | // å页æ¥è¯¢ |
| | | export function invoiceLedgerList(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/page', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢ |
| | | export function invoiceLedgerSaveOrUpdate(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/saveOrUpdate', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // å¼ç¥¨å°è´¦å é¤ |
| | | export function invoiceLedgerDel(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/del', |
| | | method: 'delete', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // 详æ
æ¥è¯¢ |
| | | export function invoiceLedgerDetail(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/info', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // éä»¶æäº¤ |
| | | export function commitFile(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/commitFile', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // å¼ç¥¨å°è´¦é¨å乿¥è¯¢ |
| | | export function invoiceLedgerListNoPage(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/list', |
| | | method: 'get', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // å页æ¥è¯¢ |
| | | /** 忬¾å°è´¦ - 客æ·éå®è´¦æ·å页 */ |
| | | export function invoiceLedgerSalesAccount(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/salesAccount', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | return request({ |
| | | url: '/invoiceLedger/salesAccount', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // 产åå¼ç¥¨è®°å½å页æ¥è¯¢ |
| | | export function registrationProductPage(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/registrationProductPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // 产åå¼ç¥¨è¯¦æ
æ¥è¯¢ |
| | | export function invoiceLedgerProductInfo(query) { |
| | | return request({ |
| | | url: '/invoiceLedger/invoiceLedgerProductInfo', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | export function delInvoiceLedgerByRegProductId(invoiceRegistrationProductId) { |
| | | return request({ |
| | | url: '/invoiceLedger/delInvoiceLedger/'+ invoiceRegistrationProductId, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | |
| | |
| | | // å¼ç¥¨ç»è®°é¡µé¢æ¥å£ |
| | | import request from '@/utils/request' |
| | | |
| | | // æ°å¢/ä¿®æ¹ |
| | | export function receiptPaymentSaveOrUpdate(query) { |
| | | return request({ |
| | | url: '/receiptPayment/saveOrUpdate', |
| | | method: 'post', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // 客æ·å¾æ¥è®°å½æ¥è¯¢ |
| | | /** 忬¾å°è´¦ - 客æ·å¾æ¥è®°å½ */ |
| | | export function customerInteractions(query) { |
| | | return request({ |
| | | url: '/receiptPayment/customerInteractions', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // 详æ
|
| | | export function receiptPaymentInfo(query) { |
| | | return request({ |
| | | url: '/receiptPayment/info', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // å é¤ |
| | | export function receiptPaymentDel(query) { |
| | | return request({ |
| | | url: '/receiptPayment/del', |
| | | method: 'delete', |
| | | data: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢å·²ç»ç»å®å票çå¼ç¥¨å°è´¦ |
| | | export function bindInvoiceNoRegPage(query) { |
| | | return request({ |
| | | url: '/sales/product/listPageSalesLedger', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // å¼ç¥¨å°è´¦è¯¦æ
|
| | | export function invoiceInfo(query) { |
| | | return request({ |
| | | url: '/receiptPayment/invoiceInfo', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // è¯¢åæ¬¾è®°å½ |
| | | export function receiptPaymentHistoryList(query) { |
| | | return request({ |
| | | url: '/receiptPayment/receiptPaymentHistoryList', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å款记å½å页æ¥è¯¢ |
| | | */ |
| | | export function receiptPaymentHistoryListPage(query) { |
| | | return request({ |
| | | url: '/receiptPayment/receiptPaymentHistoryListPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | export function receiptPaymentHistoryListNoPage(query) { |
| | | return request({ |
| | | url: '/receiptPayment/receiptPaymentHistoryListNoPage', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | return request({ |
| | | url: '/receiptPayment/customerInteractions', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | |
| | | method: "get", |
| | | }); |
| | | }; |
| | | |
| | | export const productionOverview = () => { |
| | | return request({ |
| | | url: "/home/productionOverview", |
| | | method: "get", |
| | | headers: { |
| | | handleAuthError: false, |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | export const productionRealtimeBoard = () => { |
| | | return request({ |
| | | url: "/home/productionRealtimeBoard", |
| | | method: "get", |
| | | headers: { |
| | | handleAuthError: false, |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | export const productionOrderProgress = (params = {}) => { |
| | | const safePageNum = Math.max(1, Number(params.pageNum || 1)); |
| | | const safePageSize = Math.min(50, Math.max(1, Number(params.pageSize || 10))); |
| | | const safeTab = ["all", "inProgress", "completed", "paused"].includes(params.tab) |
| | | ? params.tab |
| | | : "all"; |
| | | return request({ |
| | | url: "/home/productionOrderProgress", |
| | | method: "get", |
| | | params: { |
| | | ...params, |
| | | tab: safeTab, |
| | | pageNum: safePageNum, |
| | | pageSize: safePageSize, |
| | | }, |
| | | headers: { |
| | | handleAuthError: false, |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | export const todayProductionPlan = (params = {}) => { |
| | | const safeLimit = Math.min(20, Math.max(1, Number(params.limit || 4))); |
| | | return request({ |
| | | url: "/home/todayProductionPlan", |
| | | method: "get", |
| | | params: { |
| | | ...params, |
| | | limit: safeLimit, |
| | | }, |
| | | headers: { |
| | | handleAuthError: false, |
| | | }, |
| | | }); |
| | | }; |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | // è´¢å¡ç®¡ç模åè·¯ç± |
| | | { |
| | | path: "/financial", |
| | | component: Layout, |
| | | hidden: false, |
| | | redirect: "/financial/general-ledger", |
| | | alwaysShow: true, |
| | | meta: { title: "è´¢å¡ç®¡ç", icon: "money" }, |
| | | children: [ |
| | | { |
| | | path: "sales-out", |
| | | component: () => import("@/views/financialManagement/receivable/salesOut.vue"), |
| | | name: "SalesOut", |
| | | meta: { title: "éå®åºåº" }, |
| | | }, |
| | | { |
| | | path: "sales-return", |
| | | component: () => import("@/views/financialManagement/receivable/salesReturn.vue"), |
| | | name: "SalesReturn", |
| | | meta: { title: "éå®éè´§" }, |
| | | }, |
| | | |
| | | { |
| | | path: "invoice-apply", |
| | | component: () => import("@/views/financialManagement/receivable/invoiceApply.vue"), |
| | | name: "InvoiceApply", |
| | | meta: { title: "å¼ç¥¨ç³è¯·" }, |
| | | }, |
| | | { |
| | | path: "output-invoice", |
| | | component: () => import("@/views/financialManagement/receivable/outputInvoice.vue"), |
| | | name: "OutputInvoice", |
| | | meta: { title: "é项å票" }, |
| | | }, |
| | | { |
| | | path: "receipt", |
| | | component: () => import("@/views/financialManagement/receivable/receipt.vue"), |
| | | name: "Receipt", |
| | | meta: { title: "æ¶æ¬¾å" }, |
| | | }, |
| | | { |
| | | path: "receivable-reconciliation", |
| | | component: () => import("@/views/financialManagement/receivable/reconciliation.vue"), |
| | | name: "ReceivableReconciliation", |
| | | meta: { title: "åºæ¶å¯¹è´¦" }, |
| | | }, |
| | | { |
| | | path: "purchase-in", |
| | | component: () => import("@/views/financialManagement/payable/purchaseIn.vue"), |
| | | name: "PurchaseIn", |
| | | meta: { title: "éè´å
¥åº" }, |
| | | }, |
| | | { |
| | | path: "purchase-return", |
| | | component: () => import("@/views/financialManagement/payable/purchaseReturn.vue"), |
| | | name: "PurchaseReturn", |
| | | meta: { title: "éè´éè´§" }, |
| | | }, |
| | | { |
| | | path: "input-invoice", |
| | | component: () => import("@/views/financialManagement/payable/input-invoice.vue"), |
| | | name: "InputInvoice", |
| | | meta: { title: "è¿é¡¹å票" }, |
| | | }, |
| | | { |
| | | path: "payment-apply", |
| | | component: () => import("@/views/financialManagement/payable/paymentApply.vue"), |
| | | name: "PaymentApply", |
| | | meta: { title: "仿¬¾ç³è¯·" }, |
| | | }, |
| | | |
| | | { |
| | | path: "payment", |
| | | component: () => import("@/views/financialManagement/payable/payment.vue"), |
| | | name: "Payment", |
| | | meta: { title: "仿¬¾å" }, |
| | | }, |
| | | { |
| | | path: "payable-reconciliation", |
| | | component: () => import("@/views/financialManagement/payable/reconciliation.vue"), |
| | | name: "PayableReconciliation", |
| | | meta: { title: "åºä»å¯¹è´¦" }, |
| | | }, |
| | | { |
| | | path: "fixed-assets", |
| | | component: () => import("@/views/financialManagement/assets/fixedAssets.vue"), |
| | | name: "FixedAssets", |
| | | meta: { title: "åºå®èµäº§" }, |
| | | }, |
| | | { |
| | | path: "intangible-assets", |
| | | component: () => import("@/views/financialManagement/assets/intangibleAssets.vue"), |
| | | name: "IntangibleAssets", |
| | | meta: { title: "æ å½¢èµäº§" }, |
| | | }, |
| | | { |
| | | path: "general-ledger", |
| | | component: () => import("@/views/financialManagement/generalLedger/index.vue"), |
| | | name: "GeneralLedger", |
| | | meta: { title: "æ»å¸ç§ç®" }, |
| | | }, |
| | | { |
| | | path: "voucher", |
| | | component: () => import("@/views/financialManagement/voucher/index.vue"), |
| | | name: "Voucher", |
| | | meta: { title: "åè¯" }, |
| | | }, |
| | | { |
| | | path: "voucher-general-ledger", |
| | | component: () => import("@/views/financialManagement/voucher/generalLedger.vue"), |
| | | name: "VoucherGeneralLedger", |
| | | meta: { title: "ç§ç®æ»å¸" }, |
| | | }, |
| | | { |
| | | path: "voucher-detail-ledger", |
| | | component: () => import("@/views/financialManagement/voucher/detailLedger.vue"), |
| | | name: "VoucherDetailLedger", |
| | | meta: { title: "ç§ç®æç»å¸" }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | |
| | | // å¨æè·¯ç±ï¼åºäºç¨æ·æé卿å»å è½½ |
| | |
| | | import {login, logout, getInfo, loginCheck, loginCheckFactory,tideLogin} from '@/api/login'
|
| | | import { getToken, setToken, removeToken } from '@/utils/auth'
|
| | | import { isHttp, isEmpty } from "@/utils/validate"
|
| | | import defAva from '@/assets/images/profile.jpg'
|
| | | import { defineStore } from 'pinia'
|
| | |
|
| | | const useUserStore = defineStore(
|
| | | 'user',
|
| | | {
|
| | | import {login, logout, getInfo, loginCheck, loginCheckFactory,tideLogin} from '@/api/login' |
| | | import { getToken, setToken, removeToken } from '@/utils/auth' |
| | | import { isHttp, isEmpty } from "@/utils/validate" |
| | | import defAva from '@/assets/images/profile.jpg' |
| | | import { defineStore } from 'pinia' |
| | | |
| | | const useUserStore = defineStore( |
| | | 'user', |
| | | { |
| | | state: () => ({ |
| | | token: getToken(), |
| | | id: '', |
| | |
| | | roles: [], |
| | | permissions: [], |
| | | aiEnabled: 0 |
| | | }),
|
| | | actions: {
|
| | | // ç»å½
|
| | | login(userInfo) {
|
| | | const username = userInfo.username.trim()
|
| | | const password = userInfo.password
|
| | | const code = userInfo.code
|
| | | const uuid = userInfo.uuid
|
| | | return new Promise((resolve, reject) => {
|
| | | login(username, password, code, uuid).then(res => {
|
| | | setToken(res.token)
|
| | | this.token = res.token
|
| | | resolve()
|
| | | }).catch(error => {
|
| | | reject(error)
|
| | | })
|
| | | })
|
| | | },
|
| | | getCurrentTime() {
|
| | | const now = new Date();
|
| | | const year = now.getFullYear(); // è·å年份
|
| | | const month = String(now.getMonth() + 1).padStart(2, '0'); // æä»½ä»0å¼å§ï¼è¦+1ï¼å¹¶è¡¥é¶
|
| | | const day = String(now.getDate()).padStart(2, '0'); // æ¥æè¡¥é¶
|
| | | const hours = String(now.getHours()).padStart(2, '0'); // å°æ¶è¡¥é¶
|
| | | const minutes = String(now.getMinutes()).padStart(2, '0'); // åéè¡¥é¶
|
| | | const seconds = String(now.getSeconds()).padStart(2, '0'); // ç§æ°è¡¥é¶
|
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
| | | },
|
| | | // è·åç¨æ·ä¿¡æ¯
|
| | | getInfo() {
|
| | | return new Promise((resolve, reject) => {
|
| | | getInfo().then(res => {
|
| | | const user = res.user
|
| | | let avatar = user.avatar || ""
|
| | | avatar = import.meta.env.VITE_APP_BASE_API + '/profile/' + avatar
|
| | | if (res.roles && res.roles.length > 0) { // éªè¯è¿åçrolesæ¯å¦æ¯ä¸ä¸ªé空æ°ç»
|
| | | this.roles = res.roles
|
| | | this.permissions = res.permissions
|
| | | } else {
|
| | | this.roles = ['ROLE_DEFAULT']
|
| | | }
|
| | | this.id = user.userId
|
| | | this.name = user.userName
|
| | | this.avatar = avatar
|
| | | this.currentFactoryName = user.currentFactoryName |
| | | this.nickName = user.nickName |
| | | this.roleName = user.roles[0].roleName |
| | | this.currentDeptId = user.tenantId |
| | | this.currentLoginTime = this.getCurrentTime() |
| | | this.aiEnabled = Number(res.aiEnabled) === 1 ? 1 : 0 |
| | | resolve(res) |
| | | }), |
| | | actions: { |
| | | // ç»å½ |
| | | login(userInfo) { |
| | | const username = userInfo.username.trim() |
| | | const password = userInfo.password |
| | | const code = userInfo.code |
| | | const uuid = userInfo.uuid |
| | | return new Promise((resolve, reject) => { |
| | | login(username, password, code, uuid).then(res => { |
| | | const token = res?.token || res?.data?.token |
| | | if (!token) { |
| | | reject(new Error('æªè·åå°ç»å½ä»¤ç')) |
| | | return |
| | | } |
| | | setToken(token) |
| | | this.token = token |
| | | resolve() |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | },
|
| | | // éåºç³»ç»
|
| | | logOut() {
|
| | | return new Promise((resolve, reject) => {
|
| | | logout(this.token).then(() => {
|
| | | }, |
| | | getCurrentTime() { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); // è·å年份 |
| | | const month = String(now.getMonth() + 1).padStart(2, '0'); // æä»½ä»0å¼å§ï¼è¦+1ï¼å¹¶è¡¥é¶ |
| | | const day = String(now.getDate()).padStart(2, '0'); // æ¥æè¡¥é¶ |
| | | const hours = String(now.getHours()).padStart(2, '0'); // å°æ¶è¡¥é¶ |
| | | const minutes = String(now.getMinutes()).padStart(2, '0'); // åéè¡¥é¶ |
| | | const seconds = String(now.getSeconds()).padStart(2, '0'); // ç§æ°è¡¥é¶ |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | }, |
| | | // è·åç¨æ·ä¿¡æ¯ |
| | | getInfo() { |
| | | return new Promise((resolve, reject) => { |
| | | getInfo().then(res => { |
| | | const data = res?.data ?? res |
| | | const user = data.user || {} |
| | | let avatar = user.avatar || "" |
| | | avatar = import.meta.env.VITE_APP_BASE_API + '/profile/' + avatar |
| | | if (data.roles && data.roles.length > 0) { // éªè¯è¿åçrolesæ¯å¦æ¯ä¸ä¸ªé空æ°ç» |
| | | this.roles = data.roles |
| | | this.permissions = data.permissions |
| | | } else { |
| | | this.roles = ['ROLE_DEFAULT'] |
| | | } |
| | | this.id = user.userId || '' |
| | | this.name = user.userName || '' |
| | | this.avatar = avatar |
| | | this.currentFactoryName = user.currentFactoryName || '' |
| | | this.nickName = user.nickName || '' |
| | | this.roleName = Array.isArray(user.roles) && user.roles.length > 0 ? (user.roles[0].roleName || '') : '' |
| | | this.currentDeptId = user.tenantId || '' |
| | | this.currentLoginTime = this.getCurrentTime() |
| | | this.aiEnabled = Number(data.aiEnabled) === 1 ? 1 : 0 |
| | | resolve(data) |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | // éåºç³»ç» |
| | | logOut() { |
| | | return new Promise((resolve, reject) => { |
| | | logout(this.token).then(() => { |
| | | this.token = '' |
| | | this.roles = [] |
| | | this.permissions = [] |
| | |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | })
|
| | | },
|
| | | // ç»å½æ ¡éª
|
| | | loginCheck(userInfo) {
|
| | | const username = userInfo.username.trim()
|
| | | const password = userInfo.password
|
| | | return new Promise((resolve, reject) => {
|
| | | loginCheck(username, password).then(res => {
|
| | | resolve(res)
|
| | | }).catch(error => {
|
| | | reject(error)
|
| | | })
|
| | | })
|
| | | },
|
| | | // é¨é¨ç»å½
|
| | | loginCheckFactory(userInfo) {
|
| | | const username = userInfo.username.trim()
|
| | | const password = userInfo.password
|
| | | return new Promise((resolve, reject) => {
|
| | | loginCheckFactory(username, password).then(res => {
|
| | | setToken(res.token)
|
| | | this.token = res.token
|
| | | resolve()
|
| | | }).catch(error => {
|
| | | reject(error)
|
| | | })
|
| | | })
|
| | | },
|
| | | TideLogin(code) {
|
| | | return new Promise((resolve, reject) => {
|
| | | tideLogin(code)
|
| | | .then((res) => {
|
| | | setToken(res.token);
|
| | | this.token = res.token
|
| | | Vue.prototype.uploadHeader = {
|
| | | Authorization: "Bearer " + res.token,
|
| | | };
|
| | | resolve();
|
| | | })
|
| | | .catch((error) => {
|
| | | reject(error);
|
| | | });
|
| | | });
|
| | | },
|
| | | }
|
| | | })
|
| | |
|
| | | export default useUserStore
|
| | | }) |
| | | }, |
| | | // ç»å½æ ¡éª |
| | | loginCheck(userInfo) { |
| | | const username = userInfo.username.trim() |
| | | const password = userInfo.password |
| | | return new Promise((resolve, reject) => { |
| | | loginCheck(username, password).then(res => { |
| | | resolve(res) |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | // é¨é¨ç»å½ |
| | | loginCheckFactory(userInfo) { |
| | | const username = userInfo.username.trim() |
| | | const password = userInfo.password |
| | | return new Promise((resolve, reject) => { |
| | | loginCheckFactory(username, password).then(res => { |
| | | const token = res?.token || res?.data?.token |
| | | if (!token) { |
| | | reject(new Error('æªè·åå°ç»å½ä»¤ç')) |
| | | return |
| | | } |
| | | setToken(token) |
| | | this.token = token |
| | | resolve() |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | TideLogin(code) { |
| | | return new Promise((resolve, reject) => { |
| | | tideLogin(code) |
| | | .then((res) => { |
| | | const token = res?.token || res?.data?.token |
| | | if (!token) { |
| | | reject(new Error('æªè·åå°ç»å½ä»¤ç')) |
| | | return |
| | | } |
| | | setToken(token); |
| | | this.token = token |
| | | Vue.prototype.uploadHeader = { |
| | | Authorization: "Bearer " + token, |
| | | }; |
| | | resolve(); |
| | | }) |
| | | .catch((error) => { |
| | | reject(error); |
| | | }); |
| | | }); |
| | | }, |
| | | } |
| | | }) |
| | | |
| | | export default useUserStore |
| | |
| | | })
|
| | |
|
| | | // requestæ¦æªå¨
|
| | | service.interceptors.request.use(config => {
|
| | | service.interceptors.request.use(config => { |
| | | config.headers = config.headers || {} |
| | | // æ¯å¦éè¦è®¾ç½® token
|
| | | const isToken = (config.headers || {}).isToken === false
|
| | | const isToken = config.headers.isToken === false |
| | | // æ¯å¦éè¦é²æ¢æ°æ®éå¤æäº¤
|
| | | const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
|
| | | const isRepeatSubmit = config.headers.repeatSubmit === false |
| | | if (getToken() && !isToken) {
|
| | | config.headers['Authorization'] = 'Bearer ' + getToken() // 让æ¯ä¸ªè¯·æ±æºå¸¦èªå®ä¹token è¯·æ ¹æ®å®é
æ
åµèªè¡ä¿®æ¹
|
| | | }
|
| | |
| | | }
|
| | | }
|
| | | return config
|
| | | }, error => {
|
| | | console.log(error)
|
| | | Promise.reject(error)
|
| | | })
|
| | | }, error => { |
| | | console.log(error) |
| | | return Promise.reject(error) |
| | | }) |
| | |
|
| | | // ååºæ¦æªå¨
|
| | | service.interceptors.response.use(res => {
|
| | | // æªè®¾ç½®ç¶æç åé»è®¤æåç¶æ
|
| | | const code = res.data.code || 200
|
| | | const code = res.data.code || 200 |
| | | const handleAuthError = (res.config && res.config.headers && res.config.headers.handleAuthError) !== false |
| | | // è·åé误信æ¯
|
| | | const msg = errorCode[code] || res.data.msg || errorCode['default']
|
| | | // äºè¿å¶æ°æ®åç´æ¥è¿å
|
| | | if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
|
| | | return res.data
|
| | | }
|
| | | if (code === 401) {
|
| | | if (!isRelogin.show) {
|
| | | if (code === 401) { |
| | | if (!handleAuthError) { |
| | | return Promise.reject(new Error(msg)) |
| | | } |
| | | if (!isRelogin.show) { |
| | | isRelogin.show = true
|
| | | ElMessageBox.confirm('ç»å½ç¶æå·²è¿æï¼æ¨å¯ä»¥ç»§ç»çå¨è¯¥é¡µé¢ï¼æè
éæ°ç»å½', 'ç³»ç»æç¤º', { confirmButtonText: 'éæ°ç»å½', cancelButtonText: 'åæ¶', type: 'warning' }).then(() => {
|
| | | isRelogin.show = false
|
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="仿¬¾åå·:"> |
| | | <el-input v-model="filters.paymentNumber" placeholder="请è¾å
¥ä»æ¬¾åå·" clearable style="width: 200px;" /> |
| | | <el-input v-model="filters.paymentNumber" |
| | | placeholder="请è¾å
¥ä»æ¬¾åå·" |
| | | clearable |
| | | style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable filterable style="width: 200px;"> |
| | | <el-option |
| | | v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id" |
| | | /> |
| | | <el-select v-model="filters.supplierId" |
| | | placeholder="è¯·éæ©ä¾åºå" |
| | | clearable |
| | | filterable |
| | | style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="仿¬¾æ¹å¼:"> |
| | | <el-select v-model="filters.paymentMethod" placeholder="è¯·éæ©ä»æ¬¾æ¹å¼" clearable style="width: 150px;"> |
| | | <el-option |
| | | v-for="item in checkout_payment" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | <el-select v-model="filters.paymentMethod" |
| | | placeholder="è¯·éæ©ä»æ¬¾æ¹å¼" |
| | | clearable |
| | | style="width: 150px;"> |
| | | <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 label="仿¬¾æ¥æ:"> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | type="daterange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | style="width: 240px;" |
| | | /> |
| | | <el-date-picker v-model="filters.dateRange" |
| | | type="daterange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | style="width: 240px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <el-statistic title="æ¬é¡µä»æ¬¾å计" :value="totalPaymentAmount" :precision="2" prefix="Â¥" /> |
| | | <el-statistic title="æ¬é¡µä»æ¬¾å计" |
| | | :value="totalPaymentAmount" |
| | | :precision="2" |
| | | prefix="Â¥" /> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleExport" icon="Download">导åº</el-button> |
| | | <el-button @click="handleExport" |
| | | icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | <PIMTable rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | @pagination="changePage"> |
| | | <template #amount="{ row }"> |
| | | <span class="text-danger">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | |
| | | <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | <el-button :disabled="row.accountStatemen" |
| | | type="danger" |
| | | link |
| | | @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { |
| | | listPageAccountPurchasePayment, |
| | | deleteAccountPurchasePayment, |
| | | } from "@/api/financialManagement/accountPurchasePayment.js"; |
| | | import { ref, reactive, computed, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { |
| | | listPageAccountPurchasePayment, |
| | | deleteAccountPurchasePayment, |
| | | } from "@/api/financialManagement/accountPurchasePayment.js"; |
| | | |
| | | defineOptions({ |
| | | name: "仿¬¾å", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { checkout_payment } = proxy.useDict("checkout_payment"); |
| | | |
| | | const filters = reactive({ |
| | | paymentNumber: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "仿¬¾åå·", prop: "paymentNumber", width: "150" }, |
| | | { label: "å
³èç³è¯·å", prop: "invoiceApplicationNo", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "仿¬¾æ¥æ", prop: "paymentDate", width: "120" }, |
| | | { label: "仿¬¾éé¢", prop: "amount", dataType: "slot", slot: "amount" }, |
| | | { label: "仿¬¾æ¹å¼", prop: "paymentMethod", dataType: "slot", slot: "paymentMethod", width: "120" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "80", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const supplierList = ref([]); |
| | | |
| | | const totalPaymentAmount = computed(() => |
| | | dataList.value.reduce((sum, item) => sum + Number(item.amount ?? 0), 0) |
| | | ); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getPaymentMethodLabel = (value) => { |
| | | if (value === undefined || value === null || value === "") return "-"; |
| | | const item = checkout_payment.value?.find((m) => String(m.value) === String(value)); |
| | | return item?.label ?? value; |
| | | }; |
| | | |
| | | const normalizeTableRow = (row) => ({ |
| | | ...row, |
| | | paymentNumber: row.paymentNumber ?? row.paymentCode, |
| | | invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "", |
| | | amount: row.paymentAmount ?? row.amount, |
| | | bankAccountNum: row.bankAccountNum ?? row.bankAccount ?? "", |
| | | bankAccountName: row.bankAccountName ?? row.bankName ?? "", |
| | | }); |
| | | |
| | | const getSupplierList = () => { |
| | | getOptions().then((res) => { |
| | | if (res.code === 200) { |
| | | supplierList.value = res.data ?? []; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const appendFilterParams = (params) => { |
| | | if (filters.paymentNumber) { |
| | | params.paymentNumber = filters.paymentNumber; |
| | | } |
| | | if (filters.supplierId) { |
| | | params.supplierId = filters.supplierId; |
| | | } |
| | | if (filters.paymentMethod) { |
| | | params.paymentMethod = filters.paymentMethod; |
| | | } |
| | | if (filters.dateRange?.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | }; |
| | | |
| | | const buildListParams = () => |
| | | appendFilterParams({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | defineOptions({ |
| | | name: "仿¬¾å", |
| | | }); |
| | | |
| | | const buildExportParams = () => appendFilterParams({}); |
| | | const { proxy } = getCurrentInstance(); |
| | | const { checkout_payment } = proxy.useDict("checkout_payment"); |
| | | |
| | | const handleExport = () => { |
| | | proxy.download( |
| | | "/accountPurchasePayment/exportAccountPurchasePayment", |
| | | buildExportParams(), |
| | | `仿¬¾å_${Date.now()}.xlsx` |
| | | const filters = reactive({ |
| | | paymentNumber: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "仿¬¾åå·", prop: "paymentNumber", width: "150" }, |
| | | { label: "å
³èç³è¯·å", prop: "invoiceApplicationNo", width: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", width: "180" }, |
| | | { label: "仿¬¾æ¥æ", prop: "paymentDate", width: "120" }, |
| | | { label: "仿¬¾éé¢", prop: "amount", dataType: "slot", slot: "amount" }, |
| | | { |
| | | label: "仿¬¾æ¹å¼", |
| | | prop: "paymentMethod", |
| | | dataType: "slot", |
| | | slot: "paymentMethod", |
| | | width: "120", |
| | | }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { |
| | | label: "æä½", |
| | | prop: "operation", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | width: "80", |
| | | fixed: "right", |
| | | }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const supplierList = ref([]); |
| | | |
| | | const totalPaymentAmount = computed(() => |
| | | dataList.value.reduce((sum, item) => sum + Number(item.amount ?? 0), 0) |
| | | ); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | listPageAccountPurchasePayment(buildListParams()) |
| | | .then((res) => { |
| | | const formatMoney = value => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value) |
| | | .toFixed(2) |
| | | .replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const getPaymentMethodLabel = value => { |
| | | if (value === undefined || value === null || value === "") return "-"; |
| | | const item = checkout_payment.value?.find( |
| | | m => String(m.value) === String(value) |
| | | ); |
| | | return item?.label ?? value; |
| | | }; |
| | | |
| | | const normalizeTableRow = row => ({ |
| | | ...row, |
| | | paymentNumber: row.paymentNumber ?? row.paymentCode, |
| | | invoiceApplicationNo: row.invoiceApplicationNo ?? row.applyCode ?? "", |
| | | amount: row.paymentAmount ?? row.amount, |
| | | bankAccountNum: row.bankAccountNum ?? row.bankAccount ?? "", |
| | | bankAccountName: row.bankAccountName ?? row.bankName ?? "", |
| | | }); |
| | | |
| | | const getSupplierList = () => { |
| | | getOptions().then(res => { |
| | | if (res.code === 200) { |
| | | dataList.value = (res.data?.records ?? []).map(normalizeTableRow); |
| | | pagination.total = res.data?.total ?? 0; |
| | | } else { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | supplierList.value = res.data ?? []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | const appendFilterParams = params => { |
| | | if (filters.paymentNumber) { |
| | | params.paymentNumber = filters.paymentNumber; |
| | | } |
| | | if (filters.supplierId) { |
| | | params.supplierId = filters.supplierId; |
| | | } |
| | | if (filters.paymentMethod) { |
| | | params.paymentMethod = filters.paymentMethod; |
| | | } |
| | | if (filters.dateRange?.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.paymentNumber = ""; |
| | | filters.supplierId = ""; |
| | | filters.paymentMethod = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | const buildListParams = () => |
| | | appendFilterParams({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }); |
| | | |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | const buildExportParams = () => appendFilterParams({}); |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm(`确认å é¤ä»æ¬¾åã${row.paymentNumber}ãåï¼`, "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | deleteAccountPurchasePayment([row.id]) |
| | | .then((res) => { |
| | | const handleExport = () => { |
| | | proxy.download( |
| | | "/accountPurchasePayment/exportAccountPurchasePayment", |
| | | buildExportParams(), |
| | | `仿¬¾å_${Date.now()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | listPageAccountPurchasePayment(buildListParams()) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | dataList.value = (res.data?.records ?? []).map(normalizeTableRow); |
| | | pagination.total = res.data?.total ?? 0; |
| | | } else { |
| | | ElMessage.error(res.msg || "å é¤å¤±è´¥"); |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }); |
| | | }; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getSupplierList(); |
| | | getTableData(); |
| | | }); |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.paymentNumber = ""; |
| | | filters.supplierId = ""; |
| | | filters.paymentMethod = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm(`确认å é¤ä»æ¬¾åã${row.paymentNumber}ãåï¼`, "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | deleteAccountPurchasePayment([row.id]) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | } else { |
| | | ElMessage.error(res.msg || "å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getSupplierList(); |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <!-- éè´å
¥åº --> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="å
¥åºåå·:"> |
| | | <el-input v-model="filters.inboundBatches" placeholder="请è¾å
¥å
¥åºåå·" clearable style="width: 200px;" /> |
| | | <el-input v-model="filters.inboundBatches" |
| | | placeholder="请è¾å
¥å
¥åºåå·" |
| | | clearable |
| | | style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå:"> |
| | | <el-select v-model="filters.supplierId" placeholder="è¯·éæ©ä¾åºå" clearable filterable style="width: 200px;"> |
| | | <el-option |
| | | v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id" |
| | | /> |
| | | <el-select v-model="filters.supplierId" |
| | | placeholder="è¯·éæ©ä¾åºå" |
| | | clearable |
| | | filterable |
| | | style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å
¥åºæ¥æ:"> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | /> |
| | | <el-date-picker v-model="filters.dateRange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | <div class="actions"> |
| | | <div></div> |
| | | <div> |
| | | <el-button @click="handleOut" icon="Download">导åº</el-button> |
| | | <el-button @click="handleOut" |
| | | icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | <PIMTable rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | @pagination="changePage"> |
| | | <template #inboundDate="{ row }"> |
| | | {{ row.inboundDate ?? row.InboundDate ?? "" }} |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listPageAccountPurchase } from "@/api/financialManagement/accountPurchase"; |
| | | import { listSupplier } from "@/api/basicData/supplierManageFile.js"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listPageAccountPurchase } from "@/api/financialManagement/accountPurchase"; |
| | | import { listSupplier } from "@/api/basicData/supplierManageFile.js"; |
| | | |
| | | defineOptions({ |
| | | name: "éè´å
¥åº", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const filters = reactive({ |
| | | inboundBatches: "", |
| | | supplierId: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å
¥åºåå·", prop: "inboundBatches", minWidth: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", minWidth: "180" }, |
| | | { |
| | | label: "å
¥åºæ¥æ", |
| | | prop: "inboundDate", |
| | | minWidth: "170", |
| | | dataType: "slot", |
| | | slot: "inboundDate", |
| | | }, |
| | | { label: "产ååç§°", prop: "productName", minWidth: "140" }, |
| | | { label: "产åè§æ ¼", prop: "specificationModel", minWidth: "140" }, |
| | | { label: "éè´è®¢åå·", prop: "purchaseContractNumber", minWidth: "150" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const supplierList = ref([]); |
| | | |
| | | const buildFilterParams = () => { |
| | | const params = {}; |
| | | if (filters.inboundBatches) { |
| | | params.inboundBatches = filters.inboundBatches; |
| | | } |
| | | if (filters.supplierId) { |
| | | params.supplierId = filters.supplierId; |
| | | } |
| | | if (filters.dateRange?.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | }; |
| | | |
| | | const getSupplierList = () => { |
| | | listSupplier({ current: -1, size: -1, isWhite: 0 }).then((res) => { |
| | | if (res.code === 200) { |
| | | supplierList.value = res.data?.records ?? []; |
| | | } |
| | | defineOptions({ |
| | | name: "éè´å
¥åº", |
| | | }); |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | listPageAccountPurchase({ |
| | | ...buildFilterParams(), |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = res.data.records ?? []; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | const filters = reactive({ |
| | | inboundBatches: "", |
| | | supplierId: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "å
¥åºåå·", prop: "inboundBatches", minWidth: "150" }, |
| | | { label: "ä¾åºå", prop: "supplierName", minWidth: "180" }, |
| | | { |
| | | label: "å
¥åºæ¥æ", |
| | | prop: "inboundDate", |
| | | minWidth: "170", |
| | | dataType: "slot", |
| | | slot: "inboundDate", |
| | | }, |
| | | { label: "产ååç§°", prop: "productName", minWidth: "140" }, |
| | | { label: "产åè§æ ¼", prop: "specificationModel", minWidth: "140" }, |
| | | { |
| | | label: "éé¢", |
| | | prop: "inboundAmount", |
| | | minWidth: "120", |
| | | align: "right", |
| | | formatData: val => |
| | | val === null || val === undefined || val === "" |
| | | ? "" |
| | | : Number(val).toLocaleString("zh-CN", { |
| | | minimumFractionDigits: 2, |
| | | maximumFractionDigits: 2, |
| | | }), |
| | | }, |
| | | { label: "éè´è®¢åå·", prop: "purchaseContractNumber", minWidth: "150" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const supplierList = ref([]); |
| | | |
| | | const buildFilterParams = () => { |
| | | const params = {}; |
| | | if (filters.inboundBatches) { |
| | | params.inboundBatches = filters.inboundBatches; |
| | | } |
| | | if (filters.supplierId) { |
| | | params.supplierId = filters.supplierId; |
| | | } |
| | | if (filters.dateRange?.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | }; |
| | | |
| | | const getSupplierList = () => { |
| | | listSupplier({ current: -1, size: -1, isWhite: 0 }).then(res => { |
| | | if (res.code === 200) { |
| | | supplierList.value = res.data?.records ?? []; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | listPageAccountPurchase({ |
| | | ...buildFilterParams(), |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }) |
| | | .then(res => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = res.data.records ?? []; |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.inboundBatches = ""; |
| | | filters.supplierId = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | const resetFilters = () => { |
| | | filters.inboundBatches = ""; |
| | | filters.supplierId = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | proxy.download( |
| | | "/accountPurchase/exportAccountPurchaseInbound", |
| | | buildFilterParams(), |
| | | `éè´å
¥åº_${Date.now()}.xlsx` |
| | | ); |
| | | }; |
| | | const handleOut = () => { |
| | | proxy.download( |
| | | "/accountPurchase/exportAccountPurchaseInbound", |
| | | buildFilterParams(), |
| | | `éè´å
¥åº_${Date.now()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getSupplierList(); |
| | | getTableData(); |
| | | }); |
| | | onMounted(() => { |
| | | getSupplierList(); |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 15px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form :model="filters" |
| | | :inline="true"> |
| | | <el-form-item label="æ¶æ¬¾åå·:"> |
| | | <el-input v-model="filters.collectionNumber" placeholder="请è¾å
¥æ¶æ¬¾åå·" clearable style="width: 200px;" /> |
| | | <el-input v-model="filters.collectionNumber" |
| | | placeholder="请è¾å
¥æ¶æ¬¾åå·" |
| | | clearable |
| | | style="width: 200px;" /> |
| | | </el-form-item> |
| | | <el-form-item label="客æ·:"> |
| | | <el-select v-model="filters.customerId" placeholder="è¯·éæ©å®¢æ·" clearable filterable style="width: 200px;"> |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" /> |
| | | <el-select v-model="filters.customerId" |
| | | placeholder="è¯·éæ©å®¢æ·" |
| | | clearable |
| | | filterable |
| | | style="width: 200px;"> |
| | | <el-option v-for="item in customerList" |
| | | :key="item.id" |
| | | :label="item.customerName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶æ¬¾æ¹å¼:"> |
| | | <el-select v-model="filters.collectionMethod" placeholder="è¯·éæ©æ¶æ¬¾æ¹å¼" clearable style="width: 150px;"> |
| | | <el-option |
| | | v-for="item in payment_methods" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | <el-select v-model="filters.collectionMethod" |
| | | placeholder="è¯·éæ©æ¶æ¬¾æ¹å¼" |
| | | clearable |
| | | style="width: 150px;"> |
| | | <el-option v-for="item in payment_methods" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶æ¬¾æ¥æ:"> |
| | | <el-date-picker |
| | | v-model="filters.dateRange" |
| | | type="daterange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | style="width: 240px;" |
| | | /> |
| | | <el-date-picker v-model="filters.dateRange" |
| | | type="daterange" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | clearable |
| | | style="width: 240px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="onSearch">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="onSearch">æç´¢</el-button> |
| | | <el-button @click="resetFilters">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <div> |
| | | <el-statistic title="æ¬é¡µæ¶æ¬¾å计" :value="totalReceiptAmount" :precision="2" prefix="Â¥" /> |
| | | <el-statistic title="æ¬é¡µæ¶æ¬¾å计" |
| | | :value="totalReceiptAmount" |
| | | :precision="2" |
| | | prefix="Â¥" /> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="add" icon="Plus">æ°å¢æ¶æ¬¾</el-button> |
| | | <el-button type="success" @click="handleExport" icon="Download">导åº</el-button> |
| | | <el-button type="primary" |
| | | @click="add" |
| | | icon="Plus">æ°å¢æ¶æ¬¾</el-button> |
| | | <el-button type="success" |
| | | @click="handleExport" |
| | | icon="Download">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | v-loading="tableLoading" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | <PIMTable rowKey="id" |
| | | v-loading="tableLoading" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | total: pagination.total, |
| | | }" |
| | | @pagination="changePage" |
| | | > |
| | | @pagination="changePage"> |
| | | <template #amount="{ row }"> |
| | | <span class="text-success">Â¥{{ formatMoney(row.amount) }}</span> |
| | | </template> |
| | |
| | | <span>{{ getReceiptMethodLabel(row.receiptMethod) }}</span> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" link @click="view(row)">æ¥ç</el-button> |
| | | <el-button type="primary" link @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | <el-button type="primary" |
| | | link |
| | | @click="view(row)">æ¥ç</el-button> |
| | | <el-button :disabled="row.accountStatemen" |
| | | type="primary" |
| | | link |
| | | @click="edit(row)">ç¼è¾</el-button> |
| | | <el-button :disabled="row.accountStatemen" |
| | | type="danger" |
| | | link |
| | | @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <FormDialog |
| | | :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | | width="800px" |
| | | :operation-type="isView ? 'detail' : ''" |
| | | @confirm="submitForm" |
| | | @cancel="closeDialog" |
| | | > |
| | | <el-form :model="form" :rules="rules" ref="formRef" label-width="120px"> |
| | | <FormDialog :title="dialogTitle" |
| | | v-model="dialogVisible" |
| | | width="800px" |
| | | :operation-type="isView ? 'detail' : ''" |
| | | @confirm="submitForm" |
| | | @cancel="closeDialog"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="æ¶æ¬¾åå·" prop="receiptCode"> |
| | | <el-input v-model="form.receiptCode" placeholder="ç³»ç»èªå¨çæ" disabled /> |
| | | <el-form-item label="æ¶æ¬¾åå·" |
| | | prop="receiptCode"> |
| | | <el-input v-model="form.receiptCode" |
| | | placeholder="ç³»ç»èªå¨çæ" |
| | | disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="客æ·" prop="customerId"> |
| | | <el-select |
| | | v-model="form.customerId" |
| | | placeholder="è¯·éæ©å®¢æ·" |
| | | style="width: 100%;" |
| | | :disabled="isEdit || isView" |
| | | filterable |
| | | @change="handleCustomerChange" |
| | | > |
| | | <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" /> |
| | | <el-form-item label="客æ·" |
| | | prop="customerId"> |
| | | <el-select v-model="form.customerId" |
| | | placeholder="è¯·éæ©å®¢æ·" |
| | | style="width: 100%;" |
| | | :disabled="isEdit || isView" |
| | | filterable |
| | | @change="handleCustomerChange"> |
| | | <el-option v-for="item in customerList" |
| | | :key="item.id" |
| | | :label="item.customerName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
³èåæ®" prop="stockOutRecordIds"> |
| | | <el-input |
| | | :model-value="outboundBatchDisplayText" |
| | | placeholder="请å
鿩客æ·" |
| | | readonly |
| | | :disabled="!form.customerId || isEdit || isView" |
| | | class="outbound-batch-input" |
| | | @click="handleOutboundInputClick" |
| | | > |
| | | <template v-if="!isEdit && !isView" #append> |
| | | <el-button |
| | | :disabled="!form.customerId" |
| | | :loading="outboundBatchLoading" |
| | | @click.stop="openOutboundSelectDialog" |
| | | > |
| | | <el-form-item label="å
³èåæ®" |
| | | prop="stockOutRecordIds"> |
| | | <el-input :model-value="outboundBatchDisplayText" |
| | | placeholder="请å
鿩客æ·" |
| | | readonly |
| | | :disabled="!form.customerId || isEdit || isView" |
| | | class="outbound-batch-input" |
| | | @click="handleOutboundInputClick"> |
| | | <template v-if="!isEdit && !isView" |
| | | #append> |
| | | <el-button :disabled="!form.customerId" |
| | | :loading="outboundBatchLoading" |
| | | @click.stop="openOutboundSelectDialog"> |
| | | éæ© |
| | | </el-button> |
| | | </template> |
| | |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¶æ¬¾æ¥æ" prop="receiptDate"> |
| | | <el-date-picker |
| | | v-model="form.receiptDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | /> |
| | | <el-form-item label="æ¶æ¬¾æ¥æ" |
| | | prop="receiptDate"> |
| | | <el-date-picker v-model="form.receiptDate" |
| | | type="date" |
| | | placeholder="éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%;" |
| | | :disabled="isView" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¶æ¬¾éé¢" prop="amount"> |
| | | <el-input-number |
| | | v-model="form.amount" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | placeholder="æ ¹æ®å
³èåæ®èªå¨æ±æ»ï¼å¯ä¿®æ¹" |
| | | /> |
| | | <el-form-item label="æ¶æ¬¾éé¢" |
| | | prop="amount"> |
| | | <el-input-number v-model="form.amount" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | placeholder="æ ¹æ®å
³èåæ®èªå¨æ±æ»ï¼å¯ä¿®æ¹" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ¶æ¬¾æ¹å¼" prop="receiptMethod"> |
| | | <el-select |
| | | v-model="form.receiptMethod" |
| | | placeholder="è¯·éæ©æ¶æ¬¾æ¹å¼" |
| | | style="width: 100%;" |
| | | :disabled="isView" |
| | | > |
| | | <el-option |
| | | v-for="item in payment_methods" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | <el-form-item label="æ¶æ¬¾æ¹å¼" |
| | | prop="receiptMethod"> |
| | | <el-select v-model="form.receiptMethod" |
| | | placeholder="è¯·éæ©æ¶æ¬¾æ¹å¼" |
| | | style="width: 100%;" |
| | | :disabled="isView"> |
| | | <el-option v-for="item in payment_methods" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨" :disabled="isView" /> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥å¤æ³¨" |
| | | :disabled="isView" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template v-if="!isView" #footer> |
| | | <el-button type="primary" :loading="submitLoading" @click="submitForm">ç¡®å®</el-button> |
| | | <template v-if="!isView" |
| | | #footer> |
| | | <el-button type="primary" |
| | | :loading="submitLoading" |
| | | @click="submitForm">ç¡®å®</el-button> |
| | | <el-button @click="closeDialog">åæ¶</el-button> |
| | | </template> |
| | | </FormDialog> |
| | | |
| | | <el-dialog |
| | | v-model="outboundSelectVisible" |
| | | title="éæ©å
³èåæ®" |
| | | width="1200px" |
| | | append-to-body |
| | | destroy-on-close |
| | | :close-on-click-modal="false" |
| | | @closed="handleOutboundDialogClosed" |
| | | > |
| | | <el-table |
| | | ref="outboundTableRef" |
| | | v-loading="outboundBatchLoading" |
| | | :data="outboundBatchList" |
| | | row-key="id" |
| | | border |
| | | stripe |
| | | max-height="480" |
| | | @selection-change="handleOutboundDialogSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column prop="outboundBatches" label="åºåºåå·" min-width="140" show-overflow-tooltip /> |
| | | <el-table-column prop="customerName" label="客æ·åç§°" min-width="120" show-overflow-tooltip /> |
| | | <el-table-column prop="productName" label="产ååç§°" min-width="120" show-overflow-tooltip /> |
| | | <el-table-column prop="specificationModel" label="è§æ ¼åå·" min-width="140" show-overflow-tooltip /> |
| | | <el-table-column prop="salesContractNo" label="éå®ååå·" min-width="140" show-overflow-tooltip /> |
| | | <el-table-column prop="shippingNo" label="åè´§åå·" min-width="130" show-overflow-tooltip /> |
| | | <el-table-column prop="shippingDate" label="åè´§æ¥æ" width="110" align="center" /> |
| | | <el-table-column prop="outboundAmount" label="åºåºéé¢" width="110" align="right"> |
| | | <el-dialog v-model="outboundSelectVisible" |
| | | title="éæ©å
³èåæ®" |
| | | width="1200px" |
| | | append-to-body |
| | | destroy-on-close |
| | | :close-on-click-modal="false" |
| | | @closed="handleOutboundDialogClosed"> |
| | | <el-table ref="outboundTableRef" |
| | | v-loading="outboundBatchLoading" |
| | | :data="outboundBatchList" |
| | | row-key="id" |
| | | border |
| | | stripe |
| | | max-height="480" |
| | | @selection-change="handleOutboundDialogSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="55" |
| | | align="center" /> |
| | | <el-table-column prop="outboundBatches" |
| | | label="åºåºåå·" |
| | | min-width="140" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="customerName" |
| | | label="客æ·åç§°" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="productName" |
| | | label="产ååç§°" |
| | | min-width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="specificationModel" |
| | | label="è§æ ¼åå·" |
| | | min-width="140" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="salesContractNo" |
| | | label="éå®ååå·" |
| | | min-width="140" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="shippingNo" |
| | | label="åè´§åå·" |
| | | min-width="130" |
| | | show-overflow-tooltip /> |
| | | <el-table-column prop="shippingDate" |
| | | label="åè´§æ¥æ" |
| | | width="110" |
| | | align="center" /> |
| | | <el-table-column prop="outboundAmount" |
| | | label="åºåºéé¢" |
| | | width="110" |
| | | align="right"> |
| | | <template #default="{ row }">Â¥{{ formatMoney(row.outboundAmount) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="taxRate" label="ç¨ç" width="80" align="center"> |
| | | <el-table-column prop="taxRate" |
| | | label="ç¨ç" |
| | | width="80" |
| | | align="center"> |
| | | <template #default="{ row }">{{ row.taxRate }}%</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <template #footer> |
| | | <el-button type="primary" @click="confirmOutboundSelection">ç¡®å®</el-button> |
| | | <el-button type="primary" |
| | | @click="confirmOutboundSelection">ç¡®å®</el-button> |
| | | <el-button @click="outboundSelectVisible = false">åæ¶</el-button> |
| | | </template> |
| | | </el-dialog> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listCustomer } from "@/api/basicData/customer.js"; |
| | | import { |
| | | getOutboundBatchesByCustomer, |
| | | addAccountSalesCollection, |
| | | listPageAccountSalesCollection, |
| | | updateAccountSalesCollection, |
| | | deleteAccountSalesCollection, |
| | | } from "@/api/financialManagement/accountSalesCollection.js"; |
| | | import { |
| | | ref, |
| | | reactive, |
| | | computed, |
| | | onMounted, |
| | | nextTick, |
| | | getCurrentInstance, |
| | | } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listCustomer } from "@/api/basicData/customer.js"; |
| | | import { |
| | | getOutboundBatchesByCustomer, |
| | | addAccountSalesCollection, |
| | | listPageAccountSalesCollection, |
| | | updateAccountSalesCollection, |
| | | deleteAccountSalesCollection, |
| | | } from "@/api/financialManagement/accountSalesCollection.js"; |
| | | |
| | | defineOptions({ |
| | | name: "æ¶æ¬¾å", |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const { payment_methods } = proxy.useDict("payment_methods"); |
| | | |
| | | const filters = reactive({ |
| | | collectionNumber: "", |
| | | customerId: "", |
| | | collectionMethod: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "æ¶æ¬¾åå·", prop: "receiptCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "æ¶æ¬¾æ¥æ", prop: "receiptDate", width: "120" }, |
| | | { label: "æ¶æ¬¾éé¢", prop: "amount", dataType: "slot", slot: "amount" }, |
| | | { label: "æ¶æ¬¾æ¹å¼", prop: "receiptMethod", dataType: "slot", slot: "receiptMethod", width: "120" }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { label: "æä½", prop: "operation", dataType: "slot", slot: "operation", width: "200", fixed: "right" }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const submitLoading = ref(false); |
| | | |
| | | const customerList = ref([]); |
| | | const outboundBatchList = ref([]); |
| | | const outboundBatchOptions = ref([]); |
| | | const outboundBatchLoading = ref(false); |
| | | const outboundSelectVisible = ref(false); |
| | | const outboundTableRef = ref(null); |
| | | const dialogOutboundSelection = ref([]); |
| | | |
| | | const getReceiptMethodLabel = (value) => { |
| | | if (value === undefined || value === null || value === "") return "-"; |
| | | const item = payment_methods.value?.find((m) => String(m.value) === String(value)); |
| | | return item?.label ?? value; |
| | | }; |
| | | |
| | | const getDefaultReceiptMethod = () => payment_methods.value?.[0]?.value ?? ""; |
| | | |
| | | const form = reactive({ |
| | | receiptCode: "", |
| | | customerId: "", |
| | | receiptDate: "", |
| | | amount: 0, |
| | | receiptMethod: "", |
| | | stockOutRecordIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | stockOutRecordIds: [{ required: true, type: "array", min: 1, message: "è¯·éæ©å
³èåæ®", trigger: "change" }], |
| | | receiptDate: [{ required: true, message: "è¯·éæ©æ¶æ¬¾æ¥æ", trigger: "change" }], |
| | | amount: [{ required: true, message: "请è¾å
¥æ¶æ¬¾éé¢", trigger: "blur" }], |
| | | receiptMethod: [{ required: true, message: "è¯·éæ©æ¶æ¬¾æ¹å¼", trigger: "change" }], |
| | | }; |
| | | |
| | | const totalReceiptAmount = computed(() => |
| | | dataList.value.reduce((sum, item) => sum + Number(item.amount || 0), 0) |
| | | ); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const parseStockOutRecordIds = (value) => { |
| | | if (!value) return []; |
| | | if (Array.isArray(value)) return value; |
| | | return String(value) |
| | | .split(/[,ï¼]/) |
| | | .map((s) => s.trim()) |
| | | .filter(Boolean) |
| | | .map((s) => (/^\d+$/.test(s) ? Number(s) : s)); |
| | | }; |
| | | |
| | | const formatOutboundBatches = (value) => { |
| | | if (value === undefined || value === null || value === "") return ""; |
| | | if (Array.isArray(value)) return value.filter(Boolean).join("ã"); |
| | | return String(value) |
| | | .split(/[,ï¼]/) |
| | | .map((s) => s.trim()) |
| | | .filter(Boolean) |
| | | .join("ã"); |
| | | }; |
| | | |
| | | const normalizeTableRow = (row) => ({ |
| | | ...row, |
| | | receiptCode: row.collectionNumber ?? row.receiptCode, |
| | | receiptDate: row.collectionDate ?? row.receiptDate, |
| | | amount: row.collectionAmount ?? row.amount, |
| | | receiptMethod: row.collectionMethod ?? row.receiptMethod ?? "", |
| | | stockOutRecordIds: row.stockOutRecordIds ?? row.stockOutRecordId ?? "", |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | }); |
| | | |
| | | const getCustomerList = () => { |
| | | listCustomer({ current: -1, size: -1, type: 0 }).then((res) => { |
| | | if (res.code === 200) { |
| | | customerList.value = res.data?.records || []; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const normalizeOutboundBatchOptions = (data) => { |
| | | const list = Array.isArray(data) ? data : []; |
| | | return list.map((item, index) => { |
| | | if (typeof item === "string" || typeof item === "number") { |
| | | const text = String(item); |
| | | return { label: text, value: text, outboundAmount: 0 }; |
| | | } |
| | | const label = |
| | | item.outboundBatches ?? |
| | | item.batchNo ?? |
| | | item.shippingNo ?? |
| | | item.outboundNo ?? |
| | | item.label ?? |
| | | `åºåºå${index + 1}`; |
| | | const value = item.id ?? item.stockOutRecordId ?? label; |
| | | return { label: String(label), value, outboundAmount: Number(item.outboundAmount) || 0 }; |
| | | }); |
| | | }; |
| | | |
| | | const isSameOutboundId = (a, b) => String(a) === String(b); |
| | | |
| | | const getOutboundRowId = (row) => row?.id ?? row?.stockOutRecordId; |
| | | |
| | | const outboundBatchDisplayText = computed(() => { |
| | | if (isEdit.value || isView.value) { |
| | | return form.outboundBatches || ""; |
| | | } |
| | | if (form.outboundBatches) return form.outboundBatches; |
| | | const ids = form.stockOutRecordIds || []; |
| | | if (!ids.length) return ""; |
| | | const labels = outboundBatchOptions.value |
| | | .filter((opt) => ids.some((id) => isSameOutboundId(id, opt.value))) |
| | | .map((opt) => opt.label); |
| | | if (labels.length) return labels.join("ã"); |
| | | return ids.join("ã"); |
| | | }); |
| | | |
| | | const handleOutboundInputClick = () => { |
| | | if (isEdit.value || isView.value) return; |
| | | openOutboundSelectDialog(); |
| | | }; |
| | | |
| | | /** 为已é ID è¡¥å
¨é项ï¼ç¼è¾/æ¥çåæ¾ï¼ */ |
| | | const ensureOutboundOptionsForSelected = () => { |
| | | const ids = form.stockOutRecordIds || []; |
| | | ids.forEach((id) => { |
| | | const exists = outboundBatchOptions.value.some((opt) => isSameOutboundId(opt.value, id)); |
| | | if (exists) return; |
| | | const fromList = outboundBatchList.value.find((row) => isSameOutboundId(getOutboundRowId(row), id)); |
| | | if (fromList) { |
| | | const [option] = normalizeOutboundBatchOptions([fromList]); |
| | | if (option) outboundBatchOptions.value.push(option); |
| | | return; |
| | | } |
| | | outboundBatchOptions.value.push({ |
| | | label: String(id), |
| | | value: id, |
| | | outboundAmount: 0, |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const syncCollectionAmount = () => { |
| | | const selected = form.stockOutRecordIds || []; |
| | | const sum = outboundBatchOptions.value |
| | | .filter((opt) => selected.some((id) => isSameOutboundId(id, opt.value))) |
| | | .reduce((acc, opt) => acc + (Number(opt.outboundAmount) || 0), 0); |
| | | form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0; |
| | | }; |
| | | |
| | | const restoreOutboundTableSelection = () => { |
| | | nextTick(() => { |
| | | const table = outboundTableRef.value; |
| | | if (!table) return; |
| | | table.clearSelection(); |
| | | const selectedIds = new Set((form.stockOutRecordIds || []).map((id) => String(id))); |
| | | outboundBatchList.value.forEach((row) => { |
| | | const rowId = getOutboundRowId(row); |
| | | if (rowId !== undefined && rowId !== null && selectedIds.has(String(rowId))) { |
| | | table.toggleRowSelection(row, true); |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const loadOutboundBatches = (customerId, keepSelected = false) => { |
| | | if (!customerId) { |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | if (!keepSelected) { |
| | | form.stockOutRecordIds = []; |
| | | form.amount = 0; |
| | | } |
| | | return Promise.resolve(); |
| | | } |
| | | outboundBatchLoading.value = true; |
| | | return getOutboundBatchesByCustomer({ customerId }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | const list = res.data?.records ?? res.data ?? []; |
| | | outboundBatchList.value = Array.isArray(list) ? list : []; |
| | | outboundBatchOptions.value = normalizeOutboundBatchOptions(list); |
| | | } else { |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | }) |
| | | .finally(() => { |
| | | outboundBatchLoading.value = false; |
| | | if (keepSelected) { |
| | | ensureOutboundOptionsForSelected(); |
| | | restoreOutboundTableSelection(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const handleCustomerChange = (customerId) => { |
| | | form.stockOutRecordIds = []; |
| | | form.outboundBatches = ""; |
| | | form.amount = 0; |
| | | loadOutboundBatches(customerId); |
| | | }; |
| | | |
| | | const openOutboundSelectDialog = () => { |
| | | if (!form.customerId || isEdit.value || isView.value) return; |
| | | outboundSelectVisible.value = true; |
| | | loadOutboundBatches(form.customerId, true).then(() => { |
| | | restoreOutboundTableSelection(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOutboundDialogSelectionChange = (selection) => { |
| | | dialogOutboundSelection.value = selection; |
| | | }; |
| | | |
| | | const confirmOutboundSelection = () => { |
| | | if (dialogOutboundSelection.value.length === 0) { |
| | | ElMessage.warning("请è³å°éæ©ä¸æ¡å
³èåæ®"); |
| | | return; |
| | | } |
| | | form.stockOutRecordIds = dialogOutboundSelection.value |
| | | .map((row) => getOutboundRowId(row)) |
| | | .filter((id) => id !== undefined && id !== null); |
| | | form.outboundBatches = dialogOutboundSelection.value |
| | | .map((row) => row.outboundBatches ?? row.batchNo ?? row.shippingNo ?? "") |
| | | .filter(Boolean) |
| | | .join("ã"); |
| | | outboundSelectVisible.value = false; |
| | | syncCollectionAmount(); |
| | | formRef.value?.validateField("stockOutRecordIds"); |
| | | }; |
| | | |
| | | const handleOutboundDialogClosed = () => { |
| | | dialogOutboundSelection.value = []; |
| | | }; |
| | | |
| | | const appendFilterParams = (params) => { |
| | | if (filters.collectionNumber) { |
| | | params.collectionNumber = filters.collectionNumber; |
| | | } |
| | | if (filters.customerId) { |
| | | params.customerId = filters.customerId; |
| | | } |
| | | if (filters.collectionMethod) { |
| | | params.collectionMethod = filters.collectionMethod; |
| | | } |
| | | if (filters.dateRange?.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | }; |
| | | |
| | | const buildListParams = () => |
| | | appendFilterParams({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | defineOptions({ |
| | | name: "æ¶æ¬¾å", |
| | | }); |
| | | |
| | | const buildExportParams = () => appendFilterParams({}); |
| | | const { proxy } = getCurrentInstance(); |
| | | const { payment_methods } = proxy.useDict("payment_methods"); |
| | | |
| | | const buildSubmitPayload = (forUpdate = false) => { |
| | | const payload = { |
| | | customerId: form.customerId, |
| | | collectionDate: form.receiptDate, |
| | | collectionAmount: form.amount, |
| | | collectionMethod: form.receiptMethod, |
| | | collectionNumber: form.receiptCode || "", |
| | | remark: form.remark || "", |
| | | stockOutRecordIds: (form.stockOutRecordIds || []).join(","), |
| | | }; |
| | | if (forUpdate) { |
| | | payload.id = currentId.value; |
| | | } |
| | | return payload; |
| | | }; |
| | | |
| | | const fillFormFromRow = (row) => { |
| | | const stockOutRecordIds = parseStockOutRecordIds(row.stockOutRecordIds ?? row.stockOutRecordId); |
| | | Object.assign(form, { |
| | | receiptCode: row.receiptCode ?? row.collectionNumber ?? "", |
| | | customerId: row.customerId, |
| | | receiptDate: row.receiptDate ?? row.collectionDate ?? "", |
| | | amount: Number(row.amount ?? row.collectionAmount ?? 0), |
| | | receiptMethod: row.receiptMethod ?? row.collectionMethod ?? "", |
| | | stockOutRecordIds, |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | remark: row.remark ?? "", |
| | | }); |
| | | }; |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | outboundSelectVisible.value = false; |
| | | isView.value = false; |
| | | isEdit.value = false; |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | const params = buildExportParams(); |
| | | proxy.download("/accountSalesCollection/exportAccountSalesCollection", params, `æ¶æ¬¾å_${Date.now()}.xlsx`); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | listPageAccountSalesCollection(buildListParams()) |
| | | .then((res) => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = (res.data.records ?? []).map(normalizeTableRow); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.collectionNumber = ""; |
| | | filters.customerId = ""; |
| | | filters.collectionMethod = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | dialogTitle.value = "æ°å¢æ¶æ¬¾"; |
| | | Object.assign(form, { |
| | | receiptCode: "SK" + Date.now().toString().slice(-8), |
| | | const filters = reactive({ |
| | | collectionNumber: "", |
| | | customerId: "", |
| | | receiptDate: new Date().toISOString().split("T")[0], |
| | | collectionMethod: "", |
| | | dateRange: [], |
| | | }); |
| | | |
| | | const pagination = reactive({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "æ¶æ¬¾åå·", prop: "receiptCode", width: "150" }, |
| | | { label: "客æ·åç§°", prop: "customerName", width: "180" }, |
| | | { label: "æ¶æ¬¾æ¥æ", prop: "receiptDate", width: "120" }, |
| | | { label: "æ¶æ¬¾éé¢", prop: "amount", dataType: "slot", slot: "amount" }, |
| | | { |
| | | label: "æ¶æ¬¾æ¹å¼", |
| | | prop: "receiptMethod", |
| | | dataType: "slot", |
| | | slot: "receiptMethod", |
| | | width: "120", |
| | | }, |
| | | { label: "夿³¨", prop: "remark", showOverflowTooltip: true }, |
| | | { |
| | | label: "æä½", |
| | | prop: "operation", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | width: "200", |
| | | fixed: "right", |
| | | }, |
| | | ]; |
| | | |
| | | const dataList = ref([]); |
| | | const tableLoading = ref(false); |
| | | const dialogVisible = ref(false); |
| | | const dialogTitle = ref(""); |
| | | const formRef = ref(null); |
| | | const isEdit = ref(false); |
| | | const isView = ref(false); |
| | | const currentId = ref(null); |
| | | const submitLoading = ref(false); |
| | | |
| | | const customerList = ref([]); |
| | | const outboundBatchList = ref([]); |
| | | const outboundBatchOptions = ref([]); |
| | | const outboundBatchLoading = ref(false); |
| | | const outboundSelectVisible = ref(false); |
| | | const outboundTableRef = ref(null); |
| | | const dialogOutboundSelection = ref([]); |
| | | |
| | | const getReceiptMethodLabel = value => { |
| | | if (value === undefined || value === null || value === "") return "-"; |
| | | const item = payment_methods.value?.find( |
| | | m => String(m.value) === String(value) |
| | | ); |
| | | return item?.label ?? value; |
| | | }; |
| | | |
| | | const getDefaultReceiptMethod = () => payment_methods.value?.[0]?.value ?? ""; |
| | | |
| | | const form = reactive({ |
| | | receiptCode: "", |
| | | customerId: "", |
| | | receiptDate: "", |
| | | amount: 0, |
| | | receiptMethod: getDefaultReceiptMethod(), |
| | | receiptMethod: "", |
| | | stockOutRecordIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | }); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = (row) => { |
| | | isEdit.value = true; |
| | | isView.value = false; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ¶æ¬¾"; |
| | | fillFormFromRow(row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | const rules = { |
| | | customerId: [{ required: true, message: "è¯·éæ©å®¢æ·", trigger: "change" }], |
| | | stockOutRecordIds: [ |
| | | { |
| | | required: true, |
| | | type: "array", |
| | | min: 1, |
| | | message: "è¯·éæ©å
³èåæ®", |
| | | trigger: "change", |
| | | }, |
| | | ], |
| | | receiptDate: [ |
| | | { required: true, message: "è¯·éæ©æ¶æ¬¾æ¥æ", trigger: "change" }, |
| | | ], |
| | | amount: [{ required: true, message: "请è¾å
¥æ¶æ¬¾éé¢", trigger: "blur" }], |
| | | receiptMethod: [ |
| | | { required: true, message: "è¯·éæ©æ¶æ¬¾æ¹å¼", trigger: "change" }, |
| | | ], |
| | | }; |
| | | |
| | | const view = (row) => { |
| | | isView.value = true; |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ¥çæ¶æ¬¾"; |
| | | fillFormFromRow(row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | const totalReceiptAmount = computed(() => |
| | | dataList.value.reduce((sum, item) => sum + Number(item.amount || 0), 0) |
| | | ); |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm(`确认å 餿¶æ¬¾åã${row.receiptCode ?? row.collectionNumber}ãåï¼`, "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | deleteAccountSalesCollection([row.id]) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | } else { |
| | | ElMessage.error(res.msg || "å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | }); |
| | | const formatMoney = value => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | | return Number(value) |
| | | .toFixed(2) |
| | | .replace(/\B(?=(\d{3})+(?!\d))/g, ","); |
| | | }; |
| | | |
| | | const parseStockOutRecordIds = value => { |
| | | if (!value) return []; |
| | | if (Array.isArray(value)) return value; |
| | | return String(value) |
| | | .split(/[,ï¼]/) |
| | | .map(s => s.trim()) |
| | | .filter(Boolean) |
| | | .map(s => (/^\d+$/.test(s) ? Number(s) : s)); |
| | | }; |
| | | |
| | | const formatOutboundBatches = value => { |
| | | if (value === undefined || value === null || value === "") return ""; |
| | | if (Array.isArray(value)) return value.filter(Boolean).join("ã"); |
| | | return String(value) |
| | | .split(/[,ï¼]/) |
| | | .map(s => s.trim()) |
| | | .filter(Boolean) |
| | | .join("ã"); |
| | | }; |
| | | |
| | | const normalizeTableRow = row => ({ |
| | | ...row, |
| | | receiptCode: row.collectionNumber ?? row.receiptCode, |
| | | receiptDate: row.collectionDate ?? row.receiptDate, |
| | | amount: row.collectionAmount ?? row.amount, |
| | | receiptMethod: row.collectionMethod ?? row.receiptMethod ?? "", |
| | | stockOutRecordIds: row.stockOutRecordIds ?? row.stockOutRecordId ?? "", |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (!valid) return; |
| | | submitLoading.value = true; |
| | | const request = isEdit.value |
| | | ? updateAccountSalesCollection(buildSubmitPayload(true)) |
| | | : addAccountSalesCollection(buildSubmitPayload()); |
| | | request |
| | | .then((res) => { |
| | | const getCustomerList = () => { |
| | | listCustomer({ current: -1, size: -1, type: 0 }).then(res => { |
| | | if (res.code === 200) { |
| | | customerList.value = res.data?.records || []; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const normalizeOutboundBatchOptions = data => { |
| | | const list = Array.isArray(data) ? data : []; |
| | | return list.map((item, index) => { |
| | | if (typeof item === "string" || typeof item === "number") { |
| | | const text = String(item); |
| | | return { label: text, value: text, outboundAmount: 0 }; |
| | | } |
| | | const label = |
| | | item.outboundBatches ?? |
| | | item.batchNo ?? |
| | | item.shippingNo ?? |
| | | item.outboundNo ?? |
| | | item.label ?? |
| | | `åºåºå${index + 1}`; |
| | | const value = item.id ?? item.stockOutRecordId ?? label; |
| | | return { |
| | | label: String(label), |
| | | value, |
| | | outboundAmount: Number(item.outboundAmount) || 0, |
| | | }; |
| | | }); |
| | | }; |
| | | |
| | | const isSameOutboundId = (a, b) => String(a) === String(b); |
| | | |
| | | const getOutboundRowId = row => row?.id ?? row?.stockOutRecordId; |
| | | |
| | | const outboundBatchDisplayText = computed(() => { |
| | | if (isEdit.value || isView.value) { |
| | | return form.outboundBatches || ""; |
| | | } |
| | | if (form.outboundBatches) return form.outboundBatches; |
| | | const ids = form.stockOutRecordIds || []; |
| | | if (!ids.length) return ""; |
| | | const labels = outboundBatchOptions.value |
| | | .filter(opt => ids.some(id => isSameOutboundId(id, opt.value))) |
| | | .map(opt => opt.label); |
| | | if (labels.length) return labels.join("ã"); |
| | | return ids.join("ã"); |
| | | }); |
| | | |
| | | const handleOutboundInputClick = () => { |
| | | if (isEdit.value || isView.value) return; |
| | | openOutboundSelectDialog(); |
| | | }; |
| | | |
| | | /** 为已é ID è¡¥å
¨é项ï¼ç¼è¾/æ¥çåæ¾ï¼ */ |
| | | const ensureOutboundOptionsForSelected = () => { |
| | | const ids = form.stockOutRecordIds || []; |
| | | ids.forEach(id => { |
| | | const exists = outboundBatchOptions.value.some(opt => |
| | | isSameOutboundId(opt.value, id) |
| | | ); |
| | | if (exists) return; |
| | | const fromList = outboundBatchList.value.find(row => |
| | | isSameOutboundId(getOutboundRowId(row), id) |
| | | ); |
| | | if (fromList) { |
| | | const [option] = normalizeOutboundBatchOptions([fromList]); |
| | | if (option) outboundBatchOptions.value.push(option); |
| | | return; |
| | | } |
| | | outboundBatchOptions.value.push({ |
| | | label: String(id), |
| | | value: id, |
| | | outboundAmount: 0, |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const syncCollectionAmount = () => { |
| | | const selected = form.stockOutRecordIds || []; |
| | | const sum = outboundBatchOptions.value |
| | | .filter(opt => selected.some(id => isSameOutboundId(id, opt.value))) |
| | | .reduce((acc, opt) => acc + (Number(opt.outboundAmount) || 0), 0); |
| | | form.amount = sum > 0 ? Number(sum.toFixed(2)) : 0; |
| | | }; |
| | | |
| | | const restoreOutboundTableSelection = () => { |
| | | nextTick(() => { |
| | | const table = outboundTableRef.value; |
| | | if (!table) return; |
| | | table.clearSelection(); |
| | | const selectedIds = new Set( |
| | | (form.stockOutRecordIds || []).map(id => String(id)) |
| | | ); |
| | | outboundBatchList.value.forEach(row => { |
| | | const rowId = getOutboundRowId(row); |
| | | if ( |
| | | rowId !== undefined && |
| | | rowId !== null && |
| | | selectedIds.has(String(rowId)) |
| | | ) { |
| | | table.toggleRowSelection(row, true); |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const loadOutboundBatches = (customerId, keepSelected = false) => { |
| | | if (!customerId) { |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | if (!keepSelected) { |
| | | form.stockOutRecordIds = []; |
| | | form.amount = 0; |
| | | } |
| | | return Promise.resolve(); |
| | | } |
| | | outboundBatchLoading.value = true; |
| | | return getOutboundBatchesByCustomer({ customerId }) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success(isEdit.value ? "ä¿®æ¹æå" : "æ°å¢æå"); |
| | | closeDialog(); |
| | | getTableData(); |
| | | const list = res.data?.records ?? res.data ?? []; |
| | | outboundBatchList.value = Array.isArray(list) ? list : []; |
| | | outboundBatchOptions.value = normalizeOutboundBatchOptions(list); |
| | | } else { |
| | | ElMessage.error(res.msg || (isEdit.value ? "ä¿®æ¹å¤±è´¥" : "æ°å¢å¤±è´¥")); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error(isEdit.value ? "ä¿®æ¹å¤±è´¥" : "æ°å¢å¤±è´¥"); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | }) |
| | | .finally(() => { |
| | | submitLoading.value = false; |
| | | outboundBatchLoading.value = false; |
| | | if (keepSelected) { |
| | | ensureOutboundOptionsForSelected(); |
| | | restoreOutboundTableSelection(); |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getCustomerList(); |
| | | getTableData(); |
| | | }); |
| | | const handleCustomerChange = customerId => { |
| | | form.stockOutRecordIds = []; |
| | | form.outboundBatches = ""; |
| | | form.amount = 0; |
| | | loadOutboundBatches(customerId); |
| | | }; |
| | | |
| | | const openOutboundSelectDialog = () => { |
| | | if (!form.customerId || isEdit.value || isView.value) return; |
| | | outboundSelectVisible.value = true; |
| | | loadOutboundBatches(form.customerId, true).then(() => { |
| | | restoreOutboundTableSelection(); |
| | | }); |
| | | }; |
| | | |
| | | const handleOutboundDialogSelectionChange = selection => { |
| | | dialogOutboundSelection.value = selection; |
| | | }; |
| | | |
| | | const confirmOutboundSelection = () => { |
| | | if (dialogOutboundSelection.value.length === 0) { |
| | | ElMessage.warning("请è³å°éæ©ä¸æ¡å
³èåæ®"); |
| | | return; |
| | | } |
| | | form.stockOutRecordIds = dialogOutboundSelection.value |
| | | .map(row => getOutboundRowId(row)) |
| | | .filter(id => id !== undefined && id !== null); |
| | | form.outboundBatches = dialogOutboundSelection.value |
| | | .map(row => row.outboundBatches ?? row.batchNo ?? row.shippingNo ?? "") |
| | | .filter(Boolean) |
| | | .join("ã"); |
| | | outboundSelectVisible.value = false; |
| | | syncCollectionAmount(); |
| | | formRef.value?.validateField("stockOutRecordIds"); |
| | | }; |
| | | |
| | | const handleOutboundDialogClosed = () => { |
| | | dialogOutboundSelection.value = []; |
| | | }; |
| | | |
| | | const appendFilterParams = params => { |
| | | if (filters.collectionNumber) { |
| | | params.collectionNumber = filters.collectionNumber; |
| | | } |
| | | if (filters.customerId) { |
| | | params.customerId = filters.customerId; |
| | | } |
| | | if (filters.collectionMethod) { |
| | | params.collectionMethod = filters.collectionMethod; |
| | | } |
| | | if (filters.dateRange?.length === 2) { |
| | | params.startDate = filters.dateRange[0]; |
| | | params.endDate = filters.dateRange[1]; |
| | | } |
| | | return params; |
| | | }; |
| | | |
| | | const buildListParams = () => |
| | | appendFilterParams({ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | | }); |
| | | |
| | | const buildExportParams = () => appendFilterParams({}); |
| | | |
| | | const buildSubmitPayload = (forUpdate = false) => { |
| | | const payload = { |
| | | customerId: form.customerId, |
| | | collectionDate: form.receiptDate, |
| | | collectionAmount: form.amount, |
| | | collectionMethod: form.receiptMethod, |
| | | collectionNumber: form.receiptCode || "", |
| | | remark: form.remark || "", |
| | | stockOutRecordIds: (form.stockOutRecordIds || []).join(","), |
| | | }; |
| | | if (forUpdate) { |
| | | payload.id = currentId.value; |
| | | } |
| | | return payload; |
| | | }; |
| | | |
| | | const fillFormFromRow = row => { |
| | | const stockOutRecordIds = parseStockOutRecordIds( |
| | | row.stockOutRecordIds ?? row.stockOutRecordId |
| | | ); |
| | | Object.assign(form, { |
| | | receiptCode: row.receiptCode ?? row.collectionNumber ?? "", |
| | | customerId: row.customerId, |
| | | receiptDate: row.receiptDate ?? row.collectionDate ?? "", |
| | | amount: Number(row.amount ?? row.collectionAmount ?? 0), |
| | | receiptMethod: row.receiptMethod ?? row.collectionMethod ?? "", |
| | | stockOutRecordIds, |
| | | outboundBatches: formatOutboundBatches(row.outboundBatches), |
| | | remark: row.remark ?? "", |
| | | }); |
| | | }; |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | outboundSelectVisible.value = false; |
| | | isView.value = false; |
| | | isEdit.value = false; |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | const params = buildExportParams(); |
| | | proxy.download( |
| | | "/accountSalesCollection/exportAccountSalesCollection", |
| | | params, |
| | | `æ¶æ¬¾å_${Date.now()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | tableLoading.value = true; |
| | | listPageAccountSalesCollection(buildListParams()) |
| | | .then(res => { |
| | | const ok = res.code === 200 || res.code === 0; |
| | | if (ok && res.data) { |
| | | pagination.total = res.data.total ?? 0; |
| | | dataList.value = (res.data.records ?? []).map(normalizeTableRow); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ¥è¯¢å¤±è´¥"); |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error("æ¥è¯¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | | filters.collectionNumber = ""; |
| | | filters.customerId = ""; |
| | | filters.collectionMethod = ""; |
| | | filters.dateRange = []; |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const add = () => { |
| | | isEdit.value = false; |
| | | isView.value = false; |
| | | dialogTitle.value = "æ°å¢æ¶æ¬¾"; |
| | | Object.assign(form, { |
| | | receiptCode: "SK" + Date.now().toString().slice(-8), |
| | | customerId: "", |
| | | receiptDate: new Date().toISOString().split("T")[0], |
| | | amount: 0, |
| | | receiptMethod: getDefaultReceiptMethod(), |
| | | stockOutRecordIds: [], |
| | | outboundBatches: "", |
| | | remark: "", |
| | | }); |
| | | outboundBatchList.value = []; |
| | | outboundBatchOptions.value = []; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const edit = row => { |
| | | isEdit.value = true; |
| | | isView.value = false; |
| | | currentId.value = row.id; |
| | | dialogTitle.value = "ç¼è¾æ¶æ¬¾"; |
| | | fillFormFromRow(row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const view = row => { |
| | | isView.value = true; |
| | | isEdit.value = false; |
| | | dialogTitle.value = "æ¥çæ¶æ¬¾"; |
| | | fillFormFromRow(row); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm( |
| | | `确认å 餿¶æ¬¾åã${row.receiptCode ?? row.collectionNumber}ãåï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | } |
| | | ).then(() => { |
| | | deleteAccountSalesCollection([row.id]) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("å 餿å"); |
| | | getTableData(); |
| | | } else { |
| | | ElMessage.error(res.msg || "å é¤å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (!valid) return; |
| | | submitLoading.value = true; |
| | | const request = isEdit.value |
| | | ? updateAccountSalesCollection(buildSubmitPayload(true)) |
| | | : addAccountSalesCollection(buildSubmitPayload()); |
| | | request |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success(isEdit.value ? "ä¿®æ¹æå" : "æ°å¢æå"); |
| | | closeDialog(); |
| | | getTableData(); |
| | | } else { |
| | | ElMessage.error(res.msg || (isEdit.value ? "ä¿®æ¹å¤±è´¥" : "æ°å¢å¤±è´¥")); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error(isEdit.value ? "ä¿®æ¹å¤±è´¥" : "æ°å¢å¤±è´¥"); |
| | | }) |
| | | .finally(() => { |
| | | submitLoading.value = false; |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getCustomerList(); |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .outbound-batch-input:not(.is-disabled) { |
| | | :deep(.el-input__wrapper) { |
| | | cursor: pointer; |
| | | .actions { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | } |
| | | |
| | | .text-success { |
| | | color: #67c23a; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .outbound-batch-input:not(.is-disabled) { |
| | | :deep(.el-input__wrapper) { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <section v-if="dashboardCards.length > 0" class="top-row"> |
| | | <div class="stats-grid"> |
| | | <article |
| | | v-for="card in dashboardCards" |
| | | :key="card.key" |
| | | class="stat-card" |
| | | :class="card.key" |
| | | v-for="card in dashboardCards" |
| | | :key="card.key" |
| | | class="stat-card" |
| | | :class="card.key" |
| | | > |
| | | <div class="stat-header"> |
| | | <div class="stat-title-wrap"> |
| | |
| | | <div class="process-body"> |
| | | <div class="process-chart" :class="{ empty: !hasProcessData }"> |
| | | <Echarts |
| | | :options="chartBaseOptions" |
| | | :chartStyle="{ width: '100%', height: '100%' }" |
| | | :grid="processGrid" |
| | | :series="processSeries" |
| | | :tooltip="processTooltip" |
| | | :xAxis="processXAxis" |
| | | :yAxis="processYAxis" |
| | | :style="{ height: hasProcessData ? '340px' : '280px' }" |
| | | @click="handleChartClick" |
| | | :options="chartBaseOptions" |
| | | :chartStyle="{ width: '100%', height: '100%' }" |
| | | :grid="processGrid" |
| | | :series="processSeries" |
| | | :tooltip="processTooltip" |
| | | :xAxis="processXAxis" |
| | | :yAxis="processYAxis" |
| | | :style="{ height: hasProcessData ? '340px' : '280px' }" |
| | | @click="handleChartClick" |
| | | /> |
| | | <div v-if="!hasProcessData" class="chart-empty"> |
| | | <el-icon><DataAnalysis /></el-icon> |
| | |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">ç产订åè¿åº¦</div> |
| | | <el-radio-group v-model="orderFilter" size="small"> |
| | | <el-radio-button label="all">å
¨é¨</el-radio-button> |
| | | <el-radio-button label="in_progress">è¿è¡ä¸</el-radio-button> |
| | | <el-radio-button label="completed">已宿</el-radio-button> |
| | | <el-radio-button label="paused">å·²æå</el-radio-button> |
| | | <el-radio-button label="all">å
¨é¨({{ orderProgressMeta.total }})</el-radio-button> |
| | | <el-radio-button label="inProgress">è¿è¡ä¸({{ orderProgressMeta.inProgressCount }})</el-radio-button> |
| | | <el-radio-button label="completed">已宿({{ orderProgressMeta.completedCount }})</el-radio-button> |
| | | <el-radio-button label="paused">å·²æå({{ orderProgressMeta.pausedCount }})</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | <el-table :data="filteredOrders" stripe> |
| | |
| | | <template #default="{ row }"> |
| | | <div class="table-progress"> |
| | | <el-progress |
| | | :stroke-width="8" |
| | | :percentage="row.completionRate" |
| | | :show-text="false" |
| | | status="success" |
| | | :stroke-width="8" |
| | | :percentage="row.completionRate" |
| | | :show-text="false" |
| | | status="success" |
| | | /> |
| | | <span>{{ row.completionRate }}%</span> |
| | | </div> |
| | |
| | | <el-table-column label="ç¶æ" min-width="90"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getOrderStatusType(row.status)" effect="light"> |
| | | {{ getOrderStatusText(row.status) }} |
| | | {{ row.statusLabel || getOrderStatusText(row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </div> |
| | | |
| | | <div v-if="visiblePanels.contract" class="cockpit-panel contract-panel"> |
| | | <div class="panel-title">客æ·ååéé¢åæ</div> |
| | | <div class="contract-summary"> |
| | | <div class="contract-card"> |
| | | <div class="contract-name">æ»ååéé¢(å
)</div> |
| | | <div class="contract-main digital-number">{{ formatNumber(sum) }}</div> |
| | | <div class="contract-compare"> |
| | | 忝 |
| | | <span class="rise">{{ trendText(yny) }}</span> |
| | | ç¯æ¯ |
| | | <span class="rise">{{ trendText(chain) }}</span> |
| | | </div> |
| | | <div class="panel-title">客æ·ååéé¢åæ</div> |
| | | <div class="contract-summary"> |
| | | <div class="contract-card"> |
| | | <div class="contract-name">æ»ååéé¢(å
)</div> |
| | | <div class="contract-main digital-number">{{ formatNumber(sum) }}</div> |
| | | <div class="contract-compare"> |
| | | 忝 |
| | | <span class="rise">{{ trendText(yny) }}</span> |
| | | ç¯æ¯ |
| | | <span class="rise">{{ trendText(chain) }}</span> |
| | | </div> |
| | | <div class="contract-chart-wrap"> |
| | | <Echarts |
| | | </div> |
| | | <div class="contract-chart-wrap"> |
| | | <Echarts |
| | | :options="chartBaseOptions" |
| | | :legend="pieLegend" |
| | | :chartStyle="chartStylePie" |
| | | :series="materialPieSeries" |
| | | :tooltip="pieTooltip" |
| | | /> |
| | | </div> |
| | | /> |
| | | </div> |
| | | <ul class="contract-list"> |
| | | <li v-for="item in materialPieSeries[0].data" :key="item.name"> |
| | | <span class="legend-dot" :style="{ backgroundColor: item.itemStyle?.color }"></span> |
| | | <span class="contract-item-name">{{ item.name }}</span> |
| | | <span class="contract-item-rate">{{ item.rate }}%</span> |
| | | <span class="contract-item-value digital-number">Â¥{{ formatNumber(item.value) }}</span> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | <ul class="contract-list"> |
| | | <li v-for="item in materialPieSeries[0].data" :key="item.name"> |
| | | <span class="legend-dot" :style="{ backgroundColor: item.itemStyle?.color }"></span> |
| | | <span class="contract-item-name">{{ item.name }}</span> |
| | | <span class="contract-item-rate">{{ item.rate }}%</span> |
| | | <span class="contract-item-value digital-number">Â¥{{ formatNumber(item.value) }}</span> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | |
| | | <div v-if="visiblePanels.quality" class="cockpit-panel quality-panel"> |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">è´¨éç»è®¡</div> |
| | | <el-radio-group v-model="qualityRange" size="small" @change="qualityStatisticsInfo"> |
| | | <el-radio-button :value="1">å¨</el-radio-button> |
| | | <el-radio-button :value="2">æ</el-radio-button> |
| | | <el-radio-button :value="3">å£åº¦</el-radio-button> |
| | | </el-radio-group> |
| | | </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 |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">è´¨éç»è®¡</div> |
| | | <el-radio-group v-model="qualityRange" size="small" @change="qualityStatisticsInfo"> |
| | | <el-radio-button :value="1">å¨</el-radio-button> |
| | | <el-radio-button :value="2">æ</el-radio-button> |
| | | <el-radio-button :value="3">å£åº¦</el-radio-button> |
| | | </el-radio-group> |
| | | </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 |
| | | :options="chartBaseOptions" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | style="height: 270px" |
| | | /> |
| | | </div> |
| | | /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="hasRightPanels" class="right-column"> |
| | |
| | | <div class="realtime-grid"> |
| | | <div class="realtime-item" v-for="item in realtimeBoard" :key="item.key"> |
| | | <el-progress |
| | | type="circle" |
| | | :percentage="item.percent" |
| | | :stroke-width="10" |
| | | :width="94" |
| | | :color="item.color" |
| | | type="circle" |
| | | :percentage="item.percent" |
| | | :stroke-width="10" |
| | | :width="94" |
| | | :color="item.color" |
| | | > |
| | | <template #default> |
| | | <div class="realtime-value digital-number">{{ item.display }}</div> |
| | |
| | | </div> |
| | | <div class="quick-grid"> |
| | | <button |
| | | v-for="item in quickEntries" |
| | | :key="item.label" |
| | | class="quick-item" |
| | | type="button" |
| | | @click="goToQuick(item.path)" |
| | | v-for="item in quickEntries" |
| | | :key="item.label" |
| | | class="quick-item" |
| | | type="button" |
| | | @click="goToQuick(item.path)" |
| | | > |
| | | <span class="quick-icon"> |
| | | <el-icon> |
| | |
| | | <div v-if="visiblePanels.plan" class="cockpit-panel plan-panel"> |
| | | <div class="panel-title-row"> |
| | | <div class="panel-title">仿¥ç产计å</div> |
| | | <span class="panel-more">{{ todayPlanList.length }}项</span> |
| | | <span class="panel-more">{{ todayPlanTotal }}项</span> |
| | | </div> |
| | | <ul class="plan-list"> |
| | | <li v-for="item in todayPlanList" :key="item.orderNo" class="plan-item"> |
| | |
| | | <div v-if="visiblePanels.receipt" class="cockpit-panel receipt-panel"> |
| | | <div class="panel-title">忬¾ä¸å¼ç¥¨åæ</div> |
| | | <Echarts |
| | | :options="chartBaseOptions" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries" |
| | | :tooltip="tooltipLine" |
| | | :xAxis="xAxis2" |
| | | :yAxis="yAxis2" |
| | | style="height: 300px" |
| | | :options="chartBaseOptions" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries" |
| | | :tooltip="tooltipLine" |
| | | :xAxis="xAxis2" |
| | | :yAxis="yAxis2" |
| | | style="height: 300px" |
| | | /> |
| | | </div> |
| | | |
| | |
| | | getBusiness, |
| | | homeTodos, |
| | | processDataProductionStatistics, |
| | | productionOrderProgress, |
| | | productionOverview, |
| | | productionRealtimeBoard, |
| | | qualityInspectionStatistics, |
| | | statisticsReceivablePayable, |
| | | todayProductionPlan, |
| | | } from "@/api/viewIndex.js"; |
| | | import { list } from "@/api/productionManagement/productionProcess"; |
| | | |
| | |
| | | }); |
| | | |
| | | const welcomeAvatar = computed(() => |
| | | welcomeAvatarLoadFailed.value || !userStore.avatar ? defaultWelcomeAvatar : userStore.avatar |
| | | welcomeAvatarLoadFailed.value || !userStore.avatar ? defaultWelcomeAvatar : userStore.avatar |
| | | ); |
| | | |
| | | const handleWelcomeAvatarError = () => { |
| | |
| | | }; |
| | | |
| | | watch( |
| | | () => userStore.avatar, |
| | | () => { |
| | | welcomeAvatarLoadFailed.value = false; |
| | | } |
| | | () => userStore.avatar, |
| | | () => { |
| | | welcomeAvatarLoadFailed.value = false; |
| | | } |
| | | ); |
| | | |
| | | const axisTextColor = "#5f6f86"; |
| | |
| | | processNum: 0, |
| | | factoryNum: 0, |
| | | }); |
| | | |
| | | const productionOverviewData = ref({ |
| | | totalOutput: 0, |
| | | totalScrap: 0, |
| | | yieldRate: 0, |
| | | }); |
| | | |
| | | const realtimeBoardData = ref({ |
| | | deviceOee: { value: 0, compareYesterday: 0 }, |
| | | orderAchievementRate: { value: 0, compareYesterday: 0 }, |
| | | defectRate: { value: 0, compareYesterday: 0 }, |
| | | }); |
| | | |
| | | const orderProgressMeta = ref({ |
| | | tab: "all", |
| | | total: 0, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | inProgressCount: 0, |
| | | completedCount: 0, |
| | | pausedCount: 0, |
| | | }); |
| | | |
| | | const todayPlanList = ref([]); |
| | | const todayPlanTotal = ref(0); |
| | | |
| | | const sum = ref(0); |
| | | const yny = ref(0); |
| | |
| | | const name = params?.[0]?.name ?? ""; |
| | | const list = Array.isArray(params) ? params : []; |
| | | const lines = list |
| | | .map((p) => { |
| | | const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>`; |
| | | return `${colorBox}${p.seriesName}<b style="float:right;">${Number(p.value || 0).toFixed(2)}</b>`; |
| | | }) |
| | | .join("<br/>"); |
| | | .map((p) => { |
| | | const colorBox = `<span style="display:inline-block;margin-right:6px;border-radius:2px;width:10px;height:10px;background:${p.color}"></span>`; |
| | | return `${colorBox}${p.seriesName}<b style="float:right;">${Number(p.value || 0).toFixed(2)}</b>`; |
| | | }) |
| | | .join("<br/>"); |
| | | return `<div style="min-width:140px;"><div style="font-weight:700;margin-bottom:6px;">${name}</div>${lines}</div>`; |
| | | }, |
| | | }); |
| | |
| | | }); |
| | | |
| | | const processTotals = computed(() => |
| | | processChartData.value.reduce( |
| | | (acc, cur) => { |
| | | acc.input += Number(cur.input || 0); |
| | | acc.scrap += Number(cur.scrap || 0); |
| | | acc.output += Number(cur.output || 0); |
| | | return acc; |
| | | }, |
| | | { input: 0, scrap: 0, output: 0 } |
| | | ) |
| | | processChartData.value.reduce( |
| | | (acc, cur) => { |
| | | acc.input += Number(cur.input || 0); |
| | | acc.scrap += Number(cur.scrap || 0); |
| | | acc.output += Number(cur.output || 0); |
| | | return acc; |
| | | }, |
| | | { input: 0, scrap: 0, output: 0 } |
| | | ) |
| | | ); |
| | | |
| | | const hasProcessData = computed(() => { |
| | |
| | | subLabel: "å¾
仿¬¾éé¢", |
| | | subValue: formatNumber(businessInfo.value.monthPurchaseHaveMoney), |
| | | trend: `å æ¯ ${ratioText( |
| | | businessInfo.value.monthPurchaseHaveMoney, |
| | | businessInfo.value.monthPurchaseMoney |
| | | businessInfo.value.monthPurchaseHaveMoney, |
| | | businessInfo.value.monthPurchaseMoney |
| | | )}`, |
| | | icon: ShoppingCartFull, |
| | | visible: visibleModules.value.procurement, |
| | |
| | | key: "production", |
| | | title: "ç产æ»è§", |
| | | desc: "累计产åº(ä»¶)", |
| | | value: formatNumber(processTotals.value.output), |
| | | value: formatNumber(productionOverviewData.value.totalOutput), |
| | | subLabel: "累计æ¥åº", |
| | | subValue: formatNumber(processTotals.value.scrap), |
| | | trend: `è¯ç ${ratioText(processTotals.value.output, processTotals.value.input)}`, |
| | | subValue: formatNumber(productionOverviewData.value.totalScrap), |
| | | trend: `è¯ç ${Number(productionOverviewData.value.yieldRate || 0).toFixed(2)}%`, |
| | | icon: Operation, |
| | | visible: visibleModules.value.production, |
| | | }, |
| | | ].filter((item) => item.visible)); |
| | | |
| | | const productionOrders = ref([ |
| | | { |
| | | orderNo: "MO-20260518-001", |
| | | productName: "æºè½æ§å¶å¨", |
| | | planQty: 1000, |
| | | completedQty: 860, |
| | | completionRate: 86, |
| | | deliveryDate: "2026-05-20", |
| | | status: "in_progress", |
| | | }, |
| | | { |
| | | orderNo: "MO-20260518-002", |
| | | productName: "çµæºæ¨¡å", |
| | | planQty: 800, |
| | | completedQty: 640, |
| | | completionRate: 80, |
| | | deliveryDate: "2026-05-22", |
| | | status: "in_progress", |
| | | }, |
| | | { |
| | | orderNo: "MO-20260518-003", |
| | | productName: "ä¼ æå¨ç»ä»¶", |
| | | planQty: 500, |
| | | completedQty: 150, |
| | | completionRate: 30, |
| | | deliveryDate: "2026-05-25", |
| | | status: "paused", |
| | | }, |
| | | { |
| | | orderNo: "MO-20260518-004", |
| | | productName: "ç»æä»¶A", |
| | | planQty: 1200, |
| | | completedQty: 1200, |
| | | completionRate: 100, |
| | | deliveryDate: "2026-05-15", |
| | | status: "completed", |
| | | }, |
| | | ]); |
| | | const productionOrders = ref([]); |
| | | |
| | | const orderFilter = ref("all"); |
| | | const filteredOrders = computed(() => { |
| | | if (orderFilter.value === "all") return productionOrders.value; |
| | | return productionOrders.value.filter((item) => item.status === orderFilter.value); |
| | | }); |
| | | const filteredOrders = computed(() => productionOrders.value); |
| | | |
| | | const todayPlanList = computed(() => |
| | | productionOrders.value |
| | | .slice() |
| | | .sort((a, b) => dayjs(a.deliveryDate).valueOf() - dayjs(b.deliveryDate).valueOf()) |
| | | .slice(0, 5) |
| | | ); |
| | | const getCompareTrend = (value) => { |
| | | const num = Number(value || 0); |
| | | if (num > 0) return "up"; |
| | | if (num < 0) return "down"; |
| | | return "flat"; |
| | | }; |
| | | |
| | | const avgCompletionRate = computed(() => { |
| | | if (!productionOrders.value.length) return 0; |
| | | const total = productionOrders.value.reduce((acc, cur) => acc + Number(cur.completionRate || 0), 0); |
| | | return Number((total / productionOrders.value.length).toFixed(1)); |
| | | }); |
| | | const getCompareText = (value) => { |
| | | const num = Number(value || 0); |
| | | const abs = Math.abs(num).toFixed(2); |
| | | if (num > 0) return `è¾æ¨æ¥ â ${abs}%`; |
| | | if (num < 0) return `è¾æ¨æ¥ â ${abs}%`; |
| | | return "è¾æ¨æ¥ æå¹³"; |
| | | }; |
| | | |
| | | const realtimeBoard = computed(() => { |
| | | const oee = ratioNumber(processTotals.value.output, processTotals.value.input); |
| | | const defectRate = ratioNumber(processTotals.value.scrap, processTotals.value.input); |
| | | const oee = Number(realtimeBoardData.value.deviceOee?.value || 0); |
| | | const orderAchievement = Number(realtimeBoardData.value.orderAchievementRate?.value || 0); |
| | | const defectRate = Number(realtimeBoardData.value.defectRate?.value || 0); |
| | | const oeeCompare = Number(realtimeBoardData.value.deviceOee?.compareYesterday || 0); |
| | | const orderCompare = Number(realtimeBoardData.value.orderAchievementRate?.compareYesterday || 0); |
| | | const defectCompare = Number(realtimeBoardData.value.defectRate?.compareYesterday || 0); |
| | | return [ |
| | | { |
| | | key: "oee", |
| | | label: "è®¾å¤ OEE", |
| | | percent: clampPercent(oee), |
| | | display: `${oee.toFixed(1)}%`, |
| | | delta: "è¾æ¨æ¥ â 4.0%", |
| | | trend: "up", |
| | | display: `${oee.toFixed(2)}%`, |
| | | delta: getCompareText(oeeCompare), |
| | | trend: getCompareTrend(oeeCompare), |
| | | color: "#2d8cff", |
| | | }, |
| | | { |
| | | key: "order", |
| | | label: "订åè¾¾æç", |
| | | percent: clampPercent(avgCompletionRate.value), |
| | | display: `${avgCompletionRate.value.toFixed(1)}%`, |
| | | delta: "è¾æ¨æ¥ â 2.6%", |
| | | trend: "up", |
| | | percent: clampPercent(orderAchievement), |
| | | display: `${orderAchievement.toFixed(2)}%`, |
| | | delta: getCompareText(orderCompare), |
| | | trend: getCompareTrend(orderCompare), |
| | | color: "#31d2ff", |
| | | }, |
| | | { |
| | | key: "defect", |
| | | label: "ä¸è¯ç", |
| | | percent: clampPercent(defectRate), |
| | | display: `${defectRate.toFixed(1)}%`, |
| | | delta: "è¾æ¨æ¥ â 0.5%", |
| | | trend: "down", |
| | | display: `${defectRate.toFixed(2)}%`, |
| | | delta: getCompareText(defectCompare), |
| | | trend: getCompareTrend(defectCompare), |
| | | color: "#f6a23f", |
| | | }, |
| | | ]; |
| | |
| | | |
| | | const normalizeMenuTitle = (title) => String(title || "").replace(/\s+/g, "").trim(); |
| | | const normalizeRoutePath = (path) => |
| | | String(path || "") |
| | | .trim() |
| | | .replace(/\/+/g, "/") |
| | | .replace(/\/$/, "") |
| | | .toLowerCase(); |
| | | String(path || "") |
| | | .trim() |
| | | .replace(/\/+/g, "/") |
| | | .replace(/\/$/, "") |
| | | .toLowerCase(); |
| | | |
| | | const resolveRoutePath = (route, parentPath = "") => { |
| | | const currentPath = String(route?.path || "").trim(); |
| | |
| | | |
| | | const accessibleMenuRoutes = computed(() => { |
| | | const routePool = |
| | | permissionStore.defaultRoutes?.length > 0 |
| | | ? permissionStore.defaultRoutes |
| | | : permissionStore.sidebarRouters?.length > 0 |
| | | ? permissionStore.sidebarRouters |
| | | : permissionStore.routes; |
| | | permissionStore.defaultRoutes?.length > 0 |
| | | ? permissionStore.defaultRoutes |
| | | : permissionStore.sidebarRouters?.length > 0 |
| | | ? permissionStore.sidebarRouters |
| | | : permissionStore.routes; |
| | | return collectAccessibleRoutes(routePool || []); |
| | | }); |
| | | |
| | |
| | | }; |
| | | |
| | | const hasModuleAccess = (config) => |
| | | accessibleMenuRoutes.value.some((route) => { |
| | | const matchedTitle = (config.titles || []).some((title) => route.title === normalizeMenuTitle(title)); |
| | | const matchedPath = (config.pathPrefixes || []).some( |
| | | (prefix) => route.path === prefix || route.path.startsWith(`${prefix}/`) |
| | | ); |
| | | return matchedTitle || matchedPath; |
| | | }); |
| | | accessibleMenuRoutes.value.some((route) => { |
| | | const matchedTitle = (config.titles || []).some((title) => route.title === normalizeMenuTitle(title)); |
| | | const matchedPath = (config.pathPrefixes || []).some( |
| | | (prefix) => route.path === prefix || route.path.startsWith(`${prefix}/`) |
| | | ); |
| | | return matchedTitle || matchedPath; |
| | | }); |
| | | |
| | | const visibleModules = computed(() => ({ |
| | | sales: hasModuleAccess(moduleAccessConfig.sales), |
| | |
| | | })); |
| | | |
| | | const hasLeftPanels = computed( |
| | | () => visiblePanels.value.process || visiblePanels.value.order || visiblePanels.value.contract || visiblePanels.value.quality |
| | | () => visiblePanels.value.process || visiblePanels.value.order || visiblePanels.value.contract || visiblePanels.value.quality |
| | | ); |
| | | const hasRightPanels = computed( |
| | | () => visiblePanels.value.todo || visiblePanels.value.realtime || visiblePanels.value.quick || visiblePanels.value.plan || visiblePanels.value.receipt |
| | | () => visiblePanels.value.todo || visiblePanels.value.realtime || visiblePanels.value.quick || visiblePanels.value.plan || visiblePanels.value.receipt |
| | | ); |
| | | const hasVisiblePanels = computed(() => hasLeftPanels.value || hasRightPanels.value); |
| | | |
| | | const quickEntries = computed(() => |
| | | quickEntryConfigs |
| | | .map((item) => { |
| | | const targetRoute = accessibleMenuRoutes.value.find((route) => |
| | | item.titles.some((title) => route.title === normalizeMenuTitle(title)) |
| | | ); |
| | | const resolvedPath = targetRoute?.path || ""; |
| | | return resolvedPath |
| | | ? { |
| | | label: item.label, |
| | | icon: item.icon, |
| | | path: resolvedPath, |
| | | } |
| | | : null; |
| | | }) |
| | | .filter(Boolean) |
| | | quickEntryConfigs |
| | | .map((item) => { |
| | | const targetRoute = accessibleMenuRoutes.value.find((route) => |
| | | item.titles.some((title) => route.title === normalizeMenuTitle(title)) |
| | | ); |
| | | const resolvedPath = targetRoute?.path || ""; |
| | | return resolvedPath |
| | | ? { |
| | | label: item.label, |
| | | icon: item.icon, |
| | | path: resolvedPath, |
| | | } |
| | | : null; |
| | | }) |
| | | .filter(Boolean) |
| | | ); |
| | | |
| | | const updateNowTime = () => { |
| | |
| | | |
| | | const getOrderStatusText = (status) => { |
| | | const mapping = { |
| | | in_progress: "è¿è¡ä¸", |
| | | completed: "已宿", |
| | | paused: "æå", |
| | | 1: "å¾
å¼å§", |
| | | 2: "è¿è¡ä¸", |
| | | 3: "已宿", |
| | | 4: "å·²æå", |
| | | }; |
| | | return mapping[status] || "æªç¥"; |
| | | }; |
| | | |
| | | const getOrderStatusType = (status) => { |
| | | const mapping = { |
| | | in_progress: "success", |
| | | completed: "primary", |
| | | paused: "warning", |
| | | 1: "info", |
| | | 2: "success", |
| | | 3: "primary", |
| | | 4: "warning", |
| | | }; |
| | | return mapping[status] || "info"; |
| | | }; |
| | | |
| | | const formatDueDate = (value) => { |
| | | if (!value) return "--"; |
| | | const date = dayjs(value); |
| | | return date.isValid() ? date.format("YYYY-MM-DD") : "--"; |
| | | }; |
| | | |
| | | const mapOrderProgressRecord = (item = {}) => ({ |
| | | orderNo: item.orderNo || "--", |
| | | productName: item.productName || "--", |
| | | planQty: Number(item.plannedQuantity || 0), |
| | | completedQty: Number(item.completedQuantity || 0), |
| | | completionRate: clampPercent(Number(item.completionRate || 0)), |
| | | deliveryDate: formatDueDate(item.dueDate), |
| | | status: Number(item.status || 0), |
| | | statusLabel: item.statusLabel || "", |
| | | }); |
| | | |
| | | const mapTodayPlanRecord = (item = {}) => ({ |
| | | orderNo: item.orderNo || "--", |
| | | productName: item.productName || "--", |
| | | planQty: Number(item.plannedQuantity || 0), |
| | | deliveryDate: formatDueDate(item.dueDate), |
| | | status: Number(item.status || 0), |
| | | statusLabel: item.statusLabel || "", |
| | | }); |
| | | |
| | | const refreshProductionOverview = async () => { |
| | | try { |
| | | const res = await productionOverview(); |
| | | const data = res?.data || {}; |
| | | productionOverviewData.value = { |
| | | totalOutput: Number(data.totalOutput || 0), |
| | | totalScrap: Number(data.totalScrap || 0), |
| | | yieldRate: Number(data.yieldRate || 0), |
| | | }; |
| | | } catch { |
| | | productionOverviewData.value = { |
| | | totalOutput: 0, |
| | | totalScrap: 0, |
| | | yieldRate: 0, |
| | | }; |
| | | } |
| | | }; |
| | | |
| | | const refreshProductionRealtimeBoard = async () => { |
| | | try { |
| | | const res = await productionRealtimeBoard(); |
| | | const data = res?.data || {}; |
| | | realtimeBoardData.value = { |
| | | deviceOee: { |
| | | value: Number(data.deviceOee?.value || 0), |
| | | compareYesterday: Number(data.deviceOee?.compareYesterday || 0), |
| | | }, |
| | | orderAchievementRate: { |
| | | value: Number(data.orderAchievementRate?.value || 0), |
| | | compareYesterday: Number(data.orderAchievementRate?.compareYesterday || 0), |
| | | }, |
| | | defectRate: { |
| | | value: Number(data.defectRate?.value || 0), |
| | | compareYesterday: Number(data.defectRate?.compareYesterday || 0), |
| | | }, |
| | | }; |
| | | } catch { |
| | | realtimeBoardData.value = { |
| | | deviceOee: { value: 0, compareYesterday: 0 }, |
| | | orderAchievementRate: { value: 0, compareYesterday: 0 }, |
| | | defectRate: { value: 0, compareYesterday: 0 }, |
| | | }; |
| | | } |
| | | }; |
| | | |
| | | const refreshProductionOrderProgress = async () => { |
| | | try { |
| | | const res = await productionOrderProgress({ |
| | | tab: orderFilter.value, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | }); |
| | | const data = res?.data || {}; |
| | | orderProgressMeta.value = { |
| | | tab: data.tab || orderFilter.value, |
| | | total: Number(data.total || 0), |
| | | pageNum: Number(data.pageNum || 1), |
| | | pageSize: Number(data.pageSize || 10), |
| | | inProgressCount: Number(data.inProgressCount || 0), |
| | | completedCount: Number(data.completedCount || 0), |
| | | pausedCount: Number(data.pausedCount || 0), |
| | | }; |
| | | productionOrders.value = (data.records || []).map(mapOrderProgressRecord); |
| | | } catch { |
| | | orderProgressMeta.value = { |
| | | tab: orderFilter.value, |
| | | total: 0, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | inProgressCount: 0, |
| | | completedCount: 0, |
| | | pausedCount: 0, |
| | | }; |
| | | productionOrders.value = []; |
| | | } |
| | | }; |
| | | |
| | | const refreshTodayProductionPlan = async () => { |
| | | try { |
| | | const res = await todayProductionPlan({ limit: 4 }); |
| | | const data = res?.data || {}; |
| | | todayPlanTotal.value = Number(data.total || 0); |
| | | todayPlanList.value = (data.records || []).map(mapTodayPlanRecord); |
| | | } catch { |
| | | todayPlanTotal.value = 0; |
| | | todayPlanList.value = []; |
| | | } |
| | | }; |
| | | |
| | | const getBusinessData = async () => { |
| | |
| | | router.push(path).catch(() => {}); |
| | | }; |
| | | |
| | | watch(orderFilter, () => { |
| | | if (visiblePanels.value.order) { |
| | | refreshProductionOrderProgress(); |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | updateNowTime(); |
| | | clockTimer = setInterval(updateNowTime, 1000); |
| | | if (dashboardCards.value.length > 0) { |
| | | getBusinessData(); |
| | | } |
| | | if (visibleModules.value.production) { |
| | | refreshProductionOverview(); |
| | | } |
| | | if (visiblePanels.value.contract) { |
| | | analysisCustomer(); |
| | |
| | | if (visiblePanels.value.process) { |
| | | getProcessList(); |
| | | refreshProcessStats(); |
| | | } |
| | | if (visiblePanels.value.order) { |
| | | refreshProductionOrderProgress(); |
| | | } |
| | | if (visiblePanels.value.realtime) { |
| | | refreshProductionRealtimeBoard(); |
| | | } |
| | | if (visiblePanels.value.plan) { |
| | | refreshTodayProductionPlan(); |
| | | } |
| | | }); |
| | | |
| | |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | overflow-x: clip; |
| | | margin-top: 10px; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .digital-number { |
| | |
| | | min-height: 92px; |
| | | padding: 18px 22px; |
| | | background: |
| | | radial-gradient(circle at 8% 20%, rgba(59, 130, 246, 0.12), transparent 32%), |
| | | linear-gradient(135deg, rgba(255, 255, 255, 0.94), rgba(239, 246, 255, 0.88)); |
| | | radial-gradient(circle at 8% 20%, rgba(59, 130, 246, 0.12), transparent 32%), |
| | | linear-gradient(135deg, rgba(255, 255, 255, 0.94), rgba(239, 246, 255, 0.88)); |
| | | } |
| | | |
| | | .welcome-user { |
| | |
| | | z-index: 1; |
| | | pointer-events: none; |
| | | background: |
| | | url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 340 40' preserveAspectRatio='none'%3E%3Cpath d='M0 31C20 16 44 36 66 24C87 12 107 31 129 18C148 8 169 28 193 16C214 5 237 25 259 14C280 3 306 19 340 8' fill='none' stroke='%236ea4ee' stroke-width='1.5'/%3E%3C/svg%3E") |
| | | url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 340 40' preserveAspectRatio='none'%3E%3Cpath d='M0 31C20 16 44 36 66 24C87 12 107 31 129 18C148 8 169 28 193 16C214 5 237 25 259 14C280 3 306 19 340 8' fill='none' stroke='%236ea4ee' stroke-width='1.5'/%3E%3C/svg%3E") |
| | | center / 100% 100% no-repeat; |
| | | } |
| | | |
| | |
| | | border: 1px solid rgba(148, 163, 184, 0.24); |
| | | border-radius: 14px; |
| | | background: |
| | | linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(244, 249, 255, 0.9)), |
| | | repeating-linear-gradient( |
| | | to right, |
| | | rgba(148, 163, 184, 0.07) 0, |
| | | rgba(148, 163, 184, 0.07) 1px, |
| | | transparent 1px, |
| | | transparent 48px |
| | | ), |
| | | repeating-linear-gradient( |
| | | to bottom, |
| | | rgba(148, 163, 184, 0.06) 0, |
| | | rgba(148, 163, 184, 0.06) 1px, |
| | | transparent 1px, |
| | | transparent 34px |
| | | ); |
| | | linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(244, 249, 255, 0.9)), |
| | | repeating-linear-gradient( |
| | | to right, |
| | | rgba(148, 163, 184, 0.07) 0, |
| | | rgba(148, 163, 184, 0.07) 1px, |
| | | transparent 1px, |
| | | transparent 48px |
| | | ), |
| | | repeating-linear-gradient( |
| | | to bottom, |
| | | rgba(148, 163, 184, 0.06) 0, |
| | | rgba(148, 163, 184, 0.06) 1px, |
| | | transparent 1px, |
| | | transparent 34px |
| | | ); |
| | | overflow: hidden; |
| | | padding: 10px; |
| | | } |
| | |
| | | color: #f59e0b; |
| | | } |
| | | |
| | | .realtime-delta.flat { |
| | | color: #64748b; |
| | | } |
| | | |
| | | .warning-list { |
| | | margin-top: 10px; |
| | | display: flex; |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div class="search_form" |
| | | style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">ä¾åºååç§°:</span> |
| | | <el-input |
| | | v-model="searchForm.supplierName" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥ä¾åºååç§°" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | <el-input v-model="searchForm.supplierName" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥ä¾åºååç§°" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | <div></div> |
| | | </div> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="14"> |
| | | <div class="table_list"> |
| | | <el-table |
| | | ref="multipleTable" |
| | | border |
| | | v-loading="tableLoading" |
| | | :data="tableData" |
| | | :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" |
| | | height="calc(100vh - 18.5em)" |
| | | :highlight-current-row="true" |
| | | style="width: 100%" |
| | | tooltip-effect="dark" |
| | | @row-click="rowClick" |
| | | :show-summary="isShowSummary" |
| | | :summary-method="summarizeMainTable" |
| | | class="lims-table" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column label="ä¾åºååç§°" prop="supplierName" /> |
| | | <el-table-column |
| | | label="ååéé¢(å
)" |
| | | prop="invoiceAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="仿¬¾éé¢(å
)" |
| | | prop="paymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="åºä»éé¢(å
)" |
| | | prop="payableAmount" |
| | | show-overflow-tooltip |
| | | > |
| | | <el-table ref="multipleTable" |
| | | border |
| | | v-loading="tableLoading" |
| | | :data="tableData" |
| | | :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" |
| | | height="calc(100vh - 18.5em)" |
| | | :highlight-current-row="true" |
| | | style="width: 100%" |
| | | tooltip-effect="dark" |
| | | @row-click="rowClick" |
| | | :show-summary="isShowSummary" |
| | | :summary-method="summarizeMainTable" |
| | | class="lims-table"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="ä¾åºååç§°" |
| | | prop="supplierName" /> |
| | | <el-table-column label="ååéé¢(å
)" |
| | | prop="contractAmounts" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="仿¬¾éé¢(å
)" |
| | | prop="paymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="åºä»éé¢(å
)" |
| | | prop="payableAmount" |
| | | show-overflow-tooltip> |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.payableAmount) }} |
| | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination |
| | | v-show="total > 0" |
| | | @pagination="paginationSearch" |
| | | :total="total" |
| | | :layout="page.layout" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | /> |
| | | <pagination v-show="total > 0" |
| | | @pagination="paginationSearch" |
| | | :total="total" |
| | | :layout="page.layout" |
| | | :page="page.current" |
| | | :limit="page.size" /> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="10"> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumnSon" |
| | | :tableData="originalTableDataSon" |
| | | :isSelection="false" |
| | | :isShowPagination="false" |
| | | :tableLoading="tableLoadingSon" |
| | | :isShowSummary="isShowSummarySon" |
| | | :summaryMethod="summarizeMainTable1" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumnSon" |
| | | :tableData="originalTableDataSon" |
| | | :isSelection="false" |
| | | :isShowPagination="true" |
| | | :page="sonPage" |
| | | :tableLoading="tableLoadingSon" |
| | | :isShowSummary="isShowSummarySon" |
| | | :summaryMethod="summarizeMainTable1" |
| | | height="calc(100vh - 18.5em)" |
| | | @pagination="sonPaginationSearch"> |
| | | <template #payableAmountSlot="{ row }"> |
| | | <el-text type="danger"> |
| | | {{ parseFloat(row.payableAmount).toFixed(2) }} |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, toRefs } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | paymentLedgerList, |
| | | paymentRecordList, |
| | | } from "@/api/procurementManagement/paymentLedger.js"; |
| | | import Pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | import { ref, toRefs } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | paymentLedgerList, |
| | | paymentRecordList, |
| | | } from "@/api/procurementManagement/paymentLedger.js"; |
| | | import Pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierNameOrContractNo: "", |
| | | }, |
| | | }); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const sonPage = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const sonTotal = ref(0); |
| | | const isShowSummary = ref(true); |
| | | const { searchForm } = toRefs(data); |
| | | const currentSupplierId = ref(""); |
| | | const rowClick = (row) => { |
| | | currentSupplierId.value = row.supplierId; |
| | | getPaymenRecordtList(row.supplierId); |
| | | }; |
| | | // 忍¡å |
| | | const tableColumnSon = ref([ |
| | | { |
| | | label: "åçæ¥æ", |
| | | prop: "paymentDate", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: "éè´ååå·", |
| | | prop: "purchaseContractNumber", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "ååéé¢(å
)", |
| | | prop: "invoiceAmount", |
| | | width: 200, |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | supplierName: "", |
| | | }, |
| | | }, |
| | | { |
| | | label: "仿¬¾éé¢(å
)", |
| | | prop: "paymentAmount", |
| | | width: 200, |
| | | formatData: (params) => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "åºä»éé¢(å
)", |
| | | dataType: "slot", |
| | | width: 200, |
| | | prop: "payableAmount", |
| | | slot: "payableAmountSlot", |
| | | }, |
| | | ]); |
| | | const tableDataSon = ref([]); |
| | | const originalTableDataSon = ref([]); |
| | | const tableLoadingSon = ref(false); |
| | | const isShowSummarySon = ref(true); |
| | | const detailPageNum = ref(1); |
| | | const detailPageSize = ref(10); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["invoiceAmount", "paymentAmount", "payableAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = (param) => { |
| | | let summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["invoiceAmount", "paymentAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | if (originalTableDataSon.value.length > 0) { |
| | | summarizeTable[summarizeTable.length - 1] = |
| | | originalTableDataSon.value[ |
| | | originalTableDataSon.value.length - 1 |
| | | ].payableAmount.toFixed(2); |
| | | } else { |
| | | summarizeTable[summarizeTable.length - 1] = 0.0; |
| | | } |
| | | return summarizeTable; |
| | | }; |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const paginationSearch = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | paymentLedgerList({ |
| | | ...searchForm.value, |
| | | ...page, |
| | | }).then((res) => { |
| | | let result = res.data; |
| | | tableLoading.value = false; |
| | | tableData.value = result.records || []; |
| | | total.value = result.total || 0; |
| | | if (tableData.value.length > 0) { |
| | | getPaymenRecordtList(tableData.value[0].supplierId); |
| | | currentSupplierId.value = tableData.value[0].supplierId; |
| | | } |
| | | }); |
| | | }; |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const sonPage = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | layout: "total, sizes, prev, pager, next, jumper", |
| | | }); |
| | | const total = ref(0); |
| | | const isShowSummary = ref(true); |
| | | const { searchForm } = toRefs(data); |
| | | const currentSupplierId = ref(""); |
| | | const rowClick = row => { |
| | | currentSupplierId.value = row.supplierId; |
| | | sonPage.current = 1; |
| | | getPaymenRecordtList(row.supplierId); |
| | | }; |
| | | // 忍¡å |
| | | const tableColumnSon = ref([ |
| | | { |
| | | label: "ååç¾è®¢æ¥æ", |
| | | prop: "executionDate", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: "éè´ååå·", |
| | | prop: "purchaseContractNumber", |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "ååéé¢(å
)", |
| | | prop: "contractAmount", |
| | | width: 200, |
| | | formatData: params => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "仿¬¾éé¢(å
)", |
| | | prop: "paymentAmount", |
| | | width: 200, |
| | | formatData: params => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "åºä»éé¢(å
)", |
| | | dataType: "slot", |
| | | width: 200, |
| | | prop: "payableAmount", |
| | | slot: "payableAmountSlot", |
| | | }, |
| | | ]); |
| | | const tableDataSon = ref([]); |
| | | const originalTableDataSon = ref([]); |
| | | const tableLoadingSon = ref(false); |
| | | const isShowSummarySon = ref(true); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const getPaymenRecordtList = (supplierId) => { |
| | | tableLoadingSon.value = true; |
| | | paymentRecordList({supplierId: supplierId}) |
| | | .then((res) => { |
| | | tableLoadingSon.value = false; |
| | | tableDataSon.value = res.data; |
| | | handlePagination({ page: 1, limit: sonPage.size }); |
| | | sonTotal.value = res.data.length; |
| | | }) |
| | | .catch((e) => { |
| | | tableLoadingSon.value = false; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["contractAmounts", "paymentAmount", "payableAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = param => { |
| | | let summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["contractAmount", "invoiceAmount", "paymentAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | if (originalTableDataSon.value.length > 0) { |
| | | summarizeTable[summarizeTable.length - 1] = |
| | | originalTableDataSon.value[ |
| | | originalTableDataSon.value.length - 1 |
| | | ].payableAmount.toFixed(2); |
| | | } else { |
| | | summarizeTable[summarizeTable.length - 1] = 0.0; |
| | | } |
| | | return summarizeTable; |
| | | }; |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const paginationSearch = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | paymentLedgerList({ |
| | | ...searchForm.value, |
| | | ...page, |
| | | }).then(res => { |
| | | let result = res.data; |
| | | tableLoading.value = false; |
| | | tableData.value = result.records || []; |
| | | total.value = result.total || 0; |
| | | if (tableData.value.length > 0) { |
| | | currentSupplierId.value = tableData.value[0].supplierId; |
| | | sonPage.current = 1; |
| | | getPaymenRecordtList(tableData.value[0].supplierId); |
| | | } |
| | | }); |
| | | }; |
| | | const handlePagination = ({ page, limit }) => { |
| | | sonPage.current = page; |
| | | sonPage.size = limit; |
| | | }; |
| | | |
| | | const start = (page - 1) * limit; |
| | | const end = start + limit; |
| | | const getPaymenRecordtList = supplierId => { |
| | | tableLoadingSon.value = true; |
| | | paymentRecordList({ |
| | | supplierId: supplierId, |
| | | current: sonPage.current, |
| | | size: sonPage.size, |
| | | }) |
| | | .then(res => { |
| | | tableLoadingSon.value = false; |
| | | let result = res.data; |
| | | if (Array.isArray(result)) { |
| | | tableDataSon.value = result; |
| | | sonPage.total = result.length; |
| | | handlePagination({ page: sonPage.current, limit: sonPage.size }); |
| | | } else { |
| | | originalTableDataSon.value = result.records || []; |
| | | sonPage.total = result.total || 0; |
| | | } |
| | | }) |
| | | .catch(e => { |
| | | tableLoadingSon.value = false; |
| | | }); |
| | | }; |
| | | const handlePagination = ({ page, limit }) => { |
| | | console.log(page, limit); |
| | | sonPage.current = page; |
| | | sonPage.size = limit; |
| | | |
| | | originalTableDataSon.value = tableDataSon.value.slice(start, end); |
| | | }; |
| | | const start = (page - 1) * limit; |
| | | const end = start + limit; |
| | | |
| | | const sonPaginationSearch = (pagination) => { |
| | | // æ¥æ¶å页å¨åæ° { page, limit } |
| | | handlePagination(pagination); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | if (column.property !== "supplierName") { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | } else { |
| | | return cellValue; |
| | | } |
| | | }; |
| | | getList(); |
| | | originalTableDataSon.value = tableDataSon.value.slice(start, end); |
| | | }; |
| | | |
| | | const sonPaginationSearch = pagination => { |
| | | // æ¥æ¶å页å¨åæ° { page, limit } |
| | | sonPage.current = pagination.page; |
| | | sonPage.size = pagination.limit; |
| | | getPaymenRecordtList(currentSupplierId.value); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | if (column.property !== "supplierName") { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | } else { |
| | | return cellValue; |
| | | } |
| | | }; |
| | | getList(); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .el-pagination { |
| | | width: 100%; |
| | | height: 55px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | float: right; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | background: #fff; |
| | | margin: -20px 0 0 0; |
| | | padding: 0 20px; |
| | | } |
| | | .pagination-container { |
| | | margin-top: 0; |
| | | } |
| | | .el-pagination { |
| | | width: 100%; |
| | | height: 55px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | float: right; |
| | | flex-direction: row; |
| | | align-items: center; |
| | | background: #fff; |
| | | margin: -20px 0 0 0; |
| | | padding: 0 20px; |
| | | } |
| | | .pagination-container { |
| | | margin-top: 0; |
| | | } |
| | | </style> |
| | |
| | | import EditProcess from "@/views/productionManagement/processRoute/Edit.vue"; |
| | | import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue"; |
| | | import { listPage, del } from "@/api/productionManagement/processRoute.js"; |
| | | const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue")); |
| | | const FileList = defineAsyncComponent(() => |
| | | import("@/components/Dialog/FileList.vue") |
| | | ); |
| | | |
| | | import { useRouter } from "vue-router"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | |
| | | <span class="info-value">{{ routeInfo.quantity || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="info-item full-width" |
| | | v-if="routeInfo.description"> |
| | | <div class="info-item"> |
| | | <div class="info-label-wrapper"> |
| | | <span class="info-label">æè¿°</span> |
| | | <span class="info-label">夿³¨</span> |
| | | </div> |
| | | <div class="info-value-wrapper"> |
| | | <span class="info-value">{{ routeInfo.description }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | <!-- é件模å --> |
| | | <div v-if="pageType === 'order'" |
| | | class="section-header"> |
| | | <div class="section-title">éä»¶</div> |
| | | </div> |
| | | <el-card v-if="pageType === 'order'" |
| | | class="attachment-card" |
| | | shadow="hover" |
| | | style="margin-top: 10px; margin-bottom: 20px;"> |
| | | <el-table :data="attachmentTableData" |
| | | border |
| | | class="attachment-table"> |
| | | <el-table-column label="éä»¶åç§°" |
| | | prop="originalFilename" |
| | | show-overflow-tooltip /> |
| | | <el-table-column fixed="right" |
| | | label="æä½" |
| | | width="200" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="downloadAttachmentFile(scope.row.downloadURL)"> |
| | | ä¸è½½ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | <!-- è¡¨æ ¼è§å¾ --> |
| | | <div v-if="viewMode === 'table'" |
| | |
| | | v-model="bomDataValue.showProductDialog" |
| | | :single="true" |
| | | @confirm="handleBomProduct" /> |
| | | <!-- ä¸ä¼ ç»ä»¶å¼¹çª --> |
| | | <el-dialog v-model="uploadDialogVisible" |
| | | title="ä¸ä¼ éä»¶" |
| | | width="50%" |
| | | @close="closeAttachmentUpload"> |
| | | <AttachmentUpload v-model:file-list="newFileList" /> |
| | | <template #footer> |
| | | <el-button @click="saveAttachmentUpload" |
| | | type="primary">ä¿å</el-button> |
| | | <el-button @click="closeAttachmentUpload">å
³é</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- æ°å¢/ç¼è¾å¼¹çª --> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢å·¥èºè·¯çº¿é¡¹ç®' : 'ç¼è¾å·¥èºè·¯çº¿é¡¹ç®'" |
| | |
| | | queryList2, |
| | | add2, |
| | | } from "@/api/productionManagement/productStructure.js"; |
| | | import AttachmentUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | import { |
| | | attachmentList, |
| | | deleteAttachment, |
| | | createAttachment, |
| | | } from "@/api/basicData/storageAttachment.js"; |
| | | |
| | | import { useRoute } from "vue-router"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | |
| | | const orderId = computed(() => route.query.orderId); |
| | | const pageType = computed(() => route.query.type); |
| | | const editable = computed(() => route.query.editable !== "false"); |
| | | const technologyRoutingId = computed(() => route.query.technologyRoutingId); |
| | | |
| | | const tableLoading = ref(false); |
| | | const tableData = ref([]); |
| | |
| | | bomNo: "", |
| | | description: "", |
| | | quantity: 0, |
| | | technologyRoutingId: "", |
| | | }); |
| | | |
| | | // éä»¶ç¸å
³ |
| | | const attachmentTableData = ref([]); |
| | | const uploadDialogVisible = ref(false); |
| | | const newFileList = ref([]); |
| | | |
| | | const getAttachmentList = () => { |
| | | if (!technologyRoutingId.value) return; |
| | | attachmentList({ |
| | | recordType: "technology_routing", |
| | | recordId: technologyRoutingId.value, |
| | | }).then(res => { |
| | | attachmentTableData.value = (res && res.data) || []; |
| | | }); |
| | | }; |
| | | |
| | | const handleUploadAttachment = () => { |
| | | uploadDialogVisible.value = true; |
| | | }; |
| | | |
| | | const saveAttachmentUpload = async () => { |
| | | if (newFileList.value.length > 0) { |
| | | createAttachment({ |
| | | application: "file", |
| | | recordType: "technology_routing", |
| | | recordId: technologyRoutingId.value, |
| | | storageBlobDTOs: [...newFileList.value, ...attachmentTableData.value], |
| | | }) |
| | | .then(res => { |
| | | if (res && res.code === 200) { |
| | | proxy?.$modal?.msgSuccess("ä¸ä¼ æå"); |
| | | newFileList.value = []; |
| | | getAttachmentList(); |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | uploadDialogVisible.value = false; |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const closeAttachmentUpload = () => { |
| | | newFileList.value = []; |
| | | uploadDialogVisible.value = false; |
| | | }; |
| | | |
| | | const handleDeleteAttachment = async row => { |
| | | deleteAttachment([row.storageAttachmentId]).then(res => { |
| | | if (res && res.code === 200) { |
| | | proxy?.$modal?.msgSuccess("å 餿å"); |
| | | getAttachmentList(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const downloadAttachmentFile = url => { |
| | | window.open(url, "_blank"); |
| | | }; |
| | | |
| | | const processOptions = ref([]); |
| | | const showProductSelectDialog = ref(false); |
| | |
| | | bomId: route.query.bomId || "", |
| | | description: route.query.description || "", |
| | | quantity: route.query.quantity || 0, |
| | | technologyRoutingId: route.query.technologyRoutingId || "", |
| | | status: !(route.query.status == 1 || route.query.status === "false"), |
| | | }; |
| | | bomTableData.value[0].productName = routeInfo.value.productName; |
| | |
| | | const handleBomProcessChange = (row, value) => { |
| | | row.processId = value || ""; |
| | | syncProcessOperationFields(row); |
| | | |
| | | // åä¸å±çº§åªè½é䏿 ·çå·¥åº |
| | | |
| | | // æ£æ¥åä¸å±çº§æ¯å¦å·²ç»æå
¶ä»ä¸åçå·¥åºè¢«éä¸ |
| | | const siblings = findSiblings(bomDataValue.value.dataList, row.tempId); |
| | | if (siblings && value) { |
| | | siblings.forEach(sibling => { |
| | | if (sibling.tempId !== row.tempId) { |
| | | sibling.processId = value; |
| | | syncProcessOperationFields(sibling); |
| | | } |
| | | const hasDifferentProcess = siblings.some(sibling => { |
| | | return ( |
| | | sibling.tempId !== row.tempId && |
| | | sibling.processId && |
| | | sibling.processId !== value |
| | | ); |
| | | }); |
| | | if (hasDifferentProcess) { |
| | | ElMessage.warning("åä¸å±çº§å·²åå¨ä¸åçå·¥åºï¼è¯·å
ç»ä¸å·¥åºååè¿è¡ä¿®æ¹"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | // æ ¡éªåä¸å±çº§çå·¥åºæ¯å¦ä¸è´ |
| | | const validateProcessConsistency = items => { |
| | | if (!items || items.length === 0) return; |
| | | |
| | | // æ£æ¥å½åå±çº§ |
| | | const processes = items |
| | | .filter(item => item.processId) |
| | | .map(item => item.processId); |
| | | if (processes.length > 1) { |
| | | const uniqueProcesses = [...new Set(processes)]; |
| | | if (uniqueProcesses.length > 1) { |
| | | ElMessage.error("åä¸å±çº§çå·¥åºå¿
é¡»ä¸è´"); |
| | | isValid = false; |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // é彿£æ¥å级 |
| | | items.forEach(item => { |
| | | if (item.children && item.children.length > 0) { |
| | | validateProcessConsistency(item.children); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | bomDataValue.value.dataList.forEach(item => { |
| | | validateItem(item, true); |
| | | }); |
| | | |
| | | validateProcessConsistency(bomDataValue.value.dataList); |
| | | |
| | | return isValid; |
| | | }; |
| | |
| | | getList(); |
| | | getProcessList(); |
| | | fetchBomData(); |
| | | if (pageType.value === "order") { |
| | | getAttachmentList(); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | |
| | | row.processId = value || ""; |
| | | syncProcessOperationFields(row); |
| | | |
| | | // åä¸å±çº§åªè½é䏿 ·çå·¥åº |
| | | // æ£æ¥åä¸å±çº§æ¯å¦å·²ç»æå
¶ä»ä¸åçå·¥åºè¢«éä¸ |
| | | const siblings = findSiblings(dataValue.dataList, row.tempId); |
| | | if (siblings && value) { |
| | | siblings.forEach(sibling => { |
| | | if (sibling.tempId !== row.tempId) { |
| | | sibling.processId = value; |
| | | syncProcessOperationFields(sibling); |
| | | } |
| | | const hasDifferentProcess = siblings.some(sibling => { |
| | | return sibling.tempId !== row.tempId && sibling.processId && sibling.processId !== value; |
| | | }); |
| | | if (hasDifferentProcess) { |
| | | ElMessage.warning("åä¸å±çº§å·²åå¨ä¸åçå·¥åºï¼è¯·å
ç»ä¸å·¥åºååè¿è¡ä¿®æ¹"); |
| | | } |
| | | } |
| | | }; |
| | | |
| | |
| | | bomNo: row.bomNo || "", |
| | | description: data.description || "", |
| | | quantity: row.quantity || 0, |
| | | technologyRoutingId: data.technologyRoutingId, |
| | | orderId, |
| | | type: "order", |
| | | editable: !row.endOrder, |
| | |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from "vue"; |
| | | import { invoiceLedgerSalesAccount } from "@/api/salesManagement/invoiceLedger.js"; |
| | | import { customerInteractions } from "@/api/salesManagement/receiptPayment.js"; |
| | | import Pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { |
| | | safeTrainingDetailListPage, |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form" style="margin-bottom: 20px;"> |
| | | <div class="search_form" |
| | | style="margin-bottom: 20px;"> |
| | | <div> |
| | | <span class="search_title">客æ·åç§°ï¼</span> |
| | | <el-input |
| | | v-model="searchForm.searchText" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥å®¢æ·åç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | <el-input v-model="searchForm.searchText" |
| | | style="width: 240px" |
| | | placeholder="è¾å
¥å®¢æ·åç§°æç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">æç´¢</el-button> |
| | | </div> |
| | | </div> |
| | | <div style="display: flex"> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @row-click="rowClickMethod" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column |
| | | label="客æ·åç§°" |
| | | prop="customerName" |
| | | show-overflow-tooltip |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="ååéé¢(å
)" |
| | | prop="invoiceTotal" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="忬¾éé¢(å
)" |
| | | prop="receiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="åºæ¶éé¢(å
)" |
| | | prop="unReceiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | width="200" |
| | | > |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.unReceiptPaymentAmount) }} |
| | | </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> |
| | | <div class="table_list"> |
| | | <el-table |
| | | :data="receiptRecord" |
| | | border |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable1" |
| | | height="calc(100vh - 18.5em)" |
| | | > |
| | | <el-table-column |
| | | align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" |
| | | /> |
| | | <el-table-column |
| | | label="åçæ¥æ" |
| | | prop="receiptPaymentDate" |
| | | show-overflow-tooltip |
| | | width="110" |
| | | /> |
| | | <el-table-column |
| | | label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | show-overflow-tooltip |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="ååéé¢(å
)" |
| | | prop="invoiceTotal" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="忬¾éé¢(å
)" |
| | | prop="receiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" |
| | | /> |
| | | <el-table-column |
| | | label="åºæ¶éé¢(å
)" |
| | | prop="unReceiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | width="200" |
| | | > |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.unReceiptPaymentAmount) }} |
| | | </el-text> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <div class="table_list" |
| | | style="width: 100%"> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | :row-key="(row) => row.customerId" |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | @row-click="rowClickMethod" |
| | | highlight-current-row |
| | | height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="客æ·åç§°" |
| | | prop="customerName" |
| | | show-overflow-tooltip |
| | | width="200" /> |
| | | <el-table-column label="ååéé¢(å
)" |
| | | prop="invoiceTotal" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="忬¾éé¢(å
)" |
| | | prop="receiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="åºæ¶éé¢(å
)" |
| | | prop="unReceiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | width="200"> |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.unReceiptPaymentAmount) }} |
| | | </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-col> |
| | | <el-col :span="12"> |
| | | <div class="table_list" |
| | | style="width: 100%"> |
| | | <el-table :data="receiptRecord" |
| | | border |
| | | :row-key="(row) => row.id" |
| | | show-summary |
| | | :summary-method="summarizeMainTable1" |
| | | height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="ååç¾è®¢æ¥æ" |
| | | prop="executionDate" |
| | | show-overflow-tooltip |
| | | width="110" /> |
| | | <el-table-column label="éå®ååå·" |
| | | prop="salesContractNo" |
| | | show-overflow-tooltip |
| | | width="200" /> |
| | | <el-table-column label="ååéé¢(å
)" |
| | | prop="contractAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="忬¾éé¢(å
)" |
| | | prop="receiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="åºæ¶éé¢(å
)" |
| | | prop="receiptableAmount" |
| | | show-overflow-tooltip |
| | | width="200"> |
| | | <template #default="{ row, column }"> |
| | | <el-text type="danger"> |
| | | {{ formattedNumber(row, column, row.receiptableAmount) }} |
| | | </el-text> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="recordTotal > 0" |
| | | :total="recordTotal" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="recordPage.current" |
| | | :limit="recordPage.size" |
| | | @pagination="recordPaginationChange" /> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {onMounted, ref} from "vue"; |
| | | import { invoiceLedgerSalesAccount } from "../../../api/salesManagement/invoiceLedger.js"; |
| | | import { customerInteractions } from "../../../api/salesManagement/receiptPayment.js"; |
| | | import Pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const receiptRecord = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const recordPage = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const recordTotal = ref(0); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | searchText: "", |
| | | invoiceDate: "", |
| | | }, |
| | | }); |
| | | const customerId = ref(""); |
| | | const { searchForm } = toRefs(data); |
| | | const originReceiptRecord = ref([]); |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | invoiceLedgerSalesAccount({ ...searchForm.value, ...page }).then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | if (tableData.value.length > 0) { |
| | | recordPage.current = 1; |
| | | customerId.value = tableData.value[0].id; |
| | | receiptPaymentList(customerId.value); |
| | | } |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue"; |
| | | import { |
| | | customewTransactions, |
| | | customewTransactionsDetails, |
| | | } from "@/api/salesManagement/indicatorStats.js"; |
| | | import Pagination from "../../../components/PIMTable/Pagination.vue"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const receiptRecord = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = (param) => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["invoiceTotal", "receiptPaymentAmount", "unReceiptPaymentAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = (param) => { |
| | | var summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["invoiceAmount", "receiptAmount", "unReceiptAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | // åæåä¸è¡æ°æ®; |
| | | if (receiptRecord.value?.length > 0) { |
| | | const index = tableData.value.findIndex( |
| | | (item) => item.id == customerId.value |
| | | ); |
| | | summarizeTable[summarizeTable.length - 1] = |
| | | tableData.value[index].unReceiptPaymentAmount.toFixed(2); |
| | | } else { |
| | | summarizeTable[summarizeTable.length - 1] = 0.0; |
| | | } |
| | | return summarizeTable; |
| | | }; |
| | | |
| | | const receiptPaymentList = (id) => { |
| | | const param = { |
| | | customerId: id, |
| | | const recordPage = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | }); |
| | | const total = ref(0); |
| | | const recordTotal = ref(0); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | searchText: "", |
| | | invoiceDate: "", |
| | | }, |
| | | }); |
| | | const customerId = ref(""); |
| | | const { searchForm } = toRefs(data); |
| | | const originReceiptRecord = ref([]); |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | console.log("param", param); |
| | | customerInteractions(param).then((res) => { |
| | | originReceiptRecord.value = res.data; |
| | | handlePagination({ page: 1, limit: recordPage.size }); |
| | | recordTotal.value = res.data.length; |
| | | const paginationChange = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | customewTransactions({ ...searchForm.value, ...page }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | if (tableData.value.length > 0) { |
| | | recordPage.current = 1; |
| | | customerId.value = tableData.value[0].customerId; |
| | | receiptPaymentList(customerId.value); |
| | | } |
| | | }); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return cellValue ? parseFloat(cellValue).toFixed(2) : "0.00"; |
| | | }; |
| | | // 主表åè®¡æ¹æ³ |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["invoiceTotal", "receiptPaymentAmount", "unReceiptPaymentAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | }; |
| | | // å表åè®¡æ¹æ³ |
| | | const summarizeMainTable1 = param => { |
| | | var summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["contractAmount", "receiptPaymentAmount", "receiptableAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | futureTickets: { noDecimal: true }, // ä¸ä¿çå°æ° |
| | | } |
| | | ); |
| | | return summarizeTable; |
| | | }; |
| | | |
| | | const receiptPaymentList = id => { |
| | | const param = { |
| | | customerId: id, |
| | | current: recordPage.current, |
| | | size: recordPage.size, |
| | | }; |
| | | customewTransactionsDetails(param).then(res => { |
| | | if (Array.isArray(res.data)) { |
| | | originReceiptRecord.value = res.data; |
| | | recordTotal.value = res.data.length; |
| | | handlePagination({ page: 1, limit: recordPage.size }); |
| | | } else { |
| | | receiptRecord.value = res.data.records; |
| | | recordTotal.value = res.data.total; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // æ±æ¬¾è®°å½å表å页 |
| | | const recordPaginationChange = pagination => { |
| | | recordPage.current = pagination.page; |
| | | recordPage.size = pagination.limit; |
| | | receiptPaymentList(customerId.value); |
| | | }; |
| | | |
| | | const rowClickMethod = row => { |
| | | customerId.value = row.customerId; |
| | | receiptPaymentList(customerId.value); |
| | | }; |
| | | |
| | | const handlePagination = ({ page, limit }) => { |
| | | recordPage.current = page; |
| | | recordPage.size = limit; |
| | | |
| | | const start = (page - 1) * limit; |
| | | const end = start + limit; |
| | | |
| | | receiptRecord.value = originReceiptRecord.value.slice(start, end); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | }; |
| | | |
| | | // æ±æ¬¾è®°å½å表å页 |
| | | const recordPaginationChange = (pagination) => { |
| | | handlePagination(pagination); |
| | | }; |
| | | |
| | | const rowClickMethod = (row) => { |
| | | customerId.value = row.id; |
| | | receiptPaymentList(customerId.value); |
| | | }; |
| | | |
| | | const handlePagination = ({ page, limit }) => { |
| | | recordPage.current = page; |
| | | recordPage.size = limit; |
| | | |
| | | const start = (page - 1) * limit; |
| | | const end = start + limit; |
| | | |
| | | receiptRecord.value = originReceiptRecord.value.slice(start, end); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .table_list { |
| | | width: 50%; |
| | | } |
| | | .table_list { |
| | | width: 50%; |
| | | } |
| | | </style> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="éå®ååå·ï¼" |
| | | prop="salesContractNo"> |
| | | <el-input v-model="form.salesContractNo" |
| | | placeholder="èªå¨çæ" |
| | | clearable |
| | | disabled /> |
| | | <div style="display: flex; align-items: center; gap: 12px;width: 100%;"> |
| | | <el-checkbox v-model="form.autoGenerateContractNo" v-if="operationType === 'add'">èªå¨çæ |
| | | </el-checkbox> |
| | | <el-input v-model="form.salesContractNo" |
| | | :placeholder="form.autoGenerateContractNo ? 'èªå¨çæ' : '请è¾å
¥'" |
| | | clearable |
| | | :disabled="form.autoGenerateContractNo" /> |
| | | |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | |
| | | }, |
| | | form: { |
| | | salesContractNo: "", |
| | | autoGenerateContractNo: true, |
| | | salesman: "", |
| | | customerId: "", |
| | | entryPerson: "", |
| | |
| | | form.value.entryDate = getCurrentDate(); |
| | | // ç¾è®¢æ¥æé»è®¤ä¸ºå½å¤© |
| | | form.value.executionDate = getCurrentDate(); |
| | | // é»è®¤èªå¨çæéå®ååå· |
| | | form.value.autoGenerateContractNo = true; |
| | | } else { |
| | | currentId.value = row.id; |
| | | getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => { |
| | |
| | | form.value.entryPerson = Number(res.entryPerson); |
| | | productData.value = form.value.productData; |
| | | fileList.value = form.value.storageBlobVOs; |
| | | // ç¼è¾æ¶è®¾ç½®èªå¨çæä¸ºfalseï¼å
许æå¨ä¿®æ¹ |
| | | form.value.autoGenerateContractNo = false; |
| | | }); |
| | | } |
| | | // let userAll = await userStore.getInfo() |
| | |
| | | } |
| | | form.value.storageBlobDTOs = fileList; |
| | | form.value.type = 1; |
| | | if (form.value.autoGenerateContractNo) { |
| | | form.value.salesContractNo = ''; |
| | | } |
| | | addOrUpdateSalesLedger(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | |
| | | page-break-after: avoid; |
| | | } |
| | | } |
| | | </style> |
| | | </style> |