From 4951f434ba268fdb0771bbdb89ca09a581caf43c Mon Sep 17 00:00:00 2001
From: buhuazhen <hua100783@gmail.com>
Date: 星期一, 18 五月 2026 10:05:24 +0800
Subject: [PATCH] feat(质量检验模块): 新增查看按钮并优化查看弹窗

---
 src/views/financialManagement/voucher/generalLedger.vue |  382 +++++++++++++++++++++++++++++++++---------------------
 1 files changed, 232 insertions(+), 150 deletions(-)

diff --git a/src/views/financialManagement/voucher/generalLedger.vue b/src/views/financialManagement/voucher/generalLedger.vue
index 5da2d70..b362279 100644
--- a/src/views/financialManagement/voucher/generalLedger.vue
+++ b/src/views/financialManagement/voucher/generalLedger.vue
@@ -1,114 +1,128 @@
 <template>
-  <div class="app-container">
-    <el-form :model="filters" :inline="true">
-      <el-form-item label="浼氳绉戠洰:">
-        <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="璇烽�夋嫨浼氳绉戠洰" clearable style="width: 250px;" filterable />
-      </el-form-item>
-      <el-form-item label="鏈熼棿:">
-        <el-date-picker v-model="filters.startMonth" type="month" placeholder="寮�濮嬫湀浠�" value-format="YYYY-MM" style="width: 140px;" />
-        <span style="margin: 0 10px;">鑷�</span>
-        <el-date-picker v-model="filters.endMonth" type="month" placeholder="缁撴潫鏈堜唤" value-format="YYYY-MM" style="width: 140px;" />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="getTableData">鏌ヨ</el-button>
-        <el-button @click="resetFilters">閲嶇疆</el-button>
-        <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>
-        <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
-      </el-form-item>
-    </el-form>
+  <div class="app-container ledger-page">
+    <div class="ledger-layout">
+      <aside class="subject-panel">
+        <el-input v-model="subjectKeyword" placeholder="璇疯緭鍏ョ鐩悕绉�/缂栧彿" clearable prefix-icon="Search" />
+        <el-scrollbar class="subject-tree-scroll">
+          <el-tree
+            ref="subjectTreeRef"
+            :data="subjectOptions"
+            node-key="code"
+            :props="{ label: 'name', children: 'children' }"
+            highlight-current
+            default-expand-all
+            :expand-on-click-node="false"
+            :filter-node-method="filterSubjectNode"
+            @node-click="handleSubjectClick"
+          >
+            <template #default="{ data }">
+              <span class="subject-node">{{ data.code }} {{ data.name }}</span>
+            </template>
+          </el-tree>
+        </el-scrollbar>
+      </aside>
 
-    <div class="ledger-header" v-if="currentSubject">
-      <h2>绉戠洰鎬昏处</h2>
-      <p>绉戠洰: {{ currentSubject.code }} {{ currentSubject.name }}</p>
-      <p>鏈熼棿: {{ filters.startMonth }} 鑷� {{ filters.endMonth }}</p>
+      <section class="ledger-content">
+        <el-form :model="filters" :inline="true" class="filter-form">
+          <el-form-item label="鏈熼棿:">
+            <el-date-picker v-model="filters.startMonth" type="month" placeholder="寮�濮嬫湀浠�" value-format="YYYY-MM" style="width: 140px;" />
+            <span style="margin: 0 10px;">鑷�</span>
+            <el-date-picker v-model="filters.endMonth" type="month" placeholder="缁撴潫鏈堜唤" value-format="YYYY-MM" style="width: 140px;" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getTableData">鏌ヨ</el-button>
+            <el-button @click="resetFilters">閲嶇疆</el-button>
+<!--            <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>-->
+            <!-- <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button> -->
+          </el-form-item>
+        </el-form>
+
+        <div class="table_list">
+          <el-table :data="dataList" border style="width: 100%">
+            <el-table-column prop="date" label="鏃ユ湡"/>
+            <!-- <el-table-column prop="voucherNo" label="鍑瘉瀛楀彿" width="120" /> -->
+            <!-- <el-table-column prop="summary" label="鎽樿" min-width="200" show-overflow-tooltip /> -->
+            <el-table-column prop="debit" label="鍊熸柟">
+              <template #default="{ row }">
+                <span v-if="row.debit > 0" class="text-danger">楼{{ formatMoney(row.debit) }}</span>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="credit" label="璐锋柟">
+              <template #default="{ row }">
+                <span v-if="row.credit > 0" class="text-success">楼{{ formatMoney(row.credit) }}</span>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="鏂瑰悜">
+              <template #default="{ row }">
+                <el-tag :type="row.direction === '鍊�' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="浣欓">
+              <template #default="{ row }">
+                <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">楼{{ formatMoney(Math.abs(row.balance)) }}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+
+        <el-empty v-if="!currentSubject" description="璇烽�夋嫨浼氳绉戠洰鏌ヨ" style="margin-top: 50px;" />
+      </section>
     </div>
