Merge branch 'zqy-mst' of http://1.14.121.39:8430/gitadmin/eden-web into zqy-mst

This commit is contained in:
zhongchangyuyu 2025-04-25 09:19:10 +08:00
commit f2b1115bd9
12 changed files with 3036 additions and 183 deletions

View File

@ -1,5 +1,6 @@
{
"dependencies": {
"element-china-area-data": "^6.1.0",
"html2canvas": "^1.4.1",
"mammoth": "^1.9.0",
"pdf-lib": "^1.17.1",

View File

@ -50,7 +50,10 @@
"vue-router": "3.4.9",
"vuedraggable": "^2.24.3",
"vuex": "3.6.0",
"xlsx": "^0.18.5"
"xlsx": "^0.18.5",
"element-china-area-data": "^6.1.0",
"mammoth": "^1.9.0",
"qrcode.vue": "^3.6.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.6",

View File

@ -416,7 +416,7 @@ export function uploadBillAttachment(billId, file) {
formData.append('billId', billId)
formData.append('file', file)
return request({
url: '/bill/attachment/upload',
url: '/bill/upload-attachment',
method: 'post',
data: formData,
headers: {
@ -425,10 +425,11 @@ export function uploadBillAttachment(billId, file) {
})
}
// 账单附件删除
export function deleteBillAttachment(attachmentId) {
return request({
url: `/bill/attachment/${attachmentId}`,
method: 'delete'
url: `/bill/attachment/delete/${attachmentId}`,
method: 'get'
})
}
@ -446,4 +447,267 @@ export function exportBillList(data) {
data,
responseType: 'blob'
})
}
// 收支流水相关接口
export function getTransactionList(params) {
return request({
url: '/finance/transaction/page',
method: 'get',
params
})
}
export function getTransactionDetail(id) {
return request({
url: `/finance/transaction/${id}`,
method: 'get'
})
}
export function addTransaction(data) {
return request({
url: '/finance/transaction',
method: 'post',
data
})
}
export function updateTransaction(data) {
return request({
url: '/finance/transaction',
method: 'put',
data
})
}
export function exportTransactionList(params) {
return request({
url: '/finance/transaction/export',
method: 'get',
params,
responseType: 'blob'
})
}
export function getTransactionAttachments(transactionId) {
return request({
url: `/finance/transaction/${transactionId}`,
method: 'get'
})
}
export function uploadTransactionAttachment(transactionId, file) {
const formData = new FormData()
formData.append('transactionId', transactionId)
formData.append('userId', localStorage.getItem('userId') || '')
formData.append('userName', localStorage.getItem('userName') || '')
formData.append('file', file)
return request({
url: '/finance/transaction/attachment',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
export function deleteTransactionAttachment(attachmentId) {
return request({
url: `/finance/transaction/attachment/file/${attachmentId}`,
method: 'delete'
})
}
export function previewAttachment(attachmentId) {
return request({
url: `/finance/transaction/attachment/preview/file/${attachmentId}`,
method: 'get',
responseType: 'blob'
})
}
export function previewAttachmentByFileId(fileId) {
return request({
url: `/finance/transaction/attachment/preview/file/${fileId}`,
method: 'get',
responseType: 'blob'
})
}
export function downloadAttachment(fileId, fileName) {
return request({
url: `/finance/transaction/attachment/download/${fileId}`,
method: 'get',
params: { fileName },
responseType: 'blob'
})
}
export function deleteAttachmentByFileId(fileId) {
return request({
url: `/finance/transaction/attachment/file/${fileId}`,
method: 'delete',
params: {
userId: localStorage.getItem('userId') || ''
}
})
}
export function addTransactionOperation(data) {
return request({
url: '/finance/transaction/operation',
method: 'post',
data
})
}
export function getTransactionOperationLogs(transactionId, query) {
return request({
url: `/finance/transaction/${transactionId}`,
method: 'get'
})
}
// 获取所有收支账户
export function getAllFinanceAccounts() {
return request({
url: '/finance/account/list',
method: 'get'
})
}
// 账单调整相关接口
// 添加账单调整(按金额或按比例)
export function addBillAdjustment(data) {
return request({
url: '/v1/bill-adjustments',
method: 'post',
data
})
}
// 作废账单调整
export function voidBillAdjustment(data) {
return request({
url: '/v1/bill-adjustments/void',
method: 'put',
data
})
}
// 分页查询账单调整记录
export function getBillAdjustmentList(params) {
return request({
url: '/v1/bill-adjustments',
method: 'get',
params
})
}
// 获取账单调整详情
export function getBillAdjustmentDetail(id) {
return request({
url: `/v1/bill-adjustments/${id}`,
method: 'get'
})
}
// 上传账单调整附件
export function uploadBillAdjustmentAttachment(id, file) {
const formData = new FormData()
formData.append('file', file)
return request({
url: `/v1/bill-adjustments/${id}/attachments`,
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
// 删除账单调整附件
export function deleteBillAdjustmentAttachment(attachmentId) {
return request({
url: `/v1/bill-adjustments/attachments/${attachmentId}`,
method: 'delete'
})
}
// 获取账单明细
export function getBillDetailById(detailId) {
return request({
url: `/v1/bill-details/${detailId}`,
method: 'get'
})
}
// 获取账单调整附件列表
export function getBillAdjustmentAttachmentList(adjustmentId) {
return request({
url: `/v1/bill-adjustments/attachments/view/${adjustmentId}`,
method: 'get'
})
}
// 获取账单操作记录
export function getBillOperationRecords(billId, params) {
return request({
url: `/v1/operation-records/list/${billId}`,
method: 'get',
params
})
}
// 账单支付相关接口
// 提交账单支付
export function submitBillPayment(data) {
return request({
url: '/v1/bill-payments',
method: 'post',
data
})
}
// 分页查询支付记录
export function getBillPaymentRecords(params) {
return request({
url: '/v1/bill-payments',
method: 'get',
params
})
}
// 获取支付详情
export function getBillPaymentDetail(id) {
return request({
url: `/v1/bill-payments/${id}`,
method: 'get'
})
}
// 上传支付附件
export function uploadBillPaymentAttachment(id, file) {
const formData = new FormData()
formData.append('file', file)
return request({
url: `/v1/bill-payments/${id}/attachments`,
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
// 删除支付附件
export function deleteBillPaymentAttachment(attachmentId) {
return request({
url: `/v1/bill-payments/attachments/${attachmentId}`,
method: 'delete'
})
}

View File

@ -0,0 +1,136 @@
<template>
<el-cascader
v-model="selectedRegion"
:options="regionOptions"
:props="{ expandTrigger: 'hover', value: 'value', label: 'label' }"
:placeholder="placeholder"
filterable
clearable
@change="handleChange"
style="width: 100%"
/>
</template>
<script>
import { regionData, pcaTextArr } from 'element-china-area-data'
export default {
name: 'RegionSelector',
props: {
value: {
type: [Array, String],
default: () => []
},
placeholder: {
type: String,
default: '请选择省/市/区'
},
// 使
useTextData: {
type: Boolean,
default: true
}
},
data() {
return {
regionOptions: this.useTextData ? pcaTextArr : regionData,
selectedRegion: []
}
},
watch: {
value: {
handler(val) {
if (val) {
//
if (typeof val === 'string') {
//
const parts = val.split(' ').filter(item => item);
if (parts.length > 0) {
if (this.useTextData) {
// 使
this.selectedRegion = parts;
} else {
// 使
this.convertTextToCode(parts);
}
} else {
this.selectedRegion = [];
}
} else if (Array.isArray(val)) {
// 使
this.selectedRegion = val;
} else {
this.selectedRegion = [];
}
} else {
this.selectedRegion = [];
}
},
immediate: true
}
},
methods: {
//
convertTextToCode(textArray) {
// TextToCode
const findValueByLabel = (options, label, level = 0) => {
for (const option of options) {
if (option.label === label) {
return level === textArray.length - 1 ? option.value :
findValueByLabel(option.children || [], textArray[level + 1], level + 1);
}
}
return null;
};
const value = findValueByLabel(this.regionOptions, textArray[0], 0);
if (value) {
this.selectedRegion = value.split(',');
} else {
this.selectedRegion = [];
}
},
handleChange(value) {
//
if (value && value.length > 0) {
const labels = this.getSelectedLabels(value);
const regionText = labels.join(' ');
this.$emit('input', regionText);
this.$emit('change', regionText, value, labels);
} else {
this.$emit('input', '');
this.$emit('change', '', [], []);
}
},
getSelectedLabels(codes) {
//
const labels = [];
let options = this.regionOptions;
for (let i = 0; i < codes.length; i++) {
const code = codes[i];
const option = options.find(item => item.value === code);
if (option) {
labels.push(option.label);
options = option.children || [];
}
}
return labels;
}
}
}
</script>
<style scoped>
.el-cascader {
width: 100%;
}
</style>

View File

@ -30,6 +30,12 @@ export default {
component: () => import('@/views/finance/billList/index.vue'),
name: 'BillList',
meta: { title: '所有账单', icon: 'el-icon-notebook-2' }
},
{
path: 'transaction',
component: () => import('@/views/finance/transaction/index.vue'),
name: 'Transaction',
meta: { title: '收支流水', icon: 'el-icon-money' }
}
]
}

View File

@ -5,8 +5,8 @@
<el-form ref="chargeModeForm" :model="billForm" :rules="chargeModeRules" label-width="120px">
<el-form-item label="含税规则" prop="taxInclusiveRule" required>
<el-radio-group v-model="billForm.taxInclusiveRule">
<el-radio label="含税">含税</el-radio>
<el-radio label="不含税">不含税</el-radio>
<el-radio label="1" value="1">含税</el-radio>
<el-radio label="2" value="2">不含税</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="税率(%)" prop="taxRate" required>
@ -14,18 +14,18 @@
</el-form-item>
<el-form-item label="特殊账单类型" prop="specialBillType" required>
<el-radio-group v-model="billForm.specialBillType">
<el-radio label="正常">正常</el-radio>
<el-radio label="罚金">罚金</el-radio>
<el-radio label="1" value="1">正常</el-radio>
<el-radio label="2" value="2">罚金</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="滞纳金起算天数" prop="lateFeeStartDays">
<el-input-number v-model="billForm.lateFeeStartDays" :min="0" :precision="0" style="width: 200px"></el-input-number>
<el-form-item label="滞纳金起算天数" prop="ovdueStartDays">
<el-input-number v-model="billForm.ovdueStartDays" :min="0" :precision="0" style="width: 200px"></el-input-number>
</el-form-item>
<el-form-item label="滞纳金比例(%/天)" prop="lateFeeRate">
<el-input-number v-model="billForm.lateFeeRate" :min="0" :max="100" :precision="10" style="width: 200px"></el-input-number>
<el-form-item label="滞纳金比例(%/天)" prop="ovdueIntRate">
<el-input-number v-model="billForm.ovdueIntRate" :min="0" :max="100" :precision="10" style="width: 200px"></el-input-number>
</el-form-item>
<el-form-item label="滞纳金上限(%)" prop="lateFeeLimit">
<el-input-number v-model="billForm.lateFeeLimit" :min="0" :max="100" :precision="10" style="width: 200px"></el-input-number>
<el-form-item label="滞纳金上限(%)" prop="ovdueLimitRate">
<el-input-number v-model="billForm.ovdueLimitRate" :min="0" :max="100" :precision="10" style="width: 200px"></el-input-number>
</el-form-item>
</el-form>
</el-tab-pane>
@ -64,8 +64,8 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="付款方" prop="payeeId" required>
<el-select v-model="billForm.payeeId" placeholder="请选择付款方" filterable remote clearable
<el-form-item label="付款方" prop="paysdId" required>
<el-select v-model="billForm.paysdId" placeholder="请选择付款方" filterable remote clearable
:remote-method="searchPayees" @change="handlePayeeChange" style="width: 400px">
<el-option v-for="item in payeeOptions" :key="item.id"
:label="item.name" :value="item.id">
@ -75,12 +75,12 @@
<el-form-item label="币种" prop="currCode" required>
<el-input v-model="billForm.currCode" disabled style="width: 400px"></el-input>
</el-form-item>
<el-form-item label="应收金额" prop="receivableAmount" required>
<el-input-number v-model="billForm.receivableAmount" :min="0" :precision="2" style="width: 400px"></el-input-number>
<el-form-item label="应收金额" prop="accigRcvAmt" required>
<el-input-number v-model="billForm.accigRcvAmt" :min="0" :precision="2" style="width: 400px"></el-input-number>
</el-form-item>
<el-form-item label="应收日期" prop="receivableDate" required>
<el-form-item label="应收日期" prop="pybDt" required>
<el-date-picker
v-model="billForm.receivableDate"
v-model="billForm.pybDt"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
@ -139,18 +139,39 @@
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="previewFile(scope.row)">预览</el-button>
<el-button size="mini" type="text" @click="downloadFile(scope.row)">下载</el-button>
<el-button size="mini" type="text" class="delete-btn" @click="deleteFile(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 文件预览对话框 -->
<el-dialog title="附件预览" :visible.sync="previewDialogVisible" width="800px" append-to-body>
<div class="preview-container" v-loading="previewLoading">
<div v-if="previewUrl && isPdf" class="preview-iframe">
<iframe :src="previewUrl" width="100%" height="500px" frameborder="0"></iframe>
</div>
<div v-else-if="previewUrl && isImage" class="preview-image">
<img :src="previewUrl" style="max-width: 100%;" />
</div>
<div v-else-if="!previewLoading" class="preview-error">
<i class="el-icon-warning"></i>
<p>预览加载失败请尝试下载后查看</p>
<div class="preview-actions">
<el-button type="primary" @click="downloadCurrentPreview">下载文件</el-button>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="previewDialogVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getFeeTypeTree } from '@/api/finance'
import { getFeeTypeTree, uploadBillAttachment, addBill } from '@/api/finance'
export default {
name: 'AddBill',
@ -167,30 +188,28 @@ export default {
billForm: {
contractId: '',
contractNumber: '',
payeeId: '',
payeeName: '',
paysdId: '',
paysdName: '',
payeeContact: '',
feeCategoryId: '',
feeTypeId: '',
feeTypeName: '',
feTpName: '',
currCode: '156', //
billingStartDate: '',
billingEndDate: '',
receivableAmount: 0,
price: 0,
priceU: '元/月',
taxInclusiveRule: '含税',
chggBgnDt: '',
chggEndDt: '',
accigRcvAmt: 0,
taxInclusiveRule: '1',
taxRate: 0,
receivableDate: '',
specialBillType: '正常',
lateFeeStartDays: 0,
lateFeeRate: 0,
lateFeeLimit: 0,
pybDt: '',
specialBillType: '1',
ovdueStartDays: 0,
ovdueIntRate: 0,
ovdueLimitRate: 0,
companyId: '',
companyName: '',
corNm: '',
accountId: '',
billRemark: '',
roomInfoList: []
roomIds: []
},
//
chargeModeRules: {
@ -205,7 +224,7 @@ export default {
]
},
billInfoRules: {
payeeId: [
paysdId: [
{ required: true, message: '请选择付款方', trigger: 'change' }
],
companyId: [
@ -214,10 +233,10 @@ export default {
accountId: [
{ required: true, message: '请选择收支账户', trigger: 'change' }
],
receivableAmount: [
accigRcvAmt: [
{ required: true, message: '请输入应收金额', trigger: 'blur' }
],
receivableDate: [
pybDt: [
{ required: true, message: '请选择应收日期', trigger: 'change' }
]
},
@ -244,7 +263,16 @@ export default {
selectedRoomIds: [],
//
fileList: [],
attachmentLoading: false
attachmentLoading: false,
//
previewDialogVisible: false,
previewLoading: false,
previewUrl: '',
previewBlob: null,
currentPreviewName: '',
isPdf: false,
isImage: false,
}
},
created() {
@ -280,10 +308,10 @@ export default {
children = category.financeFeeTypes.map(feeType => {
return {
id: feeType.id || Math.random().toString(36).substr(2, 9),
label: feeType.feeTypeName || '未命名费用',
label: feeType.feTpName || '未命名费用',
categoryId: categoryId,
// 便使
feeTypeName: feeType.feeTypeName || '未命名费用',
feTpName: feeType.feTpName || '未命名费用',
//
children: null
}
@ -302,7 +330,7 @@ export default {
getCompanyOptions() {
// API
this.companyOptions = [
{ id: 'C001', name: '智慧园区物业管理有限公司' }
{ id: 123, name: '智慧园区物业管理有限公司' }
]
},
//
@ -318,8 +346,8 @@ export default {
if (query) {
// API
this.contractOptions = [
{ contractId: 'CT001', contractNumber: 'HT2023001', customerName: '张三' },
{ contractId: 'CT002', contractNumber: 'HT2023002', customerName: '李四' }
{ contractId: 11, contractNumber: 'HT2023001', customerName: '张三' },
{ contractId: 22, contractNumber: 'HT2023002', customerName: '李四' }
]
} else {
this.contractOptions = []
@ -338,19 +366,21 @@ export default {
}
},
//
handlePayeeChange(payeeId) {
const payee = this.payeeOptions.find(item => item.id === payeeId)
handlePayeeChange(paysdId) {
const payee = this.payeeOptions.find(item => item.id === paysdId)
if (payee) {
this.billForm.payeeName = payee.name
this.billForm.paysdName = payee.name
this.billForm.payeeContact = payee.contact
//
this.$nextTick(() => {
this.$refs.billInfoForm.validateField('payeeId')
this.$refs.billInfoForm.validateField('paysdId')
})
}
},
//
handleFeeTypeChange(value) {
console.log(value);
if (value) {
//
let selectedFeeType = null;
@ -370,7 +400,7 @@ export default {
}
//
if (category.children && category.children.length > 0) {
const found = category.children.find(item => item.id === value);
const found = category.children.find(item => item.id === value[1]);
if (found) {
selectedCategory = category;
selectedFeeType = {
@ -389,25 +419,25 @@ export default {
if (selectedFeeType.type === '分类') {
this.billForm.feeCategoryId = selectedFeeType.id;
this.billForm.feeTypeId = '';
this.billForm.feeTypeName = selectedFeeType.name;
this.billForm.feTpName = selectedFeeType.name;
} else {
this.billForm.feeCategoryId = selectedCategory.id;
this.billForm.feeTypeId = selectedFeeType.id;
this.billForm.feeTypeName = selectedFeeType.name;
this.billForm.feTpName = selectedFeeType.name;
}
}
} else {
//
this.billForm.feeCategoryId = '';
this.billForm.feeTypeId = '';
this.billForm.feeTypeName = '';
this.billForm.feTpName = '';
}
},
//
handleCompanyChange(companyId) {
const company = this.companyOptions.find(item => item.id === companyId)
if (company) {
this.billForm.companyName = company.name
this.billForm.corNm = company.name
//
this.$nextTick(() => {
this.$refs.billInfoForm.validateField('companyId')
@ -420,11 +450,11 @@ export default {
//
handleBillingPeriodChange(val) {
if (val && val.length === 2) {
this.billForm.billingStartDate = val[0]
this.billForm.billingEndDate = val[1]
this.billForm.chggBgnDt = val[0]
this.billForm.chggEndDt = val[1]
} else {
this.billForm.billingStartDate = ''
this.billForm.billingEndDate = ''
this.billForm.chggBgnDt = ''
this.billForm.chggEndDt = ''
}
},
@ -525,9 +555,6 @@ export default {
// ID
this.selectedRoomIds = roomNodes.map(node => node.id)
//
this.billForm.roomInfoList = []
// Map
const projectMap = new Map()
@ -553,20 +580,63 @@ export default {
//
if (roomInfo && floorNode && buildingNode && projectNode) {
//
this.billForm.roomInfoList.push({
roomId: roomNode.id,
// Map
if (!projectMap.has(projectNode.id)) {
projectMap.set(projectNode.id, {
id: projectNode.id,
projectName: projectNode.label,
projectType: '产业园区', // projectType使projectNode
buildings: []
})
}
//
const project = projectMap.get(projectNode.id)
//
let building = project.buildings.find(b => b.id === buildingNode.id)
if (!building) {
building = {
id: buildingNode.id,
buildingName: buildingNode.label,
buildingCode: '', // buildingCode使buildingNode
floors: []
}
project.buildings.push(building)
}
//
let floor = building.floors.find(f => f.id === floorNode.id)
if (!floor) {
floor = {
id: floorNode.id,
floorName: floorNode.label,
floorNumber: 0, // floorNumber使floorNode
rooms: []
}
building.floors.push(floor)
}
//
const room = {
id: roomInfo.id,
roomNumber: roomInfo.roomNumber || roomNode.label.split('')[0],
buildingId: buildingNode.id,
buildingName: buildingNode.label,
floorId: floorNode.id,
floorName: floorNode.label,
projectId: projectNode.id,
projectName: projectNode.label,
rentArea: roomInfo.rentalArea || 0
})
roomType: roomInfo.roomType || '',
roomStatus: roomInfo.roomStatus || '1',
buildingArea: roomInfo.buildingArea || 0,
rentalArea: roomInfo.rentalArea || 0
}
//
const roomExists = floor.rooms.some(r => r.id === room.id)
if (!roomExists) {
floor.rooms.push(room)
}
}
})
// Map
this.billForm.roomIds = Array.from(projectMap.values())
},
//
beforeUpload(file) {
@ -586,32 +656,87 @@ export default {
//
uploadFile(options) {
this.attachmentLoading = true
const file = options.file
// API
setTimeout(() => {
this.fileList.push({
name: options.file.name,
url: URL.createObjectURL(options.file),
operatorName: '当前用户',
operateTime: new Date().toLocaleString()
})
this.attachmentLoading = false
this.$message.success('上传成功')
}, 1000)
// URL
const tempUrl = URL.createObjectURL(file)
const fileName = file.name
//
const currentUser = this.$store.getters.name || '当前用户'
this.fileList.push({
name: fileName,
file: file,
url: tempUrl,
operatorName: currentUser,
operateTime: new Date().toLocaleString()
})
this.attachmentLoading = false
this.$message.success('上传成功')
console.log(this.fileList);
},
//
previewFile(file) {
window.open(file.url)
this.previewLoading = true
this.previewDialogVisible = true
this.currentPreviewName = file.name || '附件'
this.previewUrl = ''
this.isPdf = false
this.isImage = false
//
const fileExt = file.name ? file.name.split('.').pop().toLowerCase() : ''
try {
//
const blob = file.file ? file.file : new Blob()
this.previewBlob = blob
//
if (fileExt === 'pdf') {
this.isPdf = true
} else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) {
this.isImage = true
}
// URL
this.previewUrl = file.url || URL.createObjectURL(blob)
this.previewLoading = false
} catch (error) {
console.error('预览失败', error)
this.$message.error('获取预览数据失败')
this.previewLoading = false
}
},
//
downloadCurrentPreview() {
if (!this.previewUrl) {
this.$message.error('没有可下载的文件')
return
}
const link = document.createElement('a')
link.href = this.previewUrl
link.setAttribute('download', this.currentPreviewName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
},
//
downloadFile(file) {
// API
const link = document.createElement('a')
link.href = file.url
link.download = file.name
link.click()
},
//
deleteFile(index) {
this.$confirm('确认删除该附件?', '提示', {
@ -631,8 +756,8 @@ export default {
}
if (this.formExtension.billingPeriod && this.formExtension.billingPeriod.length === 2) {
this.billForm.billingStartDate = this.formExtension.billingPeriod[0]
this.billForm.billingEndDate = this.formExtension.billingPeriod[1]
this.billForm.chggBgnDt = this.formExtension.billingPeriod[0]
this.billForm.chggEndDt = this.formExtension.billingPeriod[1]
}
return this.billForm
@ -665,7 +790,7 @@ export default {
}
//
if (this.billForm.roomInfoList.length === 0) {
if (this.billForm.roomIds.length === 0) {
this.$message.warning('请至少选择一个房源')
this.activeTab = 'roomInfo'
resolve(false)
@ -698,30 +823,28 @@ export default {
this.billForm = {
contractId: '',
contractNumber: '',
payeeId: '',
payeeName: '',
paysdId: '',
paysdName: '',
payeeContact: '',
feeCategoryId: '',
feeTypeId: '',
feeTypeName: '',
feTpName: '',
currCode: '156', //
billingStartDate: '',
billingEndDate: '',
receivableAmount: 0,
price: 0,
priceU: '元/月',
taxInclusiveRule: '含税',
chggBgnDt: '',
chggEndDt: '',
accigRcvAmt: 0,
taxInclusiveRule: '1',
taxRate: 0,
receivableDate: '',
specialBillType: '正常',
lateFeeStartDays: 0,
lateFeeRate: 0,
lateFeeLimit: 0,
pybDt: '',
specialBillType: '1',
ovdueStartDays: 0,
ovdueIntRate: 0,
ovdueLimitRate: 0,
companyId: '',
companyName: '',
corNm: '',
accountId: '',
billRemark: '',
roomInfoList: []
roomIds: []
}
//
@ -738,7 +861,62 @@ export default {
//
this.fileList = []
}
},
//
submitForm() {
this.validateForm().then(valid => {
if (valid) {
const formData = this.getFormData()
this.loading = true
addBill(formData)
.then(response => {
if (response.code === '0000000000000000') {
this.$message.success('添加账单成功')
//
if (this.fileList.length > 0) {
this.uploadAttachments(response.data)
} else {
this.$emit('success')
this.resetForm()
}
} else {
this.$message.error(response.message || '添加账单失败')
this.loading = false
}
})
.catch(error => {
console.error('添加账单失败', error)
this.$message.error('添加账单失败,请检查表单数据')
this.loading = false
})
}
})
},
//
uploadAttachments(billId) {
const uploadPromises = this.fileList.map(fileItem => {
return uploadBillAttachment(billId, fileItem.file)
})
Promise.all(uploadPromises)
.then(() => {
this.$message.success('附件上传成功')
this.$emit('success')
this.resetForm()
this.loading = false
})
.catch(error => {
console.error('附件上传失败', error)
this.$message.warning('账单创建成功,但部分附件上传失败')
this.$emit('success')
this.resetForm()
this.loading = false
})
},
}
}
</script>
@ -755,4 +933,45 @@ export default {
color: #F56C6C;
}
}
.preview-container {
min-height: 500px;
max-height: 700px;
overflow: auto;
.preview-iframe {
width: 100%;
height: 500px;
border: none;
}
.preview-image {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
}
.preview-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 300px;
color: #909399;
i {
font-size: 48px;
margin-bottom: 20px;
}
p {
font-size: 16px;
}
.preview-actions {
margin-top: 20px;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -2,17 +2,17 @@
<div class="app-container">
<!-- 搜索表单 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" class="search-form">
<el-form-item label="合同编号" prop="contractNumber">
<el-input v-model="queryParams.contractNumber" placeholder="请输入合同编号" clearable style="width: 220px" />
<el-form-item label="合同编号" prop="contractNo">
<el-input v-model="queryParams.contractNo" placeholder="请输入合同编号" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="房号" prop="roomNumber">
<el-input v-model="queryParams.roomNumber" placeholder="请输入房号" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="对方名称" prop="payeeName">
<el-input v-model="queryParams.payeeName" placeholder="请输入对方名称" clearable style="width: 220px" />
<el-form-item label="对方名称" prop="paysdName">
<el-input v-model="queryParams.paysdName" placeholder="请输入对方名称" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="账单编号" prop="billNumber">
<el-input v-model="queryParams.billNumber" placeholder="请输入账单编号" clearable style="width: 220px" />
<el-form-item label="账单编号" prop="totBillNo">
<el-input v-model="queryParams.totBillNo" placeholder="请输入账单编号" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="账单状态" prop="billStatus">
<el-select v-model="queryParams.billStatus" placeholder="请选择账单状态" clearable multiple style="width: 220px">
@ -20,7 +20,7 @@
<el-option label="关闭" value="0" />
</el-select>
</el-form-item>
<el-form-item label="应收时间" prop="receivableDate">
<el-form-item label="应收时间" prop="pybDt">
<el-date-picker
v-model="dateRange"
type="daterange"
@ -77,11 +77,11 @@
<!-- 表格区域 -->
<el-table v-loading="loading" :data="billList" border>
<el-table-column label="对方名称" prop="payeeName" min-width="120" show-overflow-tooltip />
<el-table-column label="对方名称" prop="paysdName" min-width="120" show-overflow-tooltip />
<el-table-column label="项目楼宇名称" prop="projectName" min-width="120" show-overflow-tooltip />
<el-table-column label="房号" prop="roomNumber" min-width="100" show-overflow-tooltip />
<el-table-column label="账单编号" prop="billNumber" min-width="150" show-overflow-tooltip />
<el-table-column label="合同编号" prop="contractNumber" min-width="150" show-overflow-tooltip />
<el-table-column label="账单编号" prop="totBillNo" min-width="150" show-overflow-tooltip />
<el-table-column label="合同编号" prop="contractNo" min-width="150" show-overflow-tooltip />
<el-table-column label="结清状态" min-width="100" align="center">
<template slot-scope="scope">
<el-tag :type="getClearStatusType(scope.row.clearStatus)">
@ -89,25 +89,25 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="费用类型" prop="feeTypeName" min-width="120" show-overflow-tooltip />
<el-table-column label="账单金额" prop="billAmount" min-width="100" align="right">
<el-table-column label="费用类型" prop="feTpName" min-width="120" show-overflow-tooltip />
<el-table-column label="账单金额" prop="accblAmt" min-width="100" align="right">
<template slot-scope="scope">
{{ scope.row.billAmount ? scope.row.billAmount.toFixed(2) : '0.00' }}
{{ scope.row.accblAmt ? scope.row.accblAmt.toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column label="应收金额" prop="receivableAmount" min-width="100" align="right">
<el-table-column label="应收金额" prop="accigRcvAmt" min-width="100" align="right">
<template slot-scope="scope">
{{ scope.row.receivableAmount ? scope.row.receivableAmount.toFixed(2) : '0.00' }}
{{ scope.row.accigRcvAmt ? scope.row.accigRcvAmt.toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column label="实收金额" prop="receivedAmount" min-width="100" align="right">
<el-table-column label="实收金额" prop="atmRecvAmt" min-width="100" align="right">
<template slot-scope="scope">
{{ scope.row.receivedAmount ? scope.row.receivedAmount.toFixed(2) : '0.00' }}
{{ scope.row.atmRecvAmt ? scope.row.atmRecvAmt.toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column label="开始日期" prop="billingStartDate" min-width="120" align="center" />
<el-table-column label="结束日期" prop="billingEndDate" min-width="120" align="center" />
<el-table-column label="应收日期" prop="receivableDate" min-width="120" align="center" />
<el-table-column label="开始日期" prop="chggBgnDt" min-width="120" align="center" />
<el-table-column label="结束日期" prop="chggEndDt" min-width="120" align="center" />
<el-table-column label="应收日期" prop="pybDt" min-width="120" align="center" />
<el-table-column label="操作" width="150" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleViewDetail(scope.row)">查看</el-button>
@ -171,10 +171,10 @@ export default {
queryParams: {
pageNum: 1,
pageSize: 10,
contractNumber: '',
contractNo: '',
roomNumber: '',
payeeName: '',
billNumber: '',
paysdName: '',
totBillNo: '',
billStatus: [],
receivableDateStart: '',
receivableDateEnd: '',
@ -217,7 +217,7 @@ export default {
}
getBillList(queryParams).then(response => {
this.billList = response.data.data || []
this.billList = response.data.list || []
this.total = response.data.total
this.loading = false
}).catch(() => {
@ -250,10 +250,10 @@ export default {
children = category.financeFeeTypes.map(feeType => {
return {
id: feeType.id || Math.random().toString(36).substr(2, 9),
label: feeType.feeTypeName || '未命名费用',
label: feeType.feTpName || '未命名费用',
categoryId: categoryId,
// 便使
feeTypeName: feeType.feeTypeName || '未命名费用',
feTpName: feeType.feTpName || '未命名费用',
//
children: null
}
@ -353,12 +353,28 @@ export default {
this.$refs.addBillForm.validateForm().then(valid => {
if (valid) {
const billData = this.$refs.addBillForm.getFormData()
billData.billType = 1
billData.billSource = 3
addBill(billData).then(response => {
this.$message.success('添加账单成功')
this.dialogVisible = false
this.getList()
if (response.code === '0000000000000000') {
this.$message.success('添加账单成功')
//
const fileList = this.$refs.addBillForm.fileList
if (fileList && fileList.length > 0) {
this.$refs.addBillForm.uploadAttachments(response.data)
//
} else {
this.dialogVisible = false
this.getList()
}
} else {
this.$message.error(response.message || '添加账单失败')
}
}).catch(() => {
//
this.$message.error('添加账单失败')
})
}
})

View File

@ -0,0 +1,520 @@
<template>
<div class="transaction-detail-container" v-loading="loading">
<!-- 详情主体 -->
<el-tabs v-model="activeTab">
<!-- 总体信息 -->
<el-tab-pane label="总体信息" name="overall">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>总体信息</span>
</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="对方单位名称">{{ detailData.totalInfo && detailData.totalInfo.unitName || '-' }}</el-descriptions-item>
<el-descriptions-item label="总金额">
<span class="amount">{{ formatAmount(detailData.totalInfo && detailData.totalInfo.totalAmount) }}</span>
</el-descriptions-item>
<el-descriptions-item label="已匹配金额">
<span class="amount matched">{{ formatAmount(detailData.totalInfo && detailData.totalInfo.matchedAmount) }}</span>
</el-descriptions-item>
<el-descriptions-item label="未匹配金额">
<span class="amount unmatched">{{ formatAmount(detailData.totalInfo && detailData.totalInfo.unmatchedAmount) }}</span>
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-tab-pane>
<!-- 流水信息 -->
<el-tab-pane label="流水信息" name="transaction">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>流水信息</span>
</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="借贷标">
<el-tag :type="(detailData.transaction && detailData.transaction.dcFlagCode === '0') ? 'danger' : 'success'">
{{ (detailData.transaction && detailData.transaction.dcFlagCode === '0') ? '借(支出)' : '贷(收入)' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="发生额">
<span class="amount">{{ formatAmount(detailData.transaction && detailData.transaction.occuAmt) }}</span>
</el-descriptions-item>
<el-descriptions-item label="入账日期">{{ detailData.transaction && detailData.transaction.inaccDate || '-' }}</el-descriptions-item>
<el-descriptions-item label="对方单位名称">{{ detailData.transaction && detailData.transaction.unitName || '-' }}</el-descriptions-item>
<el-descriptions-item label="支付方式">{{ getPaymentModeName(detailData.transaction && detailData.transaction.payModeName) }}</el-descriptions-item>
<el-descriptions-item label="对方账号">{{ detailData.transaction && detailData.transaction.opsAccname || '-' }}</el-descriptions-item>
<el-descriptions-item label="凭证号">{{ detailData.transaction && detailData.transaction.txVchrNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="收据编号">{{ detailData.transaction && detailData.transaction.receiptNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="联系人">{{ detailData.transaction && detailData.transaction.conterName || '-' }}</el-descriptions-item>
<el-descriptions-item label="流水账户">{{ detailData.transaction && detailData.transaction.openaccBankName || '-' }}</el-descriptions-item>
<el-descriptions-item label="摘要">{{ detailData.transaction && detailData.transaction.summ || '-' }}</el-descriptions-item>
<el-descriptions-item label="备注">{{ detailData.transaction && detailData.transaction.remark || '-' }}</el-descriptions-item>
</el-descriptions>
</el-card>
</el-tab-pane>
<!-- 匹配账单新增的独立标签页 -->
<el-tab-pane label="匹配账单" name="matchBills">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>匹配账单</span>
</div>
<el-table :data="detailData.relatedBills || []" border style="width: 100%">
<el-table-column prop="unitName" label="对方名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="feeTypeName" label="费用类型" min-width="120" show-overflow-tooltip />
<el-table-column label="费用周期" min-width="200">
<template slot-scope="scope">
{{ scope.row.billingStartDate || '-' }} {{ scope.row.billingEndDate || '-' }}
</template>
</el-table-column>
<el-table-column prop="receivableAmount" label="应收金额" min-width="120" align="right">
<template slot-scope="scope">
{{ formatAmount(scope.row.receivableAmount) }}
</template>
</el-table-column>
</el-table>
<div v-if="!detailData.relatedBills || detailData.relatedBills.length === 0" class="empty-data">
暂无匹配账单
</div>
</el-card>
</el-tab-pane>
<!-- 附件信息 -->
<el-tab-pane label="附件信息" name="attachment">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>附件信息</span>
<el-button style="float: right;" type="primary" size="small" @click="handleAddAttachment">
<i class="el-icon-plus"></i> 添加附件
</el-button>
</div>
<el-table :data="detailData.attachments || []" border style="width: 100%">
<el-table-column prop="fileName" label="文件名" min-width="250" show-overflow-tooltip />
<el-table-column prop="createUserName" label="操作人" width="150" />
<el-table-column prop="createTime" label="操作时间" width="180" />
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="previewFile(scope.row)">预览</el-button>
<el-button size="mini" type="text" @click="downloadFile(scope.row)">下载</el-button>
<el-button size="mini" type="text" class="delete-btn" @click="deleteFile(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="!detailData.attachments || detailData.attachments.length === 0" class="empty-data">
暂无附件
</div>
</el-card>
<!-- 文件上传对话框 -->
<el-dialog title="上传附件" :visible.sync="uploadDialogVisible" width="500px" append-to-body>
<el-upload
class="upload-area"
action="#"
:http-request="uploadFile"
:file-list="uploadFileList"
:before-upload="beforeUpload"
multiple
:limit="10">
<el-button type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png/pdf文件且不超过5MB</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button @click="uploadDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmUpload"> </el-button>
</div>
</el-dialog>
</el-tab-pane>
<!-- 操作记录 -->
<el-tab-pane label="操作记录" name="logs">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>操作记录</span>
</div>
<el-table :data="detailData.operationRecords || []" border style="width: 100%" v-loading="logsLoading">
<el-table-column prop="oprPersonNm" label="操作人" width="150" />
<el-table-column prop="oprContent" label="操作内容" min-width="250" show-overflow-tooltip />
<el-table-column prop="createTime" label="操作时间" width="180" />
</el-table>
<div v-if="!detailData.operationRecords || detailData.operationRecords.length === 0" class="empty-data">
暂无操作记录
</div>
</el-card>
</el-tab-pane>
</el-tabs>
<!-- 文件预览对话框 -->
<el-dialog title="附件预览" :visible.sync="previewDialogVisible" width="800px" append-to-body>
<div class="preview-container" v-loading="previewLoading">
<div v-if="previewUrl && isPdf" class="preview-iframe">
<iframe :src="previewUrl" width="100%" height="500px" frameborder="0"></iframe>
</div>
<div v-else-if="previewUrl && isImage" class="preview-image">
<img :src="previewUrl" style="max-width: 100%;" />
</div>
<div v-else-if="!previewLoading" class="preview-error">
<i class="el-icon-warning"></i>
<p>预览加载失败请尝试下载后查看</p>
<div class="preview-actions">
<el-button type="primary" @click="downloadCurrentPreview">下载文件</el-button>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="previewDialogVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
getTransactionDetail,
uploadTransactionAttachment,
deleteTransactionAttachment,
getTransactionOperationLogs,
previewAttachment,
downloadAttachment,
addTransactionOperation
} from '@/api/finance'
export default {
name: 'TransactionDetail',
props: {
transactionId: {
type: [String, Number],
required: true
}
},
data() {
return {
loading: false,
activeTab: 'overall',
detailData: {},
//
uploadDialogVisible: false,
uploadFileList: [],
//
previewDialogVisible: false,
previewLoading: false,
previewUrl: '',
previewBlob: null,
currentPreviewName: '',
isPdf: false,
isImage: false,
//
logsLoading: false,
logsTotal: 0,
logsQuery: {
pageNum: 1,
pageSize: 10,
transactionId: undefined
}
}
},
created() {
this.getDetail()
},
methods: {
//
getDetail() {
this.loading = true
getTransactionDetail(this.transactionId).then(response => {
this.detailData = response.data || {}
this.loading = false
}).catch(() => {
this.loading = false
})
},
//
getOperationLogs() {
this.logsLoading = true
this.logsQuery.transactionId = this.transactionId
getTransactionOperationLogs(this.logsQuery).then(response => {
if (this.detailData.operationRecords) {
this.detailData.operationRecords = response.data.data || []
}
this.logsTotal = response.data.total
this.logsLoading = false
}).catch(() => {
this.logsLoading = false
})
},
//
handleLogsSizeChange(val) {
this.logsQuery.pageSize = val
this.getOperationLogs()
},
//
handleLogsCurrentChange(val) {
this.logsQuery.pageNum = val
this.getOperationLogs()
},
//
formatAmount(amount) {
if (amount === undefined || amount === null) {
return '0.00'
}
return parseFloat(amount).toFixed(2)
},
//
handleAddAttachment() {
this.uploadDialogVisible = true
this.uploadFileList = []
},
//
beforeUpload(file) {
const isValidType = ['image/jpeg', 'image/png', 'application/pdf'].includes(file.type)
const isLt5M = file.size / 1024 / 1024 < 5
if (!isValidType) {
this.$message.error('只能上传JPG/PNG/PDF格式的文件!')
}
if (!isLt5M) {
this.$message.error('文件大小不能超过5MB!')
}
return isValidType && isLt5M
},
//
uploadFile(options) {
//
const file = options.file
// URL
const tempUrl = URL.createObjectURL(file)
//
this.uploadFileList.push({
name: file.name,
file: file,
url: tempUrl
})
},
//
confirmUpload() {
if (this.uploadFileList.length === 0) {
this.$message.warning('请选择要上传的文件')
return
}
const userId = this.$store.getters.userId || ''
const userName = this.$store.getters.name || ''
const uploadPromises = this.uploadFileList.map(fileItem => {
return uploadTransactionAttachment(this.transactionId, fileItem.file, userId, userName)
})
this.loading = true
Promise.all(uploadPromises)
.then(() => {
this.$message.success('附件上传成功')
this.uploadDialogVisible = false
//
this.getDetail()
this.loading = false
})
.catch(() => {
this.$message.error('部分附件上传失败,请重试')
this.loading = false
})
},
//
previewFile(file) {
this.previewLoading = true
this.previewDialogVisible = true
this.currentPreviewName = file.fileName || '附件'
this.previewUrl = ''
this.isPdf = false
this.isImage = false
previewAttachment(file.fileId).then(response => {
//
const contentType = response.headers['content-type']
// blob
const blob = new Blob([response.data], { type: contentType })
this.previewBlob = blob
//
this.isPdf = contentType === 'application/pdf'
this.isImage = contentType.startsWith('image/')
// URL
this.previewUrl = URL.createObjectURL(blob)
this.previewLoading = false
}).catch(error => {
console.error('预览失败', error)
this.$message.error('获取预览数据失败')
this.previewLoading = false
})
},
//
downloadCurrentPreview() {
if (!this.previewBlob) {
this.$message.error('没有可下载的文件')
return
}
const url = URL.createObjectURL(this.previewBlob)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', this.currentPreviewName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
},
//
downloadFile(file) {
downloadAttachment(file.fileId, file.fileName).then(response => {
//
const blob = new Blob([response.data])
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = file.fileName
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href)
}).catch(() => {
this.$message.warning('无法下载该文件')
})
},
//
deleteFile(index, file) {
this.$confirm('确认删除该附件?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const userId = this.$store.getters.userId || ''
deleteTransactionAttachment(file.fileId, userId).then(() => {
this.$message.success('删除成功')
if (this.detailData.attachments) {
this.detailData.attachments.splice(index, 1)
}
//
this.getDetail()
})
}).catch(() => {})
},
//
getPaymentModeName(code) {
const paymentModeMap = {
'1': '现金',
'2': '网银转账',
'3': 'POS机',
'4': '支付宝',
'5': '微信',
'6': '转账支票',
'7': '其他方式',
'8': '线上支付'
}
return paymentModeMap[code] || code || '-'
}
}
}
</script>
<style lang="scss" scoped>
.transaction-detail-container {
padding: 0 20px;
.amount {
font-weight: bold;
color: #409EFF;
&.matched {
color: #67C23A;
}
&.unmatched {
color: #E6A23C;
}
}
.box-card {
margin-bottom: 20px;
}
.upload-area {
text-align: center;
padding: 20px 0;
}
.empty-data {
color: #909399;
text-align: center;
padding: 30px 0;
}
.delete-btn {
color: #F56C6C;
}
.preview-container {
min-height: 500px;
max-height: 700px;
overflow: auto;
.preview-iframe {
width: 100%;
height: 500px;
border: none;
}
.preview-image {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
}
.preview-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 300px;
color: #909399;
i {
font-size: 48px;
margin-bottom: 20px;
}
p {
font-size: 16px;
}
.preview-actions {
margin-top: 20px;
}
}
}
}
</style>

View File

@ -0,0 +1,352 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" class="search-form">
<el-form-item label="借贷标" prop="dcFlagCode">
<el-select v-model="queryParams.dcFlagCode" placeholder="请选择借贷标" clearable style="width: 200px">
<el-option label="借(支出)" value="0" />
<el-option label="贷(收入)" value="1" />
</el-select>
</el-form-item>
<el-form-item label="支付方式" prop="payModeName">
<el-select v-model="queryParams.payModeCode" placeholder="请选择支付方式" clearable style="width: 200px">
<el-option label="现金" value="1" />
<el-option label="网银转账" value="2" />
<el-option label="POS机" value="3" />
<el-option label="支付宝" value="4" />
<el-option label="微信" value="5" />
<el-option label="转账支票" value="6" />
<el-option label="其他方式" value="7" />
<el-option label="线上支付" value="8" />
</el-select>
</el-form-item>
<el-form-item label="流水账户" prop="accountId">
<el-select v-model="queryParams.accountId" placeholder="请选择流水账户" clearable filterable style="width: 200px">
<el-option v-for="item in accountOptions" :key="item.id" :label="item.accountName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="创建人员" prop="createUserName">
<el-input v-model="queryParams.createUserName" placeholder="请输入创建人员" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="入账时间" prop="inaccDate">
<el-date-picker
v-model="accountDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 240px">
</el-date-picker>
</el-form-item>
<el-form-item label="创建时间" prop="createDate">
<el-date-picker
v-model="createDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 240px">
</el-date-picker>
</el-form-item>
<el-form-item label="发生额" prop="occuAmt">
<el-input-number v-model="queryParams.occuAmtMin" placeholder="最小值" :min="0" :precision="2" style="width: 110px" controls-position="right" />
<span style="margin: 0 5px;">-</span>
<el-input-number v-model="queryParams.occuAmtMax" placeholder="最大值" :min="0" :precision="2" style="width: 110px" controls-position="right" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
<el-button type="warning" icon="el-icon-download" style="float: right;" @click="handleExport">导出</el-button>
</el-form>
<!-- 表格区域 -->
<el-table v-loading="loading" :data="transactionList" border>
<el-table-column label="项目楼宇名称" prop="projectName" min-width="120" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.projectName }} - {{ scope.row.buildingName }}
</template>
</el-table-column>
<el-table-column label="房号" prop="roomNo" min-width="100" show-overflow-tooltip />
<el-table-column label="对方账号" prop="opsAccname" min-width="150" show-overflow-tooltip />
<el-table-column label="入账日期" prop="inaccDate" min-width="120" align="center" />
<el-table-column label="对方名称" prop="unitName" min-width="120" show-overflow-tooltip />
<el-table-column label="借贷标" min-width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.dcFlagCode === '0' ? 'danger' : 'success'">
{{ scope.row.dcFlagCode === '0' ? '借(支出)' : '贷(收入)' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="发生额" prop="occuAmt" min-width="100" align="right">
<template slot-scope="scope">
{{ scope.row.occuAmt ? parseFloat(scope.row.occuAmt).toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column label="币种(单位)" prop="currCode" min-width="100" align="center">
<template slot-scope="scope">
{{ getCurrencyName(scope.row.currCode) }}
</template>
</el-table-column>
<el-table-column label="支付方式" prop="payModeName" min-width="100" align="center">
<template slot-scope="scope">
{{ getPaymentModeName(scope.row.payModeName) }}
</template>
</el-table-column>
<el-table-column label="创建人" prop="createUserId" min-width="100" show-overflow-tooltip />
<el-table-column label="状态" prop="transactionStatus" min-width="100" align="center">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.transactionStatus)">
{{ getStatusName(scope.row.transactionStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建日期" prop="createTime" min-width="120" align="center" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleViewDetail(scope.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<!-- 分页区域 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="queryParams.pageNum"
:page-sizes="[10, 20, 30, 50]"
:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
<!-- 流水详情对话框 -->
<el-dialog title="收支流水详情" :visible.sync="detailVisible" width="80%" append-to-body>
<transaction-detail v-if="detailVisible" :transactionId="currentTransaction.id" />
</el-dialog>
</div>
</template>
<script>
import { getTransactionList, exportTransactionList, getAllFinanceAccounts } from '@/api/finance'
import TransactionDetail from './components/TransactionDetail'
export default {
name: 'Transaction',
components: {
TransactionDetail
},
data() {
return {
//
loading: false,
//
transactionList: [],
//
total: 0,
//
accountDateRange: [],
//
createDateRange: [],
//
queryParams: {
pageNum: 1,
pageSize: 10,
dcFlagCode: undefined,
payModeCode: undefined,
accountId: undefined,
createUserName: undefined,
inaccDateStart: undefined,
inaccDateEnd: undefined,
createDateStart: undefined,
createDateEnd: undefined,
occuAmtMin: undefined,
occuAmtMax: undefined
},
//
accountOptions: [],
//
detailVisible: false,
//
currentTransaction: {}
}
},
created() {
this.getList()
// this.getAccountOptions()
},
methods: {
//
getList() {
this.loading = true
//
const queryParams = { ...this.queryParams }
//
if (this.accountDateRange && this.accountDateRange.length > 0) {
queryParams.inaccDateStart = this.accountDateRange[0]
queryParams.inaccDateEnd = this.accountDateRange[1]
}
//
if (this.createDateRange && this.createDateRange.length > 0) {
queryParams.createDateStart = this.createDateRange[0]
queryParams.createDateEnd = this.createDateRange[1]
}
//
if (queryParams.payModeCode) {
queryParams.payModeName = queryParams.payModeCode
delete queryParams.payModeCode
}
getTransactionList(queryParams).then(response => {
this.transactionList = response.data.data || []
this.total = response.data.total
this.loading = false
}).catch(() => {
this.loading = false
})
},
//
getAccountOptions() {
getAllFinanceAccounts().then(response => {
this.accountOptions = response.data || []
})
},
//
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
//
resetQuery() {
this.accountDateRange = []
this.createDateRange = []
this.$refs.queryForm.resetFields()
this.handleQuery()
},
//
handleSizeChange(val) {
this.queryParams.pageSize = val
this.getList()
},
//
handleCurrentChange(val) {
this.queryParams.pageNum = val
this.getList()
},
//
handleViewDetail(row) {
this.currentTransaction = row
this.detailVisible = true
},
//
handleExport() {
//
const exportParams = { ...this.queryParams }
delete exportParams.pageNum
delete exportParams.pageSize
//
if (this.accountDateRange && this.accountDateRange.length > 0) {
exportParams.inaccDateStart = this.accountDateRange[0]
exportParams.inaccDateEnd = this.accountDateRange[1]
}
//
if (this.createDateRange && this.createDateRange.length > 0) {
exportParams.createDateStart = this.createDateRange[0]
exportParams.createDateEnd = this.createDateRange[1]
}
this.$confirm('是否确认导出所有数据项?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
exportTransactionList(exportParams).then(response => {
//
const blob = new Blob([response.data])
const fileName = `收支流水列表_${new Date().getTime()}.xlsx`
if (window.navigator.msSaveOrOpenBlob) {
// IE
navigator.msSaveBlob(blob, fileName)
} else {
// IE
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = fileName
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href)
}
})
})
},
//
getCurrencyName(currency) {
const currencyMap = {
'156': '人民币',
'840': '美元',
'978': '欧元',
'826': '英镑',
'392': '日元'
}
return currencyMap[currency] || currency || '人民币'
},
//
getStatusType(status) {
const statusMap = {
'0': 'info',
'1': 'success'
}
return statusMap[status] || 'info'
},
//
getStatusName(status) {
const statusMap = {
'0': '处理中',
'1': '已完成'
}
return statusMap[status] || '未知状态'
},
//
getPaymentModeName(code) {
const paymentModeMap = {
'1': '现金',
'2': '网银转账',
'3': 'POS机',
'4': '支付宝',
'5': '微信',
'6': '转账支票',
'7': '其他方式',
'8': '线上支付'
}
return paymentModeMap[code] || code || '-'
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
padding: 20px;
.search-form {
margin-bottom: 20px;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
}
</style>

View File

@ -63,7 +63,7 @@
</el-select>
</el-form-item>
<el-form-item label="所属地区">
<el-input v-model="queryParams.region" placeholder="请输入所属地区" clearable size="small" />
<region-selector v-model="queryParams.region" :value="queryParams.region" placeholder="请选择所属地区" size="small" @change="handleRegionChange" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@ -133,7 +133,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="所属地区" prop="region">
<el-input v-model="form.region" placeholder="请输入所属地区" />
<region-selector v-model="form.region" placeholder="请选择所属地区" />
</el-form-item>
</el-col>
</el-row>
@ -223,9 +223,13 @@
<script>
import { getProjectList, getProjectDetail, addProject, updateProject, deleteProject, getAllProjectStatistics, getTagByType } from '@/api/project'
import RegionSelector from '@/components/RegionSelector'
export default {
name: 'ProjectList',
components: {
RegionSelector
},
data() {
return {
//
@ -338,7 +342,9 @@ export default {
handleAdd() {
this.dialogTitle = '新增项目'
//
this.form = {}
this.form = {
region: '' // region
}
this.dialogVisible = true
},
/** 修改按钮操作 */
@ -474,6 +480,10 @@ export default {
//
this.form = {}
this.$refs['form'].clearValidate()
},
handleRegionChange(regionText) {
this.queryParams.region = regionText
}
}
}

View File

@ -12,7 +12,7 @@ module.exports = {
},
// proxy: {
// '/': {
// target: 'http://192.168.137.214:8082',
// target: 'http://192.168.137.214:8080',
// changeOrigin: true,
// pathRewrite: {
// '^/api': '/api'