From 2e49fa1449e43c80b39b1f9340b81c5a6ddeeb69 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 09:46:09 +0800 Subject: [PATCH 01/16] feat(dashboard): add semantic icons to KPI cards Add FontAwesome icons to all 7 KPI metric cards in buyer and supplier dashboards for better visual semantics: BuyerDashboard: - fa-check-circle (completed inquiries) - fa-clock (pending inquiries) - fa-chart-pie (quote timeliness rate) SupplierDashboard: - fa-file-invoice (total quotes) - fa-pen-to-square (pending quotes) - fa-trophy (won bids) - fa-bullseye (win rate) --- web/src/views/pisadmin/dashboard/BuyerDashboard.vue | 9 +++++++++ web/src/views/pisadmin/dashboard/SupplierDashboard.vue | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index ea88f8d..519a326 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -9,15 +9,18 @@
+
已完成询价单总数
{{ kpi.total_inquiries.toLocaleString() }}
较上月 +12%
+
进行中询价单
{{ kpi.pending_inquiries }}
+
供应商报价及时率
{{ kpi.quote_timely_rate }}%
持平
@@ -317,6 +320,12 @@ watch(trend, () => { } } +.kpi-icon { + font-size: 28px; + margin-bottom: 12px; + opacity: 0.8; +} + .kpi-title { font-size: 18px; color: var(--text-secondary); diff --git a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue index f47fa12..ae0beea 100644 --- a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue +++ b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue @@ -9,21 +9,25 @@
+
报价单总数
{{ kpi.total_quotes.toLocaleString() }}
较上月 +8%
+
待报价
{{ kpi.pending_quotes }}
等待报价
+
已中标
{{ kpi.won_quotes }}
中标成功
+
中标率
{{ kpi.conversion_rate }}%
持平
@@ -345,6 +349,12 @@ watch(trend, () => { } } +.kpi-icon { + font-size: 28px; + margin-bottom: 12px; + opacity: 0.8; +} + .kpi-title { font-size: 18px; color: var(--text-secondary); From 4d61d34c56ecb8e70057fc091f78c8cc2794834d Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 09:51:28 +0800 Subject: [PATCH 02/16] fix(dashboard): use fa-clock-o for FontAwesome 4.7 compatibility fa-clock (solid) only exists in FA5+, FA4.7 uses fa-clock-o (outline) --- web/src/views/pisadmin/dashboard/BuyerDashboard.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index 519a326..575ec21 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -15,7 +15,7 @@
较上月 +12%
-
+
进行中询价单
{{ kpi.pending_inquiries }}
From 829b030828441582637bbfa3a692c4585ebf3b74 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 09:52:28 +0800 Subject: [PATCH 03/16] fix(dashboard): replace non-existent FA4.7 icons with valid alternatives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fa-chart-pie → fa-pie-chart - fa-file-invoice → fa-file-text-o - fa-pen-to-square → fa-pencil-square-o --- web/src/views/pisadmin/dashboard/BuyerDashboard.vue | 2 +- web/src/views/pisadmin/dashboard/SupplierDashboard.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index 575ec21..2927eb3 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -20,7 +20,7 @@
{{ kpi.pending_inquiries }}
-
+
供应商报价及时率
{{ kpi.quote_timely_rate }}%
持平
diff --git a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue index ae0beea..f5bf7ec 100644 --- a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue +++ b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue @@ -9,13 +9,13 @@
-
+
报价单总数
{{ kpi.total_quotes.toLocaleString() }}
较上月 +8%
-
+
待报价
{{ kpi.pending_quotes }}
等待报价
From 22f8caa6d4882cac499dbb5aec7d7ac9b6a357d8 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 09:57:48 +0800 Subject: [PATCH 04/16] fix(dashboard): use day field for 30-day trend chart x-axis Change from d.month to d.day to properly display daily data instead of monthly aggregation on the 30-day trend chart. --- web/src/views/pisadmin/dashboard/BuyerDashboard.vue | 2 +- web/src/views/pisadmin/dashboard/SupplierDashboard.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index 2927eb3..2fa7889 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -185,7 +185,7 @@ function initChart() { let dataReceive: number[] = []; if (trend.value && trend.value.length > 0) { - labels = trend.value.map((d) => d.month); + labels = trend.value.map((d) => d.day); dataPublish = trend.value.map((d) => d.count || 0); // 议价完成数据暂无,填充0 dataReceive = trend.value.map(() => 0); diff --git a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue index f5bf7ec..598c7df 100644 --- a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue +++ b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue @@ -216,7 +216,7 @@ function initChart() { let wonData: number[] = []; if (trend.value && trend.value.length > 0) { - labels = trend.value.map((d) => d.month); + labels = trend.value.map((d) => d.day); quotesData = trend.value.map((d) => d.quotes || 0); wonData = trend.value.map((d) => d.won || 0); } else { From 6123b6ef17272c2b96f6e76398fe4c97afae70aa Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 09:59:49 +0800 Subject: [PATCH 05/16] fix(dashboard): render all days of current month in trend chart Generate all calendar days of current month and map backend data to each day. Missing days display as 0 instead of being omitted from the x-axis. --- .../pisadmin/dashboard/BuyerDashboard.vue | 22 +++++++++++++++---- .../pisadmin/dashboard/SupplierDashboard.vue | 22 ++++++++++++++++--- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index 2fa7889..1df8c25 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -185,10 +185,24 @@ function initChart() { let dataReceive: number[] = []; if (trend.value && trend.value.length > 0) { - labels = trend.value.map((d) => d.day); - dataPublish = trend.value.map((d) => d.count || 0); - // 议价完成数据暂无,填充0 - dataReceive = trend.value.map(() => 0); + // 生成当前月的所有日期,确保每天都能显示 + const now = new Date(); + const year = now.getFullYear(); + const month = now.getMonth(); + const daysInMonth = new Date(year, month + 1, 0).getDate(); + + // 创建数据映射 + const dataMap = new Map(trend.value.map((d) => [d.day, d.count || 0])); + + // 填充所有日期,缺失的填0 + labels = []; + dataPublish = []; + dataReceive = []; + for (let day = 1; day <= daysInMonth; day++) { + labels.push(`${month + 1}/${day}`); + dataPublish.push(dataMap.get(day) || 0); + dataReceive.push(0); // 议价完成数据暂无 + } } else { // 模拟数据 const today = new Date(); diff --git a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue index 598c7df..6fc7a2d 100644 --- a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue +++ b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue @@ -216,9 +216,25 @@ function initChart() { let wonData: number[] = []; if (trend.value && trend.value.length > 0) { - labels = trend.value.map((d) => d.day); - quotesData = trend.value.map((d) => d.quotes || 0); - wonData = trend.value.map((d) => d.won || 0); + // 生成当前月的所有日期,确保每天都能显示 + const now = new Date(); + const year = now.getFullYear(); + const month = now.getMonth(); + const daysInMonth = new Date(year, month + 1, 0).getDate(); + + // 创建数据映射 + const quotesMap = new Map(trend.value.map((d) => [d.day, d.quotes || 0])); + const wonMap = new Map(trend.value.map((d) => [d.day, d.won || 0])); + + // 填充所有日期,缺失的填0 + labels = []; + quotesData = []; + wonData = []; + for (let day = 1; day <= daysInMonth; day++) { + labels.push(`${month + 1}/${day}`); + quotesData.push(quotesMap.get(day) || 0); + wonData.push(wonMap.get(day) || 0); + } } else { // 模拟数据 const today = new Date(); From 41ad7b67c27e735d6f1cd4237fece7b397d369a6 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:06:07 +0800 Subject: [PATCH 06/16] feat(dashboard): enhance todo task rendering with procurement method and deadline styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BuyerDashboard: - Added procurement method column (询价/招标 badge) - Added deadline/remaining time with color coding: - <24h: red bold - 24-48h: yellow bold - expired: strikethrough - 询价 uses quote_deadline, 招标 uses bid_end_time SupplierDashboard: - Added procurement method badge - Time display varies by method: - 询价: shows quote deadline + remaining time - 招标: shows bid time range (bid_start ~ bid_end) + remaining time - Same deadline color coding as buyer --- .../pisadmin/dashboard/BuyerDashboard.vue | 109 ++++++++++++++++-- .../pisadmin/dashboard/SupplierDashboard.vue | 93 ++++++++++++--- 2 files changed, 179 insertions(+), 23 deletions(-) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index 1df8c25..74486db 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -43,20 +43,29 @@ 询价单号 + 采购方式 物料名称 当前状态 - 截止时间 + 截止时间 / 剩余时间 操作 {{ task.inquiry_no }} + + {{ getMethodText(task.method) }} + {{ task.title }} {{ task.status }} - {{ task.created_at ? formatDate(task.created_at) : '-' }} + +
+ {{ getDeadlineText(task) }} + {{ getRemainingTimeText(task) }} +
+
@@ -177,16 +185,30 @@ function getQuoteStatusText(status: number) { return '待报价'; } -function isUrgent(deadline: string) { - if (!deadline) return false; +function getMethodClass(method: string) { + if (method === '招标') return 'method-tender'; + return 'method-inquiry'; +} + +function getMethodText(method: string) { + return method || '询价'; +} + +function getDeadlineClass(quote: any): string { + const deadline = quote.method === '招标' ? quote.bid_end_time : quote.quote_deadline; + if (!deadline) return ''; const now = new Date(); const deadlineDate = new Date(deadline); const diffHours = (deadlineDate.getTime() - now.getTime()) / (1000 * 60 * 60); - return diffHours < 24; + if (diffHours < 0) return 'deadline-expired'; + if (diffHours < 24) return 'deadline-urgent'; + if (diffHours < 48) return 'deadline-warning'; + return ''; } -function getRemainingTime(deadline: string) { - if (!deadline) return '-'; +function getRemainingTimeText(quote: any): string { + const deadline = quote.method === '招标' ? quote.bid_end_time : quote.quote_deadline; + if (!deadline) return ''; const now = new Date(); const deadlineDate = new Date(deadline); const diffMs = deadlineDate.getTime() - now.getTime(); @@ -195,9 +217,9 @@ function getRemainingTime(deadline: string) { const days = Math.floor(diffHours / 24); const hours = diffHours % 24; if (days > 0) { - return `${days}天${hours}小时`; + return `剩余 ${days}天${hours}小时`; } - return `${hours}小时`; + return `剩余 ${hours}小时`; } function formatDate(date: string) { @@ -206,6 +228,14 @@ function formatDate(date: string) { return `${d.getMonth() + 1}/${d.getDate()} ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`; } +function formatDateRange(start: string, end: string) { + if (!start || !end) return '-'; + const s = new Date(start); + const e = new Date(end); + const formatShort = (d: Date) => `${d.getMonth() + 1}/${d.getDate()}`; + return `${formatShort(s)}-${formatShort(e)}`; +} + function initChart() { if (!chartRef.value) return; const chart = echarts.getInstanceByDom(chartRef.value) || echarts.init(chartRef.value); @@ -578,6 +608,39 @@ tr:last-child td { border: 1px solid #d1d5db; } +/* 采购方式badge */ +.method-badge { + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + display: inline-block; +} +.method-inquiry { + background-color: #f0fdf4; + color: #16a34a; + border: 1px solid #bbf7d0; +} +.method-tender { + background-color: #faf5ff; + color: #9333ea; + border: 1px solid #e9d5ff; +} + +/* 截止时间样式 */ +.deadline-urgent { + color: var(--danger) !important; + font-weight: 700; +} +.deadline-warning { + color: var(--warning) !important; + font-weight: 700; +} +.deadline-expired { + color: #9ca3af !important; + text-decoration: line-through; +} + .quote-countdown { display: flex; align-items: center; From 9ebda5008366026d8b1aa5a0dd3b032854a87dfe Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:07:03 +0800 Subject: [PATCH 07/16] docs: update README with project architecture and startup scripts - Replace dvadmin3 framework intro with PIS project content - Add project architecture section (tech stack, directory structure, modules) - Add startup scripts documentation (start-backend.sh, start-frontend.sh, etc.) - Update all 4 README files (EN/ZH for root and web/) --- README.md | 275 ++++++++++++++++++++------------------ README.zh.md | 341 ++++++++++++++++++++--------------------------- web/README.en.md | 271 ++++++++++++++++++++----------------- web/README.md | 305 ++++++++++++++++++++---------------------- 4 files changed, 579 insertions(+), 613 deletions(-) diff --git a/README.md b/README.md index 684b078..6bf161b 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,71 @@ -# Django-Vue3-Admin +# PIS - Procurement & Supplier Management System -[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/huge-dream/django-vue3-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/huge-dream/django-vue3-admin/badge/star.svg?theme=dark)](https://gitee.com/huge-dream/django-vue3-admin) +## Project Overview -[preview](https://demo.dvadmin.com) | [Official website document](https://www.django-vue-admin.com) | [qq group](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [community](https://bbs.django-vue-admin.com) | [plugins market](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin) +A Django-Vue3 based procurement and supplier management system with RBAC permission control. -💡 **「About」** +## Project Architecture 🏗️ -It is a completely open-source rapid development platform, provided free for personal use and authorized for group use. -Django-Vue3-Admin is a comprehensive basic development platform based on the RBAC (Role-Based Access Control) model for permission control, with column-level granularity. It follows a frontend-backend separation architecture, with Django and Django Rest Framework used for the backend, and Vue3, Composition API, TypeScript, Vite, and Element Plus used for the frontend. +### Tech Stack +| Layer | Technology | +|-------|------------| +| **Backend** | Django 4.2.14 + Django REST Framework 3.15.2 | +| **Frontend** | Vue 3 + TypeScript + Vite 5.4 + Element Plus 2.8 + FastCRUD | +| **Database** | MySQL 8.0 | +| **Cache/Queue** | Redis + Celery | +| **Real-time** | Django Channels (WebSocket) | +| **Auth** | JWT via SimpleJWT | -## framework introduction +### Directory Structure -💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) Is a set of all open source rapid development platform, no reservation for individuals and enterprises free use. +``` +pis/ +├── backend/ +│ ├── application/ # Django project settings +│ │ ├── settings.py # Main settings (imports from conf/env.py) +│ │ ├── urls.py # Root URL routing +│ │ ├── asgi.py # ASGI config (WebSocket support) +│ │ ├── wsgi.py # WSGI config +│ │ └── celery.py # Celery configuration +│ ├── apps/ +│ │ ├── pisadmin/ # Custom procurement app +│ │ │ ├── basicinfo/ # Company, currency, supplier, unit, system no +│ │ │ └── miscprocurement/ # RFQ, price templates, cost sections +│ │ └── pissupplier/ # Supplier quotation module +│ ├── dvadmin/ # Core RBAC system +│ │ └── system/ # Users, roles, menus, departments, logs +│ └── conf/ +│ └── env.py # Environment configuration (DB, Redis credentials) +├── web/ # Vue 3 frontend +│ └── src/ +│ ├── views/pisadmin/ # Custom procurement pages +│ ├── views/pissupplier/# Supplier pages +│ └── views/system/ # Admin system pages +└── scripts/ # Service management scripts + ├── start-backend.sh # Start backend service + ├── start-frontend.sh # Start frontend service + ├── start-celery.sh # Start Celery worker/beat + ├── stop-all.sh # Stop all services + └── check-status.sh # Check service status +``` -* 🧑‍🤝‍🧑Front-end adoption Vue3+TS+pinia+fastcrud。 -* 👭The backend uses the Python language Django framework as well as the powerful[Django REST Framework](https://pypi.org/project/djangorestframework)。 -* 👫Permission authentication use[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),Supports the multi-terminal authentication system. -* 👬Support loading dynamic permission menu, multi - way easy permission control. -* 👬Enhanced Column Permission Control, with granularity down to each column. -* 💏Special thanks: [vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/). -* 💡Special thanks:[jetbrains](https://www.jetbrains.com/) To provide a free IntelliJ IDEA license for this open source project. +### Module Description -## Online experience +| Module | Description | +|--------|-------------| +| `pisadmin/basicinfo` | Basic info management: companies, currencies, suppliers, units, system numbering rules | +| `pisadmin/miscprocurement` | Miscellaneous procurement: RFQ, price templates, cost section builder | +| `pissupplier` | Supplier quotation module | +| `dvadmin/system` | Core RBAC system: users, roles, menus, departments, operation logs | -👩‍👧‍👦👩‍👧‍👦 demo address:[https://demo.dvadmin.com](https://demo.dvadmin.com) +### Configuration -* demo account:superadmin +Backend settings are split: +- `application/settings.py` - Django defaults +- `conf/env.py` - Environment-specific config (database, Redis, secrets) -* demo password:admin123456 - -👩‍👦‍👦docs:[https://django-vue-admin.com](https://django-vue-admin.com) - -## communication - -* Communication community:[click here](https://bbs.django-vue-admin.com)👩‍👦‍👦 - -* plugins market:[click here](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 - -## source code url: - -gitee(Main push):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - -github:[https://github.com/huge-dream/django-vue3-admin](https://github.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - -## core function - -1. 👨‍⚕️Menu Management: Configure system menus, operation permissions, button permission flags, backend interface permissions, etc. -2. 🧑‍⚕️Department Management: Configure system organizational structure (company, department, role). -3. 👩‍⚕️Role Management: Role menu permission assignment, data permission assignment, set role-based data scope permissions by department. -4. 🧑‍🎓Button Permission Control: Authorize role-specific button permissions and interface permissions, enabling authorization of data scope for each interface. -5. 🧑‍🎓Field Column Permission Control: Authorize page field display permissions, specifically for the display permissions of a certain column. -6. 👨‍🎓User Management: Users are system operators, and this function is mainly used for system user configuration. -7. 👬API Whitelist: Configure interfaces that do not require permission verification. -8. 🧑‍🔧Dictionary Management: Maintain frequently used and relatively fixed data in the system. -9. 🧑‍🔧Region Management: Manage provinces, cities, counties, and districts. -10. 📁File Management: Unified management of all files, images, etc., on the platform. -11. 🗓️Operation Logs: Record and query logs for normal system operations and exceptional system information. -12. 🔌[Plugin Market](https://bbs.django-vue-admin.com/plugMarket.html): Applications and plugins developed based on the Django-Vue-Admin framework. - -## plugins market 🔌 - -Updating... - -## Repository Branch Explanation 💈 -Main Branch: master (stable version) -Development Branch: develop - -## before start project you need: +## Prerequisites ~~~ Python >= 3.11.0 (Minimum version 3.9+) @@ -76,100 +74,119 @@ Mysql >= 8.0 (Optional, default database: SQLite3, supports 5.7+, recommended ve Redis (Optional, latest version) ~~~ -## frontend♝ +## Quick Start + +### Backend + +#### Method 1: Script Startup (Recommended) ```bash -# clone code -git clone https://gitee.com/huge-dream/django-vue3-admin.git +# Go to project root +cd D:\AVC-PROJECT\pis -# enter code dir +# Start backend service (development mode) +./scripts/start-backend.sh + +# Start frontend service +./scripts/start-frontend.sh + +# Start Celery async tasks (optional) +./scripts/start-celery.sh +``` + +**Script Description:** + +| Script | Description | +|--------|-------------| +| `start-backend.sh` | Start backend service (auto-creates PID and log files) | +| `start-frontend.sh` | Start frontend dev server | +| `start-celery.sh` | Start Celery Worker and Beat | +| `stop-all.sh` | Stop all services | +| `stop-celery.sh` | Stop Celery only | +| `check-status.sh` | Check all service status | +| `view-logs.sh` | View service logs | + +**View Logs:** +```bash +./scripts/view-logs.sh all # View all logs +./scripts/view-logs.sh backend # Backend logs only +./scripts/view-logs.sh celery # Celery logs only +``` + +**Stop Services:** +```bash +./scripts/stop-all.sh # Stop all services +./scripts/stop-celery.sh # Stop Celery only +``` + +#### Method 2: Manual Startup + +```bash +# 1. Go to backend directory +cd backend + +# 2. Copy and configure environment file +cp ./conf/env.example.py ./conf/env.py +# Edit env.py to configure database information + +# 3. Install dependencies +pip3 install -r requirements.txt + +# 4. Run migrations +python3 manage.py makemigrations +python3 manage.py migrate + +# 5. Initialize data +python3 manage.py init +python3 manage.py init_area + +# 6. Start backend +uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 +``` + +### Frontend + +```bash +# Go to web directory cd web -# install dependence +# Install dependencies npm install yarn yarn install --registry=https://registry.npm.taobao.org -# Start service +# Start development server yarn run dev # Visit http://localhost:8080 in your browser -# Parameters such as boot port can be configured in the #.env.development file -# Build the production environment -# yarn run build +# Parameters such as boot port can be configured in the .env.development file + +# Build for production +yarn run build ``` -## backend💈 +### Access -~~~bash -1. enter code dir cd backend -2. copy ./conf/env.example.py to ./conf dir,rename as env.py -3. in env.py configure database information - mysql database recommended version: 8.0 - mysql database character set: utf8mb4 -4. install pip dependence - pip3 install -r requirements.txt -5. Execute the migration command: - python3 manage.py makemigrations - python3 manage.py migrate -6. Initialization data - python3 manage.py init -7. Initialize provincial, municipal and county data: - python3 manage.py init_area -8. start backend - python3 manage.py runserver 0.0.0.0:8000 -or uvicorn : - uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 -~~~ +- Frontend URL: http://localhost:8080 +- Backend API: http://localhost:8080/api +- Default account: `superadmin` / `admin123456` -### visit backend swagger +### Docker -* visit url:[http://localhost:8080](http://localhost:8080) (The default address is this one. If you want to change it, follow the configuration file) -* account:`superadmin` password:`admin123456` - -### docker-compose - -~~~shell +```shell docker-compose up -d # Initialize backend data (first execution only) docker exec -ti dvadmin3-django bash -python manage.py makemigrations +python manage.py makemigrations python manage.py migrate python manage.py init_area python manage.py init exit -frontend url:http://127.0.0.1:8080 -backend url:http://127.0.0.1:8080/api -# Change 127.0.0.1 to your own public ip address on the server -account:`superadmin` password:`admin123456` - -# docker-compose stop +# Stop services docker-compose down -# docker-compose restart + +# Restart services docker-compose restart -# docker-compose on start build + +# Rebuild and start docker-compose up -d --build -~~~ - -## Demo screenshot✅ - -![image-01](https://foruda.gitee.com/images/1701348994587355489/1bc749e7_5074988.png) - -![image-02](https://foruda.gitee.com/images/1701349037811908960/80d361db_5074988.png) - -![image-03](https://foruda.gitee.com/images/1701349224478845203/954f0a7b_5074988.png) - -![image-04](https://foruda.gitee.com/images/1701349248928658877/64926724_5074988.png) - -![image-05](https://foruda.gitee.com/images/1701349259068943299/1306ba40_5074988.png) - -![image-06](https://foruda.gitee.com/images/1701349294894429495/e3b3a8cf_5074988.png) - -![image-07](https://foruda.gitee.com/images/1701350432536247561/3b26685e_5074988.png) - -![image-08](https://foruda.gitee.com/images/1701350455264771992/b364c57f_5074988.png) - -![image-09](https://foruda.gitee.com/images/1701350479266000753/e4e4f7c5_5074988.png) - -![image-10](https://foruda.gitee.com/images/1701350501421625746/f8dd215e_5074988.png) - - +``` diff --git a/README.zh.md b/README.zh.md index 7309785..a2f82f6 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,118 +1,72 @@ -# Django-Vue3-Admin +# PIS - 采购与供应商管理系统 -[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/liqianglog/django-vue-admin/badge/star.svg?theme=dark)](https://gitee.com/liqianglog/django-vue-admin) +## 项目简介 -[预 览](https://demo.dvadmin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin) +基于 Django-Vue3 的采购与供应商管理系统,采用 RBAC 权限控制。 +## 项目架构 🏗️ +### 技术栈 -💡 **「关于」** +| 层级 | 技术 | +|------|------| +| **后端** | Django 4.2.14 + Django REST Framework 3.15.2 | +| **前端** | Vue 3 + TypeScript + Vite 5.4 + Element Plus 2.8 + FastCRUD | +| **数据库** | MySQL 8.0 | +| **缓存/队列** | Redis + Celery | +| **实时通信** | Django Channels (WebSocket) | +| **认证** | JWT via SimpleJWT | -我们是一群热爱代码的青年,在这个炙热的时代下,我们希望静下心来通过Code带来一点我们的色彩和颜色。 +### 目录结构 -因为热爱,所以拥抱未来! +``` +pis/ +├── backend/ +│ ├── application/ # Django 项目配置 +│ │ ├── settings.py # 主配置(从 conf/env.py 导入配置) +│ │ ├── urls.py # 根路由 +│ │ ├── asgi.py # ASGI 配置(支持 WebSocket) +│ │ ├── wsgi.py # WSGI 配置 +│ │ └── celery.py # Celery 配置 +│ ├── apps/ +│ │ ├── pisadmin/ # 采购管理应用 +│ │ │ ├── basicinfo/ # 基础信息:公司、币种、供应商、单位、编码规则 +│ │ │ └── miscprocurement/ # 杂项采购:询价、价格模板、成本节 +│ │ └── pissupplier/ # 供应商报价模块 +│ ├── dvadmin/ # 核心 RBAC 系统 +│ │ └── system/ # 用户、角色、菜单、部门、日志 +│ └── conf/ +│ └── env.py # 环境配置(数据库、Redis 凭证) +├── web/ # Vue 3 前端 +│ └── src/ +│ ├── views/pisadmin/ # 采购管理页面 +│ ├── views/pissupplier/# 供应商页面 +│ └── views/system/ # 系统管理页面 +└── scripts/ # 服务管理脚本 + ├── start-backend.sh # 启动后端服务 + ├── start-frontend.sh # 启动前端服务 + ├── start-celery.sh # 启动 Celery worker/beat + ├── stop-all.sh # 停止所有服务 + └── check-status.sh # 查看服务状态 +``` +### 模块说明 -## 平台简介 +| 模块 | 说明 | +|------|------| +| `pisadmin/basicinfo` | 基础信息管理:公司、币种、供应商、单位、系统编号规则 | +| `pisadmin/miscprocurement` | 杂项采购:询价(RFQ)、价格模板、成本节构建器 | +| `pissupplier` | 供应商报价模块 | +| `dvadmin/system` | 核心 RBAC 系统:用户、角色、菜单、部门、操作日志 | -💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) 是一套全部开源的快速开发平台,毫无保留给个人免费使用、团体授权使用。 - django-vue3-admin 基于RBAC模型的权限控制的一整套基础开发平台,权限粒度达到列级别,前后端分离,后端采用django + django-rest-framework,前端采用基于 vue3 + CompositionAPI + typescript + vite + element plus - - - - -* 🧑‍🤝‍🧑前端采用 Vue3+TS+pinia+fastcrud(感谢[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)) -* 👭后端采用 Python 语言 Django 框架以及强大的 [Django REST Framework](https://pypi.org/project/djangorestframework)。 -* 👫权限认证使用[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),支持多终端认证系统。 -* 👬支持加载动态权限菜单,多方式轻松权限控制。 -* 👬全新的列权限管控,粒度细化到每一列。 -* 💏特别鸣谢:[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)。 -* 💡特别感谢[jetbrains](https://www.jetbrains.com/) 为本开源项目提供免费的 IntelliJ IDEA 授权。 - -#### 🏭 环境支持 - -| Edge | Firefox | Chrome | Safari | -| --------- | ------------ | ----------- | ----------- | -| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 | - -> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。 - - - -## 在线体验 - -👩‍👧‍👦演示地址:[https://demo.dvadmin.com](https://demo.dvadmin.com) - -- 账号:superadmin - -- 密码:admin123456 - -👩‍👦‍👦文档地址:[DVAdmin官网](https://www.django-vue-admin.com) - - - -## 交流 - -- 交流社区:[戳我](https://bbs.django-vue-admin.com)👩‍👦‍👦 -- 插件市场:[戳我](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 -- django-vue-admin交流01群(已满):812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi) -- django-vue-admin交流02群(已满):687252418 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=4jJN4IjWGfxJ8YJXbb_gTsuWjR34WLdc&jump_from=webapi) -- django-vue-admin交流03群(已满):442108213 [点击链接加入群聊](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=wsm5oSz3K8dElBYUDtLTcQSEPhINFkl8&authKey=M6sbER0z59ZakgBr5erFeZyFZU15CI52bErNZa%2FxSvvGIuVAbY0N5866v89hm%2FK4&noverify=0&group_code=442108213) -- django-vue-admin交流04群:442108213 [点击链接加入群聊](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=wsm5oSz3K8dElBYUDtLTcQSEPhINFkl8&authKey=M6sbER0z59ZakgBr5erFeZyFZU15CI52bErNZa%2FxSvvGIuVAbY0N5866v89hm%2FK4&noverify=0&group_code=442108213) - - - -## 给框架点赞 - -
- - -
- - -## 源码地址 - -gitee地址(主推):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - -github地址:[https://github.com/huge-dream/django-vue3-admin](https://github.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - - -## 内置功能 - -1. 👨‍⚕️菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。 -2. 🧑‍⚕️部门管理:配置系统组织机构(公司、部门、角色)。 -3. 👩‍⚕️角色管理:角色菜单权限分配、数据权限分配、设置角色按部门进行数据范围权限划分。 -4. 🧑‍🎓按钮权限控制:授权角色的按钮权限和接口权限,可做到每一个接口都能授权数据范围。 -5. 🧑‍🎓字段列权限控制:授权页面的字段显示权限,具体到某一列的显示权限。 -7. 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。 -8. 👬接口白名单:配置不需要进行权限校验的接口。 -9. 🧑‍🔧字典管理:对系统中经常使用的一些较为固定的数据进行维护。 -10. 🧑‍🔧地区管理:对省市县区域进行管理。 -11. 📁附件管理:对平台上所有文件、图片等进行统一管理。 -12. 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 -13. 🔌[插件市场 ](https://bbs.django-vue-admin.com/plugMarket.html):基于Django-Vue-Admin框架开发的应用和插件。 - -## 插件市场 🔌 -1. #### [dvadmin3-folw 后台审批流插件](https://bbs.django-vue-admin.com/plugMarket/139.html) - -2. #### [dvadmin3 celery插件前端](https://bbs.django-vue-admin.com/plugMarket/134.html) - -3. #### [dvadmin3 celery插件后端](https://bbs.django-vue-admin.com/plugMarket/133.html) - -4. #### [dvadmin3-build插件](https://bbs.django-vue-admin.com/plugMarket/136.html) - -5. #### [dvadmin3-uniapp](https://e.coding.net/dvadmin-private/code/dvadmin3-uniapp-app.git) - -6. #### dvadmin3-folw-uniapp 审批(开发中,近期上线) - - - -## 仓库分支说明 💈 -主分支:master(稳定版本) -开发分支:develop +### 配置说明 +后端配置分离: +- `application/settings.py` - Django 默认配置 +- `conf/env.py` - 环境特定配置(数据库、Redis、密钥) ## 准备工作 + ~~~ Python >= 3.11.0 (最低3.9+版本) nodejs >= 16.0 @@ -120,126 +74,119 @@ Mysql >= 8.0 (可选,默认数据库sqlite3,支持5.7+,推荐8.0版本) Redis (可选,最新版) ~~~ -## 前端♝ +## 快速启动 + +### 后端 + +#### 方式一:脚本启动(推荐) ```bash -# 克隆项目 -git clone https://gitee.com/huge-dream/django-vue3-admin.git +# 进入项目根目录 +cd D:\AVC-PROJECT\pis -# 进入项目目录 +# 启动后端服务(开发模式) +./scripts/start-backend.sh + +# 启动前端服务 +./scripts/start-frontend.sh + +# 启动 Celery 异步任务(可选) +./scripts/start-celery.sh +``` + +**脚本功能说明:** + +| 脚本 | 说明 | +|------|------| +| `start-backend.sh` | 启动后端服务(自动创建 PID 和日志文件)| +| `start-frontend.sh` | 启动前端开发服务器 | +| `start-celery.sh` | 启动 Celery Worker 和 Beat | +| `stop-all.sh` | 停止所有服务 | +| `stop-celery.sh` | 仅停止 Celery 服务 | +| `check-status.sh` | 查看所有服务状态 | +| `view-logs.sh` | 查看服务日志 | + +**查看日志:** +```bash +./scripts/view-logs.sh all # 查看所有日志 +./scripts/view-logs.sh backend # 仅看后端日志 +./scripts/view-logs.sh celery # 仅看 Celery 日志 +``` + +**停止服务:** +```bash +./scripts/stop-all.sh # 停止所有服务 +./scripts/stop-celery.sh # 仅停止 Celery +``` + +#### 方式二:手动启动 + +```bash +# 1. 进入后端目录 +cd backend + +# 2. 复制并配置环境文件 +cp ./conf/env.example.py ./conf/env.py +# 编辑 env.py 配置数据库信息 + +# 3. 安装依赖 +pip3 install -r requirements.txt + +# 4. 执行数据库迁移 +python3 manage.py makemigrations +python3 manage.py migrate + +# 5. 初始化数据 +python3 manage.py init +python3 manage.py init_area + +# 6. 启动后端 +uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 +``` + +### 前端 + +```bash +# 进入前端目录 cd web # 安装依赖 npm install yarn yarn install --registry=https://registry.npmmirror.com -# 启动服务 -yarn build +# 启动开发服务器 +yarn run dev # 浏览器访问 http://localhost:8080 # .env.development 文件中可配置启动端口等参数 + # 构建生产环境 -# yarn run build +yarn run build ``` - - -## 后端💈 - -~~~bash -1. 进入项目目录 cd backend -2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py -3. 在 env.py 中配置数据库信息 - mysql数据库版本建议:8.0 - mysql数据库字符集:utf8mb4 -4. 安装依赖环境 - pip3 install -r requirements.txt -5. 执行迁移命令: - python3 manage.py makemigrations - python3 manage.py migrate -6. 初始化数据 - python3 manage.py init -7. 初始化省市县数据: - python3 manage.py init_area -8. 启动项目 - python3 manage.py runserver 0.0.0.0:8000 -或使用 uvicorn : - uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 -~~~ -## 开发建议 -前后端backend与web各自单独一个窗口打开进行开发 - ### 访问项目 -- 访问地址:[http://localhost:8080](http://localhost:8080) (默认为此地址,如有修改请按照配置文件) -- 账号:`superadmin` 密码:`admin123456` +- 前端地址:http://localhost:8080 +- 后端接口:http://localhost:8080/api +- 默认账号:`superadmin` / `admin123456` +### Docker 部署 - - - -### docker-compose 运行 - -~~~shell -# 先安装docker-compose (自行百度安装),执行此命令等待安装,如有使用celery插件请打开docker-compose.yml中celery 部分注释 +```shell docker-compose up -d -# 初始化后端数据(第一次执行即可) +# 初始化后端数据(第一次执行即可) docker exec -ti dvadmin3-django bash -python manage.py makemigrations +python manage.py makemigrations python manage.py migrate python manage.py init_area python manage.py init exit -前端地址:http://127.0.0.1:8080 -后端地址:http://127.0.0.1:8080/api -# 在服务器上请把127.0.0.1 换成自己公网ip -账号:superadmin 密码:admin123456 - -# docker-compose 停止 +# 停止服务 docker-compose down -# docker-compose 重启 + +# 重启服务 docker-compose restart -# docker-compose 启动时重新进行 build + +# 重新构建并启动 docker-compose up -d --build -~~~ - - - -## 演示图✅ - -![image-01](https://foruda.gitee.com/images/1701348994587355489/1bc749e7_5074988.png) - -![image-02](https://foruda.gitee.com/images/1701349037811908960/80d361db_5074988.png) - -![image-03](https://foruda.gitee.com/images/1701349224478845203/954f0a7b_5074988.png) - -![image-04](https://foruda.gitee.com/images/1701349248928658877/64926724_5074988.png) - -![image-05](https://foruda.gitee.com/images/1701349259068943299/1306ba40_5074988.png) - -![image-06](https://foruda.gitee.com/images/1701349294894429495/e3b3a8cf_5074988.png) - -![image-07](https://foruda.gitee.com/images/1701350432536247561/3b26685e_5074988.png) - -![image-08](https://foruda.gitee.com/images/1701350455264771992/b364c57f_5074988.png) - -![image-09](https://foruda.gitee.com/images/1701350479266000753/e4e4f7c5_5074988.png) - -![image-10](https://foruda.gitee.com/images/1701350501421625746/f8dd215e_5074988.png) - -## 审批流插件 - -![输入链接说明](https://bbs.django-vue-admin.com/uploads/20250321/97fbbf29673edfd66a1edd49237791bb.png) - -![输入链接说明](https://bbs.django-vue-admin.com/uploads/20250321/c43aa51278cbc478287c718d22397479.png) - - -![输入链接说明](https://bbs.django-vue-admin.com/uploads/20250321/9732a5cca9c1166d1a65c35e313ab90d.png) - - -![输入链接说明](https://bbs.django-vue-admin.com/uploads/20250321/3ca9dd0801ce76d21435abcc8a3d505a.png) - -![输入链接说明](https://bbs.django-vue-admin.com/uploads/20250321/a87a8d2329ef66880af5b0f16c5ff823.png) - - - +``` diff --git a/web/README.en.md b/web/README.en.md index d8ca172..f3a74ba 100644 --- a/web/README.en.md +++ b/web/README.en.md @@ -1,168 +1,191 @@ -# Django-Vue3-Admin +# PIS - Procurement & Supplier Management System -[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/huge-dream/django-vue3-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/huge-dream/django-vue3-admin/badge/star.svg?theme=dark)](https://gitee.com/huge-dream/django-vue3-admin) +## Project Overview -[preview](https://demo.dvadmin.com) | [Official website document](https://www.django-vue-admin.com) | [qq group](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [community](https://bbs.django-vue-admin.com) | [plugins market](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin) +A Django-Vue3 based procurement and supplier management system with RBAC permission control. -💡 **「About」** +## Project Architecture 🏗️ -We are a group of young people who love Code. In this hot era, we hope to calm down and bring some of our colors and colors through code. +### Tech Stack -Because of love, so embrace the future +| Layer | Technology | +|-------|------------| +| **Backend** | Django 4.2.14 + Django REST Framework 3.15.2 | +| **Frontend** | Vue 3 + TypeScript + Vite 5.4 + Element Plus 2.8 + FastCRUD | +| **Database** | MySQL 8.0 | +| **Cache/Queue** | Redis + Celery | +| **Real-time** | Django Channels (WebSocket) | +| **Auth** | JWT via SimpleJWT | -## framework introduction +### Directory Structure -💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) Is a set of all open source rapid development platform, no reservation for individuals and enterprises free use. +``` +pis/ +├── backend/ +│ ├── application/ # Django project settings +│ │ ├── settings.py # Main settings (imports from conf/env.py) +│ │ ├── urls.py # Root URL routing +│ │ ├── asgi.py # ASGI config (WebSocket support) +│ │ └── celery.py # Celery configuration +│ ├── apps/ +│ │ ├── pisadmin/ # Custom procurement app +│ │ │ ├── basicinfo/ # Company, currency, supplier, unit, system no +│ │ │ └── miscprocurement/ # RFQ, price templates, cost sections +│ │ └── pissupplier/ # Supplier quotation module +│ ├── dvadmin/ # Core RBAC system +│ │ └── system/ # Users, roles, menus, departments, logs +│ └── conf/ +│ └── env.py # Environment configuration (DB, Redis credentials) +├── web/ # Vue 3 frontend +│ └── src/ +│ ├── views/pisadmin/ # Custom procurement pages +│ ├── views/pissupplier/# Supplier pages +│ └── views/system/ # Admin system pages +└── scripts/ # Service management scripts + ├── start-backend.sh # Start backend service + ├── start-frontend.sh # Start frontend service + ├── start-celery.sh # Start Celery worker/beat + ├── stop-all.sh # Stop all services + └── check-status.sh # Check service status +``` -* 🧑‍🤝‍🧑Front-end adoption Vue3+TS+pinia+fastcrud。 -* 👭The backend uses the Python language Django framework as well as the powerful[Django REST Framework](https://pypi.org/project/djangorestframework)。 -* 👫Permission authentication use[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),Supports the multi-terminal authentication system. -* 👬Support loading dynamic permission menu, multi - way easy permission control. -* 💏 Special thanks: [vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/). -* 💡 💏 Special thanks:[jetbrains](https://www.jetbrains.com/) To provide a free IntelliJ IDEA license for this open source project. +### Module Description -## Online experience +| Module | Description | +|--------|-------------| +| `pisadmin/basicinfo` | Basic info: companies, currencies, suppliers, units, system numbering rules | +| `pisadmin/miscprocurement` | Miscellaneous procurement: RFQ, price templates, cost section builder | +| `pissupplier` | Supplier quotation module | +| `dvadmin/system` | Core RBAC: users, roles, menus, departments, operation logs | -👩‍👧‍👦👩‍👧‍👦 demo address:[https://demo.dvadmin.com](https://demo.dvadmin.com) +### Configuration -* demo account:superadmin +Backend settings are split: +- `application/settings.py` - Django defaults +- `conf/env.py` - Environment-specific config (database, Redis, secrets) -* demo password:admin123456 - -👩‍👦‍👦docs:[https://django-vue-admin.com](https://django-vue-admin.com) - -## communication - -* Communication community:[click here](https://bbs.django-vue-admin.com)👩‍👦‍👦 - -* plugins market:[click here](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 - -## source code url: - -gitee(Main push):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - -github:no data - -## core function - -1. 👨‍⚕️ Menu management: Configure the system menu, operation permissions, button permissions, back-end interface permissions, etc. -2. 🧑‍⚕️ Department management: Configure the system organization (company, department, role). -3. 👩‍⚕️ Role management: role menu permission allocation, data permission allocation, set roles according to the department for data range permission division. -4. 🧑‍🎓 Rights Specifies the rights of the authorization role. -5. 👨‍🎓 User management: The user is the system operator, this function mainly completes the system user configuration. -6. 👬 Interface whitelist: specifies the interface that does not need permission verification. -7. 🧑‍🔧 Dictionary management: Maintenance of some fixed data frequently used in the system. -8. 🧑‍🔧 Regional management: to manage provinces, cities, counties and regions. -9. 📁 Attachment management: Unified management of all files and pictures on the platform. -10. 🗓 ️operation logs: log and query the system normal operation; Log and query system exception information. - 11.🔌 [plugins market] () : based on the Django framework - Vue - Admin application and plug-in development. - -## plugins market 🔌 - -* Celery Asynchronous task:[dvadmin-celery](https://gitee.com/huge-dream/dvadmin-celery) -* Upgrade center backend:[dvadmin-upgrade-center](https://gitee.com/huge-dream/dvadmin-upgrade-center) -* Upgrade center front:[dvadmin-upgrade-center-web](https://gitee.com/huge-dream/dvadmin-upgrade-center-web) - -## before start project you need: +## Prerequisites ~~~ -Python >= 3.8.0 +Python >= 3.8.0 nodejs >= 14.0 Mysql >= 5.7.0 (Optional. The default database is sqlite3. 8.0 is recommended) Redis(Optional, the latest edition) ~~~ -## frontend♝ +## Frontend ```bash -# clone code -git clone https://gitee.com/huge-dream/django-vue3-admin.git - -# enter code dir +# Go to web directory cd web -# install dependence +# Install dependencies +npm install yarn npm install --registry=https://registry.npm.taobao.org -# Start service -npm run dev +# Start development server +yarn run dev # Visit http://localhost:8080 in your browser -# Parameters such as boot port can be configured in the #.env.development file -# Build the production environment -# npm run build +# Parameters such as boot port can be configured in the .env.development file + +# Build for production +yarn run build ``` -## backend💈 +## Backend -~~~bash -1. enter code dir cd backend -2. copy ./conf/env.example.py to ./conf dir,rename as env.py -3. in env.py configure database information - mysql database recommended version: 8.0 - mysql database character set: utf8mb4 -4. install pip dependence - pip3 install -r requirements.txt -5. Execute the migration command: - python3 manage.py makemigrations - python3 manage.py migrate -6. Initialization data - python3 manage.py init -7. Initialize provincial, municipal and county data: - python3 manage.py init_area -8. start backend - python3 manage.py runserver 0.0.0.0:8000 -or daphne : - daphne -b 0.0.0.0 -p 8000 application.asgi:application -~~~ +### Method 1: Script Startup (Recommended) -### visit backend swagger +The project provides complete startup scripts in the `scripts/` directory: -* visit url:[http://localhost:8080](http://localhost:8080) (The default address is this one. If you want to change it, follow the configuration file) -* account:`superadmin` password:`admin123456` +```bash +# Go to project root +cd D:\AVC-PROJECT\pis -### docker-compose +# Start backend service (development mode) +./scripts/start-backend.sh -~~~shell +# Start frontend service +./scripts/start-frontend.sh + +# Start Celery async tasks (optional) +./scripts/start-celery.sh +``` + +**Script Description:** + +| Script | Description | +|--------|-------------| +| `start-backend.sh` | Start backend service (auto-creates PID and log files) | +| `start-frontend.sh` | Start frontend dev server | +| `start-celery.sh` | Start Celery Worker and Beat | +| `stop-all.sh` | Stop all services | +| `stop-celery.sh` | Stop Celery only | +| `check-status.sh` | Check all service status | +| `view-logs.sh` | View service logs | + +**View Logs:** +```bash +./scripts/view-logs.sh all # View all logs +./scripts/view-logs.sh backend # Backend logs only +./scripts/view-logs.sh celery # Celery logs only +``` + +**Stop Services:** +```bash +./scripts/stop-all.sh # Stop all services +./scripts/stop-celery.sh # Stop Celery only +``` + +### Method 2: Manual Startup + +```bash +# 1. Go to backend directory +cd backend + +# 2. Copy and configure environment file +cp ./conf/env.example.py ./conf/env.py +# Edit env.py to configure database information + +# 3. Install dependencies +pip3 install -r requirements.txt + +# 4. Run migrations +python3 manage.py makemigrations +python3 manage.py migrate + +# 5. Initialize data +python3 manage.py init +python3 manage.py init_area + +# 6. Start backend +uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 +``` + +### Access + +- Frontend URL: http://localhost:8080 +- Backend API: http://localhost:8080/api +- Default account: `superadmin` / `admin123456` + +### Docker + +```shell docker-compose up -d # Initialize backend data (first execution only) docker exec -ti dvadmin-django bash -python manage.py makemigrations +python manage.py makemigrations python manage.py migrate python manage.py init_area python manage.py init exit -frontend url:http://127.0.0.1:8080 -backend url:http://127.0.0.1:8080/api -# Change 127.0.0.1 to your own public ip address on the server -account:`superadmin` password:`admin123456` - -# docker-compose stop +# Stop services docker-compose down -# docker-compose restart + +# Restart services docker-compose restart -# docker-compose on start build + +# Rebuild and start docker-compose up -d --build -~~~ - -## Demo screenshot✅ - -![image-01](https://images.gitee.com/uploads/images/2022/0530/234137_b58c8f98_5074988.png) - -![image-02](https://images.gitee.com/uploads/images/2022/0530/234240_39834603_5074988.png) - -![image-03](https://images.gitee.com/uploads/images/2022/0530/234339_35e728a0_5074988.png) - -![image-04](https://images.gitee.com/uploads/images/2022/0530/234426_957036b0_5074988.png) - -![image-05](https://images.gitee.com/uploads/images/2022/0530/234458_898be492_5074988.png) - -![image-06](https://images.gitee.com/uploads/images/2022/0530/234521_35b40076_5074988.png) - -![image-07](https://images.gitee.com/uploads/images/2022/0530/234615_c2325639_5074988.png) - -![image-08](https://images.gitee.com/uploads/images/2022/0530/234639_1ed6cc93_5074988.png) - -![image-09](https://images.gitee.com/uploads/images/2022/0530/234815_cea2c53f_5074988.png) - -![image-10](https://images.gitee.com/uploads/images/2022/0530/234840_5f3e5f53_5074988.png) +``` diff --git a/web/README.md b/web/README.md index 86d36b1..e8abcac 100644 --- a/web/README.md +++ b/web/README.md @@ -1,102 +1,71 @@ -# Django-Vue3-Admin +# PIS - 采购与供应商管理系统 -[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/liqianglog/django-vue-admin/badge/star.svg?theme=dark)](https://gitee.com/liqianglog/django-vue-admin) +## 项目简介 -[预 览](https://demo.dvadmin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin) +基于 Django-Vue3 的采购与供应商管理系统,采用 RBAC 权限控制。 +## 项目架构 🏗️ +### 技术栈 -💡 **「关于」** +| 层级 | 技术 | +|------|------| +| **后端** | Django 4.2.14 + Django REST Framework 3.15.2 | +| **前端** | Vue 3 + TypeScript + Vite 5.4 + Element Plus 2.8 + FastCRUD | +| **数据库** | MySQL 8.0 | +| **缓存/队列** | Redis + Celery | +| **实时通信** | Django Channels (WebSocket) | +| **认证** | JWT via SimpleJWT | -我们是一群热爱代码的青年,在这个炙热的时代下,我们希望静下心来通过Code带来一点我们的色彩和颜色。 +### 目录结构 -因为热爱,所以拥抱未来! +``` +pis/ +├── backend/ +│ ├── application/ # Django 项目配置 +│ │ ├── settings.py # 主配置(从 conf/env.py 导入配置) +│ │ ├── urls.py # 根路由 +│ │ ├── asgi.py # ASGI 配置(支持 WebSocket) +│ │ └── celery.py # Celery 配置 +│ ├── apps/ +│ │ ├── pisadmin/ # 采购管理应用 +│ │ │ ├── basicinfo/ # 基础信息:公司、币种、供应商、单位、编码规则 +│ │ │ └── miscprocurement/ # 杂项采购:询价、价格模板、成本节 +│ │ └── pissupplier/ # 供应商报价模块 +│ ├── dvadmin/ # 核心 RBAC 系统 +│ │ └── system/ # 用户、角色、菜单、部门、日志 +│ └── conf/ +│ └── env.py # 环境配置(数据库、Redis 凭证) +├── web/ # Vue 3 前端 +│ └── src/ +│ ├── views/pisadmin/ # 采购管理页面 +│ ├── views/pissupplier/# 供应商页面 +│ └── views/system/ # 系统管理页面 +└── scripts/ # 服务管理脚本 + ├── start-backend.sh # 启动后端服务 + ├── start-frontend.sh # 启动前端服务 + ├── start-celery.sh # 启动 Celery worker/beat + ├── stop-all.sh # 停止所有服务 + └── check-status.sh # 查看服务状态 +``` +### 模块说明 -## 平台简介 +| 模块 | 说明 | +|------|------| +| `pisadmin/basicinfo` | 基础信息管理:公司、币种、供应商、单位、系统编号规则 | +| `pisadmin/miscprocurement` | 杂项采购:询价(RFQ)、价格模板、成本节构建器 | +| `pissupplier` | 供应商报价模块 | +| `dvadmin/system` | 核心 RBAC 系统:用户、角色、菜单、部门、操作日志 | -💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) 是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 - django-vue3-admin 基于 vue3 + CompositionAPI + typescript + vite + element plus, 是一款全栈,快速,开源的后台管理系统! +### 配置说明 - - - -* 🧑‍🤝‍🧑前端采用 Vue3+TS+pinia+fastcrud(感谢[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)) -* 👭后端采用 Python 语言 Django 框架以及强大的 [Django REST Framework](https://pypi.org/project/djangorestframework)。 -* 👫权限认证使用[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),支持多终端认证系统。 -* 👬支持加载动态权限菜单,多方式轻松权限控制。 -* 💏特别鸣谢:[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)。 -* 💡 特别感谢[jetbrains](https://www.jetbrains.com/) 为本开源项目提供免费的 IntelliJ IDEA 授权。 - -#### 🏭 环境支持 - -| Edge | Firefox | Chrome | Safari | -| --------- | ------------ | ----------- | ----------- | -| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 | - -> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。 - - - -## 在线体验 - -👩‍👧‍👦演示地址:[https://demo.dvadmin.com](https://demo.dvadmin.com) - -- 账号:superadmin - -- 密码:admin123456 - -👩‍👦‍👦文档地址:[coding](https://dvadmin-private.coding.net/share/km/cec69f3d-30fe-47d5-bd97-e9e851f0b776/K-2) - - -## 给框架点赞 - - - - -## 交流 - -- 交流社区:[戳我](https://bbs.django-vue-admin.com)👩‍👦‍👦 - -- 插件市场:[戳我](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦 - -- django-vue-admin交流01群(已满):812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi) -- django-vue-admin交流02群(已满):687252418 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=4jJN4IjWGfxJ8YJXbb_gTsuWjR34WLdc&jump_from=webapi) -- django-vue-admin交流03群:442108213 [点击链接加入群聊](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=wsm5oSz3K8dElBYUDtLTcQSEPhINFkl8&authKey=M6sbER0z59ZakgBr5erFeZyFZU15CI52bErNZa%2FxSvvGIuVAbY0N5866v89hm%2FK4&noverify=0&group_code=442108213) - -- 二维码 - - - -## 源码地址 - -gitee地址(主推):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 - -github地址:暂无 - - -## 内置功能 - -1. 👨‍⚕️菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。 -2. 🧑‍⚕️部门管理:配置系统组织机构(公司、部门、角色)。 -3. 👩‍⚕️角色管理:角色菜单权限分配、数据权限分配、设置角色按部门进行数据范围权限划分。 -4. 🧑‍🎓按钮权限权限:授权角色的按钮权限和接口权限,可做到每一个接口都能授权数据范围。 -5. 🧑‍🎓字段权限权限:授权页面的字段显示权限。 -5. 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。 -6. 👬接口白名单:配置不需要进行权限校验的接口。 -7. 🧑‍🔧字典管理:对系统中经常使用的一些较为固定的数据进行维护。 -8. 🧑‍🔧地区管理:对省市县区域进行管理。 -9. 📁附件管理:对平台上所有文件、图片等进行统一管理。 -10. 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 -11. 🔌[插件市场 ](https://bbs.django-vue-admin.com/plugMarket.html):基于Django-Vue-Admin框架开发的应用和插件。 - -## 插件市场 🔌 - -- Celery异步任务:[dvadmin-celery](https://gitee.com/huge-dream/dvadmin-celery) -- 升级中心后端:[dvadmin-upgrade-center](https://gitee.com/huge-dream/dvadmin-upgrade-center) -- 升级中心前端:[dvadmin-upgrade-center-web](https://gitee.com/huge-dream/dvadmin-upgrade-center-web) +后端配置分离: +- `application/settings.py` - Django 默认配置 +- `conf/env.py` - 环境特定配置(数据库、Redis、密钥) ## 准备工作 + ~~~ Python >= 3.8.0 (推荐3.8+版本) nodejs >= 14.0 (推荐最新) @@ -104,109 +73,119 @@ Mysql >= 5.7.0 (可选,默认数据库sqlite3,推荐8.0版本) Redis(可选,最新版) ~~~ -## 前端♝ +## 前端 ```bash -# 克隆项目 -git clone https://gitee.com/huge-dream/django-vue3-admin.git - -# 进入项目目录 +# 进入前端目录 cd web # 安装依赖 -npm install --registry=https://registry.npm.taobao.org +npm install yarn +npm install --registry=https://registry.npmmirror.com -# 启动服务 -npm run dev +# 启动开发服务器 +yarn run dev # 浏览器访问 http://localhost:8080 # .env.development 文件中可配置启动端口等参数 + # 构建生产环境 -# npm run build +yarn run build ``` +## 后端 +### 方式一:脚本启动(推荐) -## 后端💈 +项目提供了完整的启动脚本,位于项目根目录 `scripts/` 目录: -~~~bash -1. 进入项目目录 cd backend -2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py -3. 在 env.py 中配置数据库信息 - mysql数据库版本建议:8.0 - mysql数据库字符集:utf8mb4 -4. 安装依赖环境 - pip3 install -r requirements.txt -5. 执行迁移命令: - python3 manage.py makemigrations - python3 manage.py migrate -6. 初始化数据 - python3 manage.py init -7. 初始化省市县数据: - python3 manage.py init_area -8. 启动项目 - python3 manage.py runserver 0.0.0.0:8000 -或使用 daphne : - daphne -b 0.0.0.0 -p 8000 application.asgi:application -~~~ +```bash +# 进入项目根目录 +cd D:\AVC-PROJECT\pis + +# 启动后端服务(开发模式) +./scripts/start-backend.sh + +# 启动前端服务 +./scripts/start-frontend.sh + +# 启动 Celery 异步任务(可选) +./scripts/start-celery.sh +``` + +**脚本功能说明:** + +| 脚本 | 说明 | +|------|------| +| `start-backend.sh` | 启动后端服务(自动创建 PID 和日志文件)| +| `start-frontend.sh` | 启动前端开发服务器 | +| `start-celery.sh` | 启动 Celery Worker 和 Beat | +| `stop-all.sh` | 停止所有服务 | +| `stop-celery.sh` | 仅停止 Celery 服务 | +| `check-status.sh` | 查看所有服务状态 | +| `view-logs.sh` | 查看服务日志 | + +**查看日志:** +```bash +./scripts/view-logs.sh all # 查看所有日志 +./scripts/view-logs.sh backend # 仅看后端日志 +./scripts/view-logs.sh celery # 仅看 Celery 日志 +``` + +**停止服务:** +```bash +./scripts/stop-all.sh # 停止所有服务 +./scripts/stop-celery.sh # 仅停止 Celery +``` + +### 方式二:手动启动 + +```bash +# 1. 进入后端目录 +cd backend + +# 2. 复制并配置环境文件 +cp ./conf/env.example.py ./conf/env.py +# 编辑 env.py 配置数据库信息 + +# 3. 安装依赖 +pip3 install -r requirements.txt + +# 4. 执行数据库迁移 +python3 manage.py makemigrations +python3 manage.py migrate + +# 5. 初始化数据 +python3 manage.py init +python3 manage.py init_area + +# 6. 启动后端 +uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 +``` ### 访问项目 -- 访问地址:[http://localhost:8080](http://localhost:8080) (默认为此地址,如有修改请按照配置文件) -- 账号:`superadmin` 密码:`admin123456` +- 前端地址:http://localhost:8080 +- 后端接口:http://localhost:8080/api +- 默认账号:`superadmin` / `admin123456` +### Docker 部署 - - - -### docker-compose 运行 - -~~~shell -# 先安装docker-compose (自行百度安装),执行此命令等待安装,如有使用celery插件请打开docker-compose.yml中celery 部分注释 +```shell docker-compose up -d -# 初始化后端数据(第一次执行即可) +# 初始化后端数据(第一次执行即可) docker exec -ti dvadmin-django bash -python manage.py makemigrations +python manage.py makemigrations python manage.py migrate python manage.py init_area python manage.py init exit -前端地址:http://127.0.0.1:8080 -后端地址:http://127.0.0.1:8080/api -# 在服务器上请把127.0.0.1 换成自己公网ip -账号:superadmin 密码:admin123456 - -# docker-compose 停止 +# 停止服务 docker-compose down -# docker-compose 重启 + +# 重启服务 docker-compose restart -# docker-compose 启动时重新进行 build + +# 重新构建并启动 docker-compose up -d --build -~~~ - - - -## 演示图✅ - -![image-01](https://images.gitee.com/uploads/images/2022/0530/234137_b58c8f98_5074988.png) - -![image-02](https://images.gitee.com/uploads/images/2022/0530/234240_39834603_5074988.png) - -![image-03](https://images.gitee.com/uploads/images/2022/0530/234339_35e728a0_5074988.png) - -![image-04](https://images.gitee.com/uploads/images/2022/0530/234426_957036b0_5074988.png) - -![image-05](https://images.gitee.com/uploads/images/2022/0530/234458_898be492_5074988.png) - -![image-06](https://images.gitee.com/uploads/images/2022/0530/234521_35b40076_5074988.png) - -![image-07](https://images.gitee.com/uploads/images/2022/0530/234615_c2325639_5074988.png) - -![image-08](https://images.gitee.com/uploads/images/2022/0530/234639_1ed6cc93_5074988.png) - -![image-09](https://images.gitee.com/uploads/images/2022/0530/234815_cea2c53f_5074988.png) - -![image-10](https://images.gitee.com/uploads/images/2022/0530/234840_5f3e5f53_5074988.png) - - - +``` From c862c33ff08e98f73d7eb2ebe59175222331e5b1 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:09:44 +0800 Subject: [PATCH 08/16] fix(dashboard): add method and deadline fields to API responses BuyerDashboard API: - Add buying_method, quote_deadline, bid_start_time, bid_end_time to tasks - Add daily trend data with day field instead of monthly SupplierDashboard API: - Add buying_method, quote_deadline, bid_start_time, bid_end_time to pending_quotes - Add daily trend data with day field instead of monthly These fields are needed by the frontend to display procurement method badges and deadline countdown with color-coded urgency. --- backend/apps/pisadmin/dashboard/views.py | 116 +++++++++++++++++++++-- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/backend/apps/pisadmin/dashboard/views.py b/backend/apps/pisadmin/dashboard/views.py index dc7e0a2..8f52442 100644 --- a/backend/apps/pisadmin/dashboard/views.py +++ b/backend/apps/pisadmin/dashboard/views.py @@ -25,6 +25,15 @@ class BuyerTaskSerializer(serializers.Serializer): inquiry_no = serializers.CharField() status = serializers.CharField() created_at = serializers.DateTimeField() + method = serializers.SerializerMethodField() + quote_deadline = serializers.DateTimeField(allow_null=True, required=False) + bid_start_time = serializers.DateTimeField(allow_null=True, required=False) + bid_end_time = serializers.DateTimeField(allow_null=True, required=False) + + def get_method(self, obj): + if obj.get('buying_method') == 2: + return '招标' + return '询价' class SupplierKPISerializer(serializers.Serializer): @@ -42,8 +51,17 @@ class SupplierQuoteSerializer(serializers.Serializer): inquiry_no = serializers.CharField() item_name = serializers.CharField() unit = serializers.CharField() - deadline = serializers.DateTimeField() + quantity = serializers.CharField(allow_blank=True, required=False) status = serializers.IntegerField() + method = serializers.SerializerMethodField() + quote_deadline = serializers.DateTimeField(allow_null=True, required=False) + bid_start_time = serializers.DateTimeField(allow_null=True, required=False) + bid_end_time = serializers.DateTimeField(allow_null=True, required=False) + + def get_method(self, obj): + if obj.get('buying_method') == 2: + return '招标' + return '询价' class MessageSerializer(serializers.Serializer): @@ -55,7 +73,8 @@ class MessageSerializer(serializers.Serializer): class TrendSerializer(serializers.Serializer): - month = serializers.CharField() + month = serializers.CharField(required=False) + day = serializers.IntegerField(required=False) count = serializers.IntegerField(required=False) quotes = serializers.IntegerField(required=False) won = serializers.IntegerField(required=False) @@ -131,6 +150,10 @@ class DashboardView(views.APIView): 'inquiry_no': i.inquiry_no, 'status': dict(Inquiry.STATUS_CHOICES).get(i.status, str(i.status)), 'created_at': i.create_time, + 'buying_method': i.buying_method, + 'quote_deadline': i.quote_deadline, + 'bid_start_time': i.bid_start_time, + 'bid_end_time': i.bid_end_time, } for i in inquiries ] @@ -191,7 +214,6 @@ class DashboardView(views.APIView): inquiry = Inquiry.objects.filter(inquiry_no=q.inquiry_no).first() # 获取询价单中的数量 quantity = '' - quantity = '' unit = '' if inquiry: from apps.pisadmin.miscprocurement.models import InquiryRfqItem @@ -205,8 +227,11 @@ class DashboardView(views.APIView): 'item_name': inquiry.title if inquiry else '', 'quantity': quantity, 'unit': unit, - 'deadline': q.quote_deadline, 'status': q.status, + 'buying_method': q.buying_method, + 'quote_deadline': q.quote_deadline, + 'bid_start_time': q.bid_start_time, + 'bid_end_time': q.bid_end_time, }) return result @@ -277,6 +302,85 @@ class DashboardView(views.APIView): }) return result + def get_buyer_trend_daily(self, user): + """获取采购方当月每日趋势数据""" + now = timezone.now() + start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + + data = ( + Inquiry.objects.filter( + create_user=user.username, + create_time__gte=start_of_month, + create_time__lte=now + ) + .extra(select={'day': 'DAY(create_time)'}) + .values('day') + .annotate(count=Count('id')) + .order_by('day') + ) + return [ + {'day': d['day'], 'count': d['count']} + for d in data + ] + + def get_supplier_trend_daily(self, user): + """获取供应商当月每日趋势数据""" + now = timezone.now() + start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + + if user.is_superuser: + data = ( + QuotationMaster.objects.filter( + creattime__gte=start_of_month, + creattime__lte=now + ) + .extra(select={'day': 'DAY(creattime)'}) + .values('day') + .annotate(quotes=Count('autoid')) + .order_by('day') + ) + result = [] + for d in data: + won = QuotationMaster.objects.filter( + creattime__day=d['day'], + creattime__month=now.month, + creattime__year=now.year, + is_awarded=1 + ).count() + result.append({ + 'day': d['day'], + 'quotes': d['quotes'], + 'won': won, + }) + return result + else: + data = ( + QuotationMaster.objects.filter( + supplier_code=user.username, + creattime__gte=start_of_month, + creattime__lte=now + ) + .extra(select={'day': 'DAY(creattime)'}) + .values('day') + .annotate(quotes=Count('autoid')) + .order_by('day') + ) + result = [] + for d in data: + won = QuotationMaster.objects.filter( + supplier_code=user.username, + creattime__day=d['day'], + creattime__month=now.month, + creattime__year=now.year, + is_awarded=1 + ).count() + result.append({ + 'day': d['day'], + 'quotes': d['quotes'], + 'won': won, + }) + return result + def get_messages(self, user): """消息通知 - 占位实现,后续接入通知系统""" return [] @@ -287,7 +391,7 @@ class DashboardView(views.APIView): 'kpi': self.get_buyer_kpi(user), 'tasks': self.get_buyer_tasks(user), 'messages': self.get_messages(user), - 'trend': self.get_trend_data(user, is_buyer=True), + 'trend': self.get_buyer_trend_daily(user), } def get_supplier_data(self, user): @@ -296,7 +400,7 @@ class DashboardView(views.APIView): 'kpi': self.get_supplier_kpi(user), 'pending_quotes': self.get_supplier_pending_quotes(user), 'messages': self.get_messages(user), - 'trend': self.get_trend_data(user, is_buyer=False), + 'trend': self.get_supplier_trend_daily(user), } def get(self, request): From 2a753d7cfd394e84b92cef91364fa796a3913974 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:18:08 +0800 Subject: [PATCH 09/16] debug(dashboard): add console.log debugging for buyer/supplier task list data flow Trace points: - Dashboard Store: API response, buyer/supplier data, tasks/pending_quotes - BuyerDashboard: computed tasks, onMounted, getDeadlineClass, getRemainingTimeText - SupplierDashboard: computed pendingQuotes, onMounted, getDeadlineClass, getRemainingTimeText --- web/src/stores/modules/dashboard.ts | 7 +++++++ web/src/views/pisadmin/dashboard/BuyerDashboard.vue | 13 ++++++++++++- .../views/pisadmin/dashboard/SupplierDashboard.vue | 13 ++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/web/src/stores/modules/dashboard.ts b/web/src/stores/modules/dashboard.ts index d84613f..ee765a0 100644 --- a/web/src/stores/modules/dashboard.ts +++ b/web/src/stores/modules/dashboard.ts @@ -63,9 +63,16 @@ export const useDashboardStore = defineStore('dashboard', { this.loading = true; try { const res: any = await getDashboard(); + console.log('[Dashboard Store] API response:', res); + console.log('[Dashboard Store] buyer data:', res?.buyer); + console.log('[Dashboard Store] supplier data:', res?.supplier); + console.log('[Dashboard Store] buyer.tasks:', res?.buyer?.tasks); + console.log('[Dashboard Store] supplier.pending_quotes:', res?.supplier?.pending_quotes); // 后端直接返回 {buyer, supplier} 结构 this.buyer = res?.buyer || null; this.supplier = res?.supplier || null; + console.log('[Dashboard Store] store.buyer after set:', this.buyer); + console.log('[Dashboard Store] store.supplier after set:', this.supplier); } finally { this.loading = false; } diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index 74486db..ed76fa0 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -158,7 +158,12 @@ const kpi = computed( quote_timely_rate: 0, } ); -const tasks = computed(() => buyer.value?.tasks || []); +const tasks = computed(() => { + const t = buyer.value?.tasks || []; + console.log('[BuyerDashboard] buyer.value:', buyer.value); + console.log('[BuyerDashboard] tasks computed:', t); + return t; +}); const trend = computed(() => buyer.value?.trend || []); const messages = computed(() => buyer.value?.messages || []); @@ -179,10 +184,12 @@ function getMethodText(method: string) { function getDeadlineClass(task: any): string { const deadline = task.method === '招标' ? task.bid_end_time : task.quote_deadline; + console.log('[BuyerDashboard] getDeadlineClass task:', task.inquiry_no, 'method:', task.method, 'deadline:', deadline); if (!deadline) return ''; const now = new Date(); const deadlineDate = new Date(deadline); const diffHours = (deadlineDate.getTime() - now.getTime()) / (1000 * 60 * 60); + console.log('[BuyerDashboard] diffHours:', diffHours); if (diffHours < 0) return 'deadline-expired'; if (diffHours < 24) return 'deadline-urgent'; // <24h red if (diffHours < 48) return 'deadline-warning'; // 24-48h yellow @@ -198,10 +205,12 @@ function getDeadlineText(task: any): string { function getRemainingTimeText(task: any): string { const deadline = task.method === '招标' ? task.bid_end_time : task.quote_deadline; + console.log('[BuyerDashboard] getRemainingTimeText task:', task.inquiry_no, 'deadline:', deadline); if (!deadline) return ''; const now = new Date(); const deadlineDate = new Date(deadline); const diffMs = deadlineDate.getTime() - now.getTime(); + console.log('[BuyerDashboard] diffMs:', diffMs); if (diffMs <= 0) return '已到期'; const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const days = Math.floor(diffHours / 24); @@ -311,6 +320,8 @@ function initChart() { } onMounted(() => { + console.log('[BuyerDashboard] onMounted - buyer store:', buyer.value); + console.log('[BuyerDashboard] onMounted - tasks:', tasks.value); initChart(); window.addEventListener('resize', () => chartRef.value && echarts.getInstanceByDom(chartRef.value)?.resize()); }); diff --git a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue index 751d08c..41c4741 100644 --- a/web/src/views/pisadmin/dashboard/SupplierDashboard.vue +++ b/web/src/views/pisadmin/dashboard/SupplierDashboard.vue @@ -163,7 +163,12 @@ const kpi = computed( conversion_rate: 0, } ); -const pendingQuotes = computed(() => supplier.value?.pending_quotes || []); +const pendingQuotes = computed(() => { + const q = supplier.value?.pending_quotes || []; + console.log('[SupplierDashboard] supplier.value:', supplier.value); + console.log('[SupplierDashboard] pendingQuotes computed:', q); + return q; +}); const trend = computed(() => supplier.value?.trend || []); const messages = computed(() => supplier.value?.messages || []); @@ -196,10 +201,12 @@ function getMethodText(method: string) { function getDeadlineClass(quote: any): string { const deadline = quote.method === '招标' ? quote.bid_end_time : quote.quote_deadline; + console.log('[SupplierDashboard] getDeadlineClass quote:', quote.inquiry_no, 'method:', quote.method, 'deadline:', deadline); if (!deadline) return ''; const now = new Date(); const deadlineDate = new Date(deadline); const diffHours = (deadlineDate.getTime() - now.getTime()) / (1000 * 60 * 60); + console.log('[SupplierDashboard] diffHours:', diffHours); if (diffHours < 0) return 'deadline-expired'; if (diffHours < 24) return 'deadline-urgent'; if (diffHours < 48) return 'deadline-warning'; @@ -208,10 +215,12 @@ function getDeadlineClass(quote: any): string { function getRemainingTimeText(quote: any): string { const deadline = quote.method === '招标' ? quote.bid_end_time : quote.quote_deadline; + console.log('[SupplierDashboard] getRemainingTimeText quote:', quote.inquiry_no, 'deadline:', deadline); if (!deadline) return ''; const now = new Date(); const deadlineDate = new Date(deadline); const diffMs = deadlineDate.getTime() - now.getTime(); + console.log('[SupplierDashboard] diffMs:', diffMs); if (diffMs <= 0) return '已到期'; const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const days = Math.floor(diffHours / 24); @@ -324,6 +333,8 @@ function initChart() { } onMounted(() => { + console.log('[SupplierDashboard] onMounted - supplier store:', supplier.value); + console.log('[SupplierDashboard] onMounted - pendingQuotes:', pendingQuotes.value); initChart(); window.addEventListener('resize', () => chartRef.value && echarts.getInstanceByDom(chartRef.value)?.resize()); }); From 3c2ae69383282120f2660869e352d8467c646eea Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:19:08 +0800 Subject: [PATCH 10/16] debug: log full task object to identify actual API field names --- web/src/views/pisadmin/dashboard/BuyerDashboard.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index ed76fa0..b532378 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -183,6 +183,8 @@ function getMethodText(method: string) { } function getDeadlineClass(task: any): string { + // Debug: log full task object to find actual field names + console.log('[BuyerDashboard] getDeadlineClass full task:', JSON.stringify(task)); const deadline = task.method === '招标' ? task.bid_end_time : task.quote_deadline; console.log('[BuyerDashboard] getDeadlineClass task:', task.inquiry_no, 'method:', task.method, 'deadline:', deadline); if (!deadline) return ''; From f373252760568a6910c319733e68714a4f019887 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:23:21 +0800 Subject: [PATCH 11/16] fix(dashboard): add missing method/quote_deadline/bid_*_time fields to BuyerTaskSerializer and SupplierQuoteSerializer The serializers.py had incomplete field definitions - missing the deadline and method fields that views.py correctly builds. Also fixed quantity type (CharField to match view's empty string fallback) and added status field to SupplierQuoteSerializer. --- .../apps/pisadmin/dashboard/serializers.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/backend/apps/pisadmin/dashboard/serializers.py b/backend/apps/pisadmin/dashboard/serializers.py index 4691603..a1e48bb 100644 --- a/backend/apps/pisadmin/dashboard/serializers.py +++ b/backend/apps/pisadmin/dashboard/serializers.py @@ -14,6 +14,15 @@ class BuyerTaskSerializer(serializers.Serializer): inquiry_no = serializers.CharField() status = serializers.CharField() created_at = serializers.DateTimeField() + method = serializers.SerializerMethodField() + quote_deadline = serializers.DateTimeField(allow_null=True, required=False) + bid_start_time = serializers.DateTimeField(allow_null=True, required=False) + bid_end_time = serializers.DateTimeField(allow_null=True, required=False) + + def get_method(self, obj): + if obj.get('buying_method') == 2: + return '招标' + return '询价' class SupplierKPISerializer(serializers.Serializer): @@ -27,9 +36,18 @@ class SupplierQuoteSerializer(serializers.Serializer): id = serializers.IntegerField() inquiry_no = serializers.CharField() item_name = serializers.CharField() - quantity = serializers.IntegerField() unit = serializers.CharField() - deadline = serializers.DateTimeField() + quantity = serializers.CharField(allow_blank=True, required=False) + status = serializers.IntegerField() + method = serializers.SerializerMethodField() + quote_deadline = serializers.DateTimeField(allow_null=True, required=False) + bid_start_time = serializers.DateTimeField(allow_null=True, required=False) + bid_end_time = serializers.DateTimeField(allow_null=True, required=False) + + def get_method(self, obj): + if obj.get('buying_method') == 2: + return '招标' + return '询价' class MessageSerializer(serializers.Serializer): From 0a5b72fdba244cf4a26d66901dc58191949e270e Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:48:32 +0800 Subject: [PATCH 12/16] fix(dashboard): wrap response with code:200 and update store to extract data from res.data Backend: Response() now returns {code:200, msg, data: {buyer, supplier}} Frontend: store extracts buyer/supplier from res.data since axios interceptor returns full response --- backend/apps/pisadmin/dashboard/serializers.py | 3 ++- backend/apps/pisadmin/dashboard/views.py | 2 +- web/src/stores/modules/dashboard.ts | 15 ++++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/backend/apps/pisadmin/dashboard/serializers.py b/backend/apps/pisadmin/dashboard/serializers.py index a1e48bb..cab1219 100644 --- a/backend/apps/pisadmin/dashboard/serializers.py +++ b/backend/apps/pisadmin/dashboard/serializers.py @@ -59,7 +59,8 @@ class MessageSerializer(serializers.Serializer): class TrendSerializer(serializers.Serializer): - month = serializers.CharField() + day = serializers.IntegerField(required=False) + month = serializers.CharField(required=False) count = serializers.IntegerField(required=False) quotes = serializers.IntegerField(required=False) won = serializers.IntegerField(required=False) diff --git a/backend/apps/pisadmin/dashboard/views.py b/backend/apps/pisadmin/dashboard/views.py index 8f52442..49d3088 100644 --- a/backend/apps/pisadmin/dashboard/views.py +++ b/backend/apps/pisadmin/dashboard/views.py @@ -468,4 +468,4 @@ class DashboardView(views.APIView): serializer = DashboardResponseSerializer(data=response_data) serializer.is_valid(raise_exception=True) - return Response(serializer.validated_data) + return Response({'code': 200, 'msg': 'success', 'data': serializer.validated_data}) diff --git a/web/src/stores/modules/dashboard.ts b/web/src/stores/modules/dashboard.ts index ee765a0..8a597ed 100644 --- a/web/src/stores/modules/dashboard.ts +++ b/web/src/stores/modules/dashboard.ts @@ -64,13 +64,14 @@ export const useDashboardStore = defineStore('dashboard', { try { const res: any = await getDashboard(); console.log('[Dashboard Store] API response:', res); - console.log('[Dashboard Store] buyer data:', res?.buyer); - console.log('[Dashboard Store] supplier data:', res?.supplier); - console.log('[Dashboard Store] buyer.tasks:', res?.buyer?.tasks); - console.log('[Dashboard Store] supplier.pending_quotes:', res?.supplier?.pending_quotes); - // 后端直接返回 {buyer, supplier} 结构 - this.buyer = res?.buyer || null; - this.supplier = res?.supplier || null; + // 后端返回 {code: 200, msg, data: {buyer, supplier}},需要取 res.data + const data = res?.data || res; + console.log('[Dashboard Store] buyer data:', data?.buyer); + console.log('[Dashboard Store] supplier data:', data?.supplier); + console.log('[Dashboard Store] buyer.tasks:', data?.buyer?.tasks); + console.log('[Dashboard Store] supplier.pending_quotes:', data?.supplier?.pending_quotes); + this.buyer = data?.buyer || null; + this.supplier = data?.supplier || null; console.log('[Dashboard Store] store.buyer after set:', this.buyer); console.log('[Dashboard Store] store.supplier after set:', this.supplier); } finally { From 32377898ab0f3cca8c74fcb6c01835be72b72da5 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:50:36 +0800 Subject: [PATCH 13/16] fix(dashboard): use SuccessResponse from dvadmin.utils.json_response instead of manual Response --- backend/apps/pisadmin/dashboard/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/apps/pisadmin/dashboard/views.py b/backend/apps/pisadmin/dashboard/views.py index 49d3088..d76c121 100644 --- a/backend/apps/pisadmin/dashboard/views.py +++ b/backend/apps/pisadmin/dashboard/views.py @@ -5,6 +5,7 @@ from django.db.models import Count, F from django.db.models.functions import TruncMonth from rest_framework import serializers, views from rest_framework.response import Response +from dvadmin.utils.json_response import SuccessResponse from apps.pisadmin.miscprocurement.models import Inquiry from apps.pissupplier.models import QuotationMaster @@ -468,4 +469,4 @@ class DashboardView(views.APIView): serializer = DashboardResponseSerializer(data=response_data) serializer.is_valid(raise_exception=True) - return Response({'code': 200, 'msg': 'success', 'data': serializer.validated_data}) + return SuccessResponse(data=serializer.validated_data, msg="获取成功") From d1c54290c5588b30a28a873559fac76eff86f473 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 10:59:59 +0800 Subject: [PATCH 14/16] debug(dashboard): add debug print to track buyer_data exception --- backend/apps/pisadmin/dashboard/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/apps/pisadmin/dashboard/views.py b/backend/apps/pisadmin/dashboard/views.py index d76c121..bdf40e5 100644 --- a/backend/apps/pisadmin/dashboard/views.py +++ b/backend/apps/pisadmin/dashboard/views.py @@ -443,7 +443,11 @@ class DashboardView(views.APIView): if user.is_superuser or has_buyer_role: try: buyer_data = self.get_buyer_data(user) - except Exception: + print(f"[DEBUG] get_buyer_data success, tasks count: {len(buyer_data.get('tasks', []))}") + except Exception as e: + print(f"[DEBUG] get_buyer_data exception: {e}") + import traceback + traceback.print_exc() buyer_data = { 'kpi': {'total_inquiries': 0, 'pending_inquiries': 0, 'completed_quotes': 0, 'total_suppliers': 0, 'quote_timely_rate': 0}, 'tasks': [], From 4c681805cd6bcbd5874b049e4bd7084465862b03 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 11:02:51 +0800 Subject: [PATCH 15/16] fix(dashboard): replace .extra() with TruncDate to fix SQL Server column name issues .extra(select={'day': 'DAY(create_time)'}) uses Django field name but Inquiry.create_time has db_column='CreateTime' causing SQL Server error. TruncDate is the proper ORM approach for date truncation. --- backend/apps/pisadmin/dashboard/views.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/apps/pisadmin/dashboard/views.py b/backend/apps/pisadmin/dashboard/views.py index bdf40e5..6f95d5e 100644 --- a/backend/apps/pisadmin/dashboard/views.py +++ b/backend/apps/pisadmin/dashboard/views.py @@ -2,7 +2,7 @@ from datetime import timedelta from django.utils import timezone from django.db.models import Count, F -from django.db.models.functions import TruncMonth +from django.db.models.functions import TruncMonth, TruncDate from rest_framework import serializers, views from rest_framework.response import Response from dvadmin.utils.json_response import SuccessResponse @@ -314,13 +314,13 @@ class DashboardView(views.APIView): create_time__gte=start_of_month, create_time__lte=now ) - .extra(select={'day': 'DAY(create_time)'}) + .annotate(day=TruncDate('create_time')) .values('day') .annotate(count=Count('id')) .order_by('day') ) return [ - {'day': d['day'], 'count': d['count']} + {'day': d['day'].day, 'count': d['count']} for d in data ] @@ -335,7 +335,7 @@ class DashboardView(views.APIView): creattime__gte=start_of_month, creattime__lte=now ) - .extra(select={'day': 'DAY(creattime)'}) + .annotate(day=TruncDate('creattime')) .values('day') .annotate(quotes=Count('autoid')) .order_by('day') @@ -343,13 +343,13 @@ class DashboardView(views.APIView): result = [] for d in data: won = QuotationMaster.objects.filter( - creattime__day=d['day'], + creattime__day=d['day'].day, creattime__month=now.month, creattime__year=now.year, is_awarded=1 ).count() result.append({ - 'day': d['day'], + 'day': d['day'].day, 'quotes': d['quotes'], 'won': won, }) @@ -361,7 +361,7 @@ class DashboardView(views.APIView): creattime__gte=start_of_month, creattime__lte=now ) - .extra(select={'day': 'DAY(creattime)'}) + .annotate(day=TruncDate('creattime')) .values('day') .annotate(quotes=Count('autoid')) .order_by('day') @@ -370,13 +370,13 @@ class DashboardView(views.APIView): for d in data: won = QuotationMaster.objects.filter( supplier_code=user.username, - creattime__day=d['day'], + creattime__day=d['day'].day, creattime__month=now.month, creattime__year=now.year, is_awarded=1 ).count() result.append({ - 'day': d['day'], + 'day': d['day'].day, 'quotes': d['quotes'], 'won': won, }) From 3ddea70c551d7f6efc86245760cb0da278119dfe Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Tue, 7 Apr 2026 11:13:47 +0800 Subject: [PATCH 16/16] feat(dashboard): add system notification card to right sidebar Reference the original django-vue3-admin framework home page, combine the home image and notification card into one layout. The notification list appears below the image, matching the original framework's style with creator name, datetime, and title for each notification item. --- .../pisadmin/dashboard/BuyerDashboard.vue | 156 +++++++++++++++++- .../pisadmin/dashboard/SupplierDashboard.vue | 156 +++++++++++++++++- 2 files changed, 300 insertions(+), 12 deletions(-) diff --git a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue index b532378..f742a8c 100644 --- a/web/src/views/pisadmin/dashboard/BuyerDashboard.vue +++ b/web/src/views/pisadmin/dashboard/BuyerDashboard.vue @@ -80,11 +80,32 @@
- +