src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/pages/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/pages/sales/receiptPaymentHistory/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/pages/sales/receiptPaymentLedger/detail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/pages/sales/receiptPaymentLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/pages.json
@@ -120,6 +120,27 @@ } }, { "path": "pages/sales/receiptPaymentHistory/index", "style": { "navigationBarTitleText": "忬¾æµæ°´", "navigationStyle": "custom" } }, { "path": "pages/sales/receiptPaymentLedger/index", "style": { "navigationBarTitleText": "客æ·å¾æ¥", "navigationStyle": "custom" } }, { "path": "pages/sales/receiptPaymentLedger/detail", "style": { "navigationBarTitleText": "客æ·å¾æ¥è¯¦æ ", "navigationStyle": "custom" } }, { "path": "pages/common/webview/index", "style": { "navigationBarTitleText": "æµè§ç½é¡µ" src/pages/index.vue
@@ -308,6 +308,16 @@ url: '/pages/sales/receiptPayment/index' }); break; case '忬¾æµæ°´': uni.navigateTo({ url: '/pages/sales/receiptPaymentHistory/index' }); break; case '客æ·å¾æ¥': uni.navigateTo({ url: '/pages/sales/receiptPaymentLedger/index' }); break; case 'åå审æ¹': uni.navigateTo({ url: '/pages/cooperativeOffice/collaborativeApproval/index' src/pages/sales/receiptPaymentHistory/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,378 @@ <template> <view class="receipt-payment-history"> <!-- 页é¢å¤´é¨ --> <van-nav-bar title="忬¾åå²" left-text="è¿å" left-arrow @click-left="goBack" fixed placeholder /> <!-- æç´¢åºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <input class="search-text" placeholder="请è¾å ¥å®¢æ·åç§°/客æ·ååå·" v-model="searchForm.searchText" @input="getList" /> </view> <view class="search-button" @click="getList"> <up-icon name="search" size="24" color="#999"></up-icon> </view> </view> </view> <!-- ç»è®¡ä¿¡æ¯ --> <view class="summary-info" v-if="tableData.length > 0"> <view class="summary-item"> <text class="summary-label">æ»è®°å½æ°</text> <text class="summary-value">{{ tableData.length }}</text> </view> <view class="summary-item"> <text class="summary-label">æ»éé¢</text> <text class="summary-value highlight">{{ formatAmount(totalAmount) }}</text> </view> </view> <!-- 忬¾åå²å表 --> <view class="history-list" v-if="tableData.length > 0"> <view v-for="(item, index) in tableData" :key="index"> <view class="history-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">{{ item.salesContractNo }}</text> </view> <view class="item-tag" :class="getTagClass(item.receiptPaymentType)"> <text class="tag-text">{{ formatReceiptType(item.receiptPaymentType) }}</text> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">客æ·ååå·</text> <text class="detail-value">{{ item.customerContractNo }}</text> </view> <view class="detail-row"> <text class="detail-label">客æ·åç§°</text> <text class="detail-value">{{ item.customerName }}</text> </view> <view class="detail-row"> <text class="detail-label">项ç®åç§°</text> <text class="detail-value">{{ item.projectName }}</text> </view> <view class="detail-row"> <text class="detail-label">忬¾æ¥æ</text> <text class="detail-value">{{ item.receiptPaymentDate }}</text> </view> <view class="detail-row"> <text class="detail-label">忬¾éé¢(å )</text> <text class="detail-value highlight">{{ formatAmount(item.receiptPaymentAmount) }}</text> </view> <up-divider></up-divider> <view class="detail-info"> <view class="detail-row"> <text class="detail-label">ç»è®°äºº</text> <text class="detail-value">{{ item.registrant }}</text> </view> <view class="detail-row"> <text class="detail-label">ç»è®°æ¥æ</text> <text class="detail-value">{{ item.createTime }}</text> </view> </view> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ åæ¬¾å岿°æ®</text> </view> </view> </template> <script setup> import { ref, computed } from 'vue'; import { onShow } from '@dcloudio/uni-app'; import { receiptPaymentHistoryListPage } from "@/api/salesManagement/receiptPayment.js"; // æç´¢è¡¨å const searchForm = ref({ searchText: '', }); // è¡¨æ ¼æ°æ® const tableData = ref([]); // å页忰 const page = ref({ current: -1, size: -1, }); const totalAmount = computed(() => { return tableData.value.reduce((sum, item) => { return sum + (parseFloat(item.receiptPaymentAmount) || 0); }, 0); }); // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // æ¥è¯¢å表 const getList = () => { const params = { ...searchForm.value, ...page.value }; receiptPaymentHistoryListPage(params).then((res) => { tableData.value = res.records; }).catch(() => { uni.showToast({ title: 'æ¥è¯¢å¤±è´¥', icon: 'error' }); }); }; // æ ¼å¼å忬¾æ¹å¼ const formatReceiptType = (type) => { if (type == 0) { return "çµæ±"; } else if (type == 1) { return "æ¿å "; } else { return "æªç¥"; } }; // è·åæ ç¾æ ·å¼ç±» const getTagClass = (type) => { if (type == 0) { return "tag-electric"; } else if (type == 1) { return "tag-acceptance"; } else { return "tag-unknown"; } }; // æ ¼å¼åéé¢ const formatAmount = (amount) => { return amount ? parseFloat(amount).toFixed(2) : '0.00'; }; onShow(() => { // 页颿¾ç¤ºæ¶å·æ°å表 getList(); }); </script> <style scoped lang="scss"> .u-divider { margin: 0 !important; } .receipt-payment-history { min-height: 100vh; background: #f8f9fa; position: relative; } .search-section { padding: 10px 20px; background: #ffffff; } .search-bar { display: flex; align-items: center; gap: 12px; } .search-input { flex: 1; background: #f5f5f5; border-radius: 24px; padding: 10px 16px; display: flex; align-items: center; gap: 8px; } .search-text { flex: 1; font-size: 14px; color: #333; background: transparent; border: none; outline: none; } .search-text::placeholder { color: #999; } .search-button { width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; } .history-list { padding: 20px; } .history-item { background: #ffffff; border-radius: 12px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); padding: 0 16px; } .item-header { padding: 16px 0; display: flex; align-items: center; justify-content: space-between; } .item-left { display: flex; align-items: center; gap: 8px; } .document-icon { width: 24px; height: 24px; background: #2979ff; border-radius: 4px; display: flex; align-items: center; justify-content: center; } .item-id { font-size: 14px; color: #333; font-weight: 500; } .item-tag { border-radius: 4px; padding: 2px 8px; } .tag-electric { background: #4caf50; } .tag-acceptance { background: #ff9800; } .tag-unknown { background: #9e9e9e; } .tag-text { font-size: 11px; color: #ffffff; font-weight: 500; } .item-details { padding: 16px 0; } .detail-row { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 8px; &:last-child { margin-bottom: 0; } } .detail-info { margin-top: 10px; display: flex; align-items: flex-start; justify-content: space-between; } .detail-label { font-size: 12px; color: #777777; min-width: 60px; } .detail-value { font-size: 12px; color: #000000; text-align: right; flex: 1; margin-left: 16px; } .detail-value.highlight { color: #2979ff; font-weight: 500; } .no-data { padding: 40px 0; text-align: center; color: #999; } .summary-info { background: #ffffff; margin: 20px 20px 0 20px; border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .summary-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; &:last-child { margin-bottom: 0; } } .summary-label { font-size: 14px; color: #666; } .summary-value { font-size: 14px; color: #333; font-weight: 500; } .summary-value.highlight { color: #2979ff; font-weight: 600; } </style> src/pages/sales/receiptPaymentLedger/detail.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,297 @@ <template> <view class="receipt-payment-detail"> <!-- 页é¢å¤´é¨ --> <van-nav-bar title="客æ·å¾æ¥è¯¦æ " left-text="è¿å" left-arrow @click-left="goBack" fixed placeholder /> <!-- ç»è®¡ä¿¡æ¯ --> <view class="summary-info" v-if="tableData.length > 0"> <view class="summary-item"> <text class="summary-label">æ»è®°å½æ°</text> <text class="summary-value">{{ tableData.length }}</text> </view> <view class="summary-item"> <text class="summary-label">å¼ç¥¨æ»éé¢</text> <text class="summary-value">{{ formatAmount(invoiceTotal) }}</text> </view> <view class="summary-item"> <text class="summary-label">忬¾æ»éé¢</text> <text class="summary-value highlight">{{ formatAmount(receiptTotal) }}</text> </view> <view class="summary-item"> <text class="summary-label">åºæ¶æ»éé¢</text> <text class="summary-value danger">{{ formatAmount(unReceiptTotal) }}</text> </view> </view> <!-- 忬¾è®°å½æç»å表 --> <view class="detail-list" v-if="tableData.length > 0"> <view v-for="(item, index) in tableData" :key="index" class="detail-item"> <view class="item-header"> <view class="item-left"> <view class="record-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-index">{{ index + 1 }}</text> </view> <view class="item-date">{{ item.happenTime }}</view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">å¼ç¥¨éé¢(å )</text> <text class="detail-value">{{ formatAmount(item.invoiceAmount) }}</text> </view> <view class="detail-row"> <text class="detail-label">忬¾éé¢(å )</text> <text class="detail-value highlight">{{ formatAmount(item.receiptAmount) }}</text> </view> <view class="detail-row"> <text class="detail-label">åºæ¶éé¢(å )</text> <text class="detail-value danger">{{ formatAmount(item.unReceiptAmount) }}</text> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ åæ¬¾è®°å½</text> </view> </view> </template> <script setup> import { ref, computed, onMounted } from 'vue'; import { onShow } from '@dcloudio/uni-app'; import { customerInteractions } from "@/api/salesManagement/receiptPayment.js"; // 客æ·ä¿¡æ¯ const customerId = ref(''); // è¡¨æ ¼æ°æ® const tableData = ref([]); const invoiceTotal = computed(() => { return tableData.value.reduce((sum, item) => { return sum + (parseFloat(item.invoiceAmount) || 0); }, 0); }); const receiptTotal = computed(() => { return tableData.value.reduce((sum, item) => { return sum + (parseFloat(item.receiptAmount) || 0); }, 0); }); const unReceiptTotal = computed(() => { return tableData.value.reduce((sum, item) => { return sum + (parseFloat(item.unReceiptAmount) || 0); }, 0); }); // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // è·å页é¢åæ° const getPageParams = () => { const pages = getCurrentPages(); const currentPage = pages[pages.length - 1]; const options = currentPage.options; if (options.customerId) { customerId.value = options.customerId; } }; // æ¥è¯¢å表 const getList = () => { if (!customerId.value) { uni.showToast({ title: '客æ·ä¿¡æ¯ç¼ºå¤±', icon: 'error' }); return; } const param = { customerId: customerId.value, }; customerInteractions(param).then((res) => { tableData.value = res.data; }).catch(() => { uni.showToast({ title: 'æ¥è¯¢å¤±è´¥', icon: 'error' }); }); }; // æ ¼å¼åéé¢ const formatAmount = (amount) => { return amount ? parseFloat(amount).toFixed(2) : '0.00'; }; onShow(() => { // 页颿¾ç¤ºæ¶è·ååæ°å¹¶å·æ°å表 getPageParams(); getList(); }); onMounted(() => { // 页é¢å è½½æ¶è·ååæ°å¹¶å·æ°å表 getPageParams(); getList(); }); </script> <style scoped lang="scss"> .receipt-payment-detail { min-height: 100vh; background: #f8f9fa; position: relative; } .u-divider { margin: 0 !important; } .summary-info { background: #ffffff; margin: 20px 20px 0 20px; border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .summary-item { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; &:last-child { margin-bottom: 0; } } .summary-label { font-size: 14px; color: #666; } .summary-value { font-size: 14px; color: #333; font-weight: 500; } .summary-value.highlight { color: #2979ff; font-weight: 600; } .summary-value.danger { color: #ff4757; font-weight: 600; } .detail-list { padding: 20px; } .detail-item { background: #ffffff; border-radius: 12px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); padding: 0 16px; } .item-header { padding: 10px 0; display: flex; align-items: center; justify-content: space-between; } .item-left { display: flex; align-items: center; gap: 8px; } .record-icon { width: 24px; height: 24px; background: #2979ff; border-radius: 4px; display: flex; align-items: center; justify-content: center; } .item-index { font-size: 14px; color: #333; font-weight: 500; } .item-date { font-size: 12px; color: #666; } .item-details { padding: 16px 0; } .detail-row { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 8px; &:last-child { margin-bottom: 0; } } .detail-label { font-size: 12px; color: #777777; min-width: 60px; } .detail-value { font-size: 12px; color: #000000; text-align: right; flex: 1; margin-left: 16px; } .detail-value.highlight { color: #2979ff; font-weight: 500; } .detail-value.danger { color: #ff4757; font-weight: 500; } .no-data { padding: 40px 0; text-align: center; color: #999; } </style> src/pages/sales/receiptPaymentLedger/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,307 @@ <template> <view class="receipt-payment-ledger"> <!-- 页é¢å¤´é¨ --> <van-nav-bar title="客æ·å¾æ¥" left-text="è¿å" left-arrow @click-left="goBack" fixed placeholder /> <!-- æç´¢åºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <input class="search-text" placeholder="请è¾å ¥å®¢æ·åç§°" v-model="searchForm.searchText" @input="handleQuery" /> </view> <view class="search-button" @click="handleQuery"> <up-icon name="search" size="24" color="#999"></up-icon> </view> </view> </view> <!-- 客æ·å表 --> <view class="customer-list-container"> <view class="customer-list" v-if="tableData.length > 0"> <view v-for="(item, index) in tableData" :key="item.id" class="customer-item" @click="rowClickMethod(item)" > <view class="item-header"> <view class="item-left"> <view class="customer-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="customer-name">{{ item.customerName }}</text> </view> <view class="item-right"> <up-icon name="arrow-right" size="16" color="#999"></up-icon> </view> </view> <up-divider></up-divider> <view class="item-details"> <view class="detail-row"> <text class="detail-label">å¼ç¥¨éé¢(å )</text> <text class="detail-value">{{ formattedNumber(item.invoiceTotal) }}</text> </view> <view class="detail-row"> <text class="detail-label">忬¾éé¢(å )</text> <text class="detail-value">{{ formattedNumber(item.receiptPaymentAmount) }}</text> </view> <view class="detail-row"> <text class="detail-label">åºæ¶éé¢(å )</text> <text class="detail-value highlight danger">{{ formattedNumber(item.unReceiptPaymentAmount) }}</text> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ å®¢æ·æ°æ®</text> </view> </view> </view> </template> <script setup> import { onMounted, ref, reactive, toRefs } from "vue"; import { onShow } from '@dcloudio/uni-app'; import { invoiceLedgerSalesAccount } from "@/api/salesManagement/invoiceLedger"; const tableData = ref([]); const page = reactive({ current: -1, size: -1, }); const data = reactive({ searchForm: { searchText: "", invoiceDate: "", }, }); const { searchForm } = toRefs(data); // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // æ¥è¯¢å表 const handleQuery = () => { getList(); }; const getList = () => { invoiceLedgerSalesAccount({ ...searchForm.value, ...page }).then((res) => { tableData.value = res.data.records; }).catch(() => { uni.showToast({ title: 'æ¥è¯¢å¤±è´¥', icon: 'error' }); }); }; const formattedNumber = (value) => { return parseFloat(value || 0).toFixed(2); }; const rowClickMethod = (row) => { // 跳转å°åæ¬¾è®°å½æç»é¡µé¢ uni.navigateTo({ url: `/pages/sales/receiptPaymentLedger/detail?customerId=${row.id}` }); }; onShow(() => { // 页颿¾ç¤ºæ¶å·æ°å表 getList(); }); onMounted(() => { getList(); }); </script> <style scoped lang="scss"> .u-divider { margin: 0 !important; } .receipt-payment-ledger { min-height: 100vh; background: #f8f9fa; position: relative; } .search-section { padding: 10px 20px; background: #ffffff; } .search-bar { display: flex; align-items: center; gap: 12px; } .search-input { flex: 1; background: #f5f5f5; border-radius: 24px; padding: 10px 16px; display: flex; align-items: center; gap: 8px; } .search-text { flex: 1; font-size: 14px; color: #333; background: transparent; border: none; outline: none; } .search-text::placeholder { color: #999; } .search-button { width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; } .customer-list-container { padding: 20px; } .customer-list { display: flex; flex-direction: column; gap: 16px; } .customer-item { background: #ffffff; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); padding: 0 16px; transition: all 0.3s ease; &:active { transform: scale(0.98); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); } } .item-header { padding: 16px 0; display: flex; align-items: center; justify-content: space-between; } .item-left { display: flex; align-items: center; gap: 8px; } .item-right { display: flex; align-items: center; gap: 8px; } .customer-icon { width: 24px; height: 24px; background: #2979ff; border-radius: 4px; display: flex; align-items: center; justify-content: center; } .customer-name { font-size: 14px; color: #333; font-weight: 500; } .item-index { font-size: 12px; color: #999; background: #f5f5f5; padding: 2px 8px; border-radius: 12px; } .item-details { padding: 16px 0; } .detail-row { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 8px; &:last-child { margin-bottom: 0; } } .detail-label { font-size: 12px; color: #777777; min-width: 60px; } .detail-value { font-size: 12px; color: #000000; text-align: right; flex: 1; margin-left: 16px; } .detail-value.highlight { color: #2979ff; font-weight: 500; } .detail-value.danger { color: #ff4757; font-weight: 500; } .no-data { padding: 40px 0; text-align: center; color: #999; } </style>