-
-    <div class="table_list">
-      <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries">
-        <el-table-column prop="date" label="鏃ユ湡" width="120" />
-        <el-table-column prop="voucherNo" label="鍑瘉瀛楀彿" width="120" />
-        <el-table-column prop="summary" label="鎽樿" min-width="200" show-overflow-tooltip />
-        <el-table-column label="鍊熸柟" width="150">
-          <template #default="{ row }">
-            <span v-if="row.debit > 0" class="text-danger">楼{{ formatMoney(row.debit) }}</span>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="璐锋柟" width="150">
-          <template #default="{ row }">
-            <span v-if="row.credit > 0" class="text-success">楼{{ formatMoney(row.credit) }}</span>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="鏂瑰悜" width="80">
-          <template #default="{ row }">
-            <el-tag :type="row.direction === '鍊�' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="浣欓" width="150">
-          <template #default="{ row }">
-            <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">楼{{ formatMoney(Math.abs(row.balance)) }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
-    </div>
-
-    <el-empty v-if="!currentSubject" description="璇烽�夋嫨浼氳绉戠洰鏌ヨ" style="margin-top: 50px;" />
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted, computed } from "vue";
+import { ref, reactive, onMounted, computed, watch, nextTick } from "vue";
 import { ElMessage } from "element-plus";
+import { listAccountSubject } from "@/api/financialManagement/accountSubject";
+import { getGeneralLedger } from "@/api/financialManagement/ledger";
 
 defineOptions({
   name: "绉戠洰鎬昏处",
 });
 
 const filters = reactive({
-  subject: [],
-  startMonth: "2024-01",
-  endMonth: "2024-03",
+  subject: "",
+  startMonth: "",
+  endMonth: "",
 });
 
 const dataList = ref([]);
+const subjectOptions = ref([]);
+const subjectKeyword = ref("");
+const subjectTreeRef = ref();
 
-const subjectOptions = [
-  {
-    code: "1001",
-    name: "搴撳瓨鐜伴噾",
-    children: [],
-  },
-  {
-    code: "1002",
-    name: "閾惰瀛樻",
-    children: [
-      { code: "100201", name: "宸ュ晢閾惰" },
-      { code: "100202", name: "寤鸿閾惰" },
-    ],
-  },
-  {
-    code: "1122",
-    name: "搴旀敹璐︽",
-    children: [],
-  },
-  {
-    code: "2202",
-    name: "搴斾粯璐︽",
-    children: [],
-  },
-  {
-    code: "6001",
-    name: "涓昏惀涓氬姟鏀跺叆",
-    children: [],
-  },
+const getPreviousMonth = () => {
+  const date = new Date();
+  date.setDate(1);
+  date.setMonth(date.getMonth() - 1);
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, "0");
+  return `${year}-${month}`;
+};
+
+const defaultMonth = getPreviousMonth();
+filters.startMonth = defaultMonth;
+filters.endMonth = defaultMonth;
+
+const fallbackSubjects = [
+  { code: "1001", name: "搴撳瓨鐜伴噾" },
+  { code: "1002", name: "閾惰瀛樻" },
+  { code: "1122", name: "搴旀敹璐︽" },
+  { code: "2202", name: "搴斾粯璐︽" },
+  { code: "6001", name: "涓昏惀涓氬姟鏀跺叆" },
 ];
 
