From 85b3cc4cb996040632aa656c79b5047bd8b2597c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 25 八月 2025 13:57:55 +0800
Subject: [PATCH] 1.来票台账开发联调
---
src/pages/index.vue | 5
src/pages/procurementManagement/invoiceEntry/add.vue | 4
src/pages.json | 14
src/pages/procurementManagement/procurementInvoiceLedger/index.vue | 767 ++++++++++++++++++++++++++++++++++++++++
src/pages/procurementManagement/procurementInvoiceLedger/detail.vue | 292 +++++++++++++++
5 files changed, 1,080 insertions(+), 2 deletions(-)
diff --git a/src/pages.json b/src/pages.json
index 10a7870..948365c 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -184,6 +184,20 @@
}
},
{
+ "path": "pages/procurementManagement/procurementInvoiceLedger/index",
+ "style": {
+ "navigationBarTitleText": "鏉ョエ鍙拌处",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/procurementManagement/procurementInvoiceLedger/detail",
+ "style": {
+ "navigationBarTitleText": "淇敼鏉ョエ鍙拌处",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/common/webview/index",
"style": {
"navigationBarTitleText": "娴忚缃戦〉"
diff --git a/src/pages/index.vue b/src/pages/index.vue
index b6f9f4f..8f1b48e 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -330,6 +330,11 @@
url: '/pages/procurementManagement/invoiceEntry/index'
});
break;
+ case '鏉ョエ鍙拌处':
+ uni.navigateTo({
+ url: '/pages/procurementManagement/procurementInvoiceLedger/index'
+ });
+ break;
case '鍗忓悓瀹℃壒':
uni.navigateTo({
url: '/pages/cooperativeOffice/collaborativeApproval/index'
diff --git a/src/pages/procurementManagement/invoiceEntry/add.vue b/src/pages/procurementManagement/invoiceEntry/add.vue
index 6935e92..2fdcb2b 100644
--- a/src/pages/procurementManagement/invoiceEntry/add.vue
+++ b/src/pages/procurementManagement/invoiceEntry/add.vue
@@ -406,8 +406,8 @@
// 杩斿洖涓婁竴椤�
setTimeout(() => {
- uni.navigateBack()
- }, 1500)
+ goBack()
+ }, 800)
} catch (error) {
showToast('鎻愪氦澶辫触锛岃閲嶈瘯')
diff --git a/src/pages/procurementManagement/procurementInvoiceLedger/detail.vue b/src/pages/procurementManagement/procurementInvoiceLedger/detail.vue
new file mode 100644
index 0000000..ec31d69
--- /dev/null
+++ b/src/pages/procurementManagement/procurementInvoiceLedger/detail.vue
@@ -0,0 +1,292 @@
+<template>
+ <view class="account-detail">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="缂栬緫鏉ョエ鍙拌处" @back="goBack" />
+
+ <van-form @submit="submitForm" ref="formRef" label-width="120px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center">
+ <van-cell-group title="鍩烘湰淇℃伅" inset>
+ <van-field v-model="form.purchaseContractNumber" label="閲囪喘鍚堝悓鍙�" readonly />
+ <van-field v-model="form.salesContractNo" label="閿�鍞悎鍚屽彿" readonly />
+ <van-field v-model="form.taxInclusiveUnitPrice" label="鍚◣鍗曚环(鍏�)" readonly />
+ <van-field v-model="form.createdAt" label="鍒涘缓鏃堕棿" readonly />
+ <van-field v-model="form.invoiceNumber" label="鍙戠エ鍙�" placeholder="璇疯緭鍏�" readonly />
+ <van-field v-model="form.ticketsNum" label="鏉ョエ鏁�" type="number" placeholder="璇疯緭鍏�" required :rules="[{ required: true, message: '璇疯緭鍏ユ潵绁ㄦ暟' }]" @change="inputTicketsNum"/>
+ <van-field v-model="form.ticketsAmount" label="鏈鏉ョエ閲戦(鍏�)" type="number" placeholder="璇疯緭鍏�" required :rules="[{ required: true, message: '璇疯緭鍏ユ湰娆℃潵绁ㄩ噾棰�' }]" @change="inputTicketsAmount"/>
+ <view class="tip-text">鏈潵绁ㄦ暟锛歿{ formatAmount(form.futureTickets) }} 鍏�</view>
+<!-- <van-field v-model="form.invoicePerson" label="鏈潵绁ㄦ暟" readonly />-->
+ </van-cell-group>
+
+<!-- <van-cell-group title="闄勪欢鏉愭枡锛堜粎鏀寔 pdf锛�" inset>-->
+<!-- <van-uploader-->
+<!-- accept=".pdf"-->
+<!-- multiple-->
+<!-- :after-read="afterReadUpload"-->
+<!-- :before-read="beforeReadPdf"-->
+<!-- >-->
+<!-- <van-button class="upload-btn" icon="plus" type="primary" block>涓婁紶鏂囦欢</van-button>-->
+<!-- </van-uploader>-->
+<!-- <view class="uploaded-list" v-if="fileList.length">-->
+<!-- <view class="uploaded-item" v-for="(f, idx) in fileList" :key="idx">-->
+<!-- <text class="file-name">{{ f.name || getFileNameFromUrl(f.url) }}</text>-->
+<!-- <van-button size="mini" type="danger" plain @click="removeUploaded(idx)">绉婚櫎</van-button>-->
+<!-- </view>-->
+<!-- </view>-->
+<!-- </van-cell-group>-->
+
+ <view class="footer-btns">
+ <van-button class="cancel-btn" @click="goBack">鍙栨秷</van-button>
+ <van-button class="save-btn" native-type="submit" form-type="submit">淇濆瓨</van-button>
+ </view>
+ </van-form>
+ </view>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { showToast, showLoadingToast, closeToast } from 'vant'
+import dayjs from 'dayjs'
+import useUserStore from '@/store/modules/user'
+import { getToken } from '@/utils/auth'
+import { invoiceLedgerSaveOrUpdate } from '@/api/salesManagement/invoiceLedger.js'
+import config from '@/config.js'
+import {getProductRecordById, updateRegistration} from "@/api/procurementManagement/procurementInvoiceLedger";
+
+const userStore = useUserStore()
+
+const formRef = ref()
+let form = ref({
+ salesLedgerId: '',
+ customerId: '',
+ invoiceNo: '',
+ invoiceTotal: '',
+ taxRate: '',
+ invoicePerson: '',
+ invoiceDate: '',
+ customerName: '',
+ fileList: [],
+ createTime: '',
+ taxInclusiveTotalPrice: '',
+ taxInclusiveUnitPrice: ''
+})
+const fileList = ref([])
+const currentId = ref('')
+const temFutureTickets = ref(0)
+
+// 鏃ユ湡閫夋嫨
+const currentInvoiceDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
+
+const goBack = () => {
+ uni.removeStorageSync('invoiceLedgerEditRow');
+ uni.navigateBack()
+}
+const inputTicketsNum = (val) => {
+ // 纭繚鍚◣鍗曚环瀛樺湪涓斾笉涓洪浂
+ if (!form.value.taxInclusiveUnitPrice || Number(form.value.taxInclusiveUnitPrice) === 0) {
+ showToast("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
+ return;
+ }
+ if (Number(form.value.ticketsNum) > Number(temFutureTickets.value)) {
+ showToast("鏉ョエ鏁颁笉寰楀ぇ浜庢湭鏉ョエ鏁�");
+ form.value.ticketsNum = temFutureTickets.value
+ }
+
+ // 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
+ const ticketsAmount = Number(form.value.ticketsNum) * Number(form.value.taxInclusiveUnitPrice);
+ const futureTickets = Number(temFutureTickets.value) - Number(form.value.ticketsNum);
+ form.value.futureTickets = Number(futureTickets.toFixed(2));
+ form.value.ticketsAmount = Number(ticketsAmount.toFixed(2));
+};
+const inputTicketsAmount = (val) => {
+ // 纭繚鍚◣鍗曚环瀛樺湪涓斾笉涓洪浂
+ if (!form.value.taxInclusiveUnitPrice || Number(form.value.taxInclusiveUnitPrice) === 0) {
+ showToast("鍚◣鍗曚环涓嶈兘涓洪浂鎴栨湭瀹氫箟");
+ return;
+ }
+
+ if (Number(val) > Number(form.value.futureTickets*form.value.taxInclusiveUnitPrice)) {
+ showToast("鏈鏉ョエ閲戦涓嶅緱澶т簬鎬婚噾棰�");
+ form.value.ticketsAmount = (form.value.futureTickets*form.value.taxInclusiveUnitPrice).toFixed(2)
+ const ticketsNum = Number(form.value.ticketsAmount) / Number(form.value.taxInclusiveUnitPrice);
+ form.value.ticketsNum = Number(ticketsNum.toFixed(2))
+ return;
+ }
+
+ // 纭繚鎵�鏈夋暟鍊奸兘杞崲涓烘暟瀛楃被鍨嬭繘琛岃绠�
+ const ticketsNum = Number(val) / Number(form.value.taxInclusiveUnitPrice);
+ form.value.ticketsNum = Number(ticketsNum.toFixed(2));
+};
+const formatAmount = (val) => {
+ if (val === undefined || val === null || val === '') return '0.00'
+ const num = Number(val)
+ if (Number.isNaN(num)) return '0.00'
+ return num.toFixed(2)
+}
+
+// 涓婁紶鍓嶆牎楠岋紙鍏煎 Vant Uploader 鐨� file/fileList 缁撴瀯锛�
+const beforeReadPdf = (file) => {
+ const items = Array.isArray(file) ? file : [file]
+ for (const it of items) {
+ const raw = it?.file || it
+ const fileName = raw?.name || it?.name || ''
+ const ext = fileName.split('.').pop()?.toLowerCase()
+ const sizeOk = (raw?.size || 0) <= 10 * 1024 * 1024
+ if (ext !== 'pdf') {
+ showToast('浠呮敮鎸乸df鏂囦欢')
+ return false
+ }
+ if (!sizeOk) {
+ showToast('涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB')
+ return false
+ }
+ }
+ return true
+}
+
+const uploadSingleFile = async (fileObj) => {
+ return new Promise((resolve, reject) => {
+ showLoadingToast({ message: '姝e湪涓婁紶...' })
+ const baseUrl = config.baseUrl + '/invoiceLedger/uploadFile'
+
+ const filePath = fileObj?.url || fileObj?.tempFilePath || fileObj?.file?.path
+ if (filePath) {
+ uni.uploadFile({
+ url: baseUrl,
+ filePath,
+ name: 'file',
+ header: { Authorization: 'Bearer ' + getToken() },
+ success: (res) => {
+ closeToast()
+ try {
+ const data = JSON.parse(res.data || '{}')
+ if (data.code === 200) {
+ resolve(data.data)
+ } else {
+ reject(new Error(data.msg || '涓婁紶澶辫触'))
+ }
+ } catch (err) {
+ reject(err)
+ }
+ },
+ fail: (err) => {
+ closeToast()
+ reject(err)
+ }
+ })
+ return
+ }
+
+ // H5: 浣跨敤鍘熷 File锛坕nput 閫夋嫨锛�
+ const rawFile = fileObj?.file
+ if (rawFile) {
+ // uni.uploadFile 鍦� H5 涓嶆敮鎸佸師鐢� File 瀵硅薄锛岃繖閲岀敤 fetch 鍙戦�� FormData
+ const formData = new FormData()
+ formData.append('file', rawFile, rawFile.name || 'file.pdf')
+ formData.append('salesLedgerId', form.value.salesLedgerId || currentId.value || '')
+ fetch(baseUrl, {
+ method: 'POST',
+ headers: { Authorization: 'Bearer ' + getToken() },
+ body: formData
+ }).then(async (res) => {
+ closeToast()
+ const data = await res.json()
+ if (data.code === 200) {
+ resolve(data.data)
+ } else {
+ reject(new Error(data.msg || '涓婁紶澶辫触'))
+ }
+ }).catch((err) => {
+ closeToast()
+ reject(err)
+ })
+ return
+ }
+
+ closeToast()
+ reject(new Error('鏈壘鍒板彲涓婁紶鐨勬枃浠�'))
+ })
+}
+
+const afterReadUpload = async (file) => {
+ try {
+ const files = Array.isArray(file) ? file : file?.file ? [file] : [file]
+ for (const f of files) {
+ const uploaded = await uploadSingleFile(f)
+ fileList.value.push(uploaded)
+ }
+ showToast('涓婁紶鎴愬姛')
+ } catch (e) {
+ showToast('涓婁紶澶辫触')
+ }
+}
+
+const removeUploaded = (index) => {
+ fileList.value.splice(index, 1)
+}
+
+const getFileNameFromUrl = (url) => {
+ try { if (!url) return ''; return decodeURIComponent(url.split('/').pop()) } catch (e) { return url }
+}
+
+const loadDetail = async (id) => {
+ try {
+ showLoadingToast({ message: '鍔犺浇涓�...' })
+ const res = await getProductRecordById({ id })
+ const data = res?.data || res
+ form.value = { ...data }
+ temFutureTickets.value = data.futureTickets;
+ fileList.value = data?.fileList || []
+ if (!form.value.invoicePerson) {
+ form.value.invoicePerson = userStore.nickName
+ }
+ if (!form.value.invoiceDate) {
+ form.value.invoiceDate = dayjs().format('YYYY-MM-DD')
+ }
+ closeToast()
+ } catch (e) {
+ closeToast()
+ showToast('鍔犺浇澶辫触')
+ }
+}
+
+const submitForm = async () => {
+ try {
+ showLoadingToast({ message: '鎻愪氦涓�...' })
+ await updateRegistration(form.value)
+ closeToast()
+ showToast('鎻愪氦鎴愬姛')
+ setTimeout(() => { goBack() }, 800)
+ } catch (e) {
+ closeToast()
+ showToast('鎻愪氦澶辫触锛岃閲嶈瘯')
+ }
+}
+
+onMounted(() => {
+ const rowStr = uni.getStorageSync('invoiceLedgerEditRow')
+ if (rowStr) {
+ try {
+ const row = JSON.parse(rowStr)
+ currentId.value = row.id
+ loadDetail(currentId.value)
+ } catch (e) {
+ // ignore
+ }
+ }
+})
+</script>
+
+<style scoped lang="scss">
+.account-detail {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 5rem;
+}
+.uploaded-list { padding: 8px 16px 0 16px; }
+.uploaded-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f5f5f5; }
+.file-name { font-size: 12px; color: #333; margin-right: 8px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.tip-text { padding: 4px 16px 0 16px; font-size: 12px; color: #888; }
+.footer-btns { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; justify-content: space-around; align-items: center; padding: 0.75rem 0; box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05); z-index: 1000; }
+.cancel-btn { font-weight: 400; font-size: 1rem; color: #FFFFFF; width: 6.375rem; background: #C7C9CC; box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; }
+.save-btn { font-weight: 400; font-size: 1rem; color: #FFFFFF; width: 14rem; background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%); box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2); border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; }
+</style>
+
diff --git a/src/pages/procurementManagement/procurementInvoiceLedger/index.vue b/src/pages/procurementManagement/procurementInvoiceLedger/index.vue
new file mode 100644
index 0000000..dc23694
--- /dev/null
+++ b/src/pages/procurementManagement/procurementInvoiceLedger/index.vue
@@ -0,0 +1,767 @@
+<template>
+ <view class="sales-account">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="鏉ョエ鍙拌处" @back="goBack" />
+
+ <!-- 鎼滅储鍜岀瓫閫夊尯鍩燂紙淇濇寔涓庨攢鍞彴璐﹂鏍间竴鑷达級 -->
+ <view class="search-filter-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <input
+ class="search-text"
+ placeholder="渚涘簲鍟嗗悕绉�/閲囪喘鍚堝悓鍙�"
+ v-model="searchForm.searchText"
+ confirm-type="search"
+ @confirm="handleQuery"
+ />
+ </view>
+ <!-- <view class="filter-button" @click="showFilter = true">-->
+ <!-- <up-icon name="list" size="24" color="#999"></up-icon>-->
+ <!-- </view>-->
+ <view class="filter-button" @click="handleQuery">
+ <up-icon name="search" size="24" color="#999"></up-icon>
+ </view>
+ </view>
+ </view>
+
+ <!-- 鍒楄〃鍖哄煙 -->
+ <view class="ledger-list" v-if="total > 0">
+ <view v-for="(item, index) in ledgerList" :key="index">
+ <view class="ledger-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.purchaseContractNumber }}</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.salesContractNo }}</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.supplierName }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">浜у搧澶х被</text>
+ <text class="detail-value">{{ item.productCategory }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">瑙勬牸鍨嬪彿</text>
+ <text class="detail-value">{{ item.specificationModel }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍙戠エ鍙�</text>
+ <text class="detail-value">{{ item.invoiceNumber || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍚堝悓閲戦(鍏�)</text>
+ <text class="detail-value">{{ item.taxInclusiveTotalPrice || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏉ョエ鏃ユ湡</text>
+ <text class="detail-value">{{ item.createdAt || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏉ョエ閲戦(鍏�)</text>
+ <text class="detail-value highlight">{{ formatAmount(item.ticketsAmount) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">涓嶅惈绋庨噾棰�(鍏�)</text>
+ <text class="detail-value highlight">{{ formatAmount(item.unTicketsPrice) }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">澧炲�肩◣</text>
+ <text class="detail-value">{{ item.invoiceAmount }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">褰曞叆浜�</text>
+ <text class="detail-value">{{ item.issUer }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">褰曞叆鏃ユ湡</text>
+ <text class="detail-value">{{ item.createdAt }}</text>
+ </view>
+
+ </view>
+ <view class="action-buttons">
+ <van-button
+ type="primary"
+ size="small"
+ class="action-btn"
+ :disabled="item.issUer !== userStore.nickName"
+ @click="openEdit(item)"
+ >
+ 缂栬緫
+ </van-button>
+ <van-button
+ type="danger"
+ size="small"
+ plain
+ class="action-btn"
+ :disabled="item.issUer !== userStore.nickName"
+ @click="handleDelete(item)"
+ >
+ 鍒犻櫎
+ </van-button>
+ <van-button
+ type="default"
+ size="small"
+ plain
+ class="action-btn"
+ v-if="item.invoiceFileName"
+ @click="openFileActions(item.commonFiles || [])"
+ >
+ 鏌ョ湅闄勪欢
+ </van-button>
+ <van-button
+ type="primary"
+ size="small"
+ class="action-btn"
+ v-else
+ :disabled="item.issUer !== userStore.nickName"
+ @click="openUpload(item)"
+ >
+ 涓婁紶
+ </van-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else class="no-data">
+ <text>鏆傛棤鏉ョエ鍙拌处鏁版嵁</text>
+ </view>
+
+ <!-- 绛涢�夊脊绐� -->
+ <van-popup v-model:show="showFilter" position="bottom" round>
+ <view class="filter-popup">
+ <van-cell-group title="绛涢�夋潯浠�" inset>
+ <van-field
+ label="鏉ョエ鏃ユ湡"
+ readonly
+ @click="showInvoiceRange = true"
+ :placeholder="invoiceRangeLabel || '璇烽�夋嫨鏃ユ湡鑼冨洿'"
+ />
+ <van-field
+ label="褰曞叆鏃ユ湡"
+ readonly
+ @click="showCreateDatePicker = true"
+ :placeholder="searchForm.createTimeStart || '璇烽�夋嫨褰曞叆鏃ユ湡'"
+ />
+ <view class="switch-row">
+ <text class="switch-label">涓嶆樉绀烘湁鍙戠エ琛�</text>
+ <van-switch v-model="searchForm.status" size="20" />
+ </view>
+ </van-cell-group>
+ <view class="filter-actions">
+ <van-button @click="resetFilter">閲嶇疆</van-button>
+ <van-button type="primary" @click="confirmFilter">纭畾</van-button>
+ </view>
+ </view>
+ </van-popup>
+
+ <!-- 鏃ュ巻锛氭潵绁ㄦ棩鏈熻寖鍥� -->
+ <van-popup v-model:show="showInvoiceRange" position="bottom">
+ <van-calendar
+ title="閫夋嫨鏉ョエ鏃ユ湡鑼冨洿"
+ type="range"
+ color="#2979ff"
+ @confirm="onInvoiceRangeConfirm"
+ @cancel="showInvoiceRange = false"
+ />
+ </van-popup>
+
+ <!-- 鏃ユ湡锛氬綍鍏ユ棩鏈� -->
+ <van-popup v-model:show="showCreateDatePicker" position="bottom">
+ <van-date-picker
+ v-model="currentCreateDate"
+ title="閫夋嫨褰曞叆鏃ユ湡"
+ @confirm="onCreateDateConfirm"
+ @cancel="showCreateDatePicker = false"
+ />
+ </van-popup>
+
+
+
+ <!-- 鍗曡涓婁紶寮圭獥锛堟棤琛ㄥ崟锛� -->
+ <van-popup v-model:show="showUpload" position="bottom" round>
+ <view class="upload-container">
+ <van-cell-group title="涓婁紶闄勪欢锛堜粎鏀寔 pdf锛屾渶澶�10MB锛屾渶澶�10涓級" inset>
+ <van-uploader
+ accept="*"
+ multiple
+ :max-count="10"
+ :after-read="afterReadRowUpload"
+ :before-read="beforeReadPdf"
+ />
+ <view class="uploaded-list" v-if="fileList.length">
+ <view class="uploaded-item" v-for="(f, idx) in fileList" :key="idx">
+ <text class="file-name">{{ f.name || getFileNameFromUrl(f.url) }}</text>
+ <van-button size="mini" type="danger" plain @click="removeUploaded(idx)">绉婚櫎</van-button>
+ </view>
+ </view>
+ </van-cell-group>
+ <view class="filter-actions">
+ <van-button @click="showUpload = false">鍙栨秷</van-button>
+ <van-button type="primary" @click="confirmUpload">纭</van-button>
+ </view>
+ </view>
+ </van-popup>
+
+ <!-- 闄勪欢鍒楄〃閫夋嫨 -->
+ <van-action-sheet v-model:show="showFileSheet" :actions="fileActions" cancel-text="鍙栨秷" close-on-click-action @select="onSelectFile" />
+ </view>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import dayjs from 'dayjs'
+import { showToast, showLoadingToast, closeToast } from 'vant'
+import useUserStore from '@/store/modules/user'
+import { getToken } from '@/utils/auth'
+import config from '@/config.js'
+import {
+ registrationProductPage,
+ commitFile,
+ delInvoiceLedgerByRegProductId
+} from '@/api/salesManagement/invoiceLedger.js'
+import {onShow} from "@dcloudio/uni-app";
+import {productRecordPage} from "@/api/procurementManagement/procurementInvoiceLedger";
+import {delRegistration} from "@/api/procurementManagement/invoiceEntry";
+
+const userStore = useUserStore()
+
+// 鍒楄〃涓庢煡璇�
+const ledgerList = ref([])
+const total = ref(0)
+const page = reactive({ current: -1, size: -1 })
+const searchForm = reactive({
+ searchText: '',
+ status: false,
+ createTimeStart: ''
+})
+
+// 椤堕儴浜や簰
+const showFilter = ref(false)
+const showInvoiceRange = ref(false)
+const showCreateDatePicker = ref(false)
+const invoiceRangeLabel = ref('')
+const currentCreateDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
+
+const currentId = ref('')
+const fileList = ref([]) // 琛屼笂浼犳垨閫氱敤涓婁紶鍒楄〃
+
+// 琛屼笂浼犲脊绐�
+const showUpload = ref(false)
+
+// 闄勪欢鏌ョ湅
+const showFileSheet = ref(false)
+const fileActions = ref([])
+let currentFilesToOpen = []
+
+const formatAmount = (val) => {
+ if (val === undefined || val === null || val === '') return '0.00'
+ const num = Number(val)
+ if (Number.isNaN(num)) return '0.00'
+ return num.toFixed(2)
+}
+const formatDateTime = (val) => {
+ if (!val) return ''
+ return dayjs(val).format('YYYY-MM-DD HH:mm:ss')
+}
+
+const goBack = () => {
+ uni.navigateBack()
+}
+
+const handleQuery = () => {
+ getList()
+}
+
+const getList = async () => {
+ try {
+ showLoadingToast({ message: '鍔犺浇涓�...' })
+ const { invoiceDate, ...rest } = searchForm
+ const res = await productRecordPage({ ...rest, ...page })
+ // 鍏煎涓嶅悓杩斿洖缁撴瀯
+ const records = res?.data?.records || res?.records || res?.data || []
+ const totalVal = res?.data?.total || res?.total || records.length || 0
+ ledgerList.value = records
+ total.value = totalVal
+ closeToast()
+ } catch (e) {
+ closeToast()
+ showToast('鑾峰彇鍒楄〃澶辫触')
+ }
+}
+
+// 绛涢�夐�昏緫
+const resetFilter = () => {
+ searchForm.searchText = ''
+ searchForm.status = false
+ const start = dayjs().startOf('month').format('YYYY-MM-DD')
+ const end = dayjs().endOf('month').format('YYYY-MM-DD')
+ searchForm.invoiceDate = [start, end]
+ searchForm.invoiceDateStart = start
+ searchForm.invoiceDateEnd = end
+ searchForm.createTimeStart = ''
+ invoiceRangeLabel.value = ''
+}
+const confirmFilter = () => {
+ showFilter.value = false
+ getList()
+}
+const onInvoiceRangeConfirm = (e) => {
+ // e 涓� [start, end] 鐨� Date 瀵硅薄鎴栧瓧绗︿覆锛寀ni-app 涓� Vant Calendar 杩斿洖鏃堕棿鎴虫暟缁�
+ try {
+ let start, end
+ if (Array.isArray(e)) {
+ const [s, ed] = e
+ start = dayjs(s).format('YYYY-MM-DD')
+ end = dayjs(ed).format('YYYY-MM-DD')
+ } else if (e && e.detail && Array.isArray(e.detail)) {
+ const [s, ed] = e.detail
+ start = dayjs(s).format('YYYY-MM-DD')
+ end = dayjs(ed).format('YYYY-MM-DD')
+ }
+ searchForm.invoiceDateStart = start
+ searchForm.invoiceDateEnd = end
+ invoiceRangeLabel.value = `${start} 鑷� ${end}`
+ showInvoiceRange.value = false
+ } catch (err) {
+ showInvoiceRange.value = false
+ }
+}
+const onCreateDateConfirm = ({ selectedValues }) => {
+ try {
+ searchForm.createTimeStart = selectedValues.join('-')
+ currentCreateDate.value = selectedValues
+ showCreateDatePicker.value = false
+ } catch (err) {
+ showCreateDatePicker.value = false
+ }
+}
+
+// 缂栬緫閫昏緫鏀逛负璺宠浆鏂伴〉闈�
+const openEdit = (row) => {
+ try {
+ uni.setStorageSync('invoiceLedgerEditRow', JSON.stringify(row))
+ uni.navigateTo({ url: '/pages/procurementManagement/procurementInvoiceLedger/detail' })
+ } catch (e) {
+ showToast('璺宠浆澶辫触')
+ }
+}
+
+// 鍒犻櫎
+const handleDelete = (row) => {
+ let ids = [];
+ ids.push(row.id);
+ console.log(ids)
+ uni.showModal({
+ title: '鍒犻櫎纭',
+ content: '璇ュ彂绁ㄥ彴璐﹀皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�',
+ success: async (res) => {
+ if (res.confirm) {
+ try {
+ showLoadingToast({ message: '澶勭悊涓�...' })
+ await delRegistration(ids)
+ closeToast()
+ showToast('鍒犻櫎鎴愬姛')
+ await getList()
+ } catch (e) {
+ closeToast()
+ showToast('鍒犻櫎澶辫触锛岃閲嶈瘯')
+ }
+ }
+ }
+ })
+}
+
+// 琛屼笂浼�
+const openUpload = (row) => {
+ currentId.value = row.id
+ fileList.value = []
+ showUpload.value = true
+}
+const confirmUpload = async () => {
+ try {
+ const payload = { fileList: fileList.value, id: currentId.value }
+ showLoadingToast({ message: '鎻愪氦涓�...' })
+ await commitFile(payload)
+ closeToast()
+ showToast('鎻愪氦鎴愬姛')
+ showUpload.value = false
+ fileList.value = []
+ currentId.value = ''
+ getList()
+ } catch (e) {
+ closeToast()
+ showToast('鎻愪氦澶辫触锛岃閲嶈瘯')
+ }
+}
+
+// 涓婁紶鐩稿叧
+const beforeReadPdf = (file) => {
+ // 鍏煎澶氭枃浠�
+ const files = Array.isArray(file) ? file : [file]
+ for (const f of files) {
+ const sizeOk = f.size <= 10 * 1024 * 1024
+ const ext = (f.name || '').split('.').pop()?.toLowerCase()
+ if (ext !== 'pdf') {
+ showToast('浠呮敮鎸乸df鏂囦欢')
+ return false
+ }
+ if (!sizeOk) {
+ showToast('涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB')
+ return false
+ }
+ }
+ return true
+}
+
+const uploadSingleFile = async (fileObj) => {
+ return new Promise((resolve, reject) => {
+ showLoadingToast({ message: '姝e湪涓婁紶...' })
+ uni.uploadFile({
+ url: config.baseUrl + '/invoiceLedger/uploadFile',
+ filePath: fileObj.url || fileObj.file?.path || fileObj.tempFilePath,
+ name: 'file',
+ header: { Authorization: 'Bearer ' + getToken() },
+ success: (res) => {
+ closeToast()
+ try {
+ const data = JSON.parse(res.data || '{}')
+ if (data.code === 200) {
+ resolve(data.data)
+ } else {
+ reject(new Error(data.msg || '涓婁紶澶辫触'))
+ }
+ } catch (err) {
+ reject(err)
+ }
+ },
+ fail: (err) => {
+ closeToast()
+ reject(err)
+ }
+ })
+ })
+}
+
+const afterReadEditUpload = async (file) => {
+ try {
+ const files = Array.isArray(file) ? file : file?.file ? [file] : [file]
+ for (const f of files) {
+ const uploaded = await uploadSingleFile(f)
+ fileList.value.push(uploaded)
+ }
+ showToast('涓婁紶鎴愬姛')
+ } catch (e) {
+ showToast('涓婁紶澶辫触')
+ }
+}
+
+const afterReadRowUpload = async (file) => {
+ try {
+ const files = Array.isArray(file) ? file : file?.file ? [file] : [file]
+ for (const f of files) {
+ const uploaded = await uploadSingleFile(f)
+ fileList.value.push(uploaded)
+ }
+ showToast('涓婁紶鎴愬姛')
+ } catch (e) {
+ showToast('涓婁紶澶辫触')
+ }
+}
+
+const removeUploaded = (index) => {
+ fileList.value.splice(index, 1)
+}
+
+const getFileNameFromUrl = (url) => {
+ try {
+ if (!url) return ''
+ return decodeURIComponent(url.split('/').pop())
+ } catch (e) {
+ return url
+ }
+}
+
+// 闄勪欢鏌ョ湅
+const openFileActions = (commonFiles) => {
+ currentFilesToOpen = commonFiles || []
+ fileActions.value = (commonFiles || []).map((f, idx) => ({ name: getFileNameFromUrl(f.url || ''), index: idx }))
+ showFileSheet.value = true
+}
+const onSelectFile = async (action) => {
+ try {
+ const item = currentFilesToOpen[action.index]
+ if (!item || !item.url) return
+ showLoadingToast({ message: '涓嬭浇涓�...' })
+ uni.downloadFile({
+ url: item.url,
+ success: (res) => {
+ closeToast()
+ if (res.statusCode === 200) {
+ uni.openDocument({ filePath: res.tempFilePath })
+ } else {
+ showToast('涓嬭浇澶辫触')
+ }
+ },
+ fail: () => {
+ closeToast()
+ showToast('涓嬭浇澶辫触')
+ }
+ })
+ } catch (e) {
+ closeToast()
+ showToast('鎵撳紑澶辫触')
+ }
+}
+
+onShow(() => {
+ getList()
+})
+</script>
+
+<style scoped lang="scss">
+.u-divider {
+ margin: 0 !important;
+}
+.sales-account {
+ min-height: 100vh;
+ background: #f8f9fa;
+ position: relative;
+}
+
+.search-filter-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;
+}
+
+.filter-button {
+ width: 40px;
+ height: 40px;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.ledger-list {
+ padding: 20px;
+}
+
+.ledger-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-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;
+}
+
+.no-data {
+ padding: 40px 0;
+ text-align: center;
+ color: #999;
+}
+
+.action-buttons {
+ display: flex;
+ gap: 12px;
+ padding: 0 0 16px 0;
+ justify-content: space-between;
+}
+
+.action-btn {
+ flex: 1;
+}
+
+.filter-popup {
+ padding: 12px 12px 20px;
+}
+
+.switch-row {
+ padding: 12px 16px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.switch-label {
+ font-size: 14px;
+ color: #333;
+}
+
+.filter-actions {
+ display: flex;
+ gap: 12px;
+ padding: 12px 16px 16px;
+ justify-content: space-between;
+}
+
+.edit-container {
+ padding-bottom: 5rem;
+}
+
+.uploaded-list {
+ padding: 8px 16px 0 16px;
+}
+
+.uploaded-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 0;
+ border-bottom: 1px solid #f5f5f5;
+}
+
+.file-name {
+ font-size: 12px;
+ color: #333;
+ margin-right: 8px;
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.tip-text {
+ padding: 4px 16px 0 16px;
+ font-size: 12px;
+ color: #888;
+}
+
+.footer-btns {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #fff;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ padding: 0.75rem 0;
+ box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05);
+ z-index: 1000;
+}
+.cancel-btn {
+ font-weight: 400;
+ font-size: 1rem;
+ color: #FFFFFF;
+ width: 6.375rem;
+ background: #C7C9CC;
+ box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
+ border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+}
+.save-btn {
+ font-weight: 400;
+ font-size: 1rem;
+ color: #FFFFFF;
+ width: 14rem;
+ background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%);
+ box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
+ border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3