From 1850ac640e34b37a273780ce8675928d0c998763 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=89=AB=E5=9C=B0=E5=83=A7?= <357099073@qq.com>
Date: Mon, 19 Oct 2026 01:13:17 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 3 +-
backend/src/api/erpDocument.js | 96 +
backend/src/api/erpFinance.js | 103 +
backend/src/api/erpProduct.js | 74 +
backend/src/api/erpReport.js | 63 +
backend/src/api/erpStock.js | 46 +
backend/src/api/erpWarehouse.js | 50 +
backend/src/views/apps/erp/basic/product.vue | 658 ++++
.../src/views/apps/erp/basic/warehouse.vue | 247 ++
.../apps/erp/customer/components/edit.vue | 39 +
.../src/views/apps/erp/dashboard/index.vue | 146 +-
.../apps/erp/document/DocDetailDrawer.vue | 185 ++
.../src/views/apps/erp/document/DocEditor.vue | 719 ++++
.../views/apps/erp/document/DocListPage.vue | 432 +++
.../views/apps/erp/document/ProductSelect.vue | 93 +
backend/src/views/apps/erp/document/dict.js | 104 +
.../erp/finance/components/BillListPage.vue | 413 +++
.../src/views/apps/erp/finance/payable.vue | 19 +
.../src/views/apps/erp/finance/payment.vue | 644 ++++
.../src/views/apps/erp/finance/receivable.vue | 19 +
.../src/views/apps/erp/purchase/inbound.vue | 7 +
backend/src/views/apps/erp/purchase/order.vue | 7 +
.../src/views/apps/erp/purchase/return.vue | 7 +
.../views/apps/erp/report/SummaryTable.vue | 69 +
backend/src/views/apps/erp/report/index.vue | 795 +++++
backend/src/views/apps/erp/sales/order.vue | 7 +
backend/src/views/apps/erp/sales/outbound.vue | 7 +
backend/src/views/apps/erp/sales/return.vue | 7 +
backend/src/views/apps/erp/stock/inbound.vue | 7 +
backend/src/views/apps/erp/stock/outbound.vue | 7 +
backend/src/views/apps/erp/stock/query.vue | 484 +++
backend/src/views/apps/erp/stock/taking.vue | 7 +
backend/src/views/apps/erp/stock/transfer.vue | 7 +
backend/src/views/apps/erp/stock/warning.vue | 368 +++
.../apps/erp/supplier/components/edit.vue | 23 +
backend/src/views/apps/erp/system/index.vue | 489 +++
.../components/ProgressTimeline.vue | 4 +-
.../basicSettings/roles/components/detail.vue | 16 +
.../src/views/basicSettings/roles/index.vue | 27 +-
go/controllers/app_activity.go | 24 +-
go/controllers/app_aggregate.go | 18 +-
go/controllers/app_approval.go | 4 +-
go/controllers/app_auth.go | 21 +-
go/controllers/app_contact.go | 10 +-
go/controllers/app_employee_file.go | 12 +-
go/controllers/app_erp.go | 157 +
go/controllers/app_erp_archive.go | 115 +
go/controllers/app_erp_document.go | 183 ++
go/controllers/app_erp_finance.go | 230 ++
go/controllers/app_erp_report.go | 89 +
go/controllers/app_erp_stock.go | 110 +
go/controllers/app_notebook.go | 31 +-
go/controllers/app_oa.go | 41 +-
go/controllers/app_oa_expense.go | 46 +-
go/controllers/app_oa_schedule.go | 25 +-
go/controllers/app_reminder.go | 31 +-
go/controllers/auth/oidc.go | 4 +-
go/controllers/backend_admin_user.go | 120 +-
go/controllers/backend_ai_chat.go | 30 +-
go/controllers/backend_ai_chat_preset.go | 33 +-
go/controllers/backend_ai_chat_session.go | 30 +-
go/controllers/backend_ai_provider.go | 33 +-
go/controllers/backend_ai_smart_generate.go | 21 +-
go/controllers/backend_approval.go | 25 +-
go/controllers/backend_approval_center.go | 13 +-
go/controllers/backend_approval_runtime.go | 16 +-
go/controllers/backend_article.go | 83 +-
go/controllers/backend_auth.go | 870 +++--
go/controllers/backend_contact.go | 40 +-
go/controllers/backend_crm_bidding_bid.go | 31 +-
go/controllers/backend_crm_bidding_more.go | 91 +-
.../backend_crm_bidding_reminder.go | 4 +-
go/controllers/backend_crm_bidding_sub.go | 64 +-
go/controllers/backend_crm_bidding_tender.go | 22 +-
go/controllers/backend_crm_business.go | 22 +-
go/controllers/backend_crm_clue.go | 22 +-
go/controllers/backend_crm_contact.go | 30 +-
go/controllers/backend_crm_contract.go | 22 +-
go/controllers/backend_crm_dashboard.go | 4 +-
go/controllers/backend_crm_payback.go | 34 +-
go/controllers/backend_crm_pipeline_common.go | 20 +-
go/controllers/backend_crm_pool.go | 30 +-
go/controllers/backend_crm_product.go | 19 +-
.../backend_crm_product_category.go | 16 +-
go/controllers/backend_crm_project.go | 37 +-
go/controllers/backend_crm_support.go | 37 +-
go/controllers/backend_crm_unit.go | 22 +-
go/controllers/backend_dict.go | 54 +-
go/controllers/backend_domain.go | 1330 ++++----
go/controllers/backend_employee_file.go | 51 +-
go/controllers/backend_erp_account_set.go | 36 +-
go/controllers/backend_erp_base.go | 240 ++
go/controllers/backend_erp_company_contact.go | 30 +-
go/controllers/backend_erp_customer.go | 45 +-
go/controllers/backend_erp_document.go | 468 +++
go/controllers/backend_erp_finance.go | 265 ++
go/controllers/backend_erp_normal_setting.go | 119 +-
go/controllers/backend_erp_product.go | 534 +++
go/controllers/backend_erp_report.go | 344 ++
go/controllers/backend_erp_stock.go | 377 +++
go/controllers/backend_erp_supplier.go | 42 +-
go/controllers/backend_erp_warehouse.go | 234 ++
go/controllers/backend_file.go | 81 +-
go/controllers/backend_frontend_content.go | 46 +-
go/controllers/backend_login_log.go | 30 +-
go/controllers/backend_login_verify.go | 440 ++-
go/controllers/backend_mcp_server.go | 45 +-
go/controllers/backend_mcp_tool.go | 33 +-
go/controllers/backend_menu.go | 1236 +++----
go/controllers/backend_menu_front.go | 22 +-
go/controllers/backend_modules.go | 294 +-
go/controllers/backend_notebook.go | 33 +-
go/controllers/backend_notify_config.go | 25 +-
go/controllers/backend_oa_compensation.go | 12 +-
go/controllers/backend_oa_document.go | 48 +-
go/controllers/backend_oa_notice.go | 42 +-
go/controllers/backend_oa_schedule.go | 51 +-
go/controllers/backend_operation_log.go | 34 +-
go/controllers/backend_organization.go | 10 +-
go/controllers/backend_payment.go | 28 +-
go/controllers/backend_product.go | 25 +-
go/controllers/backend_product_purchase.go | 43 +-
go/controllers/backend_reimburse.go | 11 +-
go/controllers/backend_reminder.go | 42 +-
go/controllers/backend_role.go | 63 +-
go/controllers/backend_site_settings.go | 91 +-
go/controllers/backend_sitereminder.go | 290 +-
go/controllers/backend_solution.go | 25 +-
go/controllers/backend_tenant_apply.go | 28 +-
go/controllers/backend_tenant_quota.go | 7 +-
go/controllers/backend_theme.go | 13 +-
go/controllers/backend_wechat_mp.go | 34 +-
go/controllers/erp_archive_core.go | 300 ++
go/controllers/erp_document_core.go | 652 ++++
go/controllers/erp_finance_core.go | 529 +++
go/controllers/erp_report_core.go | 568 ++++
go/controllers/erp_stock_core.go | 371 +++
go/controllers/password_store.go | 13 +-
go/controllers/platform_account_pool.go | 2904 ++++++++---------
go/controllers/platform_agent_api.go | 42 +-
go/controllers/platform_approval.go | 25 +-
go/controllers/platform_auth.go | 838 +++--
go/controllers/platform_auth_client.go | 31 +-
go/controllers/platform_auth_config.go | 30 +-
go/controllers/platform_auth_idp.go | 29 +-
go/controllers/platform_bark.go | 419 ++-
go/controllers/platform_commission.go | 25 +-
go/controllers/platform_complaint.go | 716 ++--
go/controllers/platform_complaint_category.go | 396 ++-
.../platform_cursor_activation_code.go | 1494 +++++----
go/controllers/platform_cursor_equipment.go | 2182 +++++++------
go/controllers/platform_dict.go | 54 +-
go/controllers/platform_domain.go | 1423 ++++----
go/controllers/platform_email.go | 585 ++--
go/controllers/platform_file.go | 63 +-
go/controllers/platform_home.go | 7 +-
go/controllers/platform_login_log.go | 31 +-
go/controllers/platform_modules.go | 908 +++---
go/controllers/platform_notebook.go | 33 +-
go/controllers/platform_operation_log.go | 33 +-
go/controllers/platform_payment.go | 48 +-
go/controllers/platform_payment_reconcile.go | 13 +-
.../platform_reimburse_expense_type.go | 31 +-
go/controllers/platform_reminder.go | 42 +-
go/controllers/platform_role.go | 416 +--
go/controllers/platform_site_settings.go | 520 ++-
go/controllers/platform_sitereminder.go | 729 ++---
go/controllers/platform_sms.go | 986 +++---
go/controllers/platform_software_upgrade.go | 34 +-
go/controllers/platform_template.go | 28 +-
go/controllers/platform_tenant.go | 1194 +++----
go/controllers/platform_tenant_apply.go | 39 +-
go/controllers/platform_tenant_package.go | 80 +-
go/controllers/platform_website_common.go | 4 +-
go/controllers/platform_website_setting.go | 7 +-
go/controllers/platform_wechat_mini.go | 34 +-
go/controllers/platform_wechat_mp.go | 66 +-
go/controllers/qiniu_upload.go | 27 +-
go/controllers/reimburse_approval_core.go | 151 +
go/controllers/tenant_site.go | 8 +-
go/docs/sql/alter_admin_role_lock.sql | 32 +
go/docs/sql/erp_inventory_schema.sql | 289 ++
go/docs/sql/erp_menus.sql | 54 +-
go/middleware/operationLog.go | 12 +-
go/models/admin_role.go | 44 +-
go/models/erp_inventory.go | 365 +++
go/models/init.go | 11 +
go/models/tenant_crm_customer.go | 2 +
go/models/tenant_crm_supplier.go | 1 +
go/pkg/identity/identity.go | 172 +
go/pkg/identity/identity_test.go | 114 +
go/routers/api/api.go | 2 +
go/routers/api/erp.go | 69 +
go/routers/backend/backend.go | 55 +
go/services/approval/engine.go | 12 +-
go/services/approval/instance_query.go | 121 +
go/services/erp/document.go | 496 +++
go/services/erp/finance.go | 300 ++
go/services/erp/settings.go | 225 ++
go/services/erp/stock.go | 189 ++
go/services/permission.go | 76 +-
go/services/permission_test.go | 49 +
milktea-site/preview-server.mjs | 24 +
.../src/views/basicSettings/roles/index.vue | 23 +
uniapp/api/erp.js | 784 +++++
uniapp/components/erp/ErpPicker.vue | 234 ++
uniapp/pages.json | 144 +
uniapp/pages/erp/basic/product-detail.vue | 391 +++
uniapp/pages/erp/basic/product.vue | 368 +++
uniapp/pages/erp/basic/warehouse.vue | 235 ++
uniapp/pages/erp/dashboard/index.vue | 496 +++
uniapp/pages/erp/document/detail.vue | 580 ++++
uniapp/pages/erp/document/edit.vue | 1303 ++++++++
uniapp/pages/erp/document/index.vue | 476 +++
uniapp/pages/erp/finance/bill-detail.vue | 454 +++
uniapp/pages/erp/finance/bill.vue | 524 +++
uniapp/pages/erp/finance/fund-edit.vue | 782 +++++
uniapp/pages/erp/finance/fund.vue | 520 +++
uniapp/pages/erp/partner/detail.vue | 408 +++
uniapp/pages/erp/partner/index.vue | 320 ++
uniapp/pages/erp/report/index.vue | 1112 +++++++
uniapp/pages/erp/stock/batch.vue | 447 +++
uniapp/pages/erp/stock/index.vue | 454 +++
uniapp/pages/erp/stock/ledger.vue | 595 ++++
uniapp/pages/erp/stock/warning.vue | 458 +++
uniapp/utils/nav.js | 22 +
226 files changed, 37772 insertions(+), 12071 deletions(-)
create mode 100644 backend/src/api/erpDocument.js
create mode 100644 backend/src/api/erpFinance.js
create mode 100644 backend/src/api/erpProduct.js
create mode 100644 backend/src/api/erpReport.js
create mode 100644 backend/src/api/erpStock.js
create mode 100644 backend/src/api/erpWarehouse.js
create mode 100644 backend/src/views/apps/erp/basic/product.vue
create mode 100644 backend/src/views/apps/erp/basic/warehouse.vue
create mode 100644 backend/src/views/apps/erp/document/DocDetailDrawer.vue
create mode 100644 backend/src/views/apps/erp/document/DocEditor.vue
create mode 100644 backend/src/views/apps/erp/document/DocListPage.vue
create mode 100644 backend/src/views/apps/erp/document/ProductSelect.vue
create mode 100644 backend/src/views/apps/erp/document/dict.js
create mode 100644 backend/src/views/apps/erp/finance/components/BillListPage.vue
create mode 100644 backend/src/views/apps/erp/finance/payable.vue
create mode 100644 backend/src/views/apps/erp/finance/payment.vue
create mode 100644 backend/src/views/apps/erp/finance/receivable.vue
create mode 100644 backend/src/views/apps/erp/purchase/inbound.vue
create mode 100644 backend/src/views/apps/erp/purchase/order.vue
create mode 100644 backend/src/views/apps/erp/purchase/return.vue
create mode 100644 backend/src/views/apps/erp/report/SummaryTable.vue
create mode 100644 backend/src/views/apps/erp/report/index.vue
create mode 100644 backend/src/views/apps/erp/sales/order.vue
create mode 100644 backend/src/views/apps/erp/sales/outbound.vue
create mode 100644 backend/src/views/apps/erp/sales/return.vue
create mode 100644 backend/src/views/apps/erp/stock/inbound.vue
create mode 100644 backend/src/views/apps/erp/stock/outbound.vue
create mode 100644 backend/src/views/apps/erp/stock/query.vue
create mode 100644 backend/src/views/apps/erp/stock/taking.vue
create mode 100644 backend/src/views/apps/erp/stock/transfer.vue
create mode 100644 backend/src/views/apps/erp/stock/warning.vue
create mode 100644 backend/src/views/apps/erp/system/index.vue
create mode 100644 go/controllers/app_erp.go
create mode 100644 go/controllers/app_erp_archive.go
create mode 100644 go/controllers/app_erp_document.go
create mode 100644 go/controllers/app_erp_finance.go
create mode 100644 go/controllers/app_erp_report.go
create mode 100644 go/controllers/app_erp_stock.go
create mode 100644 go/controllers/backend_erp_base.go
create mode 100644 go/controllers/backend_erp_document.go
create mode 100644 go/controllers/backend_erp_finance.go
create mode 100644 go/controllers/backend_erp_product.go
create mode 100644 go/controllers/backend_erp_report.go
create mode 100644 go/controllers/backend_erp_stock.go
create mode 100644 go/controllers/backend_erp_warehouse.go
create mode 100644 go/controllers/erp_archive_core.go
create mode 100644 go/controllers/erp_document_core.go
create mode 100644 go/controllers/erp_finance_core.go
create mode 100644 go/controllers/erp_report_core.go
create mode 100644 go/controllers/erp_stock_core.go
create mode 100644 go/controllers/reimburse_approval_core.go
create mode 100644 go/docs/sql/alter_admin_role_lock.sql
create mode 100644 go/docs/sql/erp_inventory_schema.sql
create mode 100644 go/models/erp_inventory.go
create mode 100644 go/pkg/identity/identity.go
create mode 100644 go/pkg/identity/identity_test.go
create mode 100644 go/routers/api/erp.go
create mode 100644 go/services/approval/instance_query.go
create mode 100644 go/services/erp/document.go
create mode 100644 go/services/erp/finance.go
create mode 100644 go/services/erp/settings.go
create mode 100644 go/services/erp/stock.go
create mode 100644 go/services/permission_test.go
create mode 100644 milktea-site/preview-server.mjs
create mode 100644 uniapp/api/erp.js
create mode 100644 uniapp/components/erp/ErpPicker.vue
create mode 100644 uniapp/pages/erp/basic/product-detail.vue
create mode 100644 uniapp/pages/erp/basic/product.vue
create mode 100644 uniapp/pages/erp/basic/warehouse.vue
create mode 100644 uniapp/pages/erp/dashboard/index.vue
create mode 100644 uniapp/pages/erp/document/detail.vue
create mode 100644 uniapp/pages/erp/document/edit.vue
create mode 100644 uniapp/pages/erp/document/index.vue
create mode 100644 uniapp/pages/erp/finance/bill-detail.vue
create mode 100644 uniapp/pages/erp/finance/bill.vue
create mode 100644 uniapp/pages/erp/finance/fund-edit.vue
create mode 100644 uniapp/pages/erp/finance/fund.vue
create mode 100644 uniapp/pages/erp/partner/detail.vue
create mode 100644 uniapp/pages/erp/partner/index.vue
create mode 100644 uniapp/pages/erp/report/index.vue
create mode 100644 uniapp/pages/erp/stock/batch.vue
create mode 100644 uniapp/pages/erp/stock/index.vue
create mode 100644 uniapp/pages/erp/stock/ledger.vue
create mode 100644 uniapp/pages/erp/stock/warning.vue
diff --git a/.gitignore b/.gitignore
index 33eb9d1..1884a20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,7 +58,8 @@ website/dist.zip
# go
go/go-platform.zip
-go/server.exe
+# Go 构建产物(go build 默认输出 main.exe / server.exe 等),不入库
+go/*.exe
go/conf
# codegraph
diff --git a/backend/src/api/erpDocument.js b/backend/src/api/erpDocument.js
new file mode 100644
index 0000000..87605c6
--- /dev/null
+++ b/backend/src/api/erpDocument.js
@@ -0,0 +1,96 @@
+import request from '@/utils/request'
+
+/**
+ * 进销存单据中心接口(/backend/erp/document*)
+ * 采购/销售/库存十类单据共用同一套接口,用 doc_type 区分。
+ */
+
+export function getDocumentList(params) {
+ return request({
+ url: '/backend/erp/document/list',
+ method: 'get',
+ params
+ })
+}
+
+export function getDocumentDetail(id) {
+ return request({
+ url: `/backend/erp/document/${id}`,
+ method: 'get'
+ })
+}
+
+/** 新建或覆盖保存草稿:payload 带 id 即为编辑 */
+export function saveDocument(data) {
+ return request({
+ url: '/backend/erp/document',
+ method: 'post',
+ data
+ })
+}
+
+export function deleteDocument(id) {
+ return request({
+ url: `/backend/erp/document/${id}`,
+ method: 'delete'
+ })
+}
+
+/** 提交审批:flow_id 走查询参数(后端从 query 取),不传则由后端按单据类型挑选审批流 */
+export function submitDocument(id, params) {
+ return request({
+ url: `/backend/erp/document/${id}/submit`,
+ method: 'post',
+ params: params || {}
+ })
+}
+
+export function approveDocument(id) {
+ return request({
+ url: `/backend/erp/document/${id}/approve`,
+ method: 'post',
+ data: {}
+ })
+}
+
+export function cancelDocument(id) {
+ return request({
+ url: `/backend/erp/document/${id}/cancel`,
+ method: 'post',
+ data: {}
+ })
+}
+
+/** 该单据类型当前可用的审批流,为空时前端只显示「直接审核」 */
+export function getDocumentFlows(docType) {
+ return request({
+ url: '/backend/erp/document/flows',
+ method: 'get',
+ params: { doc_type: docType }
+ })
+}
+
+/** 来源订单/原单中尚可执行的明细行(含 remain_qty),用于按行带出 */
+export function getExecutableItems(id) {
+ return request({
+ url: `/backend/erp/document/${id}/executable`,
+ method: 'get'
+ })
+}
+
+/** 盘点表:带出仓库当前账面数量与加权成本 */
+export function getTakeSheet(warehouseId) {
+ return request({
+ url: '/backend/erp/document/takeSheet',
+ method: 'get',
+ params: { warehouse_id: warehouseId }
+ })
+}
+
+/** 开单页所需字典与默认值(单据类型、状态、默认仓、售价口径、负库存开关) */
+export function getDocumentMeta() {
+ return request({
+ url: '/backend/erp/document/meta',
+ method: 'get'
+ })
+}
diff --git a/backend/src/api/erpFinance.js b/backend/src/api/erpFinance.js
new file mode 100644
index 0000000..0763d42
--- /dev/null
+++ b/backend/src/api/erpFinance.js
@@ -0,0 +1,103 @@
+import request from '@/utils/request'
+
+/**
+ * 进销存财务接口(/backend/erp/finance*)
+ * 往来账(应收/应付)由已生效单据自动生成,收付款单确认时按单核销或 FIFO 核销。
+ */
+
+export function getBillList(params) {
+ return request({
+ url: '/backend/erp/finance/bill/list',
+ method: 'get',
+ params
+ })
+}
+
+export function getBillDetail(id) {
+ return request({
+ url: `/backend/erp/finance/bill/${id}`,
+ method: 'get'
+ })
+}
+
+export function getAging(params) {
+ return request({
+ url: '/backend/erp/finance/aging',
+ method: 'get',
+ params
+ })
+}
+
+export function getCashFlow(params) {
+ return request({
+ url: '/backend/erp/finance/cashflow',
+ method: 'get',
+ params
+ })
+}
+
+/** 往来单位对账汇总(应收/应付累计与未结) */
+export function getPartnerSummary(params) {
+ return request({
+ url: '/backend/erp/finance/partner',
+ method: 'get',
+ params
+ })
+}
+
+export function getFundList(params) {
+ return request({
+ url: '/backend/erp/finance/fund/list',
+ method: 'get',
+ params
+ })
+}
+
+export function getFundDetail(id) {
+ return request({
+ url: `/backend/erp/finance/fund/${id}`,
+ method: 'get'
+ })
+}
+
+/** 新建/修改收付款草稿,返回 {id, fund_no} */
+export function saveFund(data) {
+ return request({
+ url: '/backend/erp/finance/fund',
+ method: 'post',
+ data
+ })
+}
+
+/** 确认收付款:body 传 {settles:[{partner_bill_id,amount}]},不传则按账期 FIFO 自动核销 */
+export function confirmFund(id, data) {
+ return request({
+ url: `/backend/erp/finance/fund/${id}/confirm`,
+ method: 'post',
+ data: data || {}
+ })
+}
+
+export function voidFund(id) {
+ return request({
+ url: `/backend/erp/finance/fund/${id}/void`,
+ method: 'post',
+ data: {}
+ })
+}
+
+export function deleteFund(id) {
+ return request({
+ url: `/backend/erp/finance/fund/${id}`,
+ method: 'delete'
+ })
+}
+
+/** 某往来单位当前未结的往来账,供核销勾选 */
+export function getFundOpenBills(params) {
+ return request({
+ url: '/backend/erp/finance/fund/openBills',
+ method: 'get',
+ params
+ })
+}
diff --git a/backend/src/api/erpProduct.js b/backend/src/api/erpProduct.js
new file mode 100644
index 0000000..01fe54f
--- /dev/null
+++ b/backend/src/api/erpProduct.js
@@ -0,0 +1,74 @@
+import request from '@/utils/request'
+
+/**
+ * 进销存商品档案接口(/backend/erp/product*)
+ * 商品支持基础单位+辅助单位换算、五级价格、安全/最高库存与保质期批次开关。
+ */
+
+export function getProductList(params) {
+ return request({
+ url: '/backend/erp/product/list',
+ method: 'get',
+ params
+ })
+}
+
+export function getProductDetail(id) {
+ return request({
+ url: `/backend/erp/product/${id}`,
+ method: 'get'
+ })
+}
+
+export function createProduct(data) {
+ return request({
+ url: '/backend/erp/product',
+ method: 'post',
+ data
+ })
+}
+
+export function updateProduct(id, data) {
+ return request({
+ url: `/backend/erp/product/${id}`,
+ method: 'put',
+ data
+ })
+}
+
+export function deleteProduct(id) {
+ return request({
+ url: `/backend/erp/product/${id}`,
+ method: 'delete'
+ })
+}
+
+export function toggleProductStatus(id, status) {
+ return request({
+ url: `/backend/erp/product/${id}/status`,
+ method: 'post',
+ data: { status }
+ })
+}
+
+export function getCategoryList() {
+ return request({
+ url: '/backend/erp/product/category/list',
+ method: 'get'
+ })
+}
+
+export function saveCategory(data) {
+ return request({
+ url: '/backend/erp/product/category',
+ method: 'post',
+ data
+ })
+}
+
+export function deleteCategory(id) {
+ return request({
+ url: `/backend/erp/product/category/${id}`,
+ method: 'delete'
+ })
+}
diff --git a/backend/src/api/erpReport.js b/backend/src/api/erpReport.js
new file mode 100644
index 0000000..87be346
--- /dev/null
+++ b/backend/src/api/erpReport.js
@@ -0,0 +1,63 @@
+import request from '@/utils/request'
+
+/**
+ * 进销存报表接口(/backend/erp/report*)
+ * 统计口径只含「已生效」单据与库存流水,因此报表数字与往来账、库存结存永远一致。
+ * 公共参数:date_from、date_to(YYYY-MM-DD,缺省为当月)。
+ */
+
+export function getPurchaseSummary(params) {
+ return request({
+ url: '/backend/erp/report/purchase',
+ method: 'get',
+ params
+ })
+}
+
+export function getSalesSummary(params) {
+ return request({
+ url: '/backend/erp/report/sales',
+ method: 'get',
+ params
+ })
+}
+
+export function getSalesRank(params) {
+ return request({
+ url: '/backend/erp/report/rank',
+ method: 'get',
+ params
+ })
+}
+
+export function getStockMoveReport(params) {
+ return request({
+ url: '/backend/erp/report/stockmove',
+ method: 'get',
+ params
+ })
+}
+
+export function getProfitReport(params) {
+ return request({
+ url: '/backend/erp/report/profit',
+ method: 'get',
+ params
+ })
+}
+
+export function getRfmReport(params) {
+ return request({
+ url: '/backend/erp/report/rfm',
+ method: 'get',
+ params
+ })
+}
+
+export function getErpDashboard(params) {
+ return request({
+ url: '/backend/erp/report/dashboard',
+ method: 'get',
+ params
+ })
+}
diff --git a/backend/src/api/erpStock.js b/backend/src/api/erpStock.js
new file mode 100644
index 0000000..7dd7be2
--- /dev/null
+++ b/backend/src/api/erpStock.js
@@ -0,0 +1,46 @@
+import request from '@/utils/request'
+
+/**
+ * 进销存库存查询接口(/backend/erp/stock*)
+ * 结存全部由流水驱动,这里只有读接口 + 补货建议。
+ */
+
+export function getStockList(params) {
+ return request({
+ url: '/backend/erp/stock/list',
+ method: 'get',
+ params
+ })
+}
+
+export function getStockLedger(params) {
+ return request({
+ url: '/backend/erp/stock/ledger',
+ method: 'get',
+ params
+ })
+}
+
+export function getStockWarning(params) {
+ return request({
+ url: '/backend/erp/stock/warning',
+ method: 'get',
+ params
+ })
+}
+
+export function getReplenishList(params) {
+ return request({
+ url: '/backend/erp/stock/replenish',
+ method: 'get',
+ params
+ })
+}
+
+export function getBatchList(params) {
+ return request({
+ url: '/backend/erp/stock/batch',
+ method: 'get',
+ params
+ })
+}
diff --git a/backend/src/api/erpWarehouse.js b/backend/src/api/erpWarehouse.js
new file mode 100644
index 0000000..a45f005
--- /dev/null
+++ b/backend/src/api/erpWarehouse.js
@@ -0,0 +1,50 @@
+import request from '@/utils/request'
+
+/** 进销存仓库档案接口(/backend/erp/warehouse*),列表带库存品种数与货值 */
+
+export async function getWarehouseList(params) {
+ const res = await request({
+ url: '/backend/erp/warehouse/list',
+ method: 'get',
+ params
+ })
+ // 后端按 {warehouse, stock_kinds, stock_value} 嵌套返回,拍平后下拉与表格可共用同一行结构
+ if (res && res.code === 200 && Array.isArray(res.data?.list)) {
+ res.data.list = res.data.list.map((row) => ({
+ ...(row.warehouse || {}),
+ stock_kinds: row.stock_kinds,
+ stock_value: row.stock_value
+ }))
+ }
+ return res
+}
+
+export function getWarehouseDetail(id) {
+ return request({
+ url: `/backend/erp/warehouse/${id}`,
+ method: 'get'
+ })
+}
+
+export function createWarehouse(data) {
+ return request({
+ url: '/backend/erp/warehouse',
+ method: 'post',
+ data
+ })
+}
+
+export function updateWarehouse(id, data) {
+ return request({
+ url: `/backend/erp/warehouse/${id}`,
+ method: 'put',
+ data
+ })
+}
+
+export function deleteWarehouse(id) {
+ return request({
+ url: `/backend/erp/warehouse/${id}`,
+ method: 'delete'
+ })
+}
diff --git a/backend/src/views/apps/erp/basic/product.vue b/backend/src/views/apps/erp/basic/product.vue
new file mode 100644
index 0000000..4c347e2
--- /dev/null
+++ b/backend/src/views/apps/erp/basic/product.vue
@@ -0,0 +1,658 @@
+
+
+
+
+
+
+
+ 商品分类
+ 新增
+
+
+
+
+
+ {{ data.category_name }}
+ {{ data.product_count || 0 }}
+ 改名
+ 删除
+
+
+
+
全部商品
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+
+ {{ row.product_name }}
+ 批次
+
+
+
+
+
+
+ {{ row.base_unit }} / {{ row.aux_unit }}×{{ row.aux_ratio }}
+
+
+
+ {{ money(row.purchase_price) }}
+
+
+ {{ money(row.sale_price) }}
+
+
+
+ {{ money(row.min_price) }}
+
+
+
+
+ {{ qty(row.total_stock) }}
+
+
+
+
+ {{ qty(row.safety_qty) }} / {{ qty(row.max_qty) }}
+
+
+
+
+ {{ row.status === 1 ? '启用' : '停用' }}
+
+
+
+
+ 编辑
+ {{ row.status === 1 ? '停用' : '启用' }}
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+ 基本信息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 单位与换算
+
+
+
+
+
+
+
+
+
+ 库存与销售一律按基本单位记账,换算系数表示 1 个辅助单位等于多少基本单位。
+
+ 价格体系
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 最低售价用于销售出库的限价校验,低于该价需有审核权限。
+
+ 库存与批次
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/basic/warehouse.vue b/backend/src/views/apps/erp/basic/warehouse.vue
new file mode 100644
index 0000000..3a1bd32
--- /dev/null
+++ b/backend/src/views/apps/erp/basic/warehouse.vue
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+
+
+
+ {{ row.warehouse_name }}
+ 默认仓
+
+
+
+
+
+
+ {{ row.stock_kinds || 0 }}
+
+
+ {{ money(row.stock_value) }}
+
+
+
+ {{ row.status === 1 ? '启用' : '停用' }}
+
+
+
+
+ 编辑
+ 设为默认
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 开单时默认带出该仓
+
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/customer/components/edit.vue b/backend/src/views/apps/erp/customer/components/edit.vue
index 64d637d..3b923cf 100644
--- a/backend/src/views/apps/erp/customer/components/edit.vue
+++ b/backend/src/views/apps/erp/customer/components/edit.vue
@@ -148,6 +148,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 信用额度用于销售出库审核时的超限拦截,账期决定应收单的到期日与逾期起算。
+
+
({
address: "",
status: "1",
is_draft: 0,
+ credit_limit: 0,
+ payment_days: 0,
remark: "",
invoice_title: "",
tax_number: "",
@@ -499,6 +532,12 @@ function handleSaveDraft() {
padding: 0 20px;
}
+.credit-tip {
+ margin: -8px 0 12px;
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+}
+
.dialog-footer {
display: flex;
justify-content: space-between;
diff --git a/backend/src/views/apps/erp/dashboard/index.vue b/backend/src/views/apps/erp/dashboard/index.vue
index 42eeb48..8143520 100644
--- a/backend/src/views/apps/erp/dashboard/index.vue
+++ b/backend/src/views/apps/erp/dashboard/index.vue
@@ -1,5 +1,5 @@
-
+
@@ -9,8 +9,8 @@
- ¥
- {{ formatMoney(item.value) }}
+ {{ item.prefix }}
+ {{ item.text }}
@@ -25,8 +25,8 @@
- ¥
- {{ formatMoney(item.value) }}
+ {{ item.prefix }}
+ {{ item.text }}
@@ -36,19 +36,19 @@
@@ -60,39 +60,32 @@
import { ref, onMounted, onUnmounted, shallowRef, nextTick } from 'vue';
import * as echarts from 'echarts';
import { InfoFilled } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { getErpDashboard } from '@/api/erpReport';
-// 统计卡片数据 - 第一行
-const firstRowStats = ref([
- { label: '今日销售', value: 0 },
- { label: '今日零售', value: 0 },
- { label: '今日采购', value: 0 },
- { label: '本月累计销售', value: 0 },
- { label: '本月累计零售', value: 0 },
- { label: '本月累计采购', value: 0 },
-]);
+type Stat = { label: string; text: string; prefix?: string };
-// 统计卡片数据 - 第二行
-const secondRowStats = ref([
- { label: '昨日销售', value: 0 },
- { label: '昨日零售', value: 0 },
- { label: '昨日采购', value: 0 },
- { label: '今年累计销售', value: 0 },
- { label: '今年累计零售', value: 0 },
- { label: '今年累计采购', value: 0 },
-]);
+const loading = ref(false);
+
+// 统计卡片数据 - 第一行(金额)
+const firstRowStats = ref([]);
+
+// 统计卡片数据 - 第二行(成本、往来与预警)
+const secondRowStats = ref([]);
// 图表实例
const salesChartRef = ref(null);
-const retailChartRef = ref(null);
+const netChartRef = ref(null);
const purchaseChartRef = ref(null);
const salesChartInstance = shallowRef(null);
-const retailChartInstance = shallowRef(null);
+const netChartInstance = shallowRef(null);
const purchaseChartInstance = shallowRef(null);
// 格式化金额
-const formatMoney = (value: number): string => {
- return value.toLocaleString();
-};
+const money = (value: unknown): string =>
+ Number(value || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
+
+const plain = (value: unknown): string => String(Number(value || 0));
// 生成最近6个月的月份标签
const getMonthLabels = (): string[] => {
@@ -170,69 +163,80 @@ const initCharts = () => {
if (salesChartRef.value) {
salesChartInstance.value = initChart(salesChartRef.value, '销售', '#409EFF');
}
- if (retailChartRef.value) {
- retailChartInstance.value = initChart(retailChartRef.value, '零售', '#67C23A');
+ if (netChartRef.value) {
+ netChartInstance.value = initChart(netChartRef.value, '购销差额', '#67C23A');
}
if (purchaseChartRef.value) {
purchaseChartInstance.value = initChart(purchaseChartRef.value, '采购', '#E6A23C');
}
};
-// 加载数据(模拟接口调用)
+// 加载数据:口径与报表中心完全一致(只统计已生效单据与库存流水)
const loadData = async () => {
- // TODO: 调用接口获取真实数据
- // const res = await getErpDashboard();
-
- // 模拟数据更新
- firstRowStats.value = [
- { label: '今日销售', value: 12580 },
- { label: '今日零售', value: 8560 },
- { label: '今日采购', value: 23400 },
- { label: '本月累计销售', value: 358600 },
- { label: '本月累计零售', value: 245800 },
- { label: '本月累计采购', value: 568900 },
- ];
-
- secondRowStats.value = [
- { label: '昨日销售', value: 15230 },
- { label: '昨日零售', value: 9870 },
- { label: '昨日采购', value: 18900 },
- { label: '今年累计销售', value: 2856000 },
- { label: '今年累计零售', value: 1985000 },
- { label: '今年累计采购', value: 4568000 },
- ];
-
- // 更新图表数据
- updateCharts();
+ loading.value = true;
+ try {
+ const res = await getErpDashboard({});
+ if (res.code !== 200) {
+ throw new Error(res.msg || '看板数据加载失败');
+ }
+ const d = res.data || {};
+ const m = d.month || {};
+ const t = d.today || {};
+ const s = d.stock || {};
+ const b = d.partner_bill || {};
+ const w = d.warning || {};
+
+ firstRowStats.value = [
+ { label: '今日销售额', text: money(t.sales_amount), prefix: '¥' },
+ { label: '本月销售额', text: money(m.sales_amount), prefix: '¥' },
+ { label: '本月采购额', text: money(m.purchase_amount), prefix: '¥' },
+ { label: '本月毛利', text: money(m.profit), prefix: '¥' },
+ { label: '库存金额', text: money(s.value), prefix: '¥' },
+ { label: '未结应收', text: money(b.receivable), prefix: '¥' },
+ ];
+ secondRowStats.value = [
+ { label: '本月销售成本', text: money(m.cost), prefix: '¥' },
+ { label: '本月退货额', text: money(m.return_amount), prefix: '¥' },
+ { label: '未结应付', text: money(b.payable), prefix: '¥' },
+ { label: '往来净额', text: money(b.net), prefix: '¥' },
+ { label: '本月毛利率', text: `${Number(m.profit_rate || 0).toFixed(2)}%` },
+ { label: '缺货 / 低于安全库存', text: `${plain(w.out)} / ${plain(w.low)}` },
+ ];
+ updateCharts(d.trend || []);
+ } catch (e: any) {
+ ElMessage.error(e?.message || '看板数据加载失败');
+ } finally {
+ loading.value = false;
+ }
};
// 更新图表数据
-const updateCharts = () => {
- const salesData = [28000, 32000, 35000, 38000, 42000, 358600];
- const retailData = [18000, 21000, 22000, 25000, 28000, 245800];
- const purchaseData = [45000, 48000, 52000, 55000, 58000, 568900];
-
+const updateCharts = (trend: Array<{ sale?: number; buy?: number }>) => {
+ const sale = trend.map((x) => Number(x.sale || 0));
+ const buy = trend.map((x) => Number(x.buy || 0));
+ const net = trend.map((x) => Number(x.sale || 0) - Number(x.buy || 0));
+
if (salesChartInstance.value) {
- salesChartInstance.value.setOption({ series: [{ data: salesData }] });
+ salesChartInstance.value.setOption({ series: [{ data: sale }] });
}
- if (retailChartInstance.value) {
- retailChartInstance.value.setOption({ series: [{ data: retailData }] });
+ if (netChartInstance.value) {
+ netChartInstance.value.setOption({ series: [{ data: net }] });
}
if (purchaseChartInstance.value) {
- purchaseChartInstance.value.setOption({ series: [{ data: purchaseData }] });
+ purchaseChartInstance.value.setOption({ series: [{ data: buy }] });
}
};
// 自适应窗口大小
const handleResize = () => {
salesChartInstance.value?.resize();
- retailChartInstance.value?.resize();
+ netChartInstance.value?.resize();
purchaseChartInstance.value?.resize();
};
onMounted(async () => {
- initCharts();
await nextTick();
+ initCharts();
await loadData();
window.addEventListener('resize', handleResize);
});
@@ -240,7 +244,7 @@ onMounted(async () => {
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
salesChartInstance.value?.dispose();
- retailChartInstance.value?.dispose();
+ netChartInstance.value?.dispose();
purchaseChartInstance.value?.dispose();
});
diff --git a/backend/src/views/apps/erp/document/DocDetailDrawer.vue b/backend/src/views/apps/erp/document/DocDetailDrawer.vue
new file mode 100644
index 0000000..77fa976
--- /dev/null
+++ b/backend/src/views/apps/erp/document/DocDetailDrawer.vue
@@ -0,0 +1,185 @@
+
+
+
+
+
+ {{ doc.doc_no }}
+ {{ dateOnly(doc.biz_date) }}
+
+ {{ docStatusText(doc.status) }}
+
+ {{ doc.partner_name || '-' }}
+
+ {{ doc.warehouse_name || '-' }}
+
+
+ {{ doc.to_warehouse_name }}
+
+ {{ doc.source_doc_no || '-' }}
+ {{ doc.create_user_name || '-' }}
+ {{ doc.auditor_name || '-' }}
+ {{ money(doc.total_amount) }}
+ {{ money(doc.discount_amount) }}
+
+ {{ money(doc.actual_amount) }}
+
+ {{ qty(doc.total_qty) }}
+ {{ doc.remark || '-' }}
+
+
+ 明细行
+
+
+
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+ {{ qty(row.book_qty) }}
+
+
+
+ {{ qty(row.executed_qty) }}
+
+
+
+ {{ qty(row.quantity - row.executed_qty) }}
+
+
+ {{ money(row.unit_price) }}
+
+
+ {{ money(row.amount) }}
+
+
+
+ {{ dateOnly(row.expire_date) }}
+
+
+
+
+
+ 审批进度
+
+ {{ doc.instance.flow_name || '审批流程' }}
+
+ {{ doc.instance.status_label }}
+
+ 提交人:{{ doc.instance.submit_name || '-' }}
+ 当前节点:{{ doc.instance.current_node || '-' }}
+
+
+
+ {{ t.node_name }} · {{ t.approver_name || '-' }} · {{ t.status_label }}
+ {{ t.comment }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/document/DocEditor.vue b/backend/src/views/apps/erp/document/DocEditor.vue
new file mode 100644
index 0000000..8c1bf96
--- /dev/null
+++ b/backend/src/views/apps/erp/document/DocEditor.vue
@@ -0,0 +1,719 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ creditTip }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 添加明细行
+
+ 按来源单据带入
+
+
+ 载入盘点表
+
+ {{ itemsTip }}
+
+
+
+
+
+
+ onPickProduct(row, p)" />
+
+
+
+
+ {{ row.spec || '-' }}
+
+
+
+
+
+
+
+
+
+ {{ fmtQty(row.book_qty) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ fmtMoney(round2(row.quantity * row.unit_price)) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 删除
+
+
+
+
+
+ 合计数量:{{ fmtQty(totalQty) }}
+ 合计金额:{{ fmtMoney(totalAmount) }}
+ 折扣:-{{ fmtMoney(form.discount_amount) }}
+ 成交金额:{{ fmtMoney(totalAmount - form.discount_amount) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/document/DocListPage.vue b/backend/src/views/apps/erp/document/DocListPage.vue
new file mode 100644
index 0000000..070b686
--- /dev/null
+++ b/backend/src/views/apps/erp/document/DocListPage.vue
@@ -0,0 +1,432 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+ {{ row.doc_type_label || docTypeLabel(row.doc_type) }}
+
+
+
+
+ {{ row.doc_no }}
+
+
+
+ {{ dateOnly(row.biz_date) }}
+
+
+ {{ row.partner_name || '-' }}
+
+
+
+ {{ row.warehouse_name || '-' }} → {{ row.to_warehouse_name }}
+
+
+
+ {{ qty(row.total_qty) }}
+
+
+
+ {{ money(amountOf(row)) }}
+
+
+
+
+ {{ docStatusText(row.status) }}
+
+
+
+
+
+ 详情
+ 编辑
+ 提交审批
+ 审核
+ openPush(row, t)">
+ 下推
+
+
+ {{ n.label }}
+
+
+
+ 作废
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/document/ProductSelect.vue b/backend/src/views/apps/erp/document/ProductSelect.vue
new file mode 100644
index 0000000..5395256
--- /dev/null
+++ b/backend/src/views/apps/erp/document/ProductSelect.vue
@@ -0,0 +1,93 @@
+
+
+
+
+ {{ p.product_name }}
+ {{ p.product_code }}{{ p.spec ? ' · ' + p.spec : '' }} · 库存 {{ fmtQty(p.total_stock) }}{{ p.base_unit }}
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/document/dict.js b/backend/src/views/apps/erp/document/dict.js
new file mode 100644
index 0000000..a03d409
--- /dev/null
+++ b/backend/src/views/apps/erp/document/dict.js
@@ -0,0 +1,104 @@
+/**
+ * 进销存单据字典与格式化工具。
+ *
+ * 十类单据共用一套接口与界面,类型文案、状态色、往来单位要求都在这里收口,
+ * 页面只按 doc_type 取差异,避免各处硬编码。
+ */
+
+export const DOC_TYPES = {
+ purchase_order: { label: '采购订单', partner: 'supplier', affectsStock: false, prefix: 'CGDD' },
+ purchase_in: { label: '采购入库单', partner: 'supplier', affectsStock: true, source: ['purchase_order'] },
+ purchase_return: { label: '采购退货单', partner: 'supplier', affectsStock: true, source: ['purchase_order', 'purchase_in'] },
+ sales_order: { label: '销售订单', partner: 'customer', affectsStock: false },
+ sales_out: { label: '销售出库单', partner: 'customer', affectsStock: true, source: ['sales_order'] },
+ sales_return: { label: '销售退货单', partner: 'customer', affectsStock: true, source: ['sales_order', 'sales_out'] },
+ other_in: { label: '其他入库单', partner: '', affectsStock: true },
+ other_out: { label: '其他出库单', partner: '', affectsStock: true },
+ transfer: { label: '库存调拨单', partner: '', affectsStock: true, toWarehouse: true },
+ stock_take: { label: '库存盘点单', partner: '', affectsStock: true, takeSheet: true }
+}
+
+export const DOC_TYPE_ORDER = [
+ 'purchase_order', 'purchase_in', 'purchase_return',
+ 'sales_order', 'sales_out', 'sales_return',
+ 'other_in', 'other_out', 'transfer', 'stock_take'
+]
+
+export const DOC_STATUS = {
+ 0: { label: '草稿', tag: 'info' },
+ 1: { label: '审批中', tag: 'warning' },
+ 2: { label: '已生效', tag: 'success' },
+ 3: { label: '已驳回', tag: 'danger' },
+ 4: { label: '已作废', tag: 'info' }
+}
+
+/** 下游单据:从已生效单据「下推」时用的目标类型 */
+export const DOC_NEXT_TYPES = {
+ purchase_order: [
+ { type: 'purchase_in', label: '采购入库' },
+ { type: 'purchase_return', label: '采购退货' }
+ ],
+ purchase_in: [{ type: 'purchase_return', label: '采购退货' }],
+ sales_order: [
+ { type: 'sales_out', label: '销售出库' },
+ { type: 'sales_return', label: '销售退货' }
+ ],
+ sales_out: [{ type: 'sales_return', label: '销售退货' }]
+}
+
+export function docTypeLabel(docType) {
+ return DOC_TYPES[docType]?.label || docType
+}
+
+export function docTypePartner(docType) {
+ return DOC_TYPES[docType]?.partner || ''
+}
+
+export function docTypeSources(docType) {
+ return DOC_TYPES[docType]?.source || []
+}
+
+export function docStatusText(status) {
+ return DOC_STATUS[status]?.label || '未知'
+}
+
+export function docStatusTag(status) {
+ return DOC_STATUS[status]?.tag || 'info'
+}
+
+export function money(v) {
+ const n = Number(v || 0)
+ return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
+}
+
+export function qty(v) {
+ const n = Number(v || 0)
+ // 数量按 4 位小数存储,尾部多余的 0 不显示
+ return String(Number(n.toFixed(4)))
+}
+
+/**
+ * 价格体系取价:采购类取采购价,销售类按基础设置的售价口径(零售/批发/会员)。
+ * 其他出入库、调拨、盘点不带价,由后端按移动加权成本结转。
+ */
+export function pickPrice(product, docType, salesPriceType) {
+ if (!product) return 0
+ if (docType.startsWith('purchase')) return Number(product.purchase_price) || 0
+ if (docType.startsWith('sales')) {
+ const field = { sale: 'sale_price', wholesale: 'wholesale_price', member: 'member_price' }[salesPriceType] || 'sale_price'
+ return Number(product[field]) || 0
+ }
+ return 0
+}
+
+export function todayStr() {
+ const d = new Date()
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
+}
+
+export function dateOnly(val) {
+ if (!val) return '-'
+ const d = new Date(val)
+ if (isNaN(d.getTime())) return String(val)
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
+}
diff --git a/backend/src/views/apps/erp/finance/components/BillListPage.vue b/backend/src/views/apps/erp/finance/components/BillListPage.vue
new file mode 100644
index 0000000..ed6ec2e
--- /dev/null
+++ b/backend/src/views/apps/erp/finance/components/BillListPage.vue
@@ -0,0 +1,413 @@
+
+
+
+
+
+
+ 未收应收合计
+ {{ money(summary.open_receivable) }}
+
+
+ 未付应付合计
+ {{ money(summary.open_payable) }}
+
+
+ {{ title }}未结
+ {{ money(ownOpen) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+ {{ row.bill.source_doc_no || '-' }}
+
+
+
+ {{ row.bill.partner_name || '-' }}
+
+
+ {{ dateOnly(row.bill.bill_date) }}
+
+
+
+ {{ row.bill.due_date ? dateOnly(row.bill.due_date) : '现结' }}
+
+
+
+ {{ money(row.bill.amount) }}
+
+
+ {{ money(row.bill.settled_amount) }}
+
+
+
+ {{ money(row.balance) }}
+
+
+
+
+ {{ row.overdue_days }} 天
+ -
+
+
+
+
+
+ {{ row.status_label }}
+
+
+
+
+
+ 详情
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.bill_type === 1 ? '应收' : '应付' }}
+
+
+ {{ money(row.total) }}
+
+
+
+
+ {{ money(row.buckets[b] || 0) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ detail.bill.partner_name }}
+ {{ detail.bill.bill_type === 1 ? '应收' : '应付' }}
+ {{ detail.bill.source_doc_no || '-' }}
+ {{ detail.bill.status === 2 ? '已结清' : detail.bill.status === 1 ? '部分核销' : '未核销' }}
+ {{ dateOnly(detail.bill.bill_date) }}
+ {{ detail.bill.due_date ? dateOnly(detail.bill.due_date) : '现结' }}
+ {{ money(detail.bill.amount) }}
+
+ {{ money(detail.balance) }}
+
+
+
+
核销记录
+
+
+ {{ (detail.funds && detail.funds[row.fund_bill_id] ? detail.funds[row.fund_bill_id].fund_no : row.fund_bill_id) }}
+
+
+ {{ money(row.amount) }}
+
+
+ {{ dateOnly(row.create_time) }}
+
+
+
+
+ 来源单据明细
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+ {{ money(row.unit_price) }}
+
+
+ {{ money(row.amount) }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/finance/payable.vue b/backend/src/views/apps/erp/finance/payable.vue
new file mode 100644
index 0000000..618ff15
--- /dev/null
+++ b/backend/src/views/apps/erp/finance/payable.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/finance/payment.vue b/backend/src/views/apps/erp/finance/payment.vue
new file mode 100644
index 0000000..be0de0b
--- /dev/null
+++ b/backend/src/views/apps/erp/finance/payment.vue
@@ -0,0 +1,644 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+
+ {{ fundLabel(row.fund_type) }}
+
+
+
+ {{ row.partner_name || '-' }}
+
+
+
+ {{ money(row.amount) }}
+
+
+
+ {{ money(row.settled_amount) }}
+
+
+ {{ methodLabel(row.pay_method) }}
+
+
+
+ {{ dateOnly(row.biz_date) }}
+
+
+
+
+ {{ statusLabel(row.status) }}
+
+
+
+
+
+ 编辑
+ 确认
+ 核销明细
+ 作废
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
收入合计{{ money(cash.total_in) }}
+
支出合计{{ money(cash.total_out) }}
+
净额{{ money(cash.net) }}
+
+
+
+
+ {{ money(row.in) }}
+
+
+ {{ money(row.out) }}
+
+
+ {{ money(row.net) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 仅保存草稿
+ 保存并确认
+
+
+
+
+
+ {{ current.fund_no }} · {{ fundLabel(current.fund_type) }} {{ money(current.amount) }}
+ 不勾选任何往来账时,按到期日最早的单据自动冲抵
+
+
+
+
+
+ {{ dateOnly(row.bill_date) }}
+
+
+ {{ row.due_date ? dateOnly(row.due_date) : '现结' }}
+
+
+ {{ money(row.amount) }}
+
+
+ {{ money(row.balance) }}
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 确认收付款
+
+
+
+
+
+
+
+ {{ fundDetail.fund.fund_no }}
+ {{ money(fundDetail.fund.amount) }}
+ {{ money(fundDetail.fund.settled_amount) }}
+ {{ statusLabel(fundDetail.fund.status) }}
+
+
核销的往来账
+
+
+
+ {{ money(row.amount) }}
+
+
+
关联单据
+
+
+
+
+ {{ money(row.amount - row.settled_amount) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/finance/receivable.vue b/backend/src/views/apps/erp/finance/receivable.vue
new file mode 100644
index 0000000..45dc6e4
--- /dev/null
+++ b/backend/src/views/apps/erp/finance/receivable.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/purchase/inbound.vue b/backend/src/views/apps/erp/purchase/inbound.vue
new file mode 100644
index 0000000..a8d587e
--- /dev/null
+++ b/backend/src/views/apps/erp/purchase/inbound.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/purchase/order.vue b/backend/src/views/apps/erp/purchase/order.vue
new file mode 100644
index 0000000..fc1b25c
--- /dev/null
+++ b/backend/src/views/apps/erp/purchase/order.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/purchase/return.vue b/backend/src/views/apps/erp/purchase/return.vue
new file mode 100644
index 0000000..70ad12e
--- /dev/null
+++ b/backend/src/views/apps/erp/purchase/return.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/report/SummaryTable.vue b/backend/src/views/apps/erp/report/SummaryTable.vue
new file mode 100644
index 0000000..c0207ce
--- /dev/null
+++ b/backend/src/views/apps/erp/report/SummaryTable.vue
@@ -0,0 +1,69 @@
+
+
+
+ 合计数量 {{ qty(data.summary?.qty) }}
+ 合计金额 {{ money(data.summary?.amount) }}
+ {{ (data.list || []).length }} 行
+
+
+
+
+
+ {{ qty(row.qty) }}
+
+
+
+ {{ money(row.amount) }}
+
+
+
+ {{ row.lines }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/report/index.vue b/backend/src/views/apps/erp/report/index.vue
new file mode 100644
index 0000000..45cce7d
--- /dev/null
+++ b/backend/src/views/apps/erp/report/index.vue
@@ -0,0 +1,795 @@
+
+
+
+
+
+
+
+
+
+
+ {{ k.label }}
+ {{ k.text }}
+ {{ k.sub }}
+
+
+
+
+
+
+
库存预警与待办
+
+
+ {{ w.value }}
+ {{ w.label }}
+
+
+
+ 待我审批 {{ dash.todo?.approval_pending || 0 }} 单
+ 草稿 {{ dash.todo?.draft_docs || 0 }} 单
+ 今日销售 {{ money(dash.today?.sales_amount) }} / {{ dash.today?.sales_count || 0 }} 单
+
+
+
+
+
+
+
+
+
+
+ loadSummary('purchase')">
+ 按商品
+ 按供应商
+ 按月
+
+ 净额已扣除采购退货
+
+
+
+
+
+
+
+
+
+ loadSummary('sales')">
+ 按商品
+ 按客户
+ 按月
+
+ 净额已扣除销售退货
+
+
+
+
+
+
+
+
+
+
+ 热销榜
+ 滞销榜
+
+
+
+
+ {{ rankOrder === 'asc' ? '滞销榜含区间内一件未卖的在售商品' : '' }}
+
+
+
+
+
+
+
+
+ {{ qty(row.qty) }}
+
+
+ {{ money(row.amount) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+
+
+ 本期入库 {{ money(moveSummary.in_amount) }}({{ qty(moveSummary.in_qty) }})
+ 本期出库 {{ money(moveSummary.out_amount) }}({{ qty(moveSummary.out_qty) }})
+ 期末库存金额 {{ money(moveSummary.end_amount) }}
+
+
+
+
+
+
+
+
+ {{ qty(row.open_qty) }}
+
+
+ {{ money(row.open_amount) }}
+
+
+
+
+ {{ qty(row.in_qty) }}
+
+
+ {{ money(row.in_amount) }}
+
+
+
+
+ {{ qty(row.out_qty) }}
+
+
+ {{ money(row.out_amount) }}
+
+
+
+
+ {{ qty(row.end_qty) }}
+
+
+ {{ money(row.end_cost) }}
+
+
+ {{ money(row.end_amount) }}
+
+
+
+
+
+
+
+
+
+
+
+
销售收入{{ money(profitSummary.revenue) }}已扣退货
+
销售成本{{ money(profitSummary.cost) }}出库流水实际成本
+
毛利额{{ money(profitSummary.profit) }}收入 − 成本
+
毛利率{{ Number(profitSummary.profit_rate || 0).toFixed(2) }}%毛利额 / 收入
+
+
+
+
+
+
+
+
+ {{ qty(row.qty) }}
+
+
+ {{ money(row.revenue) }}
+
+
+ {{ money(row.cost) }}
+
+
+ {{ money(row.profit) }}
+
+
+
+
+
+ {{ Number(row.profit_rate || 0).toFixed(2) }}%
+
+
+
+
+
+ 有 {{ profitNegativeCount }} 个商品毛利为负,通常是售价低于移动加权成本或退货冲减,建议核查售价与采购成本。
+
+
+
+
+
+
+
+
+
+ {{ seg }} {{ n }}
+
+ R={{ rfmMedian.r ?? '-' }}天 / F={{ rfmMedian.f ?? '-' }}次 / M={{ money(rfmMedian.m) }}(中位数切分,取全部已生效销售出库)
+
+
+
+
+
+ {{ row.last_date || '-' }}
+
+
+ {{ row.r_days >= 99999 ? '-' : row.r_days }}
+
+
+
+ {{ money(row.m_amount) }}
+
+
+ {{ row.r_score }} / {{ row.f_score }} / {{ row.m_score }}
+
+
+
+ {{ row.segment }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/sales/order.vue b/backend/src/views/apps/erp/sales/order.vue
new file mode 100644
index 0000000..8a7169b
--- /dev/null
+++ b/backend/src/views/apps/erp/sales/order.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/sales/outbound.vue b/backend/src/views/apps/erp/sales/outbound.vue
new file mode 100644
index 0000000..5419590
--- /dev/null
+++ b/backend/src/views/apps/erp/sales/outbound.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/sales/return.vue b/backend/src/views/apps/erp/sales/return.vue
new file mode 100644
index 0000000..2db614e
--- /dev/null
+++ b/backend/src/views/apps/erp/sales/return.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/stock/inbound.vue b/backend/src/views/apps/erp/stock/inbound.vue
new file mode 100644
index 0000000..102a80e
--- /dev/null
+++ b/backend/src/views/apps/erp/stock/inbound.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/stock/outbound.vue b/backend/src/views/apps/erp/stock/outbound.vue
new file mode 100644
index 0000000..d54bdeb
--- /dev/null
+++ b/backend/src/views/apps/erp/stock/outbound.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/stock/query.vue b/backend/src/views/apps/erp/stock/query.vue
new file mode 100644
index 0000000..cf68fd6
--- /dev/null
+++ b/backend/src/views/apps/erp/stock/query.vue
@@ -0,0 +1,484 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 按商品
+ 按仓库批次
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+ 结存金额合计:{{ money(stockSum) }}
+ 按移动加权成本计价
+
+
+
+
+
+
+
+
+ {{ row.batch_no || '-' }}
+
+
+
+ {{ qty(row.quantity) }}
+
+
+ {{ money(row.cost_price) }}
+
+
+ {{ money(row.amount) }}
+
+
+
+ {{ qty(row.safety_qty) }} / {{ qty(row.max_qty) }}
+
+
+
+
+ {{ stateText(row.safety_state) }}
+
+
+
+
+ 流水
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+ {{ dateTime(row.create_time) }}
+
+
+
+ {{ docTypeLabel(row.doc_type) }}
+
+
+
+
+
+ {{ row.batch_no || '-' }}
+
+
+
+
+ {{ row.direction === 1 ? '+' : '-' }}{{ qty(Math.abs(row.change_qty)) }}
+
+
+
+
+ {{ qty(row.after_qty) }}
+
+
+ {{ money(row.cost_price) }}
+
+
+ {{ money(row.amount) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+ {{ money(row.cost_price) }}
+
+
+ {{ money(row.amount) }}
+
+
+ {{ row.expire_date ? dateOnly(row.expire_date) : '-' }}
+
+
+
+ 已过期
+ {{ row.expire_in_days }} 天
+ {{ row.expire_in_days }} 天
+ 未登记
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/stock/taking.vue b/backend/src/views/apps/erp/stock/taking.vue
new file mode 100644
index 0000000..5c6e6c0
--- /dev/null
+++ b/backend/src/views/apps/erp/stock/taking.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/stock/transfer.vue b/backend/src/views/apps/erp/stock/transfer.vue
new file mode 100644
index 0000000..c76d813
--- /dev/null
+++ b/backend/src/views/apps/erp/stock/transfer.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/stock/warning.vue b/backend/src/views/apps/erp/stock/warning.vue
new file mode 100644
index 0000000..ad4a446
--- /dev/null
+++ b/backend/src/views/apps/erp/stock/warning.vue
@@ -0,0 +1,368 @@
+
+
+
+
+
+
+ {{ k.label }}
+ {{ counts[k.key] || 0 }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+
+ {{ qty(row.safety_qty) }}
+
+
+
+ {{ qty(row.in_transit) }}
+
+
+
+
+ {{ qty(row.suggest_qty) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ qty(row.safety_qty) }}
+
+
+ {{ qty(row.in_transit) }}
+
+
+
+ {{ qty(row.suggest_qty) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+
+ {{ qty(row.max_qty) }}
+
+
+ 促销或退供 {{ qty(row.suggest_qty) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+ {{ row.expire_date || '-' }}
+
+
+
+
+ {{ row.expire_in_days < 0 ? `过期 ${-row.expire_in_days} 天` : `${row.expire_in_days} 天` }}
+
+
+
+
+
+ {{ row.type_label }},建议先出或报损
+
+
+
+
+
+
+
+
+ 生成采购订单
+
+
+ 建议合计 {{ money(replenishAmount) }},勾选行仅对补货建议生效(目标量取「月销 + 安全库存」与最高库存的较大值,已扣除在途)
+
+
+
+
+
+
+
+
+
+ {{ qty(row.quantity) }}
+
+
+ {{ qty(row.safety_qty) }}
+
+
+
+ {{ qty(row.in_transit) }}
+
+
+
+ {{ qty(row.sold_30) }}
+
+
+
+ {{ qty(row.suggest_qty) }}
+
+
+
+ {{ money(row.unit_price) }}
+
+
+ {{ money(row.amount) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/erp/supplier/components/edit.vue b/backend/src/views/apps/erp/supplier/components/edit.vue
index 884ecd0..4b68e4e 100644
--- a/backend/src/views/apps/erp/supplier/components/edit.vue
+++ b/backend/src/views/apps/erp/supplier/components/edit.vue
@@ -121,6 +121,22 @@
+
+
+
+
+
+
+ 账期决定应付单的到期日与逾期起算。
+
({
address: '',
status: '1',
is_draft: 0,
+ payment_days: 0,
remark: '',
invoice_title: '',
tax_number: '',
@@ -426,6 +443,12 @@ function handleSaveDraft() {
padding: 0 20px;
}
+.credit-tip {
+ margin: -8px 0 12px;
+ font-size: 12px;
+ color: var(--el-text-color-secondary);
+}
+
.dialog-footer {
display: flex;
justify-content: space-between;
diff --git a/backend/src/views/apps/erp/system/index.vue b/backend/src/views/apps/erp/system/index.vue
new file mode 100644
index 0000000..2bbb093
--- /dev/null
+++ b/backend/src/views/apps/erp/system/index.vue
@@ -0,0 +1,489 @@
+
+
+
+
+
+
+
+
交易口径
+
+
+
+ 销售单据默认售价
+
+
+
+
+
+ 零售价
+ 批发价
+ 会员价
+
+
+
+
+
+
+
+
+
库存预警阈值
+
+
商品未单独设置安全库存时按该值预警
+
+
+
+
+
+
+
单据号前缀
+
+
+ 完整单号为「前缀 + 年月日 + 流水号」。每类单据独立前缀,留空则使用括号中的默认值;
+ 前缀只影响单号展示,不参与业务判断。
+
+
+
快捷入口
+
+ {{ l.label }}
+
+
+
+
+
+
+
+ 单据审批流
+ 重新检测
+
+
+ 提交单据时优先取该类型专属流程(biz_type = erp_单据类型),没有则回落到模块级流程(biz_type = erp);
+ 两者都没有时无法提交,只能由有权限的账号直接审核生效。
+
+
+
+ {{ docTypeLabel(row.doc_type) }}
+
+
+ erp_{{ row.doc_type }}
+
+
+
+ 专属流程
+ 模块级流程
+ 未配置
+
+
+
+
+ 暂无可用流程,请先到审批流配置新增
+
+ {{ f.flow_name }}
+
+
+
+
+
+ 查看单据
+
+
+
+
+
+
+
上线检查清单
+
+
+
+
+ {{ c.label }}
+ {{ c.tip }}
+
+
前往
+
+
+
+
+
+
+
+
+
diff --git a/backend/src/views/apps/oa/approvalcenter/components/ProgressTimeline.vue b/backend/src/views/apps/oa/approvalcenter/components/ProgressTimeline.vue
index d7d7926..fbffec0 100644
--- a/backend/src/views/apps/oa/approvalcenter/components/ProgressTimeline.vue
+++ b/backend/src/views/apps/oa/approvalcenter/components/ProgressTimeline.vue
@@ -51,7 +51,7 @@
+
+
diff --git a/uniapp/pages.json b/uniapp/pages.json
index 9ff1388..1c1be56 100644
--- a/uniapp/pages.json
+++ b/uniapp/pages.json
@@ -78,6 +78,150 @@
"navigationBarTitleText": "进销存 · 我的"
}
},
+ {
+ "path": "pages/erp/dashboard/index",
+ "style": {
+ "navigationBarTitleText": "进销存工作台",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/document/index",
+ "style": {
+ "navigationBarTitleText": "单据列表",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/document/detail",
+ "style": {
+ "navigationBarTitleText": "单据详情",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/document/edit",
+ "style": {
+ "navigationBarTitleText": "开单",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/basic/product",
+ "style": {
+ "navigationBarTitleText": "商品档案",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/basic/product-detail",
+ "style": {
+ "navigationBarTitleText": "商品详情",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/basic/warehouse",
+ "style": {
+ "navigationBarTitleText": "仓库档案",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/partner/index",
+ "style": {
+ "navigationBarTitleText": "客商管理",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/partner/detail",
+ "style": {
+ "navigationBarTitleText": "客商详情",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/stock/index",
+ "style": {
+ "navigationBarTitleText": "库存结存",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/stock/ledger",
+ "style": {
+ "navigationBarTitleText": "收发存流水",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/stock/batch",
+ "style": {
+ "navigationBarTitleText": "批次效期",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/stock/warning",
+ "style": {
+ "navigationBarTitleText": "库存预警",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/finance/bill",
+ "style": {
+ "navigationBarTitleText": "应收应付",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/finance/bill-detail",
+ "style": {
+ "navigationBarTitleText": "账单详情",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/finance/fund",
+ "style": {
+ "navigationBarTitleText": "收付款单",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/finance/fund-edit",
+ "style": {
+ "navigationBarTitleText": "收付款",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
+ {
+ "path": "pages/erp/report/index",
+ "style": {
+ "navigationBarTitleText": "报表中心",
+ "navigationBarBackgroundColor": "#F8FAFC",
+ "navigationBarTextStyle": "black"
+ }
+ },
{
"path": "pages/oa/home/home",
"style": {
diff --git a/uniapp/pages/erp/basic/product-detail.vue b/uniapp/pages/erp/basic/product-detail.vue
new file mode 100644
index 0000000..d7b5291
--- /dev/null
+++ b/uniapp/pages/erp/basic/product-detail.vue
@@ -0,0 +1,391 @@
+
+
+
+ 加载中...
+
+
+
+
+
+ {{ product.product_name }}
+
+
+ {{ product.product_code || '无编码' }}
+
+
+ 总库存
+ {{ qtyText(product.total_stock) }} {{ product.base_unit || '' }}
+
+
+ 库存状态
+ {{ safetyStateLabel(product.safety_state) }}
+
+
+ 售价
+ ¥{{ moneyText(product.sale_price) }}
+
+
+
+
+
+
+ 商品资料
+
+
+ 分类
+ {{ product.category_name || '未分类' }}
+
+
+ 品牌 / 规格
+ {{ join([product.brand, product.spec]) }}
+
+
+ 型号
+ {{ dash(product.model) }}
+
+
+ 单位
+
+ {{ dash(product.base_unit) }}
+ / 辅 {{ product.aux_unit }} × {{ qtyText(product.aux_ratio || 1) }}
+
+
+
+ 条码
+ {{ dash(product.barcode) }}
+
+
+
+
+
+
+ 价格与管控
+ 手机端只读
+
+
+
+ {{ p.label }}
+ {{ p.value }}
+
+
+
+
+
+
+ 分仓库存({{ stocks.length }} 行)
+ 货值 ¥{{ moneyText(stockAmount) }}
+
+
+ 暂无在库库存
+
+
+
+ {{ s.warehouse_name || `仓库#${s.warehouse_id}` }}
+ 批次 {{ s.batch_no }}
+
+ {{ qtyText(s.quantity) }} · ¥{{ moneyText(s.cost_price) }}
+
+
+
+
+
+
+
+ 出入库流水
+
+
+ 批次台账
+
+
+ 相关单据
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/basic/product.vue b/uniapp/pages/erp/basic/product.vue
new file mode 100644
index 0000000..616a207
--- /dev/null
+++ b/uniapp/pages/erp/basic/product.vue
@@ -0,0 +1,368 @@
+
+
+
+
+
+
+ 清空
+
+
+
+ {{ categoryName || '全部分类' }}
+
+
+
+ 仅在售
+
+
+ 共 {{ total }} 个
+
+
+
+
+
+ 加载中...
+
+
+
+ 商品建档请在 Web 后台「进销存 - 商品档案」完成
+
+
+
+
+ {{ row.product_name }}
+
+
+
+ {{ [row.product_code, row.spec, row.category_name].filter(Boolean).join(' · ') || '—' }}
+
+
+ 库存
+ {{ qtyText(row.total_stock) }} {{ row.base_unit || '' }}
+
+
+ 售价
+ ¥{{ moneyText(row.sale_price) }}
+
+
+ 采购价
+ ¥{{ moneyText(row.purchase_price) }}
+
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/basic/warehouse.vue b/uniapp/pages/erp/basic/warehouse.vue
new file mode 100644
index 0000000..fbe9496
--- /dev/null
+++ b/uniapp/pages/erp/basic/warehouse.vue
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+
+
+ 仓库 {{ list.length }} 个
+ 在库货值 ¥{{ moneyText(totalValue) }}
+
+
+
+
+ 加载中...
+
+
+
+ 先在 Web 后台「进销存 - 仓库档案」建仓库
+
+
+
+
+
+
+
+
+
+ {{ row.warehouse_name }}
+
+
+
+ {{ [row.warehouse_code, row.manager, row.phone].filter(Boolean).join(' · ') || '—' }}
+
+
+
+
+
+ 在库品种
+ {{ row.stock_kinds || 0 }}
+
+
+ 货值
+ ¥{{ moneyText(row.stock_value) }}
+
+
+ 地址
+ {{ row.address || '—' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/dashboard/index.vue b/uniapp/pages/erp/dashboard/index.vue
new file mode 100644
index 0000000..867a623
--- /dev/null
+++ b/uniapp/pages/erp/dashboard/index.vue
@@ -0,0 +1,496 @@
+
+
+
+ 加载中...
+
+
+
+
+
+
+ 本月销售金额
+ ¥{{ moneyText(month.sales_amount) }}
+
+ 销售 {{ month.sales_count || 0 }} 单 · 采购 {{ month.purchase_count || 0 }} 单 · 退货 ¥{{ moneyText(month.return_amount) }}
+
+
+
+
+ 本月毛利
+ {{ moneyText(month.profit) }}
+
+
+ 毛利率
+ {{ moneyText(month.profit_rate) }}%
+
+
+ 采购金额
+ {{ moneyText(month.purchase_amount) }}
+
+
+
+
+
+
+
+ {{ moneyText(today.sales_amount) }}
+ 今日销售({{ today.sales_count || 0 }} 单)
+
+
+ {{ moneyText(stock.value) }}
+ 库存货值({{ qtyText(stock.qty) }} 件)
+
+
+ {{ moneyText(bill.receivable) }}
+ 未收应收
+
+
+ {{ moneyText(bill.payable) }}
+ 未付应付
+
+
+ {{ todo.approval_pending || 0 }}
+ 待我审批
+
+
+ {{ todo.draft_docs || 0 }}
+ 草稿单据
+
+
+
+
+
+
+ 库存预警
+
+ 查看全部
+
+
+
+
+
+ {{ warning.low || 0 }}
+ 低于安全库存
+
+
+ {{ warning.out || 0 }}
+ 缺货
+
+
+ {{ warning.over || 0 }}
+ 库存积压
+
+
+ {{ warning.expiring || 0 }}
+ 效期临近
+
+
+
+
+
+
+
+ 近 6 个月购销趋势
+
+
+
+ 采购
+
+
+
+ 销售
+
+
+
+
+ {{ row.month }}
+
+
+
+
+
+
+
+
+ ¥{{ moneyText(row.sale) }}
+
+
+
+
+
+
+ 常用功能
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+ 重新加载
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/document/detail.vue b/uniapp/pages/erp/document/detail.vue
new file mode 100644
index 0000000..19e16d4
--- /dev/null
+++ b/uniapp/pages/erp/document/detail.vue
@@ -0,0 +1,580 @@
+
+
+
+ 加载中...
+
+
+
+
+
+
+ {{ doc.doc_type_label || doc.doc_type }}
+
+
+ ¥{{ moneyText(doc.actual_amount) }}
+ {{ doc.doc_no }}
+
+
+ 业务日期
+ {{ doc.biz_date }}
+
+
+ 数量合计
+ {{ qtyText(doc.total_qty) }}
+
+
+ 优惠金额
+ ¥{{ moneyText(doc.discount_amount) }}
+
+
+
+
+
+
+
+ {{ partnerLabel }}
+ {{ doc.partner_name || '—' }}
+
+
+ 仓库
+ {{ doc.warehouse_name || '—' }}
+
+
+ 调入仓库
+ {{ doc.to_warehouse_name }}
+
+
+ 来源单据
+
+ {{ doc.source_doc_no }}
+
+
+
+
+ 制单
+ {{ doc.create_user_name || '—' }} · {{ doc.create_time_text }}
+
+
+ 审核
+ {{ doc.auditor_name }} · {{ doc.audit_time_text }}
+
+
+
+
+
+
+
+ 商品明细({{ items.length }} 行)
+ 点行可看实时库存
+
+
+
+ {{ item.product_name }}
+ ¥{{ moneyText(item.amount) }}
+
+
+ {{ item.spec }}
+ {{ qtyText(item.quantity) }} {{ item.unit }} × ¥{{ moneyText(item.unit_price) }}
+ 批次 {{ item.batch_no }}
+ 结存 {{ qtyText(item.stock_qty) }}
+
+
+
+ 暂无明细
+
+
+
+
+
+
+ 审批进度
+
+
+
+ 流程
+ {{ instance.flow_name || '—' }}
+
+
+ 当前节点
+ {{ instance.current_node || '已结束' }}
+
+
+
+
+
+ {{ task.node_name }}
+ {{ task.status_label }}
+
+
+ {{ task.approver_name }}{{ task.finish_time ? ' · ' + dateTimeText(task.finish_time) : '' }}
+
+
+
+
+
+
+
+
+
+
+ 编辑
+ 删除
+ 提交审批
+ 审核生效
+
+
+ 撤回作废
+
+
+ 作废
+
+ 生成{{ next.label }}
+
+
+
+ 再次编辑
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/document/edit.vue b/uniapp/pages/erp/document/edit.vue
new file mode 100644
index 0000000..10db28b
--- /dev/null
+++ b/uniapp/pages/erp/document/edit.vue
@@ -0,0 +1,1303 @@
+
+
+
+ {{ blocked }}
+
+ 返回单据详情
+
+
+
+
+
+
+
+ {{ typeLabel }}
+ {{ form.docNo }}
+ 保存后生成单号
+
+
+
+ 业务日期 *
+
+
+ {{ form.bizDate || '选择日期' }}
+
+
+
+
+
+
+
+ {{ row.label }} *
+
+
+ {{ row.text || row.placeholder }}
+
+
+
+
+
+ {{ sourceTip }}
+
+
+
+ 备注
+
+
+
+
+
+
+
+ 商品明细({{ items.length }} 行)
+ ¥{{ moneyText(totalAmount) }}
+
+
+
+ 还没有明细行
+
+
+
+
+ {{ item.product_name || '未选商品' }}
+ ¥{{ moneyText(lineAmount(item)) }}
+
+
+ {{ qtyText(item.quantity) }} {{ item.unit || '' }} × ¥{{ moneyText(item.unit_price) }}
+ 批次 {{ item.batch_no }}
+ 账面 {{ qtyText(item.book_qty) }} · 差异 {{ signed(qtyText(diffOf(item))) }}
+
+
+
+
+
+
+
+
+
+
+ 添加明细
+
+
+
+ 载入账面库存
+
+
+
+ 按剩余量带出
+
+
+
+
+
+
+
+ 数量合计
+ {{ qtyText(totalQty) }}
+
+
+ 单据金额
+ ¥{{ moneyText(totalAmount) }}
+
+
+ 优惠金额
+
+
+
+
+
+ 实付/实收
+ ¥{{ moneyText(actualAmount) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ {{ editingIndex >= 0 ? '编辑明细' : '添加明细' }}
+ 确定
+
+
+
+ 商品 *
+
+ {{ draft.product_name || '选择商品' }}
+
+
+
+
+ {{ stockText }}
+
+
+
+ 账面数量
+
+ {{ qtyText(draft.book_qty) }}
+
+
+
+
+ {{ isStockTake ? '实盘数量' : '数量' }} *
+
+
+
+
+ 单价
+
+
+ ¥{{ moneyText(lineAmount(draft)) }}
+
+ 留空按商品资料与价格体系自动取价
+
+
+
+
+ 批次号 *
+
+
+
+
+
+ 生产日期
+
+
+ {{ draft.production_date || '选择日期' }}
+
+
+
+
+
+
+ 保质期 {{ draft.shelf_life_days }} 天,到期日由后端按保质期推算
+
+
+
+ 行备注
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/document/index.vue b/uniapp/pages/erp/document/index.vue
new file mode 100644
index 0000000..a8940e0
--- /dev/null
+++ b/uniapp/pages/erp/document/index.vue
@@ -0,0 +1,476 @@
+
+
+
+
+
+
+ 清空
+
+
+
+
+
+ {{ currentTypeLabel }}
+
+
+
+ {{ currentWarehouseLabel }}
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+ {{ row.doc_type_label || row.doc_type }}
+ {{ row.doc_no }}
+
+
+
+
+
+ {{ row.partner_name || row.warehouse_name || '内部单据' }}
+
+
+
+ 金额
+ ¥{{ moneyText(row.actual_amount) }}
+
+
+ 数量
+ {{ qtyText(row.total_qty) }}
+
+
+ 业务日期
+ {{ row.biz_date }}
+
+
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+ 新建单据
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/finance/bill-detail.vue b/uniapp/pages/erp/finance/bill-detail.vue
new file mode 100644
index 0000000..859e94c
--- /dev/null
+++ b/uniapp/pages/erp/finance/bill-detail.vue
@@ -0,0 +1,454 @@
+
+
+
+
+ {{ bill.partner_name }}
+
+
+ {{ billTypeText }}金额
+ ¥{{ moneyText(bill.amount) }}
+
+
+ 已核销
+ ¥{{ moneyText(bill.settled_amount) }}
+
+
+ 未{{ bill.bill_type === 1 ? '收' : '付' }}
+ ¥{{ moneyText(balance) }}
+
+
+ {{ progressText }}
+ {{ progressPercent }}%
+
+
+
+
+
+
+ 账单信息
+
+
+ 账单编号
+ {{ bill.id }}
+
+
+ 来源单据
+ {{ bill.source_doc_no || '手工账单' }}
+
+
+ 账单日期
+ {{ bill.bill_date || '—' }}
+
+
+ 账期到期
+
+ {{ bill.due_date || '未设账期' }}
+ (已逾期 {{ bill.overdue_days }} 天)
+
+
+
+ 备注
+ {{ bill.remark }}
+
+
+
+
+
+ 核销记录
+ {{ settles.length }} 笔
+
+
+ 尚未核销,登记{{ bill.bill_type === 1 ? '收款' : '付款' }}后自动冲抵
+
+
+
+
+
+
+ {{ (fundOf(s) || {}).fund_no || s.partner_bill_no || '核销记录' }}
+ {{ dayText(s.create_time) }} · {{ fundMethodText(s) }}
+
+ ¥{{ moneyText(s.amount) }}
+
+
+
+
+
+ 来源单据明细
+ {{ docItems.length }} 行
+
+
+
+ {{ it.product_name }}
+ {{ [it.product_code, it.spec].filter(Boolean).join(' · ') }}
+
+
+ ¥{{ moneyText(it.amount) }}
+ {{ qtyText(it.quantity) }} {{ it.unit || '' }} × ¥{{ moneyText(it.unit_price) }}
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/finance/bill.vue b/uniapp/pages/erp/finance/bill.vue
new file mode 100644
index 0000000..8aed51f
--- /dev/null
+++ b/uniapp/pages/erp/finance/bill.vue
@@ -0,0 +1,524 @@
+
+
+
+
+ {{ bt.label }}
+ 未{{ bt.value === 1 ? '收' : '付' }} ¥{{ moneyText(openOf(bt.value)) }}
+
+
+
+
+
+
+
+ 清空
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+ {{ dateFrom || '开始日期' }}
+
+
+ 至
+
+
+
+ {{ dateTo || '结束日期' }}
+
+
+ 重置
+
+
+
+
+ 共 {{ total }} 张账单
+ 已筛「{{ partnerName }}」×
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+ {{ row.partner_name }}
+
+
+ {{ row.source_doc_no || '手工账单' }} · {{ row.bill_date }}
+
+
+ {{ billType === 1 ? '应收' : '应付' }}
+ ¥{{ moneyText(row.amount) }}
+
+
+ 已核销
+ ¥{{ moneyText(row.settled_amount) }}
+
+
+ {{ billType === 1 ? '未收' : '未付' }}
+ ¥{{ moneyText(row.balance) }}
+
+
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+ {{ billType === 1 ? '登记收款' : '登记付款' }}
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/finance/fund-edit.vue b/uniapp/pages/erp/finance/fund-edit.vue
new file mode 100644
index 0000000..3ad602c
--- /dev/null
+++ b/uniapp/pages/erp/finance/fund-edit.vue
@@ -0,0 +1,782 @@
+
+
+
+ 单号 {{ fundNo }}
+
+
+
+
+
+
+ 款项类型 *
+
+
+ {{ ft.label }}
+
+
+ {{ typeLabel }}
+
+
+
+ 业务日期 *
+
+
+ {{ form.bizDate }}
+
+
+
+ {{ form.bizDate }}
+
+
+
+
+ 往来单位*
+
+
+ {{ form.partnerName || chooseText }}
+
+
+ {{ form.partnerName || '—' }}
+
+
+
+ 金额 *
+
+ {{ income ? '+' : '-' }}
+
+ ¥{{ moneyText(form.amount) }}
+
+
+
+
+ 结算方式
+
+
+ {{ m.label }}
+
+
+ {{ fundMethodLabel(form.payMethod) }}
+
+
+
+ 收付账户
+
+ {{ form.payAccount }}
+
+
+
+ 备注
+
+ {{ form.remark }}
+
+
+
+
+
+
+ 核销往来账
+ 已选 ¥{{ moneyText(pickedSum) }} / {{ moneyText(form.amount) }}
+
+
+ {{ bills.length
+ ? '不勾选则确认时按到期日从早到晚自动冲抵'
+ : needPartner && !form.partnerId
+ ? '选择往来单位后显示其未结账单'
+ : '该往来单位没有未结账单,确认后只记资金流水' }}
+
+
+
+ 正在拉取待核销账单...
+
+
+
+
+
+
+ {{ b.source_doc_no || `账单#${b.id}` }}
+
+ {{ dayText(b.bill_date) }}{{ b.due_date ? ` · 到期 ${dayText(b.due_date)}` : '' }} · 未结 ¥{{ moneyText(b.balance) }}
+
+
+
+ ¥{{ moneyText(b.balance) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/finance/fund.vue b/uniapp/pages/erp/finance/fund.vue
new file mode 100644
index 0000000..c5abeed
--- /dev/null
+++ b/uniapp/pages/erp/finance/fund.vue
@@ -0,0 +1,520 @@
+
+
+
+
+
+
+ {{ ft.label }}
+
+
+
+
+
+
+
+
+
+ 清空
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+ {{ dateFrom || '开始日期' }}
+
+
+ 至
+
+
+
+ {{ dateTo || '结束日期' }}
+
+
+ 重置
+
+
+
+
+ 共 {{ total }} 张单据
+ 本页 {{ sumText }}
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.partner_name || typeLabelOf(row.fund_type) }}
+
+
+
+ {{ row.fund_no }} · {{ typeLabelOf(row.fund_type) }} · {{ fundMethodLabel(row.pay_method) }}
+
+ 草稿未核销,确认后才冲抵往来账
+
+
+
+ {{ incomeOf(row.fund_type) ? '+' : '-' }}¥{{ moneyText(row.amount) }}
+
+ {{ row.biz_date }}
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+ 登记收付款
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/partner/detail.vue b/uniapp/pages/erp/partner/detail.vue
new file mode 100644
index 0000000..e6fa54c
--- /dev/null
+++ b/uniapp/pages/erp/partner/detail.vue
@@ -0,0 +1,408 @@
+
+
+
+ 加载中...
+
+
+
+
+
+ {{ name }}
+
+
+ {{ [profile.contact_person, profile.contact_phone].filter(Boolean).join(' · ') || '未填联系人' }}
+
+
+ {{ isSupplier ? '未付应付' : '未收应收' }}
+ ¥{{ moneyText(openAmount) }}
+
+
+ {{ isSupplier ? '累计应付' : '累计应收' }}
+ ¥{{ moneyText(totalAmount) }}
+
+
+ 账期
+ {{ Number(profile.payment_days) > 0 ? profile.payment_days + ' 天' : '现结' }}
+
+
+
+
+
+
+ 基础资料
+
+
+ 等级 / 类型
+ {{ join([profile.supplier_level || profile.customer_level, profile.supplier_type || profile.customer_type]) }}
+
+
+ 联系电话
+ {{ dash(profile.contact_phone) }}
+
+
+ 邮箱
+ {{ dash(profile.contact_email) }}
+
+
+ 地址
+ {{ dash(profile.address) }}
+
+
+ 开票抬头
+ {{ join([profile.invoice_title, profile.tax_number]) }}
+
+
+ 收款账户
+ {{ join([profile.bank_name, profile.bank_account]) }}
+
+
+ 信用额度
+ ¥{{ moneyText(profile.credit_limit) }}
+
+
+
+
+
+ 未结账单({{ bills.length }} 张)
+ 全部 {{ billTotal }} 张
+
+
+ 没有未结账单
+
+
+
+ {{ b.source_doc_no || `账单#${b.id}` }}
+ {{ b.bill_date }}{{ b.due_date ? ' · 到期 ' + b.due_date : '' }}
+
+
+ ¥{{ moneyText(b.balance) }}
+ 逾期 {{ b.overdue_days }} 天
+
+
+
+
+
+
+
+
+ 历史单据
+
+
+ {{ isSupplier ? '去付款' : '去收款' }}
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/partner/index.vue b/uniapp/pages/erp/partner/index.vue
new file mode 100644
index 0000000..d908933
--- /dev/null
+++ b/uniapp/pages/erp/partner/index.vue
@@ -0,0 +1,320 @@
+
+
+
+
+
+ {{ t.label }}
+
+
+
+
+
+ 清空
+
+
+ 共 {{ total }} 家
+ 未结合计 ¥{{ moneyText(openTotal) }}
+
+
+
+
+ 加载中...
+
+
+
+ 客商建档请在 Web 后台 CRM / 进销存里完成
+
+
+
+
+ {{ row.partner_name }}
+
+ {{ Number(row.open_amount) > 0 ? `未结 ¥${moneyText(row.open_amount)}` : '已结清' }}
+
+
+
+ {{ row.contact_person }} {{ row.contact_phone || '' }}
+ 等级 {{ row.level }}
+ 账期 {{ row.payment_days }} 天
+ 信用额 ¥{{ moneyText(row.credit_limit) }}
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+ {{ partnerType === 'supplier' ? '应付账单' : '应收账单' }}
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/report/index.vue b/uniapp/pages/erp/report/index.vue
new file mode 100644
index 0000000..df97bdf
--- /dev/null
+++ b/uniapp/pages/erp/report/index.vue
@@ -0,0 +1,1112 @@
+
+
+
+
+
+
+ {{ t.label }}
+
+
+
+
+
+
+
+
+
+
+ {{ dateFrom || '开始日期' }}
+
+
+ 至
+
+
+
+ {{ dateTo || '结束日期' }}
+
+
+ 近30天
+
+
+
+
+
+ {{ s.label }}
+
+
+ {{ dimensionLabel }}
+
+
+
+
+
+
+
+ 热销榜
+
+
+ 滞销榜
+
+
+ Top {{ top }}
+
+
+
+
+
+
+
+
+ {{ warehouseName || '全部仓库' }}
+
+
+
+
+
+
+
+
+
+
+ 统计中...
+
+
+
+
+
+
+ {{ side === 'purchase' ? '采购总量' : '销售总量' }}
+ {{ qtyText(sumSummary.summary.qty) }}
+
+
+ {{ side === 'purchase' ? '采购总额' : '销售总额' }}
+ ¥{{ moneyText(sumSummary.summary.amount) }}
+
+
+
+
+
+
+
+ {{ r.label }}
+ {{ r.lines }} 行明细 · 数量 {{ qtyText(r.qty) }}
+
+ ¥{{ moneyText(r.amount) }}
+
+
+
+
+
+
+
+
+
+ {{ i + 1 }}
+
+ {{ r.product_name }}
+ {{ [r.product_code, r.spec].filter(Boolean).join(' · ') }} · {{ qtyText(r.qty) }} {{ r.unit || '' }}
+
+ ¥{{ moneyText(r.amount) }}
+
+
+
+
+
+
+
+ 本期入
+ ¥{{ moneyText(move.summary.in_amount) }}
+
+
+ 本期出
+ ¥{{ moneyText(move.summary.out_amount) }}
+
+
+ 期末货值
+ ¥{{ moneyText(move.summary.end_amount) }}
+
+
+
+
+
+
+
+ {{ r.product_name }}
+ {{ r.warehouse_name || '未分仓' }}
+
+ {{ [r.product_code, r.category_name].filter(Boolean).join(' · ') }}
+
+
+ 期初
+ {{ qtyText(r.open_qty) }}
+ ¥{{ moneyText(r.open_amount) }}
+
+
+ 入
+ {{ qtyText(r.in_qty) }}
+ ¥{{ moneyText(r.in_amount) }}
+
+
+ 出
+ {{ qtyText(r.out_qty) }}
+ ¥{{ moneyText(r.out_amount) }}
+
+
+ 期末
+ {{ qtyText(r.end_qty) }}
+ ¥{{ moneyText(r.end_amount) }}
+
+
+
+
+ 加载更多({{ move.list.length }}/{{ move.total }})
+
+
+
+
+
+
+
+ 毛利额
+ ¥{{ moneyText(profit.summary.profit) }}
+
+
+
+ 收入
+ ¥{{ moneyText(profit.summary.revenue) }}
+
+
+ 成本
+ ¥{{ moneyText(profit.summary.cost) }}
+
+
+ 毛利率
+ {{ Number(profit.summary.profit_rate || 0) }}%
+
+
+
+
+
+
+
+
+ {{ r.product_name }}
+ {{ Number(r.profit_rate) }}%
+
+ {{ qtyText(r.qty) }} {{ r.unit || '' }} · 收入 ¥{{ moneyText(r.revenue) }} · 成本 ¥{{ moneyText(r.cost) }}
+
+
+
+
+
+
+
+
+
+
+ {{ side.title }}
+ ¥{{ moneyText(agingTotalOf(side.key)) }}
+
+
+
+
+
+
+
+ {{ b.label }}
+ ¥{{ moneyText(bucketAmount(side.key, b.label)) }}
+
+
+
+
+ {{ row.partner_name }}
+ {{ bucketText(row) }}
+
+ ¥{{ moneyText(row.total) }}
+
+
+ 无{{ side.title }}未结余额
+
+
+
+
+
+
+
+
+ 总收入
+ ¥{{ moneyText(cash.totalIn) }}
+
+
+ 总支出
+ ¥{{ moneyText(cash.totalOut) }}
+
+
+ 净额
+
+ {{ cash.net < 0 ? '-' : '+' }}¥{{ moneyText(Math.abs(cash.net)) }}
+
+
+
+
+
+
+
+
+ {{ d.date }}
+ 入 ¥{{ moneyText(d.in) }} · 出 ¥{{ moneyText(d.out) }}
+
+
+ {{ d.net < 0 ? '-' : '+' }}¥{{ moneyText(Math.abs(d.net)) }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/stock/batch.vue b/uniapp/pages/erp/stock/batch.vue
new file mode 100644
index 0000000..3f70f83
--- /dev/null
+++ b/uniapp/pages/erp/stock/batch.vue
@@ -0,0 +1,447 @@
+
+
+
+
+
+
+ 清空
+
+
+
+ {{ warehouseName || '全部仓库' }}
+
+
+ 仅有库存
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+ 共 {{ total }} 个批次
+ 本页货值 ¥{{ moneyText(sumAmount) }}
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+ {{ row.product_name }}
+
+
+
+
+ {{ row.batch_no }}
+ {{ row.warehouse_name || '未分仓' }}
+
+ {{ [row.product_code, row.spec].filter(Boolean).join(' · ') }}
+
+
+ 数量
+ {{ qtyText(row.quantity) }} {{ row.unit || '' }}
+
+
+ 成本
+ ¥{{ moneyText(row.cost_price) }}
+
+
+ 货值
+ ¥{{ moneyText(row.amount) }}
+
+
+ 到期
+ {{ row.expire_date || '—' }}
+
+
+
+
+ 看这个批次的收发流水
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/stock/index.vue b/uniapp/pages/erp/stock/index.vue
new file mode 100644
index 0000000..f7d3e7d
--- /dev/null
+++ b/uniapp/pages/erp/stock/index.vue
@@ -0,0 +1,454 @@
+
+
+
+
+
+
+ 清空
+
+
+
+ {{ warehouseName || '全部仓库' }}
+
+
+
+ {{ groupByProduct ? '按商品汇总' : '分仓分批次' }}
+
+
+ 有货
+
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+
+
+ {{ total }} 条结存
+ 库存货值 ¥{{ moneyText(sumAmount) }}
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+ {{ row.product_name }}
+
+
+
+ {{ [row.warehouse_name, row.batch_no ? '批次 ' + row.batch_no : ''].filter(Boolean).join(' · ') || '未分仓' }}
+ · {{ row.category_name }}
+
+
+
+ 数量
+ {{ qtyText(row.quantity) }} {{ row.unit || '' }}
+
+
+ 成本价
+ ¥{{ moneyText(row.cost_price) }}
+
+
+ 货值
+ ¥{{ moneyText(row.amount) }}
+
+
+
+ 安全 {{ qtyText(row.safety_qty) }}
+ 最高 {{ qtyText(row.max_qty) }}
+
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/stock/ledger.vue b/uniapp/pages/erp/stock/ledger.vue
new file mode 100644
index 0000000..5853677
--- /dev/null
+++ b/uniapp/pages/erp/stock/ledger.vue
@@ -0,0 +1,595 @@
+
+
+
+
+
+
+ 清空
+
+
+
+ {{ warehouseName || '全部仓库' }}
+
+
+ {{ productName || '全部商品' }}
+
+
+ {{ docType ? docTypeLabel(docType) : '全部类型' }}
+
+
+
+
+
+
+ {{ dateFrom || '开始日期' }}
+
+
+ 至
+
+
+
+ {{ dateTo || '结束日期' }}
+
+
+ 重置
+
+
+
+
+ 共 {{ total }} 条流水
+ 已筛「{{ filterText }}」×
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+ {{ row.direction > 0 ? '入' : '出' }}
+
+ {{ row.product_name || `商品#${row.product_id}` }}
+
+ {{ row.direction > 0 ? '+' : '-' }}{{ qtyText(row.quantity) }}
+
+
+
+ {{ [row.warehouse_name || '未分仓', row.spec, row.batch_no ? '批次 ' + row.batch_no : '']
+ .filter(Boolean).join(' · ') }}
+
+
+ 结存
+ {{ qtyText(row.before_qty) }}
+
+ {{ qtyText(row.after_qty) }}
+
+ 成本 ¥{{ moneyText(row.unit_cost) }}
+
+
+
+ 操作人 {{ row.operator_name }}
+
+
+ {{ list.length ? '没有更多了' : '' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/pages/erp/stock/warning.vue b/uniapp/pages/erp/stock/warning.vue
new file mode 100644
index 0000000..dc6775a
--- /dev/null
+++ b/uniapp/pages/erp/stock/warning.vue
@@ -0,0 +1,458 @@
+
+
+
+
+ {{ tab.label }}
+ {{ countOf(tab.key) }}
+
+
+
+
+ 正在盘点预警...
+
+
+
+ {{ headText }}
+ 点商品看档案
+
+
+
+
+
+
+
+
+
+ {{ row.product_name }}
+
+
+ {{ [row.product_code, row.spec].filter(Boolean).join(' · ') }}
+
+
+ 现库存
+ {{ qtyText(row.quantity) }} {{ row.unit || '' }}
+
+
+ 在途
+ {{ qtyText(row.in_transit) }}
+
+
+ 近30天销量
+ {{ qtyText(row.sold_30) }}
+
+
+
+
+ 建议采购
+ {{ qtyText(row.suggest_qty) }} {{ row.unit || '' }}
+
+
+ 约 ¥{{ moneyText(row.amount) }}
+
+
+ 去开采购单
+
+
+
+
+
+
+
+
+
+ {{ row.product_name }}
+
+
+
+ {{ [row.category_name, row.warehouse_name, row.batch_no ? '批次 ' + row.batch_no : '']
+ .filter(Boolean).join(' · ') }}
+
+
+
+ 库存
+ {{ qtyText(row.quantity) }} {{ row.unit || '' }}
+
+
+ 安全库存
+ {{ qtyText(row.safety_qty) }}
+
+
+ 最高库存
+ {{ qtyText(row.max_qty) }}
+
+
+ 在途
+ {{ qtyText(row.in_transit) }}
+
+
+ 到期日
+ {{ row.expire_date || '未登记' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uniapp/utils/nav.js b/uniapp/utils/nav.js
index 8166684..9f94b0b 100644
--- a/uniapp/utils/nav.js
+++ b/uniapp/utils/nav.js
@@ -12,6 +12,28 @@ import { createAppTicket } from '@/api/auth.js'
/** 移动端已有原生页面的功能。匹配顺序即优先级。 */
const NATIVE_RULES = [
+ // 进销存(移动端原生):按菜单 path 精确分流,列表所需的 doc_type / bill_type 直接挂在路由 query 上
+ { pattern: /\/apps\/erp\/(dashboard|workbench)/i, route: '/pages/erp/dashboard/index' },
+ { pattern: /\/apps\/erp\/basic\/warehouse/i, route: '/pages/erp/basic/warehouse' },
+ { pattern: /\/apps\/erp\/basic\/product/i, route: '/pages/erp/basic/product' },
+ { pattern: /\/apps\/erp\/suppliers/i, route: '/pages/erp/partner/index?partner_type=supplier' },
+ { pattern: /\/apps\/erp\/customers/i, route: '/pages/erp/partner/index?partner_type=customer' },
+ { pattern: /\/apps\/erp\/purchase\/order/i, route: '/pages/erp/document/index?doc_type=purchase_order' },
+ { pattern: /\/apps\/erp\/purchase\/inbound/i, route: '/pages/erp/document/index?doc_type=purchase_in' },
+ { pattern: /\/apps\/erp\/purchase\/return/i, route: '/pages/erp/document/index?doc_type=purchase_return' },
+ { pattern: /\/apps\/erp\/sales\/order/i, route: '/pages/erp/document/index?doc_type=sales_order' },
+ { pattern: /\/apps\/erp\/sales\/outbound/i, route: '/pages/erp/document/index?doc_type=sales_out' },
+ { pattern: /\/apps\/erp\/sales\/return/i, route: '/pages/erp/document/index?doc_type=sales_return' },
+ { pattern: /\/apps\/erp\/stock\/transfer/i, route: '/pages/erp/document/index?doc_type=transfer' },
+ { pattern: /\/apps\/erp\/stock\/taking/i, route: '/pages/erp/document/index?doc_type=stock_take' },
+ { pattern: /\/apps\/erp\/stock\/inbound/i, route: '/pages/erp/document/index?doc_type=other_in' },
+ { pattern: /\/apps\/erp\/stock\/outbound/i, route: '/pages/erp/document/index?doc_type=other_out' },
+ { pattern: /\/apps\/erp\/stock\/query/i, route: '/pages/erp/stock/index' },
+ { pattern: /\/apps\/erp\/stock\/warning/i, route: '/pages/erp/stock/warning' },
+ { pattern: /\/apps\/erp\/finance\/receivable/i, route: '/pages/erp/finance/bill?bill_type=1' },
+ { pattern: /\/apps\/erp\/finance\/payable/i, route: '/pages/erp/finance/bill?bill_type=2' },
+ { pattern: /\/apps\/erp\/finance\/payment/i, route: '/pages/erp/finance/fund' },
+ { pattern: /\/apps\/erp\/report/i, route: '/pages/erp/report/index' },
{ pattern: /记事本|笔记|便签|notebook/i, route: '/pages/tools/notepad/index' },
// 「日程提醒」与「工作日程」是两个独立功能:提醒在前,命中"提醒"后即不再落到日历页
{ pattern: /提醒|reminder/i, route: '/pages/tools/schedule/reminder' },