-const currentSubject = computed(() => {
-  if (!filters.subject || filters.subject.length === 0) return null;
-  const code = filters.subject[filters.subject.length - 1];
-  return findSubject(subjectOptions, code);
-});
+const toTree = (nodes = []) =>
+  nodes
+    .filter(item => item.subjectCode && item.subjectName)
+    .map(item => ({
+      code: item.subjectCode,
+      name: item.subjectName,
+      children: toTree(item.children || []),
+    }));
 
 const findSubject = (options, code) => {
   for (const item of options) {
@@ -121,65 +135,104 @@
   return null;
 };
 
+const currentSubject = computed(() => {
+  if (!filters.subject) return null;
+  return findSubject(subjectOptions.value, filters.subject);
+});
+
+const getFirstSubjectCode = (nodes = []) => {
+  for (const item of nodes) {
+    if (item.code) return item.code;
+    if (item.children && item.children.length > 0) {
+      const childCode = getFirstSubjectCode(item.children);
+      if (childCode) return childCode;
+    }
+  }
+  return "";
+};
+
+const setDefaultSubjectSelection = async () => {
+  const firstCode = getFirstSubjectCode(subjectOptions.value);
+  if (!firstCode) {
+    filters.subject = "";
+    subjectTreeRef.value?.setCurrentKey(null);
+    return;
+  }
+  filters.subject = firstCode;
+  await nextTick();
+  subjectTreeRef.value?.setCurrentKey(firstCode);
+};
+
+const filterSubjectNode = (value, data) => {
+  const keyword = value?.trim();
+  if (!keyword) return true;
+  return `${data.code}${data.name}`.includes(keyword);
+};
+
+watch(subjectKeyword, (value) => {
+  subjectTreeRef.value?.filter(value || "");
+});
+
+const handleSubjectClick = async (data) => {
+  filters.subject = data.code;
+  await getTableData();
+};
+
+const loadSubjectOptions = async () => {
+  let options = [];
+  try {
+    const { data } = await listAccountSubject({
+      current: 1,
+      size: 1000,
+      status: 0,
+    });
+    options = toTree(data?.records || []);
+  } catch (error) {
+    // 鍏ㄥ眬鎷︽埅鍣ㄥ凡鎻愮ず锛屼笅闈㈣蛋鍏滃簳绉戠洰
+  }
+  if (options.length === 0) {
+    options = fallbackSubjects.map(item => ({ ...item, children: [] }));
+  }
+  subjectOptions.value = options;
+  await setDefaultSubjectSelection();
+  if (filters.subject) {
+    await getTableData();
+  }
+};
+
 const formatMoney = (value) => {
   if (value === undefined || value === null) return "0.00";
   return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
 };
 
-const mockData = [
-  { date: "2024-01-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 100000 },
-  { date: "2024-01-05", voucherNo: "璁�-0001", summary: "閿�鍞敹鍏�", debit: 5650, credit: 0, direction: "鍊�", balance: 105650 },
-  { date: "2024-01-10", voucherNo: "璁�-0002", summary: "閲囪喘鏀嚭", debit: 0, credit: 8000, direction: "鍊�", balance: 97650 },
-  { date: "2024-01-15", voucherNo: "璁�-0003", summary: "鏀跺埌璐ф", debit: 10000, credit: 0, direction: "鍊�", balance: 107650 },
-  { date: "2024-01-20", voucherNo: "璁�-0004", summary: "鏀粯璐圭敤", debit: 0, credit: 5000, direction: "鍊�", balance: 102650 },
-  { date: "2024-01-31", voucherNo: "-", summary: "鏈湀鍚堣", debit: 15650, credit: 13000, direction: "鍊�", balance: 102650 },
-  { date: "2024-02-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 102650 },
-  { date: "2024-02-10", voucherNo: "璁�-0005", summary: "閿�鍞敹鍏�", debit: 8000, credit: 0, direction: "鍊�", balance: 110650 },
-  { date: "2024-02-15", voucherNo: "璁�-0006", summary: "閲囪喘鏀嚭", debit: 0, credit: 12000, direction: "鍊�", balance: 98650 },
-  { date: "2024-02-28", voucherNo: "-", summary: "鏈湀鍚堣", debit: 8000, credit: 12000, direction: "鍊�", balance: 98650 },
-  { date: "2024-03-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 98650 },
-  { date: "2024-03-05", voucherNo: "璁�-0007", summary: "閿�鍞敹鍏�", debit: 12000, credit: 0, direction: "鍊�", balance: 110650 },
-  { date: "2024-03-10", voucherNo: "璁�-0008", summary: "鏀粯宸ヨ祫", debit: 0, credit: 15000, direction: "鍊�", balance: 95650 },
-  { date: "2024-03-31", voucherNo: "-", summary: "鏈湀鍚堣", debit: 12000, credit: 15000, direction: "鍊�", balance: 95650 },
-  { date: "2024-03-31", voucherNo: "-", summary: "鏈勾绱", debit: 35650, credit: 40000, direction: "鍊�", balance: 95650 },
-];
-
-const getTableData = () => {
+// 鑱旇皟绾﹀畾锛氭�昏处鎺ュ彛杩斿洖琛屾暟缁勶紙rowType/date/voucherNo/summary/debit/credit/direction/balance锛�
+const getTableData = async () => {
   if (!currentSubject.value) {
     dataList.value = [];
     return;
   }
-  dataList.value = [...mockData];
+  try {
+    const { data } = await getGeneralLedger({
+      subjectCode: currentSubject.value.code,
+      startMonth: filters.startMonth,
+      endMonth: filters.endMonth,
+    });
+    dataList.value = Array.isArray(data) ? data : data?.records || [];
+  } catch (error) {
+    // 鎻愮ず鐢卞叏灞�璇锋眰鎷︽埅鍣ㄥ鐞嗭紝杩欓噷浠呴槻姝㈡湭鎹曡幏寮傚父
+  }
 };
 
-const resetFilters = () => {
-  filters.subject = [];
-  filters.startMonth = "2024-01";
-  filters.endMonth = "2024-03";
+const resetFilters = async () => {
+  filters.startMonth = defaultMonth;
+  filters.endMonth = defaultMonth;
   dataList.value = [];
-};
-
-const getSummaries = (param) => {
-  const { columns, data } = param;
-  const sums = [];
-  columns.forEach((column, index) => {
-    if (index === 0) {
-      sums[index] = "鍚堣";
-      return;
-    }
-    if (column.property === "debit") {
-      const values = data.map(item => Number(item.debit));
-      const sum = values.reduce((prev, curr) => prev + curr, 0);
-      sums[index] = "楼" + formatMoney(sum);
-    } else if (column.property === "credit") {
-      const values = data.map(item => Number(item.credit));
-      const sum = values.reduce((prev, curr) => prev + curr, 0);
-      sums[index] = "楼" + formatMoney(sum);
-    } else {
-      sums[index] = "";
-    }
-  });
-  return sums;
+  subjectKeyword.value = "";
+  subjectTreeRef.value?.filter("");
+  await setDefaultSubjectSelection();
+  if (filters.subject) {
+    await getTableData();
+  }
 };
 
 const handlePrint = () => {
@@ -190,22 +243,43 @@
   ElMessage.success("瀵煎嚭鎴愬姛");
 };
 
-onMounted(() => {
-  // 榛樿涓嶅姞杞芥暟鎹紝闇�瑕侀�夋嫨绉戠洰
+onMounted(async () => {
+  await loadSubjectOptions();
 });
 </script>
 
 <style lang="scss" scoped>
-.ledger-header {
-  text-align: center;
-  margin-bottom: 20px;
-  h2 {
-    margin: 0 0 10px 0;
-  }
-  p {
-    color: #606266;
-    margin: 5px 0;
-  }
+.ledger-layout {
+  display: flex;
+  gap: 16px;
+}
+
+.subject-panel {
+  width: 260px;
+  flex-shrink: 0;
+  padding: 12px;
+  border: 1px solid #e4e7ed;
+  border-radius: 8px;
+  background-color: #fff;
+}
+
+.subject-tree-scroll {
+  height: 600px;
+  margin-top: 12px;
+}
+
+.subject-node {
+  display: inline-flex;
+  align-items: center;
+}
+
+.ledger-content {
+  flex: 1;
+  min-width: 0;
+}
+
+.filter-form {
+  margin-bottom: 12px;
 }
 
 .text-primary {
@@ -227,4 +301,12 @@
   color: #e6a23c;
   font-weight: bold;
 }
+
+.subject-panel :deep(.el-tree-node__content) {
+  height: 34px;
+}
+
+.subject-panel :deep(.el-tree-node.is-current > .el-tree-node__content) {
+  background-color: #f0f7ff;
+}
 </style>

--
Gitblit v1.9.3