业务财务相关页面对接
This commit is contained in:
parent
3ac7dbff00
commit
9068135f26
@ -1,4 +1,4 @@
|
||||
NODE_ENV = 'development'
|
||||
|
||||
# 开发环境API地址
|
||||
VUE_APP_BASE_API = http://192.168.137.3:8080/api
|
||||
VUE_APP_BASE_API = http://192.168.137.3:8080/api/api
|
@ -68,7 +68,7 @@ export function getLabelFields() {
|
||||
];
|
||||
|
||||
return Promise.resolve({
|
||||
code: '000000',
|
||||
code: '0000000000000000',
|
||||
msg: 'success',
|
||||
data: fieldData
|
||||
});
|
||||
|
@ -338,3 +338,112 @@ export function getFeeTypeTree(params) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 预览账单
|
||||
export function previewBill(data) {
|
||||
return request({
|
||||
url: '/finance/charging-standard/bill/preview',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 生成账单
|
||||
export function generateBill(data) {
|
||||
return request({
|
||||
url: '/finance/charging-standard/bill/generate',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询账单-收费标准关联表数据
|
||||
* @param {Object} data - 查询参数
|
||||
* @returns {Promise} - 返回Promise对象
|
||||
*/
|
||||
export function getBillChargingStandardRelPage(data) {
|
||||
return request({
|
||||
url: '/finance/bill-charging-standard-rel/page',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特定标准ID的绑定房间账单列表
|
||||
* @param {string} standardId 收费标准ID
|
||||
*/
|
||||
export function getBillsByStandardId(standardId) {
|
||||
return request({
|
||||
url: `/finance/bill-charging-standard-rel/${standardId}/bills`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 账单管理接口
|
||||
export function addBill(data) {
|
||||
return request({
|
||||
url: '/bill/add',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getBillList(data) {
|
||||
return request({
|
||||
url: '/bill/list',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getBillDetail(billId) {
|
||||
return request({
|
||||
url: `/bill/detail/${billId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadBillAttachment(billId, file) {
|
||||
const formData = new FormData()
|
||||
formData.append('billId', billId)
|
||||
formData.append('file', file)
|
||||
return request({
|
||||
url: '/bill/attachment/upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteBillAttachment(attachmentId) {
|
||||
return request({
|
||||
url: `/bill/attachment/${attachmentId}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function getBillAttachmentList(billId) {
|
||||
return request({
|
||||
url: `/bill/attachment/list/${billId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function exportBillList(data) {
|
||||
return request({
|
||||
url: '/bill/export',
|
||||
method: 'post',
|
||||
data,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
@ -24,6 +24,12 @@ export default {
|
||||
component: () => import('@/views/finance/chargeStandard/index.vue'),
|
||||
name: 'ChargeStandard',
|
||||
meta: { title: '收费标准', icon: 'el-icon-price-tag' }
|
||||
},
|
||||
{
|
||||
path: 'billList',
|
||||
component: () => import('@/views/finance/billList/index.vue'),
|
||||
name: 'BillList',
|
||||
meta: { title: '所有账单', icon: 'el-icon-notebook-2' }
|
||||
}
|
||||
]
|
||||
}
|
@ -4,8 +4,8 @@ import { API_SUCCESS_CODE } from './constants'
|
||||
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
baseURL: '/', // 修改为相对路径,使用代理
|
||||
// baseURL: process.env.VUE_APP_BASE_API, // 使用环境变量中的接口地址
|
||||
// baseURL: '/', // 修改为相对路径,使用代理
|
||||
baseURL: process.env.VUE_APP_BASE_API, // 使用环境变量中的接口地址
|
||||
|
||||
timeout: 10000 // 请求超时时间
|
||||
})
|
||||
|
@ -169,6 +169,7 @@
|
||||
|
||||
<script>
|
||||
import { listClassification, getClassification, addClassification, updateClassification, delClassification, enableClassification, disableClassification, getClassificationTree, checkClassificationInUse, getChildClassifications, delClassificationBatch } from '@/api/asset/classification'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetClassification',
|
||||
@ -262,7 +263,7 @@ export default {
|
||||
status: undefined // 获取所有状态的分类,包括启用和禁用
|
||||
}
|
||||
getClassificationTree(params).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
// 处理返回的树形数据,确保字段名称正确
|
||||
this.classTree = this.processTreeData(res.data)
|
||||
|
||||
@ -363,7 +364,7 @@ export default {
|
||||
getList() {
|
||||
this.loading = true
|
||||
listClassification(this.queryParams).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
// 转换API返回数据结构为驼峰命名
|
||||
this.classList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
@ -420,7 +421,7 @@ export default {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize
|
||||
}).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.classList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
@ -472,7 +473,7 @@ export default {
|
||||
handleEdit(row) {
|
||||
const id = row.id
|
||||
getClassification(id).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
// 将API响应数据映射到表单字段,使用驼峰命名
|
||||
this.form = {
|
||||
id: res.data.id,
|
||||
@ -524,7 +525,7 @@ export default {
|
||||
|
||||
const method = this.form.id ? updateClassification : addClassification
|
||||
method(submitData).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('操作成功')
|
||||
this.dialogVisible = false
|
||||
|
||||
@ -556,14 +557,14 @@ export default {
|
||||
}).then(() => {
|
||||
// 先检查分类是否被使用
|
||||
checkClassificationInUse(row.id).then(res => {
|
||||
if (res.code === '000000' && res.data) {
|
||||
if (res.code === API_SUCCESS_CODE && res.data) {
|
||||
this.$message.error('该分类已被使用,无法删除')
|
||||
return
|
||||
}
|
||||
// 未被使用,执行删除操作
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
delClassification(row.id, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('删除成功')
|
||||
// 重新获取树形结构,确保更新多级分类
|
||||
this.getTree()
|
||||
@ -590,7 +591,7 @@ export default {
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
const method = row.status === '1' ? disableClassification : enableClassification
|
||||
method(row.id, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success(`${action}成功`)
|
||||
// 状态变更后,可能会影响树的显示,所以也需要刷新树
|
||||
this.getTree()
|
||||
@ -663,7 +664,7 @@ export default {
|
||||
// 执行批量删除操作
|
||||
this.loading = true
|
||||
delClassificationBatch(selectedIds, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('批量删除成功')
|
||||
// 重新获取树形结构,确保更新多级分类
|
||||
this.getTree()
|
||||
|
@ -48,6 +48,7 @@
|
||||
import { getAsset } from '@/api/asset/inventory'
|
||||
import { getClassificationTree } from '@/api/asset/classification'
|
||||
import { getLocationTree } from '@/api/asset/location'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetDetail',
|
||||
@ -89,7 +90,7 @@ export default {
|
||||
|
||||
this.loading = true
|
||||
getAsset(this.assetId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.assetDetail = response.data || {}
|
||||
console.log('资产详情数据:', this.assetDetail)
|
||||
} else {
|
||||
@ -134,7 +135,7 @@ export default {
|
||||
/** 获取资产分类树形选项 */
|
||||
getClassificationOptions() {
|
||||
getClassificationTree({ status: '1' }).then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.classificationOptions = this.processClassificationTree(response.data || [])
|
||||
}
|
||||
})
|
||||
@ -164,7 +165,7 @@ export default {
|
||||
/** 获取位置树选项 */
|
||||
getLocationOptions() {
|
||||
getLocationTree().then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.locationOptions = this.processLocationTree(response.data || [])
|
||||
}
|
||||
})
|
||||
|
@ -227,7 +227,6 @@ import { getAsset, addAsset, updateAsset, listCompanies, listUsers, uploadAssetI
|
||||
import { getClassificationTree } from '@/api/asset/classification'
|
||||
import { getLocationTree } from '@/api/asset/location'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetForm',
|
||||
props: {
|
||||
|
@ -37,6 +37,7 @@
|
||||
<script>
|
||||
import { getLabelConfig } from '@/api/asset/inventory'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetLabelPrint',
|
||||
@ -93,7 +94,7 @@ export default {
|
||||
getLabelConfig() {
|
||||
this.loading = true
|
||||
getLabelConfig().then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.labelConfig = response.data
|
||||
|
||||
// 如果API返回了需要显示的字段,则使用API返回的字段
|
||||
|
@ -267,6 +267,7 @@ import { getLocationTree } from '@/api/asset/location'
|
||||
import AssetDetail from './components/AssetDetail'
|
||||
import AssetForm from './components/AssetForm'
|
||||
import AssetLabelPrint from './components/AssetLabelPrint'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetInventory',
|
||||
@ -449,7 +450,7 @@ export default {
|
||||
getList() {
|
||||
this.loading = true
|
||||
listAssets(this.queryParams).then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.assetList = response.data.list || []
|
||||
this.total = response.data.total
|
||||
}
|
||||
@ -471,7 +472,7 @@ export default {
|
||||
/** 获取资产分类树形选项 */
|
||||
getClassificationOptions() {
|
||||
getClassificationTree({ status: '1' }).then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.classificationOptions = this.processClassificationTree(response.data || [])
|
||||
}
|
||||
})
|
||||
@ -480,7 +481,7 @@ export default {
|
||||
/** 获取资产位置树形选项 */
|
||||
getLocationOptions() {
|
||||
getLocationTree().then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.locationOptions = this.processLocationTree(response.data || [])
|
||||
}
|
||||
})
|
||||
@ -529,7 +530,7 @@ export default {
|
||||
/** 获取公司选项 */
|
||||
getCompanyOptions() {
|
||||
listCompanies().then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.companyOptions = response.data || []
|
||||
}
|
||||
})
|
||||
@ -541,7 +542,7 @@ export default {
|
||||
this.adminLoading = true
|
||||
listUsers({ name: query }).then(response => {
|
||||
this.adminLoading = false
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.adminOptions = response.data || []
|
||||
}
|
||||
}).catch(() => {
|
||||
@ -672,7 +673,7 @@ export default {
|
||||
}).then(() => {
|
||||
return deleteAsset(row.assetCode)
|
||||
}).then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
@ -694,7 +695,7 @@ export default {
|
||||
}).then(() => {
|
||||
return deleteAssets(assetCodes)
|
||||
}).then(response => {
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('批量删除成功')
|
||||
this.getList()
|
||||
}
|
||||
@ -745,7 +746,7 @@ export default {
|
||||
this.upload.isUploading = false
|
||||
this.$refs.upload.clearFiles()
|
||||
this.upload.open = false
|
||||
if (response.code === '000000') {
|
||||
if (response.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('导入成功' + (response.data && response.data.successCount ? response.data.successCount : 0) + '条数据')
|
||||
this.getList()
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ import {
|
||||
listLabelTemplates, addLabelTemplate, updateLabelTemplate,
|
||||
getLabelFields, getFieldSampleValues
|
||||
} from '@/api/asset/label'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetLabel',
|
||||
@ -153,7 +154,7 @@ export default {
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await listLabelTemplates();
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
if (res.data) {
|
||||
const labelData = res.data;
|
||||
this.labelId = labelData.id;
|
||||
@ -209,7 +210,7 @@ export default {
|
||||
async getFieldOptions() {
|
||||
try {
|
||||
const res = await getLabelFields();
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.availableFields = res.data || [];
|
||||
console.log("获取到的字段选项: ", this.availableFields);
|
||||
} else {
|
||||
@ -342,7 +343,7 @@ export default {
|
||||
|
||||
savePromise.then(res => {
|
||||
this.loading = false;
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('标签设置保存成功');
|
||||
// 重新获取标签设置
|
||||
this.getLabelSettings();
|
||||
|
@ -174,6 +174,7 @@
|
||||
|
||||
<script>
|
||||
import { listLocation, getLocation, addLocation, updateLocation, delLocation, enableLocation, disableLocation, getLocationTree, checkLocationInUse, getChildLocations, delLocationBatch, checkLocationCode } from '@/api/asset/location'
|
||||
import { API_SUCCESS_CODE } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'AssetLocation',
|
||||
@ -245,7 +246,7 @@ export default {
|
||||
return
|
||||
}
|
||||
checkLocationCode(this.form.id, value).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
if (res.data) {
|
||||
callback()
|
||||
} else {
|
||||
@ -265,7 +266,7 @@ export default {
|
||||
status: undefined // 获取所有状态的位置
|
||||
}
|
||||
getLocationTree(params).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
// 处理返回的树形数据,确保字段名称正确
|
||||
this.locationTree = this.processTreeData(res.data)
|
||||
|
||||
@ -387,7 +388,7 @@ export default {
|
||||
getList() {
|
||||
this.loading = true
|
||||
listLocation(this.queryParams).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
// 转换API返回数据结构为驼峰命名
|
||||
this.locationList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
@ -410,7 +411,7 @@ export default {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize
|
||||
}).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.locationList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
@ -464,7 +465,7 @@ export default {
|
||||
handleEdit(row) {
|
||||
const id = row.id
|
||||
getLocation(id).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
// 将API响应数据映射到表单字段,使用驼峰命名
|
||||
this.form = {
|
||||
id: res.data.id,
|
||||
@ -518,7 +519,7 @@ export default {
|
||||
|
||||
const method = this.form.id ? updateLocation : addLocation
|
||||
method(submitData).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('操作成功')
|
||||
this.dialogVisible = false
|
||||
|
||||
@ -550,14 +551,14 @@ export default {
|
||||
}).then(() => {
|
||||
// 先检查位置是否被使用
|
||||
checkLocationInUse(row.id).then(res => {
|
||||
if (res.code === '000000' && res.data) {
|
||||
if (res.code === API_SUCCESS_CODE && res.data) {
|
||||
this.$message.error('该位置已被使用,无法删除')
|
||||
return
|
||||
}
|
||||
// 未被使用,执行删除操作
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
delLocation(row.id, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('删除成功')
|
||||
// 重新获取树形结构,确保更新多级结构
|
||||
this.getTree()
|
||||
@ -584,7 +585,7 @@ export default {
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
const method = row.status === '1' ? disableLocation : enableLocation
|
||||
method(row.id, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success(`${action}成功`)
|
||||
// 状态变更后,可能会影响树的显示,所以也需要刷新树
|
||||
this.getTree()
|
||||
@ -657,7 +658,7 @@ export default {
|
||||
// 执行批量删除操作
|
||||
this.loading = true
|
||||
delLocationBatch(selectedIds, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.code === API_SUCCESS_CODE) {
|
||||
this.$message.success('批量删除成功')
|
||||
// 重新获取树形结构,确保更新多级结构
|
||||
this.getTree()
|
||||
|
758
pc/src/views/finance/billList/components/AddBill.vue
Normal file
758
pc/src/views/finance/billList/components/AddBill.vue
Normal file
@ -0,0 +1,758 @@
|
||||
<template>
|
||||
<div class="add-bill-container">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="收费模式" name="chargeMode">
|
||||
<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-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="税率(%)" prop="taxRate" required>
|
||||
<el-input-number v-model="billForm.taxRate" :min="0" :max="100" :precision="2" style="width: 200px"></el-input-number>
|
||||
</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-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>
|
||||
<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>
|
||||
<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>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="账单信息" name="billInfo">
|
||||
<el-form ref="formExtension" :model="formExtension" :rules="formExtensionRules" label-width="120px" style="margin-bottom: 20px;">
|
||||
<el-form-item label="费用类型" prop="feeTypeSelected" required>
|
||||
<el-cascader v-model="formExtension.feeTypeSelected" :options="feeTypeOptions" :props="{
|
||||
label: 'label',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
expandTrigger: 'hover'
|
||||
}"
|
||||
clearable filterable placeholder="请选择费用类型" @change="handleFeeTypeChange" style="width: 400px">
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="计费周期" prop="billingPeriod" required>
|
||||
<el-date-picker
|
||||
v-model="formExtension.billingPeriod"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
@change="handleBillingPeriodChange"
|
||||
style="width: 400px">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form ref="billInfoForm" :model="billForm" :rules="billInfoRules" label-width="120px">
|
||||
<el-form-item label="关联合同" prop="contractId">
|
||||
<el-select v-model="billForm.contractId" placeholder="请选择关联合同" filterable remote clearable
|
||||
:remote-method="searchContracts" style="width: 400px">
|
||||
<el-option v-for="item in contractOptions" :key="item.contractId"
|
||||
:label="item.contractNumber + ' - ' + item.customerName" :value="item.contractId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="付款方" prop="payeeId" required>
|
||||
<el-select v-model="billForm.payeeId" 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">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<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>
|
||||
<el-form-item label="应收日期" prop="receivableDate" required>
|
||||
<el-date-picker
|
||||
v-model="billForm.receivableDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
style="width: 400px">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属公司" prop="companyId" required>
|
||||
<el-select v-model="billForm.companyId" placeholder="请选择所属公司" filterable @change="handleCompanyChange" style="width: 400px">
|
||||
<el-option v-for="item in companyOptions" :key="item.id"
|
||||
:label="item.name" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收支账户" prop="accountId" required>
|
||||
<el-select v-model="billForm.accountId" placeholder="请选择收支账户" filterable style="width: 400px">
|
||||
<el-option v-for="item in accountOptions" :key="item.id"
|
||||
:label="item.name" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="账单备注" prop="billRemark">
|
||||
<el-input type="textarea" v-model="billForm.billRemark" placeholder="请输入账单备注" rows="3" style="width: 400px"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="房源信息" name="roomInfo">
|
||||
<el-tree
|
||||
ref="roomTree"
|
||||
:data="roomTreeData"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:default-checked-keys="selectedRoomIds"
|
||||
@check="handleRoomTreeCheck">
|
||||
</el-tree>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="账单附件" name="attachment">
|
||||
<el-upload
|
||||
class="upload-area"
|
||||
action="#"
|
||||
:http-request="uploadFile"
|
||||
:file-list="fileList"
|
||||
: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>
|
||||
|
||||
<el-table v-loading="attachmentLoading" :data="fileList" border style="width: 100%; margin-top: 20px">
|
||||
<el-table-column prop="name" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="operatorName" label="操作人" width="150"></el-table-column>
|
||||
<el-table-column prop="operateTime" label="操作时间" width="180"></el-table-column>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFeeTypeTree } from '@/api/finance'
|
||||
|
||||
export default {
|
||||
name: 'AddBill',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'chargeMode',
|
||||
// 账单表单数据
|
||||
billForm: {
|
||||
contractId: '',
|
||||
contractNumber: '',
|
||||
payeeId: '',
|
||||
payeeName: '',
|
||||
payeeContact: '',
|
||||
feeCategoryId: '',
|
||||
feeTypeId: '',
|
||||
feeTypeName: '',
|
||||
currCode: '156', // 默认人民币
|
||||
billingStartDate: '',
|
||||
billingEndDate: '',
|
||||
receivableAmount: 0,
|
||||
price: 0,
|
||||
priceU: '元/月',
|
||||
taxInclusiveRule: '含税',
|
||||
taxRate: 0,
|
||||
receivableDate: '',
|
||||
specialBillType: '正常',
|
||||
lateFeeStartDays: 0,
|
||||
lateFeeRate: 0,
|
||||
lateFeeLimit: 0,
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
accountId: '',
|
||||
billRemark: '',
|
||||
roomInfoList: []
|
||||
},
|
||||
// 表单验证规则
|
||||
chargeModeRules: {
|
||||
taxInclusiveRule: [
|
||||
{ required: true, message: '请选择含税规则', trigger: 'change' }
|
||||
],
|
||||
taxRate: [
|
||||
{ required: true, message: '请输入税率', trigger: 'blur' }
|
||||
],
|
||||
specialBillType: [
|
||||
{ required: true, message: '请选择特殊账单类型', trigger: 'change' }
|
||||
]
|
||||
},
|
||||
billInfoRules: {
|
||||
payeeId: [
|
||||
{ required: true, message: '请选择付款方', trigger: 'change' }
|
||||
],
|
||||
companyId: [
|
||||
{ required: true, message: '请选择所属公司', trigger: 'change' }
|
||||
],
|
||||
accountId: [
|
||||
{ required: true, message: '请选择收支账户', trigger: 'change' }
|
||||
],
|
||||
receivableAmount: [
|
||||
{ required: true, message: '请输入应收金额', trigger: 'blur' }
|
||||
],
|
||||
receivableDate: [
|
||||
{ required: true, message: '请选择应收日期', trigger: 'change' }
|
||||
]
|
||||
},
|
||||
// 额外表单数据和验证规则
|
||||
formExtension: {
|
||||
feeTypeSelected: null,
|
||||
billingPeriod: []
|
||||
},
|
||||
formExtensionRules: {
|
||||
feeTypeSelected: [
|
||||
{ required: true, message: '请选择费用类型', trigger: 'change' }
|
||||
],
|
||||
billingPeriod: [
|
||||
{ type: 'array', required: true, message: '请选择计费周期', trigger: 'change' }
|
||||
]
|
||||
},
|
||||
// 选择项相关
|
||||
contractOptions: [],
|
||||
payeeOptions: [],
|
||||
feeTypeOptions: [],
|
||||
companyOptions: [],
|
||||
accountOptions: [],
|
||||
roomTreeData: [],
|
||||
selectedRoomIds: [],
|
||||
// 附件上传相关
|
||||
fileList: [],
|
||||
attachmentLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getFeeTypeOptions()
|
||||
this.getCompanyOptions()
|
||||
this.getRoomTreeData()
|
||||
},
|
||||
methods: {
|
||||
// 获取费用类型选项
|
||||
getFeeTypeOptions() {
|
||||
getFeeTypeTree({ level: 2 }).then(response => {
|
||||
if (response.code === '0000000000000000') {
|
||||
// 数据预处理
|
||||
this.feeTypeOptions = this.processTreeData(response.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 处理费用类型树形数据
|
||||
processTreeData(data) {
|
||||
if (!data || !Array.isArray(data) || data.length === 0) {
|
||||
console.warn('无费用类型数据')
|
||||
return []
|
||||
}
|
||||
|
||||
return data.map(category => {
|
||||
// 确保id字段存在
|
||||
const categoryId = category.id || Math.random().toString(36).substr(2, 9)
|
||||
|
||||
// 处理子节点
|
||||
let children = []
|
||||
if (category.financeFeeTypes && Array.isArray(category.financeFeeTypes)) {
|
||||
children = category.financeFeeTypes.map(feeType => {
|
||||
return {
|
||||
id: feeType.id || Math.random().toString(36).substr(2, 9),
|
||||
label: feeType.feeTypeName || '未命名费用',
|
||||
categoryId: categoryId,
|
||||
// 存储原始数据以便后续使用
|
||||
feeTypeName: feeType.feeTypeName || '未命名费用',
|
||||
// 确保没有更多的子节点
|
||||
children: null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
id: categoryId,
|
||||
label: category.categoryName || '未命名分类',
|
||||
categoryName: category.categoryName || '未命名分类',
|
||||
children: children.length > 0 ? children : null
|
||||
}
|
||||
}).filter(item => item.children && item.children.length > 0)
|
||||
},
|
||||
// 获取公司选项
|
||||
getCompanyOptions() {
|
||||
// 模拟数据,实际应调用API
|
||||
this.companyOptions = [
|
||||
{ id: 'C001', name: '智慧园区物业管理有限公司' }
|
||||
]
|
||||
},
|
||||
// 获取收支账户选项
|
||||
getAccountOptions(companyId) {
|
||||
// 模拟数据,实际应调用API
|
||||
this.accountOptions = [
|
||||
{ id: 1, name: '运营账户-工商银行' },
|
||||
{ id: 2, name: '日常账户-建设银行' }
|
||||
]
|
||||
},
|
||||
// 搜索合同
|
||||
searchContracts(query) {
|
||||
if (query) {
|
||||
// 实际应调用API
|
||||
this.contractOptions = [
|
||||
{ contractId: 'CT001', contractNumber: 'HT2023001', customerName: '张三' },
|
||||
{ contractId: 'CT002', contractNumber: 'HT2023002', customerName: '李四' }
|
||||
]
|
||||
} else {
|
||||
this.contractOptions = []
|
||||
}
|
||||
},
|
||||
// 搜索付款方
|
||||
searchPayees(query) {
|
||||
if (query) {
|
||||
// 实际应调用API
|
||||
this.payeeOptions = [
|
||||
{ id: '10001', name: '张三', contact: '13812345678' },
|
||||
{ id: '10002', name: '李四', contact: '13987654321' }
|
||||
]
|
||||
} else {
|
||||
this.payeeOptions = []
|
||||
}
|
||||
},
|
||||
// 处理付款方变更
|
||||
handlePayeeChange(payeeId) {
|
||||
const payee = this.payeeOptions.find(item => item.id === payeeId)
|
||||
if (payee) {
|
||||
this.billForm.payeeName = payee.name
|
||||
this.billForm.payeeContact = payee.contact
|
||||
// 触发表单验证更新
|
||||
this.$nextTick(() => {
|
||||
this.$refs.billInfoForm.validateField('payeeId')
|
||||
})
|
||||
}
|
||||
},
|
||||
// 处理费用类型变更
|
||||
handleFeeTypeChange(value) {
|
||||
if (value) {
|
||||
// 查找匹配的费用类型
|
||||
let selectedFeeType = null;
|
||||
let selectedCategory = null;
|
||||
|
||||
// 在所有分类中查找
|
||||
for (const category of this.feeTypeOptions) {
|
||||
// 先检查是否选中的是分类
|
||||
if (category.id === value) {
|
||||
selectedCategory = category;
|
||||
selectedFeeType = {
|
||||
id: category.id,
|
||||
name: category.label,
|
||||
type: '分类'
|
||||
};
|
||||
break;
|
||||
}
|
||||
// 检查子节点
|
||||
if (category.children && category.children.length > 0) {
|
||||
const found = category.children.find(item => item.id === value);
|
||||
if (found) {
|
||||
selectedCategory = category;
|
||||
selectedFeeType = {
|
||||
id: found.id,
|
||||
name: found.label,
|
||||
categoryName: category.label,
|
||||
type: '费用类型'
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedFeeType) {
|
||||
// 设置费用分类ID和费用类型ID
|
||||
if (selectedFeeType.type === '分类') {
|
||||
this.billForm.feeCategoryId = selectedFeeType.id;
|
||||
this.billForm.feeTypeId = '';
|
||||
this.billForm.feeTypeName = selectedFeeType.name;
|
||||
} else {
|
||||
this.billForm.feeCategoryId = selectedCategory.id;
|
||||
this.billForm.feeTypeId = selectedFeeType.id;
|
||||
this.billForm.feeTypeName = selectedFeeType.name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 清空选择
|
||||
this.billForm.feeCategoryId = '';
|
||||
this.billForm.feeTypeId = '';
|
||||
this.billForm.feeTypeName = '';
|
||||
}
|
||||
},
|
||||
// 处理公司变更
|
||||
handleCompanyChange(companyId) {
|
||||
const company = this.companyOptions.find(item => item.id === companyId)
|
||||
if (company) {
|
||||
this.billForm.companyName = company.name
|
||||
// 触发表单验证更新
|
||||
this.$nextTick(() => {
|
||||
this.$refs.billInfoForm.validateField('companyId')
|
||||
})
|
||||
}
|
||||
// 重新获取收支账户
|
||||
this.billForm.accountId = ''
|
||||
this.getAccountOptions(companyId)
|
||||
},
|
||||
// 处理计费周期变更
|
||||
handleBillingPeriodChange(val) {
|
||||
if (val && val.length === 2) {
|
||||
this.billForm.billingStartDate = val[0]
|
||||
this.billForm.billingEndDate = val[1]
|
||||
} else {
|
||||
this.billForm.billingStartDate = ''
|
||||
this.billForm.billingEndDate = ''
|
||||
}
|
||||
},
|
||||
|
||||
// 获取房源树形数据
|
||||
getRoomTreeData() {
|
||||
// 模拟房源数据,与收费标准页面相同格式
|
||||
const roomData = [
|
||||
{
|
||||
"id": "3",
|
||||
"projectName": "智慧产业园",
|
||||
"projectType": "产业园区",
|
||||
"buildings": []
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"projectName": "测试-1",
|
||||
"projectType": "产业园区",
|
||||
"buildings": [
|
||||
{
|
||||
"id": 7,
|
||||
"buildingName": "4121",
|
||||
"buildingCode": "1234",
|
||||
"floors": [
|
||||
{
|
||||
"id": 7,
|
||||
"floorName": "123",
|
||||
"floorNumber": 11,
|
||||
"rooms": [
|
||||
{
|
||||
"id": 31,
|
||||
"roomNumber": "504",
|
||||
"roomType": null,
|
||||
"roomStatus": "1",
|
||||
"buildingArea": 122.00,
|
||||
"rentalArea": 20.99
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"roomNumber": "504",
|
||||
"roomType": null,
|
||||
"roomStatus": "1",
|
||||
"buildingArea": 122.00,
|
||||
"rentalArea": 20.99
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"roomNumber": "505",
|
||||
"roomType": null,
|
||||
"roomStatus": "1",
|
||||
"buildingArea": 122.00,
|
||||
"rentalArea": 20.99
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 转换为树形结构
|
||||
this.roomTreeData = roomData.map(project => {
|
||||
return {
|
||||
id: project.id,
|
||||
label: project.projectName,
|
||||
children: project.buildings.map(building => {
|
||||
return {
|
||||
id: building.id,
|
||||
label: building.buildingName,
|
||||
children: building.floors.map(floor => {
|
||||
return {
|
||||
id: floor.id,
|
||||
label: floor.floorName,
|
||||
children: floor.rooms.map(room => {
|
||||
return {
|
||||
id: room.id,
|
||||
label: `${room.roomNumber}(${room.rentalArea}㎡)`,
|
||||
isRoom: true,
|
||||
// 保存原始数据供选择
|
||||
roomData: room
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 处理房源树选择
|
||||
handleRoomTreeCheck(node, data) {
|
||||
// 只选择叶子节点(房间)
|
||||
const checkedNodes = this.$refs.roomTree.getCheckedNodes(true)
|
||||
// 过滤出所有房间节点
|
||||
const roomNodes = checkedNodes.filter(node => node.isRoom)
|
||||
|
||||
// 更新选中的房间ID列表
|
||||
this.selectedRoomIds = roomNodes.map(node => node.id)
|
||||
|
||||
// 重置房源信息列表
|
||||
this.billForm.roomInfoList = []
|
||||
|
||||
// 创建一个Map用于收集项目信息
|
||||
const projectMap = new Map()
|
||||
|
||||
// 处理每个房间节点
|
||||
roomNodes.forEach(roomNode => {
|
||||
// 查找节点路径
|
||||
let currentNode = this.$refs.roomTree.getNode(roomNode.id)
|
||||
let roomInfo = roomNode.roomData
|
||||
let floorNode, buildingNode, projectNode
|
||||
|
||||
// 向上遍历找到楼层、楼宇和项目节点
|
||||
while (currentNode && currentNode.parent) {
|
||||
const parent = currentNode.parent
|
||||
if (parent.level === 3) { // 楼层级别
|
||||
floorNode = parent.data
|
||||
} else if (parent.level === 2) { // 楼宇级别
|
||||
buildingNode = parent.data
|
||||
} else if (parent.level === 1) { // 项目级别
|
||||
projectNode = parent.data
|
||||
}
|
||||
currentNode = parent
|
||||
}
|
||||
|
||||
// 只有完整路径的房间才处理
|
||||
if (roomInfo && floorNode && buildingNode && projectNode) {
|
||||
// 更新房源信息列表
|
||||
this.billForm.roomInfoList.push({
|
||||
roomId: roomNode.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
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 上传文件前检查
|
||||
beforeUpload(file) {
|
||||
const isValidType = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'application/pdf'
|
||||
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) {
|
||||
this.attachmentLoading = true
|
||||
|
||||
// 此处模拟上传,实际应调用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)
|
||||
},
|
||||
// 预览文件
|
||||
previewFile(file) {
|
||||
window.open(file.url)
|
||||
},
|
||||
// 下载文件
|
||||
downloadFile(file) {
|
||||
// 实际应调用API下载
|
||||
const link = document.createElement('a')
|
||||
link.href = file.url
|
||||
link.download = file.name
|
||||
link.click()
|
||||
},
|
||||
// 删除文件
|
||||
deleteFile(index) {
|
||||
this.$confirm('确认删除该附件?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.fileList.splice(index, 1)
|
||||
this.$message.success('删除成功')
|
||||
}).catch(() => {})
|
||||
},
|
||||
// 获取表单数据
|
||||
getFormData() {
|
||||
// 确保billForm中包含来自formExtension的数据
|
||||
if (this.formExtension.feeTypeSelected) {
|
||||
// 费用类型已经在handleFeeTypeChange中设置
|
||||
}
|
||||
|
||||
if (this.formExtension.billingPeriod && this.formExtension.billingPeriod.length === 2) {
|
||||
this.billForm.billingStartDate = this.formExtension.billingPeriod[0]
|
||||
this.billForm.billingEndDate = this.formExtension.billingPeriod[1]
|
||||
}
|
||||
|
||||
return this.billForm
|
||||
},
|
||||
// 表单验证
|
||||
validateForm() {
|
||||
return new Promise(resolve => {
|
||||
// 验证收费模式表单
|
||||
this.$refs.chargeModeForm.validate(valid1 => {
|
||||
if (!valid1) {
|
||||
this.activeTab = 'chargeMode'
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证费用类型和计费周期表单
|
||||
this.$refs.formExtension.validate(valid2 => {
|
||||
if (!valid2) {
|
||||
this.activeTab = 'billInfo'
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证账单信息表单
|
||||
this.$refs.billInfoForm.validate(valid3 => {
|
||||
if (!valid3) {
|
||||
this.activeTab = 'billInfo'
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证是否选择了房源
|
||||
if (this.billForm.roomInfoList.length === 0) {
|
||||
this.$message.warning('请至少选择一个房源')
|
||||
this.activeTab = 'roomInfo'
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
// 重置表单
|
||||
resetForm() {
|
||||
// 重置表单验证状态
|
||||
if (this.$refs.chargeModeForm) {
|
||||
this.$refs.chargeModeForm.resetFields()
|
||||
}
|
||||
if (this.$refs.billInfoForm) {
|
||||
this.$refs.billInfoForm.resetFields()
|
||||
}
|
||||
if (this.$refs.formExtension) {
|
||||
this.$refs.formExtension.resetFields()
|
||||
}
|
||||
|
||||
// 切换到第一个标签
|
||||
this.activeTab = 'chargeMode'
|
||||
|
||||
// 重置表单数据
|
||||
this.billForm = {
|
||||
contractId: '',
|
||||
contractNumber: '',
|
||||
payeeId: '',
|
||||
payeeName: '',
|
||||
payeeContact: '',
|
||||
feeCategoryId: '',
|
||||
feeTypeId: '',
|
||||
feeTypeName: '',
|
||||
currCode: '156', // 默认人民币
|
||||
billingStartDate: '',
|
||||
billingEndDate: '',
|
||||
receivableAmount: 0,
|
||||
price: 0,
|
||||
priceU: '元/月',
|
||||
taxInclusiveRule: '含税',
|
||||
taxRate: 0,
|
||||
receivableDate: '',
|
||||
specialBillType: '正常',
|
||||
lateFeeStartDays: 0,
|
||||
lateFeeRate: 0,
|
||||
lateFeeLimit: 0,
|
||||
companyId: '',
|
||||
companyName: '',
|
||||
accountId: '',
|
||||
billRemark: '',
|
||||
roomInfoList: []
|
||||
}
|
||||
|
||||
// 重置扩展表单数据
|
||||
this.formExtension = {
|
||||
feeTypeSelected: null,
|
||||
billingPeriod: []
|
||||
}
|
||||
|
||||
// 重置房源选择
|
||||
this.selectedRoomIds = []
|
||||
if (this.$refs.roomTree) {
|
||||
this.$refs.roomTree.setCheckedKeys([])
|
||||
}
|
||||
|
||||
// 重置文件上传列表
|
||||
this.fileList = []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-bill-container {
|
||||
padding: 20px;
|
||||
|
||||
.upload-area {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
color: #F56C6C;
|
||||
}
|
||||
}
|
||||
</style>
|
256
pc/src/views/finance/billList/components/BillDetail.vue
Normal file
256
pc/src/views/finance/billList/components/BillDetail.vue
Normal file
@ -0,0 +1,256 @@
|
||||
<template>
|
||||
<div class="bill-detail-container">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="账单基本信息" name="basicInfo">
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="账单编号">{{ billDetail.billNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账单来源">{{ getBillSourceName(billDetail.billSource) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账单状态">
|
||||
<el-tag :type="billDetail.billStatus === '1' ? 'success' : 'info'">
|
||||
{{ billDetail.billStatus === '1' ? '开启' : '关闭' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="对方名称">{{ billDetail.payeeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="支付联系方式">{{ billDetail.payeeContact }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联合同">{{ billDetail.contractNumber || '无' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="费用类型">{{ billDetail.feeTypeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计费周期">
|
||||
{{ billDetail.billingStartDate }} 至 {{ billDetail.billingEndDate }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="应收日期">{{ billDetail.receivableDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结清状态">
|
||||
<el-tag :type="getClearStatusType(billDetail.clearStatus)">
|
||||
{{ getClearStatusName(billDetail.clearStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="逾期状态">
|
||||
<el-tag :type="billDetail.overdueStatus === '1' ? 'danger' : 'success'">
|
||||
{{ billDetail.overdueStatus === '1' ? '逾期' : '正常' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="滞纳金状态">
|
||||
<el-tag :type="getLateFeeStatusType(billDetail.lateFeeStatus)">
|
||||
{{ getLateFeeStatusName(billDetail.lateFeeStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="账单金额">{{ formatAmount(billDetail.billAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="应收金额">{{ formatAmount(billDetail.receivableAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实收金额">{{ formatAmount(billDetail.receivedAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="需收金额">{{ formatAmount(billDetail.needAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="调整金额">{{ formatAmount(billDetail.adjustAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="应收滞纳金">{{ formatAmount(billDetail.receivableLateFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="税率">{{ billDetail.taxRate }}%</el-descriptions-item>
|
||||
<el-descriptions-item label="税额">{{ formatAmount(billDetail.taxAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="含税规则">{{ billDetail.taxInclusiveRule }}</el-descriptions-item>
|
||||
<el-descriptions-item label="特殊账单类型">{{ billDetail.specialBillType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开据状态">
|
||||
<el-tag :type="billDetail.receiptStatus === '1' ? 'success' : 'info'">
|
||||
{{ billDetail.receiptStatus === '1' ? '已开据' : '未开据' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所属公司">{{ billDetail.companyName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="租赁数(计租面积)">{{ billDetail.rentArea }} ㎡</el-descriptions-item>
|
||||
<el-descriptions-item label="项目名称">{{ billDetail.projectName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="账单备注" :span="3">{{ billDetail.billRemark || '无' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="房源信息" name="roomInfo">
|
||||
<el-table :data="roomList" border style="width: 100%">
|
||||
<el-table-column prop="roomNumber" label="房号" min-width="100"></el-table-column>
|
||||
<el-table-column prop="floorName" label="楼层" min-width="100"></el-table-column>
|
||||
<el-table-column prop="buildingName" label="楼宇" min-width="120"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目" min-width="150"></el-table-column>
|
||||
<el-table-column prop="rentArea" label="计租面积(㎡)" min-width="120" align="right"></el-table-column>
|
||||
</el-table>
|
||||
<div v-if="roomList.length === 0" class="empty-data">
|
||||
<el-empty description="暂无房源信息"></el-empty>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="附件信息" name="attachment">
|
||||
<el-table :data="attachmentList" border style="width: 100%">
|
||||
<el-table-column prop="fileName" label="文件名" min-width="200" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="operatorName" label="操作人" width="150"></el-table-column>
|
||||
<el-table-column prop="operateTime" label="操作时间" width="180"></el-table-column>
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="attachmentList.length === 0" class="empty-data">
|
||||
<el-empty description="暂无附件信息"></el-empty>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="操作记录" name="operationLog">
|
||||
<el-table :data="operationList" border style="width: 100%">
|
||||
<el-table-column prop="operationType" label="操作类型" width="150"></el-table-column>
|
||||
<el-table-column prop="operatorName" label="操作人" width="150"></el-table-column>
|
||||
<el-table-column prop="operateTime" label="操作时间" width="180"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="300" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
<div v-if="operationList.length === 0" class="empty-data">
|
||||
<el-empty description="暂无操作记录"></el-empty>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getBillDetail, getBillAttachmentList } from '@/api/finance'
|
||||
|
||||
export default {
|
||||
name: 'BillDetail',
|
||||
props: {
|
||||
billId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'basicInfo',
|
||||
loading: false,
|
||||
billDetail: {},
|
||||
roomList: [],
|
||||
attachmentList: [],
|
||||
operationList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getBillData()
|
||||
},
|
||||
methods: {
|
||||
// 获取账单数据
|
||||
getBillData() {
|
||||
this.loading = true
|
||||
|
||||
// 获取账单详情
|
||||
getBillDetail(this.billId).then(response => {
|
||||
this.billDetail = response.data || {}
|
||||
|
||||
// 模拟房源信息数据,实际应从接口获取或者账单详情中提取
|
||||
this.roomList = [
|
||||
{
|
||||
roomId: 'R001',
|
||||
roomNumber: this.billDetail.roomNumber || '',
|
||||
floorName: '1层',
|
||||
buildingName: '智慧园区A栋',
|
||||
projectName: this.billDetail.projectName || '',
|
||||
rentArea: this.billDetail.rentArea || 0
|
||||
}
|
||||
]
|
||||
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
|
||||
// 获取附件列表
|
||||
getBillAttachmentList(this.billId).then(response => {
|
||||
this.attachmentList = response.data || []
|
||||
})
|
||||
|
||||
// 模拟操作记录数据,实际应从接口获取
|
||||
this.operationList = [
|
||||
{
|
||||
operationType: '创建账单',
|
||||
operatorName: '系统管理员',
|
||||
operateTime: '2023-06-20 10:30:00',
|
||||
remark: '创建新账单'
|
||||
}
|
||||
]
|
||||
},
|
||||
// 格式化金额
|
||||
formatAmount(amount) {
|
||||
if (amount === undefined || amount === null) {
|
||||
return '0.00'
|
||||
}
|
||||
return parseFloat(amount).toFixed(2) + ' 元'
|
||||
},
|
||||
// 获取账单来源名称
|
||||
getBillSourceName(source) {
|
||||
const sourceMap = {
|
||||
'1': '合同账单',
|
||||
'2': '收费标准账单',
|
||||
'3': '自建全部账单',
|
||||
'4': '自建关联合同账单',
|
||||
'5': '自建未关联合同账单'
|
||||
}
|
||||
return sourceMap[source] || '未知来源'
|
||||
},
|
||||
// 获取结清状态类型
|
||||
getClearStatusType(status) {
|
||||
const statusMap = {
|
||||
'0': 'info',
|
||||
'1': 'success',
|
||||
'2': 'warning',
|
||||
'3': 'danger',
|
||||
'5': 'primary',
|
||||
'6': 'info',
|
||||
'7': 'success'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
// 获取结清状态名称
|
||||
getClearStatusName(status) {
|
||||
const statusMap = {
|
||||
'0': '未付款',
|
||||
'1': '已结清',
|
||||
'2': '部分结清',
|
||||
'3': '待退款',
|
||||
'5': '待收款',
|
||||
'6': '对账确认中',
|
||||
'7': '已付款'
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
},
|
||||
// 获取滞纳金状态类型
|
||||
getLateFeeStatusType(status) {
|
||||
const statusMap = {
|
||||
'0': 'info',
|
||||
'1': 'warning',
|
||||
'2': 'success'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
// 获取滞纳金状态名称
|
||||
getLateFeeStatusName(status) {
|
||||
const statusMap = {
|
||||
'0': '无滞纳金',
|
||||
'1': '有滞纳金',
|
||||
'2': '滞纳金免除'
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
},
|
||||
// 预览文件
|
||||
previewFile(file) {
|
||||
window.open(file.storePath)
|
||||
},
|
||||
// 下载文件
|
||||
downloadFile(file) {
|
||||
// 实际应调用API下载
|
||||
const link = document.createElement('a')
|
||||
link.href = file.storePath
|
||||
link.download = file.fileName
|
||||
link.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bill-detail-container {
|
||||
padding: 10px;
|
||||
|
||||
.empty-data {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
409
pc/src/views/finance/billList/index.vue
Normal file
409
pc/src/views/finance/billList/index.vue
Normal file
@ -0,0 +1,409 @@
|
||||
<template>
|
||||
<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>
|
||||
<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>
|
||||
<el-form-item label="账单编号" prop="billNumber">
|
||||
<el-input v-model="queryParams.billNumber" 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">
|
||||
<el-option label="开启" value="1" />
|
||||
<el-option label="关闭" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="应收时间" prop="receivableDate">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
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="clearStatus">
|
||||
<el-select v-model="queryParams.clearStatus" placeholder="请选择结清状态" clearable multiple style="width: 220px">
|
||||
<el-option label="未付款" value="0" />
|
||||
<el-option label="已结清" value="1" />
|
||||
<el-option label="部分结清" value="2" />
|
||||
<el-option label="待退款" value="3" />
|
||||
<el-option label="待收款" value="5" />
|
||||
<el-option label="对账确认中" value="6" />
|
||||
<el-option label="已付款" value="7" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="账单来源" prop="billSource">
|
||||
<el-select v-model="queryParams.billSource" placeholder="请选择账单来源" clearable style="width: 220px">
|
||||
<el-option label="合同账单" value="1" />
|
||||
<el-option label="收费标准账单" value="2" />
|
||||
<el-option label="自建全部账单" value="3" />
|
||||
<el-option label="自建关联合同账单" value="4" />
|
||||
<el-option label="自建未关联合同账单" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="费用类型" prop="feeType">
|
||||
<el-cascader
|
||||
v-model="feeTypeSelected"
|
||||
:options="feeTypeOptions"
|
||||
:props="{
|
||||
label: 'label',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
emitPath: false,
|
||||
expandTrigger: 'hover'
|
||||
}"
|
||||
clearable
|
||||
placeholder="请选择费用类型"
|
||||
style="width: 220px">
|
||||
</el-cascader>
|
||||
</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="success" icon="el-icon-plus" style="float: right;" @click="handleAdd">添加收款账单</el-button>
|
||||
<el-button type="warning" icon="el-icon-download" style="float: right; margin-right: 10px;" @click="handleExport">导出</el-button>
|
||||
</el-form>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<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="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="结清状态" min-width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getClearStatusType(scope.row.clearStatus)">
|
||||
{{ getClearStatusName(scope.row.clearStatus) }}
|
||||
</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">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.billAmount ? scope.row.billAmount.toFixed(2) : '0.00' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应收金额" prop="receivableAmount" min-width="100" align="right">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.receivableAmount ? scope.row.receivableAmount.toFixed(2) : '0.00' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实收金额" prop="receivedAmount" min-width="100" align="right">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.receivedAmount ? scope.row.receivedAmount.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="操作" 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>
|
||||
</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="dialogVisible" width="80%" append-to-body @closed="handleDialogClosed">
|
||||
<add-bill ref="addBillForm" />
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitAddBill">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 账单详情对话框 -->
|
||||
<el-dialog title="账单详情" :visible.sync="detailVisible" width="80%" append-to-body>
|
||||
<bill-detail v-if="detailVisible" :bill-id="currentBill.id" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFeeTypeTree, getBillList, exportBillList, addBill, getBillDetail } from '@/api/finance'
|
||||
import AddBill from './components/AddBill'
|
||||
import BillDetail from './components/BillDetail'
|
||||
|
||||
export default {
|
||||
name: 'BillList',
|
||||
components: {
|
||||
AddBill,
|
||||
BillDetail
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 加载标志
|
||||
loading: false,
|
||||
// 账单列表数据
|
||||
billList: [],
|
||||
// 总记录数
|
||||
total: 0,
|
||||
// 时间范围
|
||||
dateRange: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
contractNumber: '',
|
||||
roomNumber: '',
|
||||
payeeName: '',
|
||||
billNumber: '',
|
||||
billStatus: [],
|
||||
receivableDateStart: '',
|
||||
receivableDateEnd: '',
|
||||
clearStatus: [],
|
||||
billSource: '',
|
||||
feeTypeId: undefined,
|
||||
feeCategoryId: undefined
|
||||
},
|
||||
// 费用类型选择
|
||||
feeTypeSelected: undefined,
|
||||
// 费用类型选项
|
||||
feeTypeOptions: [],
|
||||
// 对话框显示标志
|
||||
dialogVisible: false,
|
||||
detailVisible: false,
|
||||
// 当前查看的账单详情
|
||||
currentBill: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getFeeTypeOptions()
|
||||
},
|
||||
methods: {
|
||||
// 获取账单列表
|
||||
getList() {
|
||||
this.loading = true
|
||||
// 处理查询参数
|
||||
const queryParams = { ...this.queryParams }
|
||||
|
||||
// 处理日期范围
|
||||
if (this.dateRange && this.dateRange.length > 0) {
|
||||
queryParams.receivableDateStart = this.dateRange[0]
|
||||
queryParams.receivableDateEnd = this.dateRange[1]
|
||||
}
|
||||
|
||||
// 处理费用类型
|
||||
if (this.feeTypeSelected) {
|
||||
queryParams.feeTypeId = this.feeTypeSelected
|
||||
}
|
||||
|
||||
getBillList(queryParams).then(response => {
|
||||
this.billList = response.data.data || []
|
||||
this.total = response.data.total
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取费用类型选项
|
||||
getFeeTypeOptions() {
|
||||
getFeeTypeTree({ level: 2 }).then(response => {
|
||||
if (response.code === '0000000000000000') {
|
||||
// 数据预处理
|
||||
this.feeTypeOptions = this.processTreeData(response.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 处理费用类型树形数据
|
||||
processTreeData(data) {
|
||||
if (!data || !Array.isArray(data) || data.length === 0) {
|
||||
console.warn('无费用类型数据')
|
||||
return []
|
||||
}
|
||||
|
||||
return data.map(category => {
|
||||
// 确保id字段存在
|
||||
const categoryId = category.id || Math.random().toString(36).substr(2, 9)
|
||||
|
||||
// 处理子节点
|
||||
let children = []
|
||||
if (category.financeFeeTypes && Array.isArray(category.financeFeeTypes)) {
|
||||
children = category.financeFeeTypes.map(feeType => {
|
||||
return {
|
||||
id: feeType.id || Math.random().toString(36).substr(2, 9),
|
||||
label: feeType.feeTypeName || '未命名费用',
|
||||
categoryId: categoryId,
|
||||
// 存储原始数据以便后续使用
|
||||
feeTypeName: feeType.feeTypeName || '未命名费用',
|
||||
// 确保没有更多的子节点
|
||||
children: null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
id: categoryId,
|
||||
label: category.categoryName || '未命名分类',
|
||||
categoryName: category.categoryName || '未命名分类',
|
||||
children: children.length > 0 ? children : null
|
||||
}
|
||||
}).filter(item => item.children && item.children.length > 0)
|
||||
},
|
||||
// 搜索按钮操作
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
// 重置按钮操作
|
||||
resetQuery() {
|
||||
this.dateRange = []
|
||||
this.feeTypeSelected = undefined
|
||||
this.$refs.queryForm.resetFields()
|
||||
this.handleQuery()
|
||||
},
|
||||
// 分页大小改变
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
// 分页页码改变
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
},
|
||||
// 添加账单操作
|
||||
handleAdd() {
|
||||
this.dialogVisible = true
|
||||
},
|
||||
// 关闭对话框时重置表单
|
||||
handleDialogClosed() {
|
||||
this.$refs.addBillForm.resetForm()
|
||||
},
|
||||
// 查看账单详情
|
||||
handleViewDetail(row) {
|
||||
this.currentBill = row
|
||||
this.detailVisible = true
|
||||
},
|
||||
// 导出账单操作
|
||||
handleExport() {
|
||||
// 处理导出参数
|
||||
const exportParams = { ...this.queryParams }
|
||||
delete exportParams.pageNum
|
||||
delete exportParams.pageSize
|
||||
|
||||
// 处理日期范围
|
||||
if (this.dateRange && this.dateRange.length > 0) {
|
||||
exportParams.receivableDateStart = this.dateRange[0]
|
||||
exportParams.receivableDateEnd = this.dateRange[1]
|
||||
}
|
||||
|
||||
// 处理费用类型
|
||||
if (this.feeTypeSelected) {
|
||||
exportParams.feeTypeId = this.feeTypeSelected
|
||||
}
|
||||
|
||||
this.$confirm('是否确认导出所有数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
exportBillList(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)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// 提交添加账单
|
||||
submitAddBill() {
|
||||
// 先进行表单验证
|
||||
this.$refs.addBillForm.validateForm().then(valid => {
|
||||
if (valid) {
|
||||
const billData = this.$refs.addBillForm.getFormData()
|
||||
addBill(billData).then(response => {
|
||||
this.$message.success('添加账单成功')
|
||||
this.dialogVisible = false
|
||||
this.getList()
|
||||
}).catch(() => {
|
||||
// 错误处理
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取清算状态显示类型
|
||||
getClearStatusType(status) {
|
||||
const statusMap = {
|
||||
'0': 'info',
|
||||
'1': 'success',
|
||||
'2': 'warning',
|
||||
'3': 'danger',
|
||||
'5': 'primary',
|
||||
'6': 'info',
|
||||
'7': 'success'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
// 获取清算状态名称
|
||||
getClearStatusName(status) {
|
||||
const statusMap = {
|
||||
'0': '未付款',
|
||||
'1': '已结清',
|
||||
'2': '部分结清',
|
||||
'3': '待退款',
|
||||
'5': '待收款',
|
||||
'6': '对账确认中',
|
||||
'7': '已付款'
|
||||
}
|
||||
return statusMap[status] || '未知状态'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
@ -50,7 +50,7 @@
|
||||
<div class="fee-type-search">
|
||||
<el-form :inline="true" :model="queryParams" class="search-form">
|
||||
<el-form-item label="费用名称">
|
||||
<el-input v-model="queryParams.feeTypeName" placeholder="请输入费用名称" clearable
|
||||
<el-input v-model="queryParams.feTpName" placeholder="请输入费用名称" clearable
|
||||
@keyup.enter.native="handleQuery"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@ -62,7 +62,7 @@
|
||||
<!-- 费用类型表格 -->
|
||||
<el-table v-loading="loading" :data="typeList" border>
|
||||
<el-table-column type="index" width="50" align="center" label="序号"></el-table-column>
|
||||
<el-table-column prop="feeTypeName" label="费用名称" min-width="140"></el-table-column>
|
||||
<el-table-column prop="feTpName" label="费用名称" min-width="140"></el-table-column>
|
||||
<el-table-column label="费用分类" min-width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ getCategoryNameById(scope.row.categoryId) }}
|
||||
@ -129,8 +129,8 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="费用类型名称" prop="feeTypeName">
|
||||
<el-input v-model="typeForm.feeTypeName" placeholder="请输入费用类型名称" :disabled="typeEdit"
|
||||
<el-form-item label="费用类型名称" prop="feTpName">
|
||||
<el-input v-model="typeForm.feTpName" placeholder="请输入费用类型名称" :disabled="typeEdit"
|
||||
style="width: 100%"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
@ -186,7 +186,7 @@ export default {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
feeTypeName: undefined,
|
||||
feTpName: undefined,
|
||||
categoryId: null
|
||||
},
|
||||
|
||||
@ -216,13 +216,13 @@ export default {
|
||||
typeEdit: false,
|
||||
typeForm: {
|
||||
id: undefined,
|
||||
feeTypeName: '',
|
||||
feTpName: '',
|
||||
categoryId: null,
|
||||
status: '1',
|
||||
defauVerFlag: '0'
|
||||
},
|
||||
typeRules: {
|
||||
feeTypeName: [
|
||||
feTpName: [
|
||||
{ required: true, message: '费用类型名称不能为空', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '费用类型名称长度必须在2-50个字符之间', trigger: 'blur' }
|
||||
],
|
||||
@ -296,7 +296,7 @@ export default {
|
||||
},
|
||||
// 重置查询操作
|
||||
resetQuery() {
|
||||
this.queryParams.feeTypeName = undefined
|
||||
this.queryParams.feTpName = undefined
|
||||
this.handleQuery()
|
||||
},
|
||||
// 每页条数改变
|
||||
@ -411,7 +411,7 @@ export default {
|
||||
// 重置表单数据
|
||||
this.typeForm = {
|
||||
id: undefined,
|
||||
feeTypeName: '',
|
||||
feTpName: '',
|
||||
categoryId: this.selectedCategoryId ? parseInt(this.selectedCategoryId) : null,
|
||||
status: '1',
|
||||
defauVerFlag: '0'
|
||||
@ -455,7 +455,7 @@ export default {
|
||||
// 创建提交的数据对象,只包含需要的字段
|
||||
const submitData = {
|
||||
id: this.typeForm.id,
|
||||
feeTypeName: this.typeForm.feeTypeName,
|
||||
feTpName: this.typeForm.feTpName,
|
||||
categoryId: this.typeForm.categoryId,
|
||||
status: this.typeForm.status
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<div class="filter-container">
|
||||
<el-input v-model="queryParams.payeeNameUnitName" placeholder="请输入收款方单位名称" clearable style="width: 250px;" class="filter-item" />
|
||||
<el-input v-model="queryParams.payeeUnitName" placeholder="请输入收款方单位名称" clearable style="width: 250px;" class="filter-item" />
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
<el-button type="primary" icon="el-icon-plus" style="float: right;" @click="handleAdd">新增收款方信息</el-button>
|
||||
@ -17,10 +17,10 @@
|
||||
{{ formatCompanyName(scope.row.companyId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="payeeNameUnitName" label="收款方单位名称" />
|
||||
<el-table-column prop="payeeUnitName" label="收款方单位名称" />
|
||||
<el-table-column prop="payeeName" label="收款人" />
|
||||
<el-table-column prop="addr" label="地址" />
|
||||
<el-table-column prop="contTel" label="电话" />
|
||||
<el-table-column prop="payeeAddr" label="地址" />
|
||||
<el-table-column prop="payeePhone" label="电话" />
|
||||
<el-table-column prop="buildingIds" label="应用楼宇">
|
||||
<template slot-scope="scope">
|
||||
{{ formatBuildingNames(scope.row.buildingIds) }}
|
||||
@ -53,17 +53,17 @@
|
||||
:value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款方单位名称" prop="payeeNameUnitName">
|
||||
<el-input v-model="form.payeeNameUnitName" placeholder="请输入收款方单位名称" />
|
||||
<el-form-item label="收款方单位名称" prop="payeeUnitName">
|
||||
<el-input v-model="form.payeeUnitName" placeholder="请输入收款方单位名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="收款人" prop="payeeName">
|
||||
<el-input v-model="form.payeeName" placeholder="请输入收款人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="addr">
|
||||
<el-input v-model="form.addr" placeholder="请输入地址" />
|
||||
<el-form-item label="地址" prop="payeeAddr">
|
||||
<el-input v-model="form.payeeAddr" placeholder="请输入地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="contTel">
|
||||
<el-input v-model="form.contTel" placeholder="请输入电话" />
|
||||
<el-form-item label="电话" prop="payeePhone">
|
||||
<el-input v-model="form.payeePhone" placeholder="请输入电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="应用楼宇" prop="buildingIds">
|
||||
<el-popover
|
||||
@ -127,7 +127,7 @@ export default {
|
||||
queryParams: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
payeeNameUnitName: ''
|
||||
payeeUnitName: ''
|
||||
},
|
||||
|
||||
// 加载状态
|
||||
@ -140,10 +140,10 @@ export default {
|
||||
// 收款方信息
|
||||
payeeInfo: {
|
||||
companyId: '',
|
||||
payeeNameUnitName: '',
|
||||
payeeUnitName: '',
|
||||
payeeName: '',
|
||||
addr: '',
|
||||
contTel: '',
|
||||
payeeAddr: '',
|
||||
payeePhone: '',
|
||||
buildingIds: []
|
||||
},
|
||||
|
||||
@ -174,10 +174,10 @@ export default {
|
||||
// 表单数据
|
||||
form: {
|
||||
companyId: '',
|
||||
payeeNameUnitName: '',
|
||||
payeeUnitName: '',
|
||||
payeeName: '',
|
||||
addr: '',
|
||||
contTel: '',
|
||||
payeeAddr: '',
|
||||
payeePhone: '',
|
||||
buildingIds: [],
|
||||
buildingNames: ''
|
||||
},
|
||||
@ -187,13 +187,13 @@ export default {
|
||||
companyId: [
|
||||
{ required: true, message: '请选择关联公司', trigger: 'change' }
|
||||
],
|
||||
payeeNameUnitName: [
|
||||
payeeUnitName: [
|
||||
{ required: true, message: '请输入收款方单位名称', trigger: 'blur' }
|
||||
],
|
||||
payeeName: [
|
||||
{ required: true, message: '请输入收款人', trigger: 'blur' }
|
||||
],
|
||||
contTel: [
|
||||
payeePhone: [
|
||||
{ pattern: /^(\d{3,4}-\d{7,8}(-\d{1,4})?|1[3-9]\d{9})$/, message: '电话格式不正确', trigger: 'blur' }
|
||||
],
|
||||
buildingIds: [
|
||||
@ -222,10 +222,10 @@ export default {
|
||||
this.payeeList = [{
|
||||
id: 1,
|
||||
companyId: 'COM001',
|
||||
payeeNameUnitName: '智慧园区管理有限公司',
|
||||
payeeUnitName: '智慧园区管理有限公司',
|
||||
payeeName: '张三',
|
||||
addr: '北京市海淀区中关村大街1号',
|
||||
contTel: '13800138000',
|
||||
payeeAddr: '北京市海淀区中关村大街1号',
|
||||
payeePhone: '13800138000',
|
||||
buildingIds: 'BLD001,BLD002'
|
||||
}]
|
||||
this.total = 1
|
||||
@ -239,10 +239,10 @@ export default {
|
||||
this.payeeList = [{
|
||||
id: 1,
|
||||
companyId: 'COM001',
|
||||
payeeNameUnitName: '智慧园区管理有限公司',
|
||||
payeeUnitName: '智慧园区管理有限公司',
|
||||
payeeName: '张三',
|
||||
addr: '北京市海淀区中关村大街1号',
|
||||
contTel: '13800138000',
|
||||
payeeAddr: '北京市海淀区中关村大街1号',
|
||||
payeePhone: '13800138000',
|
||||
buildingIds: 'BLD001,BLD002'
|
||||
}]
|
||||
this.total = 1
|
||||
@ -348,7 +348,7 @@ export default {
|
||||
this.queryParams = {
|
||||
current: 1,
|
||||
size: 10,
|
||||
payeeNameUnitName: ''
|
||||
payeeUnitName: ''
|
||||
}
|
||||
this.getPayeeList()
|
||||
},
|
||||
@ -371,10 +371,10 @@ export default {
|
||||
this.currentId = null
|
||||
this.form = {
|
||||
companyId: '',
|
||||
payeeNameUnitName: '',
|
||||
payeeUnitName: '',
|
||||
payeeName: '',
|
||||
addr: '',
|
||||
contTel: '',
|
||||
payeeAddr: '',
|
||||
payeePhone: '',
|
||||
buildingIds: [],
|
||||
buildingNames: ''
|
||||
}
|
||||
|
@ -10,14 +10,14 @@ module.exports = {
|
||||
warnings: false,
|
||||
errors: true
|
||||
},
|
||||
proxy: {
|
||||
'/': {
|
||||
target: 'http://192.168.137.214:8082/api',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': '/api'
|
||||
}
|
||||
}
|
||||
}
|
||||
// proxy: {
|
||||
// '/': {
|
||||
// target: 'http://192.168.137.214:8082',
|
||||
// changeOrigin: true,
|
||||
// pathRewrite: {
|
||||
// '^/api': '/api'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user