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