Explorar o código

refactor: 简化为单项目模式,隐藏嘉定烟草菜单

- 前端移除嘉定烟草菜单分组,改为"主要功能"
- 移除双项目相关变量(currentProject、jsStats、jdStats)
- 页面导航简化为 orders/import/logs
- 后端移除动态数据源中间件,使用单一数据库连接池
- 移除 X-Project 请求头传递
C hai 1 semana
pai
achega
326486e914
Modificáronse 5 ficheiros con 60 adicións e 178 borrados
  1. 5 19
      server/db.js
  2. 28 32
      server/routes/orders.js
  3. 17 62
      web/src/App.vue
  4. 1 13
      web/src/api/orders.js
  5. 9 52
      web/src/components/Sidebar.vue

+ 5 - 19
server/db.js

@@ -1,7 +1,6 @@
 const { Pool } = require('pg');
 
-// 金山烟草(默认)
-const jsPool = new Pool({
+const pool = new Pool({
   host: process.env.DB_HOST || '121.37.183.152',
   port: parseInt(process.env.DB_PORT || '5432'),
   database: process.env.DB_NAME || 'lp-card-test',
@@ -12,23 +11,10 @@ const jsPool = new Pool({
   connectionTimeoutMillis: 5000,
 });
 
-// 嘉定烟草
-const jdPool = new Pool({
-  host: process.env.JD_DB_HOST || 'pgm-uf69f8n00s9mqn91go.pg.rds.aliyuncs.com',
-  port: parseInt(process.env.JD_DB_PORT || '5432'),
-  database: process.env.JD_DB_NAME || 'gt1_fmzgo',
-  user: process.env.JD_DB_USER || 'gt1fmzgo',
-  password: process.env.JD_DB_PASSWORD || 'Bm123456@',
-  max: 20,
-  idleTimeoutMillis: 30000,
-  connectionTimeoutMillis: 5000,
-});
-
-jsPool.on('error', (err) => console.error('金山数据库连接池异常:', err.message));
-jdPool.on('error', (err) => console.error('嘉定数据库连接池异常:', err.message));
+pool.on('error', (err) => console.error('数据库连接池异常:', err.message));
 
-function getPool(project) {
-  return project === 'jd' ? jdPool : jsPool;
+function getPool() {
+  return pool;
 }
 
-module.exports = { getPool, jsPool, jdPool };
+module.exports = { getPool, pool };

+ 28 - 32
server/routes/orders.js

@@ -27,19 +27,15 @@ const USERS = [
   { username: 'user', password: 'user123', role: 'user' },
 ];
 
-// 根据请求头选择数据库中间件
-function dbPool(req, res, next) {
-  req.db = getPool(req.headers['x-project']);
-  next();
-}
-
+// 获取数据库连接池
+const db = getPool();
 // 登录接口
 router.post('/login', async (req, res) => {
   const { username, password } = req.body;
   const user = USERS.find(u => u.username === username && u.password === password);
   if (user) {
     const token = Buffer.from(`${username}:${user.role}:${Date.now()}`).toString('base64');
-    await logOperation(getPool('js'), user.username, user.role, '用户登录', `${user.username} 登录系统`, req.ip);
+    await logOperation(db, user.username, user.role, '用户登录', `${user.username} 登录系统`, req.ip);
     res.json({ success: true, token, username: user.username, role: user.role });
   } else {
     res.json({ success: false, message: '用户名或密码错误' });
@@ -101,7 +97,7 @@ const STATUS_MAP = {
 };
 
 // 动态查询订单
-router.post('/query', authMiddleware, dbPool, async (req, res) => {
+router.post('/query', authMiddleware, async (req, res) => {
   try {
     const { receiver_name, receiver_phone, order_no, start_date, end_date, status, page = 1, pageSize = 20 } = req.body;
 
@@ -177,7 +173,7 @@ router.post('/query', authMiddleware, dbPool, async (req, res) => {
     }
 
     const countSql = sql.replace(/SELECT[\s\S]*?FROM/, 'SELECT COUNT(*) AS total FROM');
-    const countResult = await req.db.query(countSql, params);
+    const countResult = await db.query(countSql, params);
     const total = parseInt(countResult.rows[0].total);
 
     const pageNum = Math.max(1, parseInt(page));
@@ -186,8 +182,8 @@ router.post('/query', authMiddleware, dbPool, async (req, res) => {
     sql += ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
     params.push(pageSizeNum, (pageNum - 1) * pageSizeNum);
 
-    const result = await req.db.query(sql, params);
-    await logOperation(req.db, req.user.username, req.user.role, '查询订单', `条件: 状态=${status||'全部'}, 收货人=${receiver_name||'-'}, 手机号=${receiver_phone||'-'}, 订单号=${order_no||'-'}, 日期=${start_date||'-'}~${end_date||'-'}, 第${pageNum}页`, req.ip);
+    const result = await db.query(sql, params);
+    await logOperation(db, req.user.username, req.user.role, '查询订单', `条件: 状态=${status||'全部'}, 收货人=${receiver_name||'-'}, 手机号=${receiver_phone||'-'}, 订单号=${order_no||'-'}, 日期=${start_date||'-'}~${end_date||'-'}, 第${pageNum}页`, req.ip);
     res.json({ success: true, data: result.rows, total, page: pageNum, pageSize: pageSizeNum });
   } catch (err) {
     console.error('查询订单失败:', err);
@@ -196,7 +192,7 @@ router.post('/query', authMiddleware, dbPool, async (req, res) => {
 });
 
 // 导出 Excel
-router.post('/export/excel', authMiddleware, dbPool, async (req, res) => {
+router.post('/export/excel', authMiddleware, async (req, res) => {
   try {
     const { batch_no, card_no, order_no, start_date, end_date, status } = req.body;
 
@@ -268,7 +264,7 @@ router.post('/export/excel', authMiddleware, dbPool, async (req, res) => {
 
     sql += ' ORDER BY c.batch_no, o.create_time, o.order_no';
 
-    const result = await req.db.query(sql, params);
+    const result = await db.query(sql, params);
 
     const workbook = new ExcelJS.Workbook();
     const sheet = workbook.addWorksheet('订单列表');
@@ -325,7 +321,7 @@ router.post('/export/excel', authMiddleware, dbPool, async (req, res) => {
 
     await workbook.xlsx.write(res);
     res.end();
-    await logOperation(req.db, req.user.username, req.user.role, '导出Excel', `导出${result.rows.length}条订单`, req.ip);
+    await logOperation(db, req.user.username, req.user.role, '导出Excel', `导出${result.rows.length}条订单`, req.ip);
   } catch (err) {
     console.error('导出Excel失败:', err);
     res.status(500).json({ success: false, message: err.message });
@@ -333,7 +329,7 @@ router.post('/export/excel', authMiddleware, dbPool, async (req, res) => {
 });
 
 // 导入运单信息 - 批量更新物流(不允许重复发货)
-router.post('/import/logistics', authMiddleware, dbPool, upload.single('file'), async (req, res) => {
+router.post('/import/logistics', authMiddleware, upload.single('file'), async (req, res) => {
   try {
     if (!req.file) {
       return res.status(400).json({ success: false, message: '请上传文件' });
@@ -408,16 +404,16 @@ router.post('/import/logistics', authMiddleware, dbPool, upload.single('file'),
       }
 
       try {
-        await req.db.query('BEGIN');
+        await db.query('BEGIN');
 
         // 查询订单是否存在及状态
-        const orderResult = await req.db.query(
+        const orderResult = await db.query(
           `SELECT id, status FROM gift_order WHERE order_no = $1 AND del_flag = '0'`,
           [orderNo]
         );
 
         if (orderResult.rows.length === 0) {
-          await req.db.query('ROLLBACK');
+          await db.query('ROLLBACK');
           results.failed++;
           results.errors.push(`第${i + 2}行: 订单 ${orderNo} 不存在`);
           continue;
@@ -428,27 +424,27 @@ router.post('/import/logistics', authMiddleware, dbPool, upload.single('file'),
 
         // 不允许重复发货/已完成
         if (orderStatus === 3) {
-          await req.db.query('ROLLBACK');
+          await db.query('ROLLBACK');
           results.failed++;
           results.errors.push(`第${i + 2}行: 订单 ${orderNo} 已发货,不能重复导入`);
           continue;
         }
         if (orderStatus === 1) {
-          await req.db.query('ROLLBACK');
+          await db.query('ROLLBACK');
           results.failed++;
           results.errors.push(`第${i + 2}行: 订单 ${orderNo} 已完成,不能导入`);
           continue;
         }
 
         // 更新订单状态为已发货,同时更新备注
-        await req.db.query(
+        await db.query(
           `UPDATE gift_order SET status = 3, remark = COALESCE($1, remark), update_time = now(), update_by = $2
            WHERE id = $3 AND del_flag = '0'`,
           [remark, 'import', orderId]
         );
 
         // 插入或更新物流信息
-        await req.db.query(
+        await db.query(
           `INSERT INTO gift_logistics (order_id, order_no, company, tracking_no, status, waybill_status, create_time, create_by, update_time, update_by, del_flag)
            VALUES ($1, $2, $3, $4, 0, '已发货', now(), 'import', now(), 'import', '0')
            ON CONFLICT (order_id) DO UPDATE
@@ -456,16 +452,16 @@ router.post('/import/logistics', authMiddleware, dbPool, upload.single('file'),
           [orderId, orderNo, company || '', trackingNo]
         );
 
-        await req.db.query('COMMIT');
+        await db.query('COMMIT');
         results.success++;
       } catch (err) {
-        await req.db.query('ROLLBACK');
+        await db.query('ROLLBACK');
         results.failed++;
         results.errors.push(`第${i + 2}行: ${err.message}`);
       }
     }
 
-    await logOperation(req.db, req.user.username, req.user.role, '导入运单', `成功${results.success}条, 失败${results.failed}条`, req.ip);
+    await logOperation(db, req.user.username, req.user.role, '导入运单', `成功${results.success}条, 失败${results.failed}条`, req.ip);
     res.json({ success: true, results });
   } catch (err) {
     console.error('导入运单失败:', err);
@@ -474,7 +470,7 @@ router.post('/import/logistics', authMiddleware, dbPool, upload.single('file'),
 });
 
 // 下载导入模板
-router.get('/import/template', authMiddleware, dbPool, async (req, res) => {
+router.get('/import/template', authMiddleware, async (req, res) => {
   try {
     const workbook = new ExcelJS.Workbook();
     const sheet = workbook.addWorksheet('运单导入模板');
@@ -513,7 +509,7 @@ router.get('/import/template', authMiddleware, dbPool, async (req, res) => {
 
     await workbook.xlsx.write(res);
     res.end();
-    await logOperation(req.db, req.user.username, req.user.role, '下载导入模板', '下载运单导入模板', req.ip);
+    await logOperation(db, req.user.username, req.user.role, '下载导入模板', '下载运单导入模板', req.ip);
   } catch (err) {
     console.error('下载模板失败:', err);
     res.status(500).json({ success: false, message: err.message });
@@ -521,9 +517,9 @@ router.get('/import/template', authMiddleware, dbPool, async (req, res) => {
 });
 
 // 统计数据接口
-router.get('/stats', dbPool, async (req, res) => {
+router.get('/stats', async (req, res) => {
   try {
-    const result = await req.db.query(`
+    const result = await db.query(`
       SELECT
         COUNT(*) AS total,
         COUNT(*) FILTER (WHERE o.status = 0) AS pending,
@@ -551,7 +547,7 @@ router.get('/stats', dbPool, async (req, res) => {
 });
 
 // 查询操作日志(仅admin)
-router.get('/logs', authMiddleware, adminOnly, dbPool, async (req, res) => {
+router.get('/logs', authMiddleware, adminOnly, async (req, res) => {
   try {
     const { page = 1, pageSize = 20, username, action, start_date, end_date } = req.query;
     let sql = `SELECT * FROM sys_operation_log WHERE 1=1`;
@@ -581,14 +577,14 @@ router.get('/logs', authMiddleware, adminOnly, dbPool, async (req, res) => {
 
     // 总数
     const countSql = sql.replace('SELECT *', 'SELECT COUNT(*) as total');
-    const countResult = await req.db.query(countSql, params);
+    const countResult = await db.query(countSql, params);
     const total = parseInt(countResult.rows[0].total);
 
     sql += ' ORDER BY create_time DESC';
     sql += ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
     params.push(pageSize, (page - 1) * pageSize);
 
-    const result = await req.db.query(sql, params);
+    const result = await db.query(sql, params);
     res.json({ success: true, data: result.rows, total, page: parseInt(page), pageSize: parseInt(pageSize) });
   } catch (err) {
     console.error('查询日志失败:', err);

+ 17 - 62
web/src/App.vue

@@ -8,7 +8,7 @@
       :currentPage="currentPage"
       :username="username"
       :role="userRole"
-      :pendingCount="{ js: jsStats.pending || 0, jd: jdStats.pending || 0 }"
+      :pendingCount="stats.pending || 0"
       @navigate="navigateTo"
       @logout="handleLogout"
     />
@@ -23,20 +23,20 @@
       <div class="content-area">
         <!-- 订单管理 -->
         <OrderManagement
-          v-show="activePage === 'orders'"
+          v-show="currentPage === 'orders'"
           ref="orderRef"
           :tableData="tableData"
           :total="totalCount"
           :loading="loading"
-          :stats="activeStats"
+          :stats="stats"
           @query="handleQuery"
           @exportExcel="handleExportExcel"
-          @openImport="navigateTo(currentProject + '-import')"
+          @openImport="navigateTo('import')"
         />
 
         <!-- 物流导入 -->
         <LogisticsImport
-          v-show="activePage === 'import'"
+          v-show="currentPage === 'import'"
           :importing="importing"
           :importResult="importResult"
           @import="handleImport"
@@ -45,7 +45,7 @@
 
         <!-- 操作日志 -->
         <OperationLogs
-          v-if="userRole === 'admin' && activePage === 'logs'"
+          v-if="userRole === 'admin' && currentPage === 'logs'"
           ref="logRef"
           :tableData="logData"
           :total="logTotal"
@@ -67,11 +67,11 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted, onUnmounted } from 'vue'
+import { ref, onMounted, onUnmounted } from 'vue'
 import { ElMessage } from 'element-plus'
 import { Top } from '@element-plus/icons-vue'
 import {
-  setToken, getToken, removeToken, setCurrentProject,
+  setToken, getToken, removeToken,
   queryOrders, exportExcel, importLogistics, downloadTemplate, queryLogs, getStats
 } from './api/orders.js'
 
@@ -88,31 +88,15 @@ const username = ref('')
 const userRole = ref('')
 const theme = ref('light')
 
-// 项目与页面导航
-const currentProject = ref('js')
-const currentPage = ref('js-orders')
-
-const activePage = computed(() => {
-  const p = currentPage.value
-  return p.substring(p.indexOf('-') + 1)
-})
-
-const activeStats = computed(() => {
-  return currentProject.value === 'jd' ? jdStats.value : jsStats.value
-})
-
-function setProject(project) {
-  currentProject.value = project
-  setCurrentProject(project)
-}
+// 页面导航
+const currentPage = ref('orders')
 
 // 订单数据
 const orderRef = ref(null)
 const tableData = ref([])
 const totalCount = ref(0)
 const loading = ref(false)
-const jsStats = ref({})
-const jdStats = ref({})
+const stats = ref({})
 
 // 导入
 const importing = ref(false)
@@ -142,7 +126,6 @@ onMounted(() => {
     userRole.value = localStorage.getItem('role') || 'user'
     theme.value = localStorage.getItem('theme') || 'light'
     applyTheme()
-    setProject('js')
     handleQuery()
     loadStats()
   }
@@ -163,8 +146,7 @@ function handleLoginSuccess(data) {
   importResult.value = null
   logData.value = []
   logTotal.value = 0
-  setProject('js')
-  currentPage.value = 'js-orders'
+  currentPage.value = 'orders'
   handleQuery()
   loadStats()
 }
@@ -180,33 +162,13 @@ function handleLogout() {
   importResult.value = null
   logData.value = []
   logTotal.value = 0
-  currentProject.value = 'js'
-  currentPage.value = 'js-orders'
+  currentPage.value = 'orders'
   ElMessage.success('已安全退出')
 }
 
 function navigateTo(page) {
-  const project = page.substring(0, 2)
-  const pageType = page.substring(page.indexOf('-') + 1)
-  const projectChanged = project !== currentProject.value
-
-  setProject(project)
   currentPage.value = page
-
-  if (projectChanged) {
-    tableData.value = []
-    totalCount.value = 0
-    importResult.value = null
-    logData.value = []
-    logTotal.value = 0
-  }
-
-  if (pageType === 'orders') {
-    handleQuery()
-    if (projectChanged) loadStats()
-  } else if (pageType === 'logs') {
-    handleQueryLogs()
-  }
+  if (page === 'logs') handleQueryLogs()
 }
 
 // 主题
@@ -223,13 +185,7 @@ function applyTheme() {
 async function loadStats() {
   try {
     const res = await getStats()
-    if (res.data.success) {
-      if (currentProject.value === 'jd') {
-        jdStats.value = res.data.data
-      } else {
-        jsStats.value = res.data.data
-      }
-    }
+    if (res.data.success) stats.value = res.data.data
   } catch (e) { /* ignore */ }
 }
 
@@ -329,9 +285,8 @@ async function handleQueryLogs() {
 }
 
 function handleRefresh() {
-  const pageType = activePage.value
-  if (pageType === 'orders') handleQuery()
-  else if (pageType === 'logs') handleQueryLogs()
+  if (currentPage.value === 'orders') handleQuery()
+  else if (currentPage.value === 'logs') handleQueryLogs()
   loadStats()
   ElMessage.success('数据已刷新')
 }

+ 1 - 13
web/src/api/orders.js

@@ -5,20 +5,8 @@ const api = axios.create({
   timeout: 30000,
 })
 
-// 当前项目(js=金山烟草, jd=嘉定烟草)
-let currentProject = 'js'
-
-export function setCurrentProject(project) {
-  currentProject = project
-}
-
-export function getCurrentProject() {
-  return currentProject
-}
-
-// 请求拦截器:自动附加项目标识和 token
+// 请求拦截器:自动附加 token
 api.interceptors.request.use((config) => {
-  config.headers['X-Project'] = currentProject
   const token = localStorage.getItem('auth_token')
   if (token) {
     config.headers['Authorization'] = token

+ 9 - 52
web/src/components/Sidebar.vue

@@ -9,12 +9,11 @@
     </div>
 
     <nav class="sidebar-nav">
-      <!-- 金山烟草 -->
-      <div class="nav-section-label">金山烟草</div>
+      <div class="nav-section-label">主要功能</div>
       <div
         class="nav-item"
-        :class="{ active: currentPage === 'js-orders' }"
-        @click="$emit('navigate', 'js-orders')"
+        :class="{ active: currentPage === 'orders' }"
+        @click="$emit('navigate', 'orders')"
       >
         <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
           <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/>
@@ -22,12 +21,12 @@
           <path d="M16 10a4 4 0 0 1-8 0"/>
         </svg>
         <span>订单管理</span>
-        <span v-if="pendingCount.js > 0" class="nav-badge">{{ pendingCount.js }}</span>
+        <span v-if="pendingCount > 0" class="nav-badge">{{ pendingCount }}</span>
       </div>
       <div
         class="nav-item"
-        :class="{ active: currentPage === 'js-import' }"
-        @click="$emit('navigate', 'js-import')"
+        :class="{ active: currentPage === 'import' }"
+        @click="$emit('navigate', 'import')"
       >
         <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
           <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
@@ -39,50 +38,8 @@
       <div
         v-if="role === 'admin'"
         class="nav-item"
-        :class="{ active: currentPage === 'js-logs' }"
-        @click="$emit('navigate', 'js-logs')"
-      >
-        <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
-          <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
-          <polyline points="14 2 14 8 20 8"/>
-          <line x1="16" y1="13" x2="8" y2="13"/>
-          <line x1="16" y1="17" x2="8" y2="17"/>
-        </svg>
-        <span>操作日志</span>
-      </div>
-
-      <!-- 嘉定烟草 -->
-      <div class="nav-section-label" style="margin-top: var(--space-3);">嘉定烟草</div>
-      <div
-        class="nav-item"
-        :class="{ active: currentPage === 'jd-orders' }"
-        @click="$emit('navigate', 'jd-orders')"
-      >
-        <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
-          <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/>
-          <line x1="3" y1="6" x2="21" y2="6"/>
-          <path d="M16 10a4 4 0 0 1-8 0"/>
-        </svg>
-        <span>订单管理</span>
-        <span v-if="pendingCount.jd > 0" class="nav-badge">{{ pendingCount.jd }}</span>
-      </div>
-      <div
-        class="nav-item"
-        :class="{ active: currentPage === 'jd-import' }"
-        @click="$emit('navigate', 'jd-import')"
-      >
-        <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
-          <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
-          <polyline points="17 8 12 3 7 8"/>
-          <line x1="12" y1="3" x2="12" y2="15"/>
-        </svg>
-        <span>物流导入</span>
-      </div>
-      <div
-        v-if="role === 'admin'"
-        class="nav-item"
-        :class="{ active: currentPage === 'jd-logs' }"
-        @click="$emit('navigate', 'jd-logs')"
+        :class="{ active: currentPage === 'logs' }"
+        @click="$emit('navigate', 'logs')"
       >
         <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
           <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
@@ -116,7 +73,7 @@ defineProps({
   currentPage: String,
   username: String,
   role: String,
-  pendingCount: { type: Object, default: () => ({ js: 0, jd: 0 }) }
+  pendingCount: { type: Number, default: 0 }
 })
 defineEmits(['navigate', 'logout'])
 </script>