Commit f61bd6e8e4c3243544baca8a00a4f0a334c5ea87

Authored by wuxw
1 parent a6fd0349

费用下功能基本搞定

src/api/fee/addOweFeeCallableApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 保存催缴欠费
  4 +export function saveOweFeeCallable(data) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/oweFeeCallable.saveOweFeeCallable',
  8 + method: 'post',
  9 + data
  10 + }).then(response => {
  11 + const res = response.data
  12 + if (res.code === 0) {
  13 + resolve(res)
  14 + } else {
  15 + reject(new Error(res.msg))
  16 + }
  17 + }).catch(error => {
  18 + reject(error)
  19 + })
  20 + })
  21 +}
  22 +
  23 +// 导出催缴函Excel
  24 +export function exportCollectionLetterExcel(params) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/export.exportData',
  28 + method: 'get',
  29 + params
  30 + }).then(response => {
  31 + const res = response.data
  32 + if (res.code === 0) {
  33 + resolve(res)
  34 + } else {
  35 + reject(new Error(res.msg))
  36 + }
  37 + }).catch(error => {
  38 + reject(error)
  39 + })
  40 + })
  41 +}
  42 +
  43 +// 获取费用配置列表
  44 +export function listFeeConfigs(params) {
  45 + return new Promise((resolve, reject) => {
  46 + request({
  47 + url: '/feeConfig.listFeeConfigs',
  48 + method: 'get',
  49 + params
  50 + }).then(response => {
  51 + const res = response.data
  52 + if (res.code === 0) {
  53 + resolve(res)
  54 + } else {
  55 + reject(new Error(res.msg))
  56 + }
  57 + }).catch(error => {
  58 + reject(error)
  59 + })
  60 + })
  61 +}
  62 +
  63 +// 查询楼栋列表
  64 +export function queryFloors(params) {
  65 + return new Promise((resolve, reject) => {
  66 + request({
  67 + url: '/floor.queryFloors',
  68 + method: 'get',
  69 + params
  70 + }).then(response => {
  71 + const res = response.data
  72 + if (res.code === 0) {
  73 + resolve(res)
  74 + } else {
  75 + reject(new Error(res.msg))
  76 + }
  77 + }).catch(error => {
  78 + reject(error)
  79 + })
  80 + })
  81 +}
  82 +
  83 +// 查询欠费房屋报表
  84 +export function queryReportOweFee(params) {
  85 + return new Promise((resolve, reject) => {
  86 + request({
  87 + url: '/reportOweFee/queryReportOweFee',
  88 + method: 'get',
  89 + params
  90 + }).then(response => {
  91 + const res = response.data
  92 + if (res.code === 0) {
  93 + resolve(res)
  94 + } else {
  95 + reject(new Error(res.msg))
  96 + }
  97 + }).catch(error => {
  98 + reject(error)
  99 + })
  100 + })
  101 +}
0 \ No newline at end of file 102 \ No newline at end of file
src/api/fee/feeApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 查询费用列表
  4 +export function listFee(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/fee.listFee',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + resolve(res)
  13 + }).catch(error => {
  14 + reject(error)
  15 + })
  16 + })
  17 +}
src/api/fee/feeSummaryApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取缴费汇总列表
  4 +export function listFeeSummarys(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/callComponent/feeSummary/list',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + resolve(res)
  13 + }).catch(error => {
  14 + reject(error)
  15 + })
  16 + })
  17 +}
  18 +
  19 +// 导出Excel
  20 +export function exportFeeSummary(params) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/feeSummary/export',
  24 + method: 'get',
  25 + params,
  26 + responseType: 'blob'
  27 + }).then(response => {
  28 + resolve(response)
  29 + }).catch(error => {
  30 + reject(error)
  31 + })
  32 + })
  33 +}
0 \ No newline at end of file 34 \ No newline at end of file
src/api/fee/listOweFeeApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询欠费信息
  5 +export function queryReportOweFee(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/reportOweFee/queryReportOweFee',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: params.communityId || getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve({
  17 + data: res.data,
  18 + total: res.total,
  19 + records: res.records
  20 + })
  21 + }).catch(error => {
  22 + reject(error)
  23 + })
  24 + })
  25 +}
  26 +
  27 +// 查询收费项列表
  28 +export function listFeeConfigs(params) {
  29 + return new Promise((resolve, reject) => {
  30 + request({
  31 + url: '/feeConfig.listFeeConfigs',
  32 + method: 'get',
  33 + params: {
  34 + ...params,
  35 + communityId: params.communityId || getCommunityId()
  36 + }
  37 + }).then(response => {
  38 + const res = response.data
  39 + resolve({
  40 + data: res.feeConfigs
  41 + })
  42 + }).catch(error => {
  43 + reject(error)
  44 + })
  45 + })
  46 +}
  47 +
  48 +// 查询楼栋列表
  49 +export function queryFloors(params) {
  50 + return new Promise((resolve, reject) => {
  51 + request({
  52 + url: '/floor.queryFloors',
  53 + method: 'get',
  54 + params: {
  55 + ...params,
  56 + communityId: params.communityId || getCommunityId()
  57 + }
  58 + }).then(response => {
  59 + const res = response.data
  60 + resolve({
  61 + data: res.apiFloorDataVoList,
  62 + total: res.records
  63 + })
  64 + }).catch(error => {
  65 + reject(error)
  66 + })
  67 + })
  68 +}
  69 +
  70 +// 查询单元列表
  71 +export function queryUnits(params) {
  72 + return new Promise((resolve, reject) => {
  73 + request({
  74 + url: '/unit.queryUnits',
  75 + method: 'get',
  76 + params: {
  77 + ...params,
  78 + communityId: params.communityId || getCommunityId()
  79 + }
  80 + }).then(response => {
  81 + const res = response.data
  82 + resolve({
  83 + data: res
  84 + })
  85 + }).catch(error => {
  86 + reject(error)
  87 + })
  88 + })
  89 +}
  90 +
  91 +// 导出数据
  92 +export function exportData(params) {
  93 + return new Promise((resolve, reject) => {
  94 + request({
  95 + url: '/export.exportData',
  96 + method: 'get',
  97 + params: {
  98 + ...params,
  99 + communityId: params.communityId || getCommunityId()
  100 + }
  101 + }).then(response => {
  102 + const res = response.data
  103 + resolve(res)
  104 + }).catch(error => {
  105 + reject(error)
  106 + })
  107 + })
  108 +}
0 \ No newline at end of file 109 \ No newline at end of file
src/api/fee/oweFeeCallableApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +/**
  4 + * 获取欠费催缴列表
  5 + * @param {Object} params 查询参数
  6 + * @returns {Promise}
  7 + */
  8 +export function listOweFeeCallable(params) {
  9 + return new Promise((resolve, reject) => {
  10 + request({
  11 + url: '/oweFeeCallable.listOweFeeCallable',
  12 + method: 'get',
  13 + params
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve({
  17 + data: res.data,
  18 + total: res.total
  19 + })
  20 + }).catch(error => {
  21 + reject(error)
  22 + })
  23 + })
  24 +}
  25 +
  26 +/**
  27 + * 登记欠费催缴
  28 + * @param {Object} data 登记数据
  29 + * @returns {Promise}
  30 + */
  31 +export function writeOweFeeCallable(data) {
  32 + return new Promise((resolve, reject) => {
  33 + request({
  34 + url: '/oweFeeCallable.writeOweFeeCallable',
  35 + method: 'post',
  36 + data
  37 + }).then(response => {
  38 + const res = response.data
  39 + if (res.code === 0) {
  40 + resolve(res)
  41 + } else {
  42 + reject(new Error(res.msg))
  43 + }
  44 + }).catch(error => {
  45 + reject(error)
  46 + })
  47 + })
  48 +}
  49 +
  50 +/**
  51 + * 删除欠费催缴记录
  52 + * @param {Object} data 删除数据
  53 + * @returns {Promise}
  54 + */
  55 +export function deleteOweFeeCallable(data) {
  56 + return new Promise((resolve, reject) => {
  57 + request({
  58 + url: '/oweFeeCallable.deleteOweFeeCallable',
  59 + method: 'post',
  60 + data
  61 + }).then(response => {
  62 + const res = response.data
  63 + if (res.code === 0) {
  64 + resolve(res)
  65 + } else {
  66 + reject(new Error(res.msg))
  67 + }
  68 + }).catch(error => {
  69 + reject(error)
  70 + })
  71 + })
  72 +}
  73 +
  74 +/**
  75 + * 获取欠费催缴详情
  76 + * @param {String} ofcId 催缴ID
  77 + * @returns {Promise}
  78 + */
  79 +export function getOweFeeCallableDetail(ofcId) {
  80 + return new Promise((resolve, reject) => {
  81 + request({
  82 + url: '/oweFeeCallable.getOweFeeCallable',
  83 + method: 'get',
  84 + params: { ofcId }
  85 + }).then(response => {
  86 + const res = response.data
  87 + if (res.code === 0) {
  88 + resolve(res.data)
  89 + } else {
  90 + reject(new Error(res.msg))
  91 + }
  92 + }).catch(error => {
  93 + reject(error)
  94 + })
  95 + })
  96 +}
  97 +
  98 +/**
  99 + * 更新欠费催缴状态
  100 + * @param {Object} data 更新数据
  101 + * @returns {Promise}
  102 + */
  103 +export function updateOweFeeCallableState(data) {
  104 + return new Promise((resolve, reject) => {
  105 + request({
  106 + url: '/oweFeeCallable.updateOweFeeCallableState',
  107 + method: 'post',
  108 + data
  109 + }).then(response => {
  110 + const res = response.data
  111 + if (res.code === 0) {
  112 + resolve(res)
  113 + } else {
  114 + reject(new Error(res.msg))
  115 + }
  116 + }).catch(error => {
  117 + reject(error)
  118 + })
  119 + })
  120 +}
0 \ No newline at end of file 121 \ No newline at end of file
src/api/fee/payFeeBatchApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取费用批次列表
  5 +export function listPayFeeBatch(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/payFeeBatch.listPayFeeBatch',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve(res)
  17 + }).catch(error => {
  18 + reject(error)
  19 + })
  20 + })
  21 +}
  22 +
  23 +// 申请取消费用批次
  24 +export function applyDeletePayFeeBatch(data) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/payFeeBatch.applyDeletePayFeeBatchCmd',
  28 + method: 'post',
  29 + data
  30 + }).then(response => {
  31 + const res = response.data
  32 + resolve(res)
  33 + }).catch(error => {
  34 + reject(error)
  35 + })
  36 + })
  37 +}
  38 +
  39 +// 更新费用批次状态
  40 +export function updatePayFeeBatch(data) {
  41 + return new Promise((resolve, reject) => {
  42 + request({
  43 + url: '/payFeeBatch.updatePayFeeBatch',
  44 + method: 'post',
  45 + data
  46 + }).then(response => {
  47 + const res = response.data
  48 + resolve(res)
  49 + }).catch(error => {
  50 + reject(error)
  51 + })
  52 + })
  53 +}
0 \ No newline at end of file 54 \ No newline at end of file
src/api/fee/staffFeeManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取员工收费列表
  4 +export function getStaffFeeList(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/api.getStaffFee',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + resolve(res)
  13 + }).catch(error => {
  14 + reject(error)
  15 + })
  16 + })
  17 +}
  18 +
  19 +// 导出员工收费数据
  20 +export function exportStaffFee(data) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/callComponent/exportReportFee/exportData',
  24 + method: 'post',
  25 + data
  26 + }).then(response => {
  27 + const res = response.data
  28 + resolve(res)
  29 + }).catch(error => {
  30 + reject(error)
  31 + })
  32 + })
  33 +}
0 \ No newline at end of file 34 \ No newline at end of file
src/components/fee/applyDeleteFeeBatch.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('applyDeleteFeeBatch.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form
  9 + ref="form"
  10 + :model="formData"
  11 + label-width="120px"
  12 + label-position="right"
  13 + >
  14 + <el-form-item :label="$t('applyDeleteFeeBatch.form.batchId')">
  15 + <el-input
  16 + v-model="formData.batchId"
  17 + disabled
  18 + />
  19 + </el-form-item>
  20 + <el-form-item :label="$t('applyDeleteFeeBatch.form.createUserName')">
  21 + <el-input
  22 + v-model="formData.createUserName"
  23 + disabled
  24 + />
  25 + </el-form-item>
  26 + <el-form-item :label="$t('applyDeleteFeeBatch.form.createTime')">
  27 + <el-input
  28 + v-model="formData.createTime"
  29 + disabled
  30 + />
  31 + </el-form-item>
  32 + <el-form-item
  33 + :label="$t('applyDeleteFeeBatch.form.remark')"
  34 + prop="remark"
  35 + :rules="[
  36 + { required: true, message: $t('applyDeleteFeeBatch.rules.remarkRequired'), trigger: 'blur' }
  37 + ]"
  38 + >
  39 + <el-input
  40 + v-model="formData.remark"
  41 + type="textarea"
  42 + :rows="3"
  43 + :placeholder="$t('applyDeleteFeeBatch.placeholder.remark')"
  44 + />
  45 + </el-form-item>
  46 + </el-form>
  47 + <div slot="footer" class="dialog-footer">
  48 + <el-button @click="visible = false">
  49 + {{ $t('common.cancel') }}
  50 + </el-button>
  51 + <el-button type="primary" @click="handleSubmit">
  52 + {{ $t('common.save') }}
  53 + </el-button>
  54 + </div>
  55 + </el-dialog>
  56 +</template>
  57 +
  58 +<script>
  59 +import { applyDeletePayFeeBatch } from '@/api/fee/payFeeBatchApi'
  60 +import { getCommunityId } from '@/api/community/communityApi'
  61 +
  62 +export default {
  63 + name: 'ApplyDeleteFeeBatch',
  64 + data() {
  65 + return {
  66 + visible: false,
  67 + formData: {
  68 + batchId: '',
  69 + createUserName: '',
  70 + createTime: '',
  71 + remark: '',
  72 + communityId: ''
  73 + }
  74 + }
  75 + },
  76 + methods: {
  77 + open(data) {
  78 + this.resetForm()
  79 + Object.assign(this.formData, data)
  80 + this.formData.communityId = getCommunityId()
  81 + this.visible = true
  82 + },
  83 + handleClose() {
  84 + this.$refs.form.resetFields()
  85 + },
  86 + resetForm() {
  87 + this.formData = {
  88 + batchId: '',
  89 + createUserName: '',
  90 + createTime: '',
  91 + remark: '',
  92 + communityId: ''
  93 + }
  94 + },
  95 + handleSubmit() {
  96 + this.$refs.form.validate(async valid => {
  97 + if (valid) {
  98 + try {
  99 + await applyDeletePayFeeBatch(this.formData)
  100 + this.$message.success(this.$t('applyDeleteFeeBatch.message.success'))
  101 + this.visible = false
  102 + this.$emit('success')
  103 + } catch (error) {
  104 + console.error('申请取消失败:', error)
  105 + }
  106 + }
  107 + })
  108 + }
  109 + }
  110 +}
  111 +</script>
0 \ No newline at end of file 112 \ No newline at end of file
src/components/fee/audit.vue
1 <template> 1 <template>
2 <el-dialog 2 <el-dialog
3 - :title="$t('audit.auditInfo')" 3 + :title="$t('audit.title')"
4 :visible.sync="visible" 4 :visible.sync="visible"
5 width="50%" 5 width="50%"
6 @close="handleClose" 6 @close="handleClose"
7 > 7 >
8 - <el-form ref="form" :model="form" :rules="rules" label-width="120px">  
9 - <el-form-item :label="$t('audit.auditStatus')" prop="state">  
10 - <el-select  
11 - v-model="form.state"  
12 - :placeholder="$t('audit.selectAudit')" 8 + <el-form
  9 + ref="form"
  10 + :model="formData"
  11 + label-width="120px"
  12 + label-position="right"
  13 + >
  14 + <el-form-item
  15 + :label="$t('audit.form.state')"
  16 + prop="state"
  17 + :rules="[
  18 + { required: true, message: $t('audit.rules.stateRequired'), trigger: 'change' }
  19 + ]"
  20 + >
  21 + <el-select
  22 + v-model="formData.state"
  23 + :placeholder="$t('audit.placeholder.state')"
13 style="width:100%" 24 style="width:100%"
14 - @change="handleStatusChange" 25 + @change="handleStateChange"
15 > 26 >
16 - <el-option  
17 - :label="$t('audit.agree')" 27 + <el-option
  28 + :label="$t('audit.state.agree')"
18 value="1100" 29 value="1100"
19 /> 30 />
20 - <el-option  
21 - :label="$t('audit.reject')" 31 + <el-option
  32 + :label="$t('audit.state.reject')"
22 value="1200" 33 value="1200"
23 /> 34 />
24 </el-select> 35 </el-select>
25 </el-form-item> 36 </el-form-item>
26 - <el-form-item :label="$t('audit.reason')" prop="remark"> 37 + <el-form-item
  38 + :label="$t('audit.form.remark')"
  39 + prop="remark"
  40 + :rules="[
  41 + { required: true, message: $t('audit.rules.remarkRequired'), trigger: 'blur' },
  42 + { max: 200, message: $t('audit.rules.remarkMaxLength'), trigger: 'blur' }
  43 + ]"
  44 + >
27 <el-input 45 <el-input
28 - v-model="form.remark" 46 + v-model="formData.remark"
29 type="textarea" 47 type="textarea"
30 - :rows="4"  
31 - :placeholder="$t('audit.inputReason')" 48 + :rows="3"
  49 + :placeholder="$t('audit.placeholder.remark')"
32 /> 50 />
33 </el-form-item> 51 </el-form-item>
34 </el-form> 52 </el-form>
35 - <span slot="footer" class="dialog-footer">  
36 - <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>  
37 - <el-button type="primary" @click="handleSubmit">{{ $t('common.submit') }}</el-button>  
38 - </span> 53 + <div slot="footer" class="dialog-footer">
  54 + <el-button @click="visible = false">
  55 + {{ $t('common.cancel') }}
  56 + </el-button>
  57 + <el-button type="primary" @click="handleSubmit">
  58 + {{ $t('common.submit') }}
  59 + </el-button>
  60 + </div>
39 </el-dialog> 61 </el-dialog>
40 </template> 62 </template>
41 63
42 <script> 64 <script>
43 export default { 65 export default {
44 - name: 'AuditModal', 66 + name: 'Audit',
45 data() { 67 data() {
46 return { 68 return {
47 visible: false, 69 visible: false,
48 - form: { 70 + formData: {
49 state: '', 71 state: '',
50 remark: '' 72 remark: ''
51 - },  
52 - rules: {  
53 - state: [  
54 - { required: true, message: this.$t('audit.stateRequired'), trigger: 'change' }  
55 - ],  
56 - remark: [  
57 - { required: true, message: this.$t('audit.reasonRequired'), trigger: 'blur' },  
58 - { max: 200, message: this.$t('audit.reasonMaxLength'), trigger: 'blur' }  
59 - ]  
60 } 73 }
61 } 74 }
62 }, 75 },
63 methods: { 76 methods: {
64 open() { 77 open() {
  78 + this.resetForm()
65 this.visible = true 79 this.visible = true
66 }, 80 },
67 handleClose() { 81 handleClose() {
68 this.$refs.form.resetFields() 82 this.$refs.form.resetFields()
69 }, 83 },
70 - handleStatusChange(val) { 84 + resetForm() {
  85 + this.formData = {
  86 + state: '',
  87 + remark: ''
  88 + }
  89 + },
  90 + handleStateChange(val) {
71 if (val === '1100') { 91 if (val === '1100') {
72 - this.form.remark = this.$t('audit.agree') 92 + this.formData.remark = this.$t('audit.state.agree')
73 } else { 93 } else {
74 - this.form.remark = '' 94 + this.formData.remark = ''
75 } 95 }
76 }, 96 },
77 handleSubmit() { 97 handleSubmit() {
78 this.$refs.form.validate(valid => { 98 this.$refs.form.validate(valid => {
79 if (valid) { 99 if (valid) {
80 - const auditInfo = {  
81 - state: this.form.state,  
82 - remark: this.form.state === '1200'  
83 - ? `${this.$t('audit.reject')}: ${this.form.remark}`  
84 - : this.form.remark 100 + if (this.formData.state === '1200') {
  101 + this.formData.remark = `${this.$t('audit.state.reject')}:${this.formData.remark}`
85 } 102 }
86 - this.$emit('success', auditInfo) 103 + this.$emit('success', this.formData)
87 this.visible = false 104 this.visible = false
88 } 105 }
89 }) 106 })
src/components/fee/deleteOweFeeCallable.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('oweFeeCallable.delete.title')"
  4 + :visible.sync="dialogVisible"
  5 + width="30%"
  6 + @close="handleClose">
  7 + <div class="delete-content">
  8 + <p>{{ $t('oweFeeCallable.delete.confirm') }}</p>
  9 + </div>
  10 + <span slot="footer" class="dialog-footer">
  11 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  12 + <el-button type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</el-button>
  13 + </span>
  14 + </el-dialog>
  15 +</template>
  16 +
  17 +<script>
  18 +import { deleteOweFeeCallable } from '@/api/fee/oweFeeCallableApi'
  19 +import { getCommunityId } from '@/api/community/communityApi'
  20 +
  21 +export default {
  22 + name: 'DeleteOweFeeCallable',
  23 + data() {
  24 + return {
  25 + dialogVisible: false,
  26 + currentItem: null
  27 + }
  28 + },
  29 + methods: {
  30 + open(item) {
  31 + this.currentItem = {
  32 + ...item,
  33 + communityId: getCommunityId()
  34 + }
  35 + this.dialogVisible = true
  36 + },
  37 + async handleConfirm() {
  38 + try {
  39 + await deleteOweFeeCallable(this.currentItem)
  40 + this.$message.success(this.$t('oweFeeCallable.delete.success'))
  41 + this.dialogVisible = false
  42 + this.$emit('success')
  43 + } catch (error) {
  44 + console.error('删除失败:', error)
  45 + this.$message.error(error.message || this.$t('oweFeeCallable.delete.error'))
  46 + }
  47 + },
  48 + handleClose() {
  49 + this.currentItem = null
  50 + }
  51 + }
  52 +}
  53 +</script>
  54 +
  55 +<style lang="scss" scoped>
  56 +.delete-content {
  57 + text-align: center;
  58 + font-size: 16px;
  59 + padding: 20px 0;
  60 +}
  61 +</style>
0 \ No newline at end of file 62 \ No newline at end of file
src/components/fee/roomTreeDiv.vue 0 → 100644
  1 +<template>
  2 + <el-card class="tree-card">
  3 + <el-tree
  4 + :data="treeData"
  5 + :props="defaultProps"
  6 + node-key="id"
  7 + default-expand-all
  8 + highlight-current
  9 + @node-click="handleNodeClick"
  10 + :expand-on-click-node="false">
  11 + <span class="custom-tree-node" slot-scope="{ node, data }">
  12 + <span>{{ node.label }}</span>
  13 + </span>
  14 + </el-tree>
  15 + </el-card>
  16 +</template>
  17 +
  18 +<script>
  19 +import { queryUnits, queryRoomsTree } from '@/api/property/roomApi'
  20 +import { getCommunityId } from '@/api/community/communityApi'
  21 +
  22 +export default {
  23 + name: 'RoomTreeDiv',
  24 + data() {
  25 + return {
  26 + treeData: [],
  27 + defaultProps: {
  28 + children: 'children',
  29 + label: 'text'
  30 + },
  31 + communityId: ''
  32 + }
  33 + },
  34 + created() {
  35 + this.communityId = getCommunityId()
  36 + this.loadTreeData()
  37 + },
  38 + methods: {
  39 + async loadTreeData() {
  40 + try {
  41 + const units = await this.getUnits()
  42 + this.treeData = this.buildTreeData(units)
  43 + } catch (error) {
  44 + console.error('加载树数据失败:', error)
  45 + }
  46 + },
  47 + async getUnits() {
  48 + const params = {
  49 + communityId: this.communityId
  50 + }
  51 + const response = await queryUnits(params)
  52 + return response.data
  53 + },
  54 + buildTreeData(units) {
  55 + const floors = {}
  56 +
  57 + // 按楼层分组
  58 + units.forEach(unit => {
  59 + if (!floors[unit.floorId]) {
  60 + floors[unit.floorId] = {
  61 + id: `f_${unit.floorId}`,
  62 + floorId: unit.floorId,
  63 + floorNum: unit.floorNum,
  64 + text: `${unit.floorNum}栋`,
  65 + icon: 'el-icon-office-building',
  66 + children: []
  67 + }
  68 + }
  69 +
  70 + floors[unit.floorId].children.push({
  71 + id: `u_${unit.unitId}`,
  72 + unitId: unit.unitId,
  73 + text: `${unit.unitNum}单元`,
  74 + icon: 'el-icon-collection',
  75 + children: []
  76 + })
  77 + })
  78 +
  79 + return Object.values(floors)
  80 + },
  81 + async handleNodeClick(data) {
  82 + if (data.id.startsWith('u_')) {
  83 + await this.loadRooms(data.unitId, data)
  84 + } else if (data.id.startsWith('r_')) {
  85 + this.$emit('selectRoom', {
  86 + roomId: data.roomId,
  87 + roomName: data.text
  88 + })
  89 + }
  90 + },
  91 + async loadRooms(unitId, node) {
  92 + try {
  93 + if (node.children && node.children.length > 0) return
  94 +
  95 + const params = {
  96 + unitId: unitId,
  97 + communityId: this.communityId,
  98 + page: 1,
  99 + row: 1000
  100 + }
  101 +
  102 + const response = await queryRoomsTree(params)
  103 + const rooms = response.data.rooms || []
  104 +
  105 + rooms.forEach(room => {
  106 + let label = room.roomNum
  107 + if (room.ownerName) {
  108 + label += `(${room.ownerName})`
  109 + }
  110 +
  111 + node.children.push({
  112 + id: `r_${room.roomId}`,
  113 + roomId: room.roomId,
  114 + text: label,
  115 + icon: 'el-icon-house',
  116 + roomName: `${room.floorNum}-${room.unitNum}-${room.roomNum}`
  117 + })
  118 + })
  119 +
  120 + // 默认选择第一个房间
  121 + if (rooms.length > 0) {
  122 + const firstRoom = rooms[0]
  123 + this.$emit('selectRoom', {
  124 + roomId: firstRoom.roomId,
  125 + roomName: `${firstRoom.floorNum}-${firstRoom.unitNum}-${firstRoom.roomNum}`
  126 + })
  127 + }
  128 + } catch (error) {
  129 + console.error('加载房间数据失败:', error)
  130 + }
  131 + }
  132 + }
  133 +}
  134 +</script>
  135 +
  136 +<style lang="scss" scoped>
  137 +.tree-card {
  138 + height: 100%;
  139 + /deep/ .el-card__body {
  140 + padding: 10px;
  141 + }
  142 + .custom-tree-node {
  143 + flex: 1;
  144 + display: flex;
  145 + align-items: center;
  146 + justify-content: space-between;
  147 + font-size: 14px;
  148 + padding-right: 8px;
  149 + }
  150 +}
  151 +</style>
0 \ No newline at end of file 152 \ No newline at end of file
src/components/fee/searchFloor.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('searchFloor.title')"
  4 + :visible.sync="dialogVisible"
  5 + width="70%"
  6 + @close="handleClose"
  7 + >
  8 + <el-row :gutter="20">
  9 + <el-col :span="6">
  10 + <el-input
  11 + v-model.trim="searchFloorInfo.floorId"
  12 + :placeholder="$t('searchFloor.floorId')"
  13 + clearable
  14 + ></el-input>
  15 + </el-col>
  16 + <el-col :span="6">
  17 + <el-input
  18 + v-model.trim="searchFloorInfo.floorName"
  19 + :placeholder="$t('searchFloor.floorName')"
  20 + clearable
  21 + ></el-input>
  22 + </el-col>
  23 + <el-col :span="8">
  24 + <el-input
  25 + v-model.trim="searchFloorInfo.floorNum"
  26 + :placeholder="$t('searchFloor.floorNum')"
  27 + clearable
  28 + style="margin-right: 10px"
  29 + ></el-input>
  30 + <el-button type="primary" @click="searchFloors">
  31 + <i class="el-icon-search"></i>
  32 + {{ $t('common.search') }}
  33 + </el-button>
  34 + <el-button type="info" @click="resetFloors">
  35 + <i class="el-icon-refresh"></i>
  36 + {{ $t('common.reset') }}
  37 + </el-button>
  38 + </el-col>
  39 + </el-row>
  40 +
  41 + <el-table
  42 + :data="searchFloorInfo.floors"
  43 + border
  44 + style="width: 100%; margin-top: 20px"
  45 + v-loading="loading"
  46 + >
  47 + <el-table-column
  48 + prop="floorId"
  49 + :label="$t('searchFloor.table.floorId')"
  50 + align="center"
  51 + ></el-table-column>
  52 + <el-table-column
  53 + prop="floorName"
  54 + :label="$t('searchFloor.table.floorName')"
  55 + align="center"
  56 + ></el-table-column>
  57 + <el-table-column
  58 + prop="floorNum"
  59 + :label="$t('searchFloor.table.floorNum')"
  60 + align="center"
  61 + ></el-table-column>
  62 + <el-table-column
  63 + prop="userName"
  64 + :label="$t('searchFloor.table.userName')"
  65 + align="center"
  66 + ></el-table-column>
  67 + <el-table-column
  68 + :label="$t('common.operation')"
  69 + align="center"
  70 + width="120"
  71 + >
  72 + <template slot-scope="scope">
  73 + <el-button
  74 + type="primary"
  75 + size="mini"
  76 + @click="chooseFloor(scope.row)"
  77 + >
  78 + {{ $t('common.select') }}
  79 + </el-button>
  80 + </template>
  81 + </el-table-column>
  82 + </el-table>
  83 +
  84 + <el-pagination
  85 + @size-change="handleSizeChange"
  86 + @current-change="handleCurrentChange"
  87 + :current-page="page.current"
  88 + :page-sizes="[10, 20, 30, 50]"
  89 + :page-size="page.size"
  90 + layout="total, sizes, prev, pager, next, jumper"
  91 + :total="page.total"
  92 + style="margin-top: 20px;"
  93 + ></el-pagination>
  94 + </el-dialog>
  95 +</template>
  96 +
  97 +<script>
  98 +import { getCommunityId } from '@/api/community/communityApi'
  99 +import { queryFloors } from '@/api/fee/listOweFeeApi'
  100 +
  101 +export default {
  102 + name: 'SearchFloor',
  103 + data() {
  104 + return {
  105 + dialogVisible: false,
  106 + loading: false,
  107 + searchFloorInfo: {
  108 + floors: [],
  109 + floorId: '',
  110 + floorName: '',
  111 + floorNum: ''
  112 + },
  113 + page: {
  114 + current: 1,
  115 + size: 10,
  116 + total: 0
  117 + }
  118 + }
  119 + },
  120 + methods: {
  121 + open() {
  122 + this.dialogVisible = true
  123 + this.refreshSearchFloorData()
  124 + this.loadAllFloorInfo()
  125 + },
  126 + async loadAllFloorInfo() {
  127 + try {
  128 + this.loading = true
  129 + const params = {
  130 + page: this.page.current,
  131 + row: this.page.size,
  132 + communityId: getCommunityId(),
  133 + floorId: this.searchFloorInfo.floorId,
  134 + floorName: this.searchFloorInfo.floorName,
  135 + floorNum: this.searchFloorInfo.floorNum
  136 + }
  137 + const { data, total } = await queryFloors(params)
  138 + this.searchFloorInfo.floors = data
  139 + this.page.total = total
  140 + } catch (error) {
  141 + console.error('获取楼栋信息失败:', error)
  142 + } finally {
  143 + this.loading = false
  144 + }
  145 + },
  146 + chooseFloor(floor) {
  147 + this.$emit('chooseFloor', floor)
  148 + this.dialogVisible = false
  149 + },
  150 + searchFloors() {
  151 + this.page.current = 1
  152 + this.loadAllFloorInfo()
  153 + },
  154 + resetFloors() {
  155 + this.searchFloorInfo.floorId = ''
  156 + this.searchFloorInfo.floorName = ''
  157 + this.searchFloorInfo.floorNum = ''
  158 + this.page.current = 1
  159 + this.loadAllFloorInfo()
  160 + },
  161 + refreshSearchFloorData() {
  162 + this.searchFloorInfo.floorId = ''
  163 + this.searchFloorInfo.floorName = ''
  164 + this.searchFloorInfo.floorNum = ''
  165 + },
  166 + handleClose() {
  167 + this.refreshSearchFloorData()
  168 + },
  169 + handleSizeChange(val) {
  170 + this.page.size = val
  171 + this.loadAllFloorInfo()
  172 + },
  173 + handleCurrentChange(val) {
  174 + this.page.current = val
  175 + this.loadAllFloorInfo()
  176 + }
  177 + }
  178 +}
  179 +</script>
  180 +
  181 +<style lang="scss" scoped>
  182 +.el-dialog {
  183 + .el-row {
  184 + margin-bottom: 20px;
  185 + }
  186 +}
  187 +</style>
0 \ No newline at end of file 188 \ No newline at end of file
src/components/fee/writeOweFeeCallable.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('oweFeeCallable.write.title')" :visible.sync="dialogVisible" width="50%" @close="handleClose">
  3 + <el-form :model="form" label-width="120px" ref="form">
  4 + <el-form-item :label="$t('oweFeeCallable.write.roomName')" prop="roomName">
  5 + <el-input v-model="form.roomName" disabled></el-input>
  6 + </el-form-item>
  7 + <el-form-item :label="$t('oweFeeCallable.write.feeName')" prop="feeIds">
  8 + <el-checkbox-group v-model="form.feeIds">
  9 + <el-checkbox v-for="fee in form.fees" :key="fee.feeId" :label="fee.feeId">
  10 + {{ fee.feeName }}
  11 + </el-checkbox>
  12 + </el-checkbox-group>
  13 + </el-form-item>
  14 + <el-form-item :label="$t('oweFeeCallable.write.remark')" prop="remark">
  15 + <el-input type="textarea" :rows="5" v-model="form.remark"
  16 + :placeholder="$t('oweFeeCallable.write.remarkPlaceholder')">
  17 + </el-input>
  18 + </el-form-item>
  19 + </el-form>
  20 + <span slot="footer" class="dialog-footer">
  21 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  22 + <el-button type="primary" @click="handleSubmit">{{ $t('common.submit') }}</el-button>
  23 + </span>
  24 + </el-dialog>
  25 +</template>
  26 +
  27 +<script>
  28 +import { listFee } from '@/api/fee/feeApi'
  29 +import { writeOweFeeCallable } from '@/api/fee/oweFeeCallableApi'
  30 +import { getCommunityId } from '@/api/community/communityApi'
  31 +
  32 +export default {
  33 + name: 'WriteOweFeeCallable',
  34 + data() {
  35 + return {
  36 + dialogVisible: false,
  37 + form: {
  38 + roomId: '',
  39 + roomName: '',
  40 + fees: [],
  41 + feeIds: [],
  42 + remark: '',
  43 + communityId: ''
  44 + }
  45 + }
  46 + },
  47 + methods: {
  48 + open(data) {
  49 + this.form = {
  50 + roomId: data.roomId,
  51 + roomName: data.roomName,
  52 + fees: [],
  53 + feeIds: [],
  54 + remark: '',
  55 + communityId: getCommunityId()
  56 + }
  57 + this.loadFees()
  58 + this.dialogVisible = true
  59 + },
  60 + async loadFees() {
  61 + try {
  62 + const params = {
  63 + communityId: this.form.communityId,
  64 + payerObjId: this.form.roomId,
  65 + state: '2008001',
  66 + page: 1,
  67 + row: 100
  68 + }
  69 + const response = await listFee(params)
  70 + this.form.fees = response.data.fees || []
  71 + } catch (error) {
  72 + console.error('加载费用数据失败:', error)
  73 + }
  74 + },
  75 + async handleSubmit() {
  76 + try {
  77 + await writeOweFeeCallable(this.form)
  78 + this.$message.success(this.$t('oweFeeCallable.write.success'))
  79 + this.dialogVisible = false
  80 + this.$emit('success')
  81 + } catch (error) {
  82 + console.error('提交失败:', error)
  83 + this.$message.error(error.message || this.$t('oweFeeCallable.write.error'))
  84 + }
  85 + },
  86 + handleClose() {
  87 + this.$refs.form.resetFields()
  88 + }
  89 + }
  90 +}
  91 +</script>
  92 +
  93 +<style lang="scss" scoped>
  94 +.el-checkbox {
  95 + margin-right: 15px;
  96 +}
  97 +</style>
0 \ No newline at end of file 98 \ No newline at end of file
src/i18n/feeI18n.js
@@ -17,6 +17,12 @@ import { messages as shareReadingFeeMessages } from &#39;../views/fee/shareReadingFe @@ -17,6 +17,12 @@ import { messages as shareReadingFeeMessages } from &#39;../views/fee/shareReadingFe
17 import { messages as feeComboManageMessages } from '../views/fee/feeComboManageLang' 17 import { messages as feeComboManageMessages } from '../views/fee/feeComboManageLang'
18 import { messages as payFeeAuditManageMessages } from '../views/fee/payFeeAuditManageLang' 18 import { messages as payFeeAuditManageMessages } from '../views/fee/payFeeAuditManageLang'
19 import { messages as feeComboMemberManageMessages } from '../views/fee/feeComboMemberManageLang' 19 import { messages as feeComboMemberManageMessages } from '../views/fee/feeComboMemberManageLang'
  20 +import { messages as payFeeBatchMessages } from '../views/fee/payFeeBatchLang'
  21 +import { messages as listOweFeeMessages } from '../views/fee/listOweFeeLang'
  22 +import { messages as oweFeeCallableMessages } from '../views/fee/oweFeeCallableLang'
  23 +import { messages as addOweFeeCallableMessages } from '../views/fee/addOweFeeCallableLang'
  24 +import { messages as staffFeeManageMessages } from '../views/fee/staffFeeManageLang'
  25 +import { messages as feeSummaryMessages } from '../views/fee/feeSummaryLang'
20 26
21 export const messages = { 27 export const messages = {
22 en: { 28 en: {
@@ -39,6 +45,12 @@ export const messages = { @@ -39,6 +45,12 @@ export const messages = {
39 ...feeComboManageMessages.en, 45 ...feeComboManageMessages.en,
40 ...payFeeAuditManageMessages.en, 46 ...payFeeAuditManageMessages.en,
41 ...feeComboMemberManageMessages.en, 47 ...feeComboMemberManageMessages.en,
  48 + ...payFeeBatchMessages.en,
  49 + ...listOweFeeMessages.en,
  50 + ...oweFeeCallableMessages.en,
  51 + ...addOweFeeCallableMessages.en,
  52 + ...staffFeeManageMessages.en,
  53 + ...feeSummaryMessages.en,
42 }, 54 },
43 zh: { 55 zh: {
44 ...contractCreateFeeMessages.zh, 56 ...contractCreateFeeMessages.zh,
@@ -60,5 +72,11 @@ export const messages = { @@ -60,5 +72,11 @@ export const messages = {
60 ...feeComboManageMessages.zh, 72 ...feeComboManageMessages.zh,
61 ...payFeeAuditManageMessages.zh, 73 ...payFeeAuditManageMessages.zh,
62 ...feeComboMemberManageMessages.zh, 74 ...feeComboMemberManageMessages.zh,
  75 + ...payFeeBatchMessages.zh,
  76 + ...listOweFeeMessages.zh,
  77 + ...oweFeeCallableMessages.zh,
  78 + ...addOweFeeCallableMessages.zh,
  79 + ...staffFeeManageMessages.zh,
  80 + ...feeSummaryMessages.zh,
63 } 81 }
64 } 82 }
65 \ No newline at end of file 83 \ No newline at end of file
src/router/feeRouter.js
@@ -79,5 +79,35 @@ export default [ @@ -79,5 +79,35 @@ export default [
79 name: '/views/fee/feeComboMemberManage', 79 name: '/views/fee/feeComboMemberManage',
80 component: () => import('@/views/fee/feeComboMemberManageList.vue') 80 component: () => import('@/views/fee/feeComboMemberManageList.vue')
81 }, 81 },
  82 + {
  83 + path: '/pages/property/payFeeBatch',
  84 + name: '/pages/property/payFeeBatch',
  85 + component: () => import('@/views/fee/payFeeBatchList.vue')
  86 + },
  87 + {
  88 + path: '/pages/property/listOweFee',
  89 + name: '/pages/property/listOweFee',
  90 + component: () => import('@/views/fee/listOweFeeList.vue')
  91 + },
  92 + {
  93 + path: '/pages/fee/oweFeeCallable',
  94 + name: '/pages/fee/oweFeeCallable',
  95 + component: () => import('@/views/fee/oweFeeCallableList.vue')
  96 + },
  97 + {
  98 + path: '/views/fee/addOweFeeCallable',
  99 + name: '/views/fee/addOweFeeCallable',
  100 + component: () => import('@/views/fee/addOweFeeCallableList.vue')
  101 + },
  102 + {
  103 + path: '/pages/property/staffFeeManage',
  104 + name: '/pages/property/staffFeeManage',
  105 + component: () => import('@/views/fee/staffFeeManageList.vue')
  106 + },
  107 + {
  108 + path: '/pages/property/feeSummary',
  109 + name: '/pages/property/feeSummary',
  110 + component: () => import('@/views/fee/feeSummaryList.vue')
  111 + },
82 112
83 ] 113 ]
84 \ No newline at end of file 114 \ No newline at end of file
src/views/fee/addOweFeeCallableLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + addOweFeeCallable: {
  4 + title: 'Collection of Arrears',
  5 + callableWay: 'Collection Method',
  6 + selectCallableWay: 'Please select collection method',
  7 + feeConfigs: 'Collection Fees',
  8 + floor: 'Building',
  9 + selectFloor: 'Please select building',
  10 + timeRange: 'Time Range',
  11 + selectTimeRange: 'Please select time range',
  12 + startTime: 'Start Time',
  13 + selectStartTime: 'Please select start time',
  14 + endTime: 'End Time',
  15 + selectEndTime: 'Please select end time',
  16 + oweRooms: 'Arrears Rooms',
  17 + noOweRooms: 'No arrears rooms',
  18 + checkAll: 'Select All',
  19 + ttsCall: 'Voice Collection',
  20 + callableWayRequired: 'Collection method is required',
  21 + roomsRequired: 'Please select arrears rooms',
  22 + noOweRoomsSelected: 'No arrears rooms selected'
  23 + }
  24 + },
  25 + zh: {
  26 + addOweFeeCallable: {
  27 + title: '催缴欠费',
  28 + callableWay: '催缴方式',
  29 + selectCallableWay: '请选择催缴方式',
  30 + feeConfigs: '催缴费用',
  31 + floor: '楼栋',
  32 + selectFloor: '请选择楼栋',
  33 + timeRange: '应收时间段',
  34 + selectTimeRange: '请选择应收时间段',
  35 + startTime: '开始时间',
  36 + selectStartTime: '请选择开始时间',
  37 + endTime: '结束时间',
  38 + selectEndTime: '请选择结束时间',
  39 + oweRooms: '欠费房屋',
  40 + noOweRooms: '没有欠费房屋',
  41 + checkAll: '全选',
  42 + ttsCall: '语音催缴',
  43 + callableWayRequired: '催缴方式不能为空',
  44 + roomsRequired: '请选择欠费房屋',
  45 + noOweRoomsSelected: '未选择欠费房屋'
  46 + }
  47 + }
  48 +}
0 \ No newline at end of file 49 \ No newline at end of file
src/views/fee/addOweFeeCallableList.vue 0 → 100644
  1 +<template>
  2 + <div class="add-owe-fee-callable-container">
  3 + <el-card>
  4 + <div slot="header" class="clearfix">
  5 + <span>{{ $t('addOweFeeCallable.title') }}</span>
  6 + </div>
  7 +
  8 + <el-form label-position="right" label-width="120px">
  9 + <el-row :gutter="20">
  10 + <el-col :span="24">
  11 + <el-form-item :label="$t('addOweFeeCallable.callableWay')">
  12 + <el-select
  13 + v-model="addOweFeeCallableInfo.callableWay"
  14 + style="width:100%"
  15 + :placeholder="$t('addOweFeeCallable.selectCallableWay')">
  16 + <el-option
  17 + v-for="item in callableWayOptions"
  18 + :key="item.value"
  19 + :label="item.label"
  20 + :value="item.value">
  21 + </el-option>
  22 + </el-select>
  23 + </el-form-item>
  24 + </el-col>
  25 + </el-row>
  26 +
  27 + <el-row :gutter="20">
  28 + <el-col :span="24">
  29 + <el-form-item :label="$t('addOweFeeCallable.feeConfigs')">
  30 + <el-checkbox-group v-model="addOweFeeCallableInfo.configIds" @change="loadOweFeeRooms">
  31 + <el-checkbox
  32 + v-for="item in addOweFeeCallableInfo.feeConfigs"
  33 + :key="item.configId"
  34 + :label="item.configId">
  35 + {{item.feeName}}
  36 + </el-checkbox>
  37 + </el-checkbox-group>
  38 + </el-form-item>
  39 + </el-col>
  40 + </el-row>
  41 +
  42 + <el-row :gutter="20">
  43 + <el-col :span="24">
  44 + <el-form-item :label="$t('addOweFeeCallable.floor')">
  45 + <el-select
  46 + v-model="addOweFeeCallableInfo.floorId"
  47 + style="width:100%"
  48 + @change="loadOweFeeRooms"
  49 + :placeholder="$t('addOweFeeCallable.selectFloor')">
  50 + <el-option
  51 + v-for="item in addOweFeeCallableInfo.floors"
  52 + :key="item.floorId"
  53 + :label="item.floorNum"
  54 + :value="item.floorId">
  55 + </el-option>
  56 + </el-select>
  57 + </el-form-item>
  58 + </el-col>
  59 + </el-row>
  60 +
  61 + <el-row :gutter="20">
  62 + <el-col :span="24">
  63 + <el-form-item :label="$t('addOweFeeCallable.timeRange')">
  64 + <el-select
  65 + v-model="addOweFeeCallableInfo.hasTime"
  66 + style="width:100%"
  67 + @change="loadOweFeeRooms"
  68 + :placeholder="$t('addOweFeeCallable.selectTimeRange')">
  69 + <el-option
  70 + v-for="item in timeRangeOptions"
  71 + :key="item.value"
  72 + :label="item.label"
  73 + :value="item.value">
  74 + </el-option>
  75 + </el-select>
  76 + </el-form-item>
  77 + </el-col>
  78 + </el-row>
  79 +
  80 + <el-row :gutter="20" v-show="addOweFeeCallableInfo.hasTime === 'ON'">
  81 + <el-col :span="12">
  82 + <el-form-item :label="$t('addOweFeeCallable.startTime')">
  83 + <el-date-picker
  84 + v-model="addOweFeeCallableInfo.startTime"
  85 + type="datetime"
  86 + style="width:100%"
  87 + :placeholder="$t('addOweFeeCallable.selectStartTime')">
  88 + </el-date-picker>
  89 + </el-form-item>
  90 + </el-col>
  91 + <el-col :span="12">
  92 + <el-form-item :label="$t('addOweFeeCallable.endTime')">
  93 + <el-date-picker
  94 + v-model="addOweFeeCallableInfo.endTime"
  95 + type="datetime"
  96 + style="width:100%"
  97 + :placeholder="$t('addOweFeeCallable.selectEndTime')">
  98 + </el-date-picker>
  99 + </el-form-item>
  100 + </el-col>
  101 + </el-row>
  102 +
  103 + <el-row :gutter="20">
  104 + <el-col :span="24">
  105 + <el-form-item :label="$t('addOweFeeCallable.oweRooms')">
  106 + <div v-if="addOweFeeCallableInfo.rooms && addOweFeeCallableInfo.rooms.length > 0">
  107 + <el-checkbox
  108 + v-model="checkAllStatus"
  109 + @change="handleCheckAllChange">
  110 + {{ $t('addOweFeeCallable.checkAll') }}
  111 + </el-checkbox>
  112 + <el-checkbox-group
  113 + v-model="addOweFeeCallableInfo.roomIds"
  114 + @change="handleCheckedRoomsChange">
  115 + <el-checkbox
  116 + v-for="item in addOweFeeCallableInfo.rooms"
  117 + :key="item.payerObjId"
  118 + :label="item.payerObjId">
  119 + {{item.payerObjName}}
  120 + </el-checkbox>
  121 + </el-checkbox-group>
  122 + </div>
  123 + <div v-else>
  124 + {{ $t('addOweFeeCallable.noOweRooms') }}
  125 + </div>
  126 + </el-form-item>
  127 + </el-col>
  128 + </el-row>
  129 +
  130 + <el-row :gutter="20">
  131 + <el-col :span="24" class="text-right">
  132 + <el-button @click="goBack">{{ $t('common.back') }}</el-button>
  133 + <el-button
  134 + v-if="addOweFeeCallableInfo.callableWay === 'EXCEL'"
  135 + type="primary"
  136 + @click="exportCollectionLetterExcel">
  137 + {{ $t('common.export') }}
  138 + </el-button>
  139 + <el-button
  140 + v-else-if="addOweFeeCallableInfo.callableWay === 'TTS'"
  141 + type="primary"
  142 + @click="ttsOweFee">
  143 + {{ $t('addOweFeeCallable.ttsCall') }}
  144 + </el-button>
  145 + <el-button
  146 + v-else
  147 + type="primary"
  148 + @click="saveOweFeeCallable">
  149 + {{ $t('common.submit') }}
  150 + </el-button>
  151 + </el-col>
  152 + </el-row>
  153 + </el-form>
  154 + </el-card>
  155 + </div>
  156 +</template>
  157 +
  158 +<script>
  159 +import { getCommunityId } from '@/api/community/communityApi'
  160 +import {
  161 + saveOweFeeCallable,
  162 + exportCollectionLetterExcel,
  163 + listFeeConfigs,
  164 + queryFloors,
  165 + queryReportOweFee
  166 +} from '@/api/fee/addOweFeeCallableApi'
  167 +
  168 +export default {
  169 + name: 'AddOweFeeCallableList',
  170 + data() {
  171 + return {
  172 + addOweFeeCallableInfo: {
  173 + callableWay: '',
  174 + remark: '',
  175 + floorId: '',
  176 + communityId: '',
  177 + configIds: [],
  178 + roomId: '',
  179 + roomIds: [],
  180 + rooms: [],
  181 + feeConfigs: [],
  182 + hasTime: 'OFF',
  183 + startTime: '',
  184 + endTime: '',
  185 + floors: []
  186 + },
  187 + callableWayOptions: [
  188 + { value: 'WECHAT', label: '微信模板消息' },
  189 + { value: 'SMS', label: '短信' },
  190 + { value: 'EXCEL', label: '导出' },
  191 + { value: 'TTS', label: '呼电话催缴' }
  192 + ],
  193 + timeRangeOptions: [
  194 + { value: 'OFF', label: '否' },
  195 + { value: 'ON', label: '是' }
  196 + ],
  197 + checkAllStatus: false,
  198 + loading: false
  199 + }
  200 + },
  201 + created() {
  202 + this.addOweFeeCallableInfo.communityId = getCommunityId()
  203 + this.listFeeConfigs()
  204 + this.loadOweFeeFloors()
  205 + },
  206 + methods: {
  207 + async listFeeConfigs() {
  208 + try {
  209 + const params = {
  210 + page: 1,
  211 + row: 100,
  212 + communityId: this.addOweFeeCallableInfo.communityId,
  213 + isDefault: 'F'
  214 + }
  215 + const { data } = await listFeeConfigs(params)
  216 + this.addOweFeeCallableInfo.feeConfigs = data.feeConfigs
  217 + } catch (error) {
  218 + console.error('获取费用配置失败:', error)
  219 + }
  220 + },
  221 + async loadOweFeeFloors() {
  222 + try {
  223 + const params = {
  224 + page: 1,
  225 + row: 100,
  226 + communityId: this.addOweFeeCallableInfo.communityId
  227 + }
  228 + const { data } = await queryFloors(params)
  229 + this.addOweFeeCallableInfo.floors = data.apiFloorDataVoList
  230 + } catch (error) {
  231 + console.error('获取楼栋失败:', error)
  232 + }
  233 + },
  234 + async loadOweFeeRooms() {
  235 + if (!this.addOweFeeCallableInfo.floorId ||
  236 + !this.addOweFeeCallableInfo.configIds ||
  237 + this.addOweFeeCallableInfo.configIds.length === 0) {
  238 + return
  239 + }
  240 +
  241 + try {
  242 + const params = {
  243 + page: 1,
  244 + row: 500,
  245 + configIds: this.addOweFeeCallableInfo.configIds.join(','),
  246 + communityId: this.addOweFeeCallableInfo.communityId,
  247 + floorId: this.addOweFeeCallableInfo.floorId
  248 + }
  249 + const { data } = await queryReportOweFee(params)
  250 + this.addOweFeeCallableInfo.rooms = data.data || []
  251 + this.addOweFeeCallableInfo.roomIds = this.addOweFeeCallableInfo.rooms.map(item => item.payerObjId)
  252 + this.checkAllStatus = this.addOweFeeCallableInfo.rooms.length > 0
  253 + } catch (error) {
  254 + console.error('获取欠费房屋失败:', error)
  255 + }
  256 + },
  257 + handleCheckAllChange(val) {
  258 + this.addOweFeeCallableInfo.roomIds = val ?
  259 + this.addOweFeeCallableInfo.rooms.map(item => item.payerObjId) : []
  260 + },
  261 + handleCheckedRoomsChange(value) {
  262 + const checkedCount = value.length
  263 + this.checkAllStatus = checkedCount === this.addOweFeeCallableInfo.rooms.length
  264 + },
  265 + validateForm() {
  266 + if (!this.addOweFeeCallableInfo.callableWay) {
  267 + this.$message.error(this.$t('addOweFeeCallable.callableWayRequired'))
  268 + return false
  269 + }
  270 + if (!this.addOweFeeCallableInfo.roomIds || this.addOweFeeCallableInfo.roomIds.length === 0) {
  271 + this.$message.error(this.$t('addOweFeeCallable.roomsRequired'))
  272 + return false
  273 + }
  274 + return true
  275 + },
  276 + async saveOweFeeCallable() {
  277 + if (!this.validateForm()) return
  278 +
  279 + try {
  280 + await saveOweFeeCallable(this.addOweFeeCallableInfo)
  281 + this.$message.success(this.$t('common.submitSuccess'))
  282 + this.goBack()
  283 + } catch (error) {
  284 + console.error('提交失败:', error)
  285 + this.$message.error(error.message || this.$t('common.submitFailed'))
  286 + }
  287 + },
  288 + async exportCollectionLetterExcel() {
  289 + if (!this.validateForm()) return
  290 +
  291 + try {
  292 + const params = {
  293 + communityId: this.addOweFeeCallableInfo.communityId,
  294 + pagePath: 'dataFeeManualCollection',
  295 + configIds: this.addOweFeeCallableInfo.configIds.join(','),
  296 + roomIds: this.addOweFeeCallableInfo.roomIds.join(','),
  297 + startTime: this.addOweFeeCallableInfo.startTime,
  298 + endTime: this.addOweFeeCallableInfo.endTime
  299 + }
  300 + await exportCollectionLetterExcel(params)
  301 + this.$message.success(this.$t('common.exportSuccess'))
  302 + this.$router.push('/pages/property/downloadTempFile?tab=下载中心')
  303 + } catch (error) {
  304 + console.error('导出失败:', error)
  305 + this.$message.error(error.message || this.$t('common.exportFailed'))
  306 + }
  307 + },
  308 + ttsOweFee() {
  309 + if (!this.validateForm()) return
  310 +
  311 + const oweFee = this.addOweFeeCallableInfo.rooms
  312 + .filter(room => this.addOweFeeCallableInfo.roomIds.includes(room.payerObjId))
  313 + .map(room => ({ ...room, state: 'W' }))
  314 +
  315 + if (oweFee.length === 0) {
  316 + this.$message.error(this.$t('addOweFeeCallable.noOweRoomsSelected'))
  317 + return
  318 + }
  319 +
  320 + const data = {
  321 + configIds: this.addOweFeeCallableInfo.configIds,
  322 + oweFee,
  323 + startTime: this.addOweFeeCallableInfo.startTime,
  324 + endTime: this.addOweFeeCallableInfo.endTime
  325 + }
  326 +
  327 + localStorage.setItem('TTS_OWE_FEE_DATA', JSON.stringify(data))
  328 + this.$router.push('/fee/ttsOweFeeCallable')
  329 + },
  330 + goBack() {
  331 + this.$router.go(-1)
  332 + }
  333 + }
  334 +}
  335 +</script>
  336 +
  337 +<style lang="scss" scoped>
  338 +.add-owe-fee-callable-container {
  339 + padding: 20px;
  340 +
  341 + .el-checkbox-group {
  342 + display: flex;
  343 + flex-wrap: wrap;
  344 + gap: 15px;
  345 + }
  346 +
  347 + .el-checkbox {
  348 + margin-right: 0;
  349 + }
  350 +
  351 + .text-right {
  352 + text-align: right;
  353 + }
  354 +}
  355 +</style>
0 \ No newline at end of file 356 \ No newline at end of file
src/views/fee/feeComboMemberManageList.vue
@@ -14,80 +14,34 @@ @@ -14,80 +14,34 @@
14 </div> 14 </div>
15 </div> 15 </div>
16 16
17 - <el-table  
18 - :data="feeComboMemberManageInfo.feeComboMembers"  
19 - border  
20 - style="width: 100%"  
21 - v-loading="loading"  
22 - >  
23 - <el-table-column  
24 - prop="feeTypeCdName"  
25 - :label="$t('feeComboMemberManage.feeType')"  
26 - align="center"  
27 - />  
28 - <el-table-column  
29 - prop="feeName"  
30 - :label="$t('feeComboMemberManage.feeItem')"  
31 - align="center"  
32 - />  
33 - <el-table-column  
34 - prop="feeFlagName"  
35 - :label="$t('feeComboMemberManage.feeFlag')"  
36 - align="center"  
37 - />  
38 - <el-table-column  
39 - :label="$t('feeComboMemberManage.paymentType')"  
40 - align="center"  
41 - > 17 + <el-table :data="feeComboMemberManageInfo.feeComboMembers" border style="width: 100%" v-loading="loading">
  18 + <el-table-column prop="feeTypeCdName" :label="$t('feeComboMemberManage.feeType')" align="center" />
  19 + <el-table-column prop="feeName" :label="$t('feeComboMemberManage.feeItem')" align="center" />
  20 + <el-table-column prop="feeFlagName" :label="$t('feeComboMemberManage.feeFlag')" align="center" />
  21 + <el-table-column :label="$t('feeComboMemberManage.paymentType')" align="center">
42 <template slot-scope="scope"> 22 <template slot-scope="scope">
43 {{ scope.row.paymentCd == '1200' ? $t('feeComboMemberManage.prepay') : $t('feeComboMemberManage.postpay') }} 23 {{ scope.row.paymentCd == '1200' ? $t('feeComboMemberManage.prepay') : $t('feeComboMemberManage.postpay') }}
44 </template> 24 </template>
45 </el-table-column> 25 </el-table-column>
46 - <el-table-column  
47 - prop="paymentCycle"  
48 - :label="$t('feeComboMemberManage.paymentCycle')"  
49 - align="center"  
50 - />  
51 - <el-table-column  
52 - :label="$t('feeComboMemberManage.unitPrice')"  
53 - align="center"  
54 - > 26 + <el-table-column prop="paymentCycle" :label="$t('feeComboMemberManage.paymentCycle')" align="center" />
  27 + <el-table-column :label="$t('feeComboMemberManage.unitPrice')" align="center">
55 <template slot-scope="scope"> 28 <template slot-scope="scope">
56 {{ scope.row.computingFormula == '2002' ? '-' : scope.row.squarePrice }} 29 {{ scope.row.computingFormula == '2002' ? '-' : scope.row.squarePrice }}
57 </template> 30 </template>
58 </el-table-column> 31 </el-table-column>
59 - <el-table-column  
60 - prop="additionalAmount"  
61 - :label="$t('feeComboMemberManage.additionalFee')"  
62 - align="center"  
63 - />  
64 - <el-table-column  
65 - :label="$t('common.operation')"  
66 - align="center"  
67 - width="150"  
68 - > 32 + <el-table-column prop="additionalAmount" :label="$t('feeComboMemberManage.additionalFee')" align="center" />
  33 + <el-table-column :label="$t('common.operation')" align="center" width="150">
69 <template slot-scope="scope"> 34 <template slot-scope="scope">
70 - <el-button  
71 - type="danger"  
72 - size="mini"  
73 - @click="_openDeleteFeeComboMemberModel(scope.row)"  
74 - > 35 + <el-button type="danger" size="mini" @click="_openDeleteFeeComboMemberModel(scope.row)">
75 {{ $t('common.delete') }} 36 {{ $t('common.delete') }}
76 </el-button> 37 </el-button>
77 </template> 38 </template>
78 </el-table-column> 39 </el-table-column>
79 </el-table> 40 </el-table>
80 41
81 - <el-pagination  
82 - @size-change="handleSizeChange"  
83 - @current-change="handleCurrentChange"  
84 - :current-page="page.current"  
85 - :page-sizes="[10, 20, 30, 50]"  
86 - :page-size="page.size"  
87 - layout="total, sizes, prev, pager, next, jumper"  
88 - :total="page.total"  
89 - class="pagination"  
90 - /> 42 + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page.current"
  43 + :page-sizes="[10, 20, 30, 50]" :page-size="page.size" layout="total, sizes, prev, pager, next, jumper"
  44 + :total="page.total" class="pagination" />
91 </el-card> 45 </el-card>
92 46
93 <add-fee-combo-member ref="addFeeComboMember" @success="handleSuccess" /> 47 <add-fee-combo-member ref="addFeeComboMember" @success="handleSuccess" />
src/views/fee/feeSummaryLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + feeSummary: {
  4 + title: 'Fee Summary',
  5 + day: 'Day',
  6 + month: 'Month',
  7 + year: 'Year',
  8 + time: 'Time',
  9 + feeItemId: 'Fee Item ID',
  10 + feeItemName: 'Fee Item Name',
  11 + receivableAmount: 'Receivable Amount',
  12 + receivedAmount: 'Received Amount',
  13 + yuan: 'yuan',
  14 + fetchError: 'Failed to fetch fee summary data'
  15 + }
  16 + },
  17 + zh: {
  18 + feeSummary: {
  19 + title: '缴费汇总表',
  20 + day: '日',
  21 + month: '月',
  22 + year: '年',
  23 + time: '时间',
  24 + feeItemId: '费用项ID',
  25 + feeItemName: '费用项名称',
  26 + receivableAmount: '应收金额',
  27 + receivedAmount: '实收金额',
  28 + yuan: '元',
  29 + fetchError: '获取缴费汇总数据失败'
  30 + }
  31 + }
  32 +}
0 \ No newline at end of file 33 \ No newline at end of file
src/views/fee/feeSummaryList.vue 0 → 100644
  1 +<template>
  2 + <div class="fee-summary-container animated fadeInRight">
  3 + <el-row>
  4 + <el-col :span="24">
  5 + <el-card>
  6 + <div slot="header" class="clearfix">
  7 + <span>{{ $t('feeSummary.title') }}</span>
  8 + <div class="card-header-right">
  9 + <el-button-group>
  10 + <el-button size="small" :type="feeSummaryInfo.feeSummaryType === 1001 ? 'primary' : ''"
  11 + @click="_switchFeeSummaryType(1001)">
  12 + {{ $t('feeSummary.day') }}
  13 + </el-button>
  14 + <el-button size="small" :type="feeSummaryInfo.feeSummaryType === 1101 ? 'primary' : ''"
  15 + @click="_switchFeeSummaryType(1101)">
  16 + {{ $t('feeSummary.month') }}
  17 + </el-button>
  18 + <el-button size="small" :type="feeSummaryInfo.feeSummaryType === 1111 ? 'primary' : ''"
  19 + @click="_switchFeeSummaryType(1111)">
  20 + {{ $t('feeSummary.year') }}
  21 + </el-button>
  22 + </el-button-group>
  23 + </div>
  24 + </div>
  25 +
  26 + <el-table :data="feeSummaryInfo.fees" border style="width: 100%" v-loading="loading">
  27 + <el-table-column prop="createTime" :label="$t('feeSummary.time')" align="center" />
  28 + <el-table-column prop="configId" :label="$t('feeSummary.feeItemId')" align="center" />
  29 + <el-table-column prop="feeName" :label="$t('feeSummary.feeItemName')" align="center" />
  30 + <el-table-column prop="receivableAmounts" :label="$t('feeSummary.receivableAmount')" align="center">
  31 + <template slot-scope="scope">
  32 + {{ scope.row.receivableAmounts }} {{ $t('feeSummary.yuan') }}
  33 + </template>
  34 + </el-table-column>
  35 + <el-table-column prop="receivedAmounts" :label="$t('feeSummary.receivedAmount')" align="center">
  36 + <template slot-scope="scope">
  37 + {{ scope.row.receivedAmounts }} {{ $t('feeSummary.yuan') }}
  38 + </template>
  39 + </el-table-column>
  40 + </el-table>
  41 +
  42 + <el-pagination class="pagination-wrapper" :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]"
  43 + :page-size="page.size" :total="page.total" layout="total, sizes, prev, pager, next, jumper"
  44 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  45 + </el-card>
  46 + </el-col>
  47 + </el-row>
  48 + </div>
  49 +</template>
  50 +
  51 +<script>
  52 +import { listFeeSummarys } from '@/api/fee/feeSummaryApi'
  53 +import { getCommunityId } from '@/api/community/communityApi'
  54 +
  55 +export default {
  56 + name: 'FeeSummaryList',
  57 + data() {
  58 + return {
  59 + loading: false,
  60 + feeSummaryInfo: {
  61 + fees: [],
  62 + feeSummaryType: '1001',
  63 + total: 0
  64 + },
  65 + page: {
  66 + current: 1,
  67 + size: 10,
  68 + total: 0
  69 + },
  70 + communityId: ''
  71 + }
  72 + },
  73 + created() {
  74 + this.communityId = getCommunityId()
  75 + this._listFeeSummarys(this.page.current, this.page.size)
  76 + },
  77 + methods: {
  78 + async _listFeeSummarys(page, size) {
  79 + try {
  80 + this.loading = true
  81 + const params = {
  82 + page,
  83 + row: size,
  84 + feeSummaryType: this.feeSummaryInfo.feeSummaryType,
  85 + communityId: this.communityId
  86 + }
  87 + const { fees, total } = await listFeeSummarys(params)
  88 + this.feeSummaryInfo.fees = fees
  89 + this.page.total = total
  90 + } catch (error) {
  91 + this.$message.error(this.$t('feeSummary.fetchError'))
  92 + } finally {
  93 + this.loading = false
  94 + }
  95 + },
  96 + _switchFeeSummaryType(feeSummaryType) {
  97 + this.feeSummaryInfo.feeSummaryType = feeSummaryType
  98 + this.page.current = 1
  99 + this._listFeeSummarys(this.page.current, this.page.size)
  100 + },
  101 + handleSizeChange(val) {
  102 + this.page.size = val
  103 + this._listFeeSummarys(this.page.current, this.page.size)
  104 + },
  105 + handleCurrentChange(val) {
  106 + this.page.current = val
  107 + this._listFeeSummarys(this.page.current, this.page.size)
  108 + }
  109 + }
  110 +}
  111 +</script>
  112 +
  113 +<style lang="scss" scoped>
  114 +.fee-summary-container {
  115 + padding: 20px;
  116 +
  117 + .clearfix {
  118 + display: flex;
  119 + justify-content: space-between;
  120 + align-items: center;
  121 + }
  122 +
  123 + .card-header-right {
  124 + display: flex;
  125 + align-items: center;
  126 + }
  127 +
  128 + .pagination-wrapper {
  129 + margin-top: 20px;
  130 + text-align: right;
  131 + }
  132 +
  133 + .el-button-group {
  134 + margin-left: 10px;
  135 + }
  136 +}
  137 +</style>
0 \ No newline at end of file 138 \ No newline at end of file
src/views/fee/listOweFeeLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + listOweFee: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + floorName: 'Please select building',
  7 + unit: 'Please select unit',
  8 + roomNum: 'Please enter room number',
  9 + roomType: 'Please select room type',
  10 + ownerName: 'Please enter owner name',
  11 + payObjType: 'Please select payment object type',
  12 + roomNumFormat: 'Please enter room number, e.g. 1-1-1123',
  13 + carportNum: 'Please enter parking space number, e.g. 6-8',
  14 + feeConfig: 'Please select fee items'
  15 + },
  16 + table: {
  17 + title: 'Arrears Information',
  18 + index: 'No.',
  19 + payerObjName: 'Payment Object',
  20 + ownerName: 'Owner Name',
  21 + ownerTel: 'Phone Number',
  22 + endTime: 'Start Time',
  23 + deadlineTime: 'End Time',
  24 + total: 'Total (Unit: Yuan)',
  25 + updateTime: 'Update Time'
  26 + },
  27 + summary: {
  28 + subtotal: 'Subtotal',
  29 + total: 'Total'
  30 + },
  31 + exportSuccess: 'Export initiated successfully',
  32 + exportError: 'Export failed'
  33 + },
  34 + searchFloor: {
  35 + title: 'Building Selection',
  36 + floorId: 'Please enter building ID',
  37 + floorName: 'Please enter building name',
  38 + floorNum: 'Please enter building number',
  39 + table: {
  40 + floorId: 'Building ID',
  41 + floorName: 'Name',
  42 + floorNum: 'Number',
  43 + userName: 'Creator'
  44 + }
  45 + }
  46 + },
  47 + zh: {
  48 + listOweFee: {
  49 + search: {
  50 + title: '查询条件',
  51 + floorName: '请选择楼栋',
  52 + unit: '单元',
  53 + roomNum: '请填写房屋编号',
  54 + roomType: '请选择房屋类型',
  55 + ownerName: '请填写业主名称',
  56 + payObjType: '请选择付费对象类型',
  57 + roomNumFormat: '请填写房屋编号,如1-1-1123',
  58 + carportNum: '请填写车位编号,如6-8',
  59 + feeConfig: '请选择收费项'
  60 + },
  61 + table: {
  62 + title: '欠费信息',
  63 + index: '序号',
  64 + payerObjName: '收费对象',
  65 + ownerName: '业主名称',
  66 + ownerTel: '手机号',
  67 + endTime: '开始时间',
  68 + deadlineTime: '结束时间',
  69 + total: '合计(单位:元)',
  70 + updateTime: '更新时间'
  71 + },
  72 + summary: {
  73 + subtotal: '小计',
  74 + total: '合计'
  75 + },
  76 + exportSuccess: '导出成功',
  77 + exportError: '导出失败'
  78 + },
  79 + searchFloor: {
  80 + title: '小区楼栋',
  81 + floorId: '请输入楼栋ID',
  82 + floorName: '请输入楼栋名称',
  83 + floorNum: '请输入楼栋编号',
  84 + table: {
  85 + floorId: '楼栋ID',
  86 + floorName: '名称',
  87 + floorNum: '编号',
  88 + userName: '创建人'
  89 + }
  90 + }
  91 + }
  92 +}
0 \ No newline at end of file 93 \ No newline at end of file
src/views/fee/listOweFeeList.vue 0 → 100644
  1 +<template>
  2 + <div class="list-owe-fee-container">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-card">
  5 + <div slot="header" class="flex justify-between">
  6 + <span>{{ $t('listOweFee.search.title') }}</span>
  7 + <el-button type="text" style="float: right; padding: 3px 0" @click="toggleMoreCondition">
  8 + {{ listOweFeeInfo.moreCondition ? $t('common.hide') : $t('common.more') }}
  9 + </el-button>
  10 + </div>
  11 + <el-row :gutter="20">
  12 + <el-col :span="6">
  13 + <el-input v-model="listOweFeeInfo.conditions.floorName" :placeholder="$t('listOweFee.search.floorName')"
  14 + class="search-item">
  15 + <el-button slot="append" type="primary" @click="openChooseFloor">
  16 + <i class="el-icon-search"></i>
  17 + {{ $t('common.select') }}
  18 + </el-button>
  19 + </el-input>
  20 + </el-col>
  21 + <el-col :span="6">
  22 + <el-select v-model="listOweFeeInfo.conditions.unitId" :placeholder="$t('listOweFee.search.unit')"
  23 + style="width:100%">
  24 + <el-option :label="$t('listOweFee.search.unit')" value=""></el-option>
  25 + <el-option v-for="(unit, index) in listOweFeeInfo.roomUnits" :key="index"
  26 + :label="unit.unitNum + $t('listOweFee.search.unit')" :value="unit.unitId"></el-option>
  27 + </el-select>
  28 + </el-col>
  29 + <el-col :span="6">
  30 + <el-input v-model.trim="listOweFeeInfo.conditions.roomNum"
  31 + :placeholder="$t('listOweFee.search.roomNum')"></el-input>
  32 + </el-col>
  33 + <el-col :span="6">
  34 + <el-button type="primary" @click="queryOweFee">
  35 + <i class="el-icon-search"></i>
  36 + {{ $t('common.search') }}
  37 + </el-button>
  38 + <el-button type="info" @click="resetOweFee">
  39 + <i class="el-icon-refresh"></i>
  40 + {{ $t('common.reset') }}
  41 + </el-button>
  42 + </el-col>
  43 + </el-row>
  44 +
  45 + <!-- 更多查询条件 -->
  46 + <el-row v-show="listOweFeeInfo.moreCondition" :gutter="20">
  47 + <el-col :span="6">
  48 + <el-select v-model="listOweFeeInfo.conditions.roomSubType" :placeholder="$t('listOweFee.search.roomType')"
  49 + style="width:100%">
  50 + <el-option :label="$t('common.selectRoomType')" value=""></el-option>
  51 + <el-option v-for="(item, index) in listOweFeeInfo.roomSubTypes" :key="index" :label="item.name"
  52 + :value="item.statusCd"></el-option>
  53 + </el-select>
  54 + </el-col>
  55 + <el-col :span="6">
  56 + <el-input v-model.trim="listOweFeeInfo.conditions.ownerName"
  57 + :placeholder="$t('listOweFee.search.ownerName')"></el-input>
  58 + </el-col>
  59 + <el-col :span="6">
  60 + <el-select v-model="listOweFeeInfo.conditions.payObjType" :placeholder="$t('listOweFee.search.payObjType')"
  61 + style="width:100%">
  62 + <el-option :label="$t('common.selectPayObjType')" value=""></el-option>
  63 + <el-option v-for="(item, index) in listOweFeeInfo.payObjTypes" :key="index" :label="item.name"
  64 + :value="item.statusCd"></el-option>
  65 + </el-select>
  66 + </el-col>
  67 + </el-row>
  68 +
  69 + <el-row v-show="listOweFeeInfo.moreCondition" :gutter="20">
  70 + <el-col v-if="listOweFeeInfo.conditions.payObjType === '3333'" :span="6">
  71 + <el-input v-model.trim="listOweFeeInfo.conditions.num"
  72 + :placeholder="$t('listOweFee.search.roomNumFormat')"></el-input>
  73 + </el-col>
  74 + <el-col v-if="listOweFeeInfo.conditions.payObjType === '6666'" :span="6">
  75 + <el-input v-model.trim="listOweFeeInfo.conditions.num"
  76 + :placeholder="$t('listOweFee.search.carportNum')"></el-input>
  77 + </el-col>
  78 + <el-col :span="6">
  79 + <el-select v-model="selectedFeeConfigs" multiple :placeholder="$t('listOweFee.search.feeConfig')"
  80 + style="width:100%">
  81 + <el-option v-for="(item, index) in listOweFeeInfo.feeConfigs" :key="index" :label="item.feeName"
  82 + :value="item.configId"></el-option>
  83 + </el-select>
  84 + </el-col>
  85 + </el-row>
  86 + </el-card>
  87 +
  88 + <!-- 欠费信息表格 -->
  89 + <el-card class="table-card">
  90 + <div slot="header" class="flex justify-between">
  91 + <span>{{ $t('listOweFee.table.title') }}</span>
  92 + <el-button type="primary" size="small" @click="exportFee">
  93 + <i class="el-icon-download"></i>
  94 + {{ $t('common.export') }}
  95 + </el-button>
  96 + </div>
  97 + <el-table :data="listOweFeeInfo.fees" border style="width: 100%" v-loading="loading">
  98 + <el-table-column prop="index" :label="$t('listOweFee.table.index')" width="80" align="center">
  99 + <template slot-scope="scope">
  100 + {{ scope.$index + 1 }}
  101 + </template>
  102 + </el-table-column>
  103 + <el-table-column prop="payerObjName" :label="$t('listOweFee.table.payerObjName')"
  104 + align="center"></el-table-column>
  105 + <el-table-column prop="ownerName" :label="$t('listOweFee.table.ownerName')" align="center"></el-table-column>
  106 + <el-table-column prop="ownerTel" :label="$t('listOweFee.table.ownerTel')" align="center"></el-table-column>
  107 + <el-table-column prop="endTime" :label="$t('listOweFee.table.endTime')" align="center"></el-table-column>
  108 + <el-table-column prop="deadlineTime" :label="$t('listOweFee.table.deadlineTime')"
  109 + align="center"></el-table-column>
  110 + <el-table-column v-for="(item, index) in listOweFeeInfo.feeConfigNames" :key="index" :label="item.configName"
  111 + align="center">
  112 + <template slot-scope="scope">
  113 + {{ getFeeOweAmount(item, scope.row) }}
  114 + </template>
  115 + </el-table-column>
  116 + <el-table-column :label="$t('listOweFee.table.total')" align="center">
  117 + <template slot-scope="scope">
  118 + {{ getAllFeeOweAmount(scope.row) }}
  119 + </template>
  120 + </el-table-column>
  121 + <el-table-column prop="updateTime" :label="$t('listOweFee.table.updateTime')" align="center"></el-table-column>
  122 + </el-table>
  123 +
  124 + <!-- 分页 -->
  125 + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page.current"
  126 + :page-sizes="[10, 20, 30, 50]" :page-size="page.size" layout="total, sizes, prev, pager, next, jumper"
  127 + :total="page.total" style="margin-top: 20px;"></el-pagination>
  128 +
  129 + <!-- 小计和合计 -->
  130 + <div class="summary-wrapper">
  131 + <div class="summary-item">
  132 + <span class="summary-title">{{ $t('listOweFee.summary.subtotal') }}</span>
  133 + <div v-for="(item, index) in listOweFeeInfo.feeConfigNames" :key="index" class="summary-content">
  134 + {{ item.configName }}: {{ getFeeOweAllAmount(item) }} {{ $t('common.yuan') }}
  135 + </div>
  136 + <div class="summary-content">
  137 + {{ $t('listOweFee.summary.total') }}: {{ getFeeOweAllAmounts() }} {{ $t('common.yuan') }}
  138 + </div>
  139 + </div>
  140 + </div>
  141 + </el-card>
  142 +
  143 + <!-- 选择楼栋组件 -->
  144 + <search-floor ref="searchFloor" @chooseFloor="handleChooseFloor"></search-floor>
  145 + </div>
  146 +</template>
  147 +
  148 +<script>
  149 +import { getDict } from '@/api/community/communityApi'
  150 +import { getCommunityId } from '@/api/community/communityApi'
  151 +import { queryReportOweFee, listFeeConfigs, exportData,queryUnits } from '@/api/fee/listOweFeeApi'
  152 +import SearchFloor from '@/components/fee/searchFloor'
  153 +
  154 +export default {
  155 + name: 'ListOweFee',
  156 + components: {
  157 + SearchFloor
  158 + },
  159 + data() {
  160 + return {
  161 + loading: false,
  162 + listOweFeeInfo: {
  163 + fees: [],
  164 + moreCondition: false,
  165 + roomUnits: [],
  166 + conditions: {
  167 + configIds: '',
  168 + payObjType: '',
  169 + num: '',
  170 + billType: '00123',
  171 + floorId: '',
  172 + floorName: '',
  173 + roomNum: '',
  174 + unitId: '',
  175 + roomSubType: '',
  176 + ownerName: '',
  177 + communityId: ''
  178 + },
  179 + feeConfigs: [],
  180 + feeConfigNames: [],
  181 + payObjTypes: [],
  182 + roomSubTypes: []
  183 + },
  184 + selectedFeeConfigs: [],
  185 + page: {
  186 + current: 1,
  187 + size: 10,
  188 + total: 0
  189 + }
  190 + }
  191 + },
  192 + created() {
  193 + this.listOweFeeInfo.conditions.communityId = getCommunityId()
  194 + this.initData()
  195 + },
  196 + watch: {
  197 + selectedFeeConfigs(newVal) {
  198 + this.listOweFeeInfo.feeConfigNames = newVal.map(configId => {
  199 + const config = this.listOweFeeInfo.feeConfigs.find(item => item.configId === configId)
  200 + return {
  201 + configId,
  202 + configName: config ? config.feeName : ''
  203 + }
  204 + })
  205 + }
  206 + },
  207 + methods: {
  208 + async initData() {
  209 + try {
  210 + this.loading = true
  211 + await Promise.all([
  212 + this.getPayObjTypes(),
  213 + this.getRoomSubTypes(),
  214 + this.loadListOweFeeInfo(),
  215 + this.listFeeConfigs()
  216 + ])
  217 + } catch (error) {
  218 + console.error('初始化数据失败:', error)
  219 + } finally {
  220 + this.loading = false
  221 + }
  222 + },
  223 + async getPayObjTypes() {
  224 + try {
  225 + const data = await getDict('report_owe_fee', 'payer_obj_type')
  226 + this.listOweFeeInfo.payObjTypes = data
  227 + } catch (error) {
  228 + console.error('获取付费对象类型失败:', error)
  229 + }
  230 + },
  231 + async getRoomSubTypes() {
  232 + try {
  233 + const data = await getDict('building_room', 'room_sub_type')
  234 + this.listOweFeeInfo.roomSubTypes = data
  235 + } catch (error) {
  236 + console.error('获取房屋类型失败:', error)
  237 + }
  238 + },
  239 + async loadListOweFeeInfo() {
  240 + try {
  241 + const params = {
  242 + ...this.listOweFeeInfo.conditions,
  243 + page: this.page.current,
  244 + row: this.page.size,
  245 + configIds: this.listOweFeeInfo.feeConfigNames.map(item => item.configId).join(',')
  246 + }
  247 + const { data, total } = await queryReportOweFee(params)
  248 + this.listOweFeeInfo.fees = data
  249 + this.page.total = total
  250 + } catch (error) {
  251 + console.error('获取欠费信息失败:', error)
  252 + }
  253 + },
  254 + async listFeeConfigs() {
  255 + try {
  256 + const params = {
  257 + page: 1,
  258 + row: 100,
  259 + communityId: this.listOweFeeInfo.conditions.communityId
  260 + }
  261 + const { data } = await listFeeConfigs(params)
  262 + this.listOweFeeInfo.feeConfigs = data
  263 + } catch (error) {
  264 + console.error('获取收费项失败:', error)
  265 + }
  266 + },
  267 + toggleMoreCondition() {
  268 + this.listOweFeeInfo.moreCondition = !this.listOweFeeInfo.moreCondition
  269 + },
  270 + queryOweFee() {
  271 + this.page.current = 1
  272 + this.loadListOweFeeInfo()
  273 + },
  274 + resetOweFee() {
  275 + this.listOweFeeInfo.conditions = {
  276 + configIds: '',
  277 + payObjType: '',
  278 + num: '',
  279 + billType: '00123',
  280 + floorId: '',
  281 + floorName: '',
  282 + roomNum: '',
  283 + unitId: '',
  284 + roomSubType: '',
  285 + ownerName: '',
  286 + communityId: this.listOweFeeInfo.conditions.communityId
  287 + }
  288 + this.selectedFeeConfigs = []
  289 + this.loadListOweFeeInfo()
  290 + },
  291 + getFeeOweAmount(item, fee) {
  292 + if (!fee.items) return 0
  293 + const found = fee.items.find(tmp => tmp.configId === item.configId)
  294 + return found ? found.amountOwed : 0
  295 + },
  296 + getFeeOweAllAmount(item) {
  297 + let amountOwed = 0.0
  298 + this.listOweFeeInfo.fees.forEach(feeItem => {
  299 + if (!feeItem.items) return
  300 + feeItem.items.forEach(tmp => {
  301 + if (tmp.configId === item.configId) {
  302 + amountOwed += parseFloat(tmp.amountOwed)
  303 + }
  304 + })
  305 + })
  306 + return amountOwed.toFixed(2)
  307 + },
  308 + getAllFeeOweAmount(fee) {
  309 + if (this.listOweFeeInfo.feeConfigNames.length < 1) {
  310 + return fee.amountOwed
  311 + }
  312 + let amountOwed = 0.0
  313 + this.listOweFeeInfo.feeConfigNames.forEach(feeItem => {
  314 + if (!fee.items) return
  315 + fee.items.forEach(item => {
  316 + if (feeItem.configId === item.configId) {
  317 + amountOwed += parseFloat(item.amountOwed)
  318 + }
  319 + })
  320 + })
  321 + return amountOwed.toFixed(2)
  322 + },
  323 + getFeeOweAllAmounts() {
  324 + let amountOwed = 0.0
  325 + this.listOweFeeInfo.fees.forEach(feeItem => {
  326 + const rowAmount = this.getAllFeeOweAmount(feeItem)
  327 + amountOwed += parseFloat(rowAmount)
  328 + })
  329 + return amountOwed.toFixed(2)
  330 + },
  331 + async exportFee() {
  332 + try {
  333 + const params = {
  334 + ...this.listOweFeeInfo.conditions,
  335 + pagePath: 'reportListOweFee',
  336 + configIds: this.listOweFeeInfo.feeConfigNames.map(item => item.configId).join(',')
  337 + }
  338 + await exportData(params)
  339 + this.$message.success(this.$t('listOweFee.exportSuccess'))
  340 + this.$router.push('/pages/property/downloadTempFile?tab=下载中心')
  341 + } catch (error) {
  342 + console.error('导出失败:', error)
  343 + this.$message.error(this.$t('listOweFee.exportError'))
  344 + }
  345 + },
  346 + handleSizeChange(val) {
  347 + this.page.size = val
  348 + this.loadListOweFeeInfo()
  349 + },
  350 + handleCurrentChange(val) {
  351 + this.page.current = val
  352 + this.loadListOweFeeInfo()
  353 + },
  354 + openChooseFloor() {
  355 + this.$refs.searchFloor.open()
  356 + },
  357 + handleChooseFloor(floor) {
  358 + this.listOweFeeInfo.conditions.floorId = floor.floorId
  359 + this.listOweFeeInfo.conditions.floorName = floor.floorName
  360 + this.loadUnits(floor.floorId)
  361 + },
  362 + async loadUnits(floorId) {
  363 + try {
  364 + const params = {
  365 + floorId,
  366 + communityId: this.listOweFeeInfo.conditions.communityId
  367 + }
  368 + const { data } = await queryUnits(params)
  369 + this.listOweFeeInfo.roomUnits = data
  370 + } catch (error) {
  371 + console.error('获取单元信息失败:', error)
  372 + }
  373 + }
  374 + }
  375 +}
  376 +</script>
  377 +
  378 +<style lang="scss" scoped>
  379 +.list-owe-fee-container {
  380 + padding: 20px;
  381 +
  382 + .search-card {
  383 + margin-bottom: 20px;
  384 +
  385 + .search-item {
  386 + margin-bottom: 15px;
  387 + }
  388 + }
  389 +
  390 + .table-card {
  391 + margin-bottom: 20px;
  392 + }
  393 +
  394 + .summary-wrapper {
  395 + margin-top: 20px;
  396 + padding: 15px;
  397 + background-color: #f5f7fa;
  398 + border-radius: 4px;
  399 +
  400 + .summary-item {
  401 + display: flex;
  402 + flex-wrap: wrap;
  403 + align-items: center;
  404 + }
  405 +
  406 + .summary-title {
  407 + font-weight: bold;
  408 + margin-right: 20px;
  409 + }
  410 +
  411 + .summary-content {
  412 + margin-right: 30px;
  413 + margin-bottom: 10px;
  414 + }
  415 + }
  416 +}
  417 +</style>
0 \ No newline at end of file 418 \ No newline at end of file
src/views/fee/meterWaterManageList.vue
@@ -223,7 +223,7 @@ export default { @@ -223,7 +223,7 @@ export default {
223 padding: 20px; 223 padding: 20px;
224 224
225 .tree-container { 225 .tree-container {
226 - height: calc(100vh - 120px); 226 + min-height: 450px;
227 overflow-y: auto; 227 overflow-y: auto;
228 background: #fff; 228 background: #fff;
229 padding: 10px; 229 padding: 10px;
src/views/fee/oweFeeCallableLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + oweFeeCallable: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + ownerName: 'Owner Name',
  7 + ownerNamePlaceholder: 'Please enter owner name',
  8 + feeName: 'Fee Name',
  9 + feeNamePlaceholder: 'Please enter fee name',
  10 + callableWay: 'Callable Way',
  11 + callableWayPlaceholder: 'Please select callable way',
  12 + staffName: 'Staff Name',
  13 + staffNamePlaceholder: 'Please enter staff name',
  14 + state: 'State',
  15 + statePlaceholder: 'Please select state'
  16 + },
  17 + list: {
  18 + title: 'Call Records',
  19 + register: 'Register',
  20 + call: 'Call'
  21 + },
  22 + table: {
  23 + id: 'ID',
  24 + ownerName: 'Owner Name',
  25 + payerObjName: 'Payer Object',
  26 + feeName: 'Fee Name',
  27 + amountdOwed: 'Amount Owed',
  28 + timeRange: 'Time Range',
  29 + callableWayName: 'Callable Way',
  30 + staffName: 'Staff Name',
  31 + stateName: 'State',
  32 + remark: 'Remark',
  33 + createTime: 'Create Time'
  34 + },
  35 + write: {
  36 + title: 'Call Registration',
  37 + roomName: 'Room',
  38 + feeName: 'Call Fees',
  39 + remark: 'Remark',
  40 + remarkPlaceholder: 'Please enter remark',
  41 + success: 'Registration successful',
  42 + error: 'Registration failed'
  43 + },
  44 + delete: {
  45 + title: 'Delete Confirmation',
  46 + confirm: 'Are you sure to delete this call record?',
  47 + success: 'Delete successful',
  48 + error: 'Delete failed'
  49 + },
  50 + fetchError: 'Failed to fetch data',
  51 + selectRoomFirst: 'Please select a room first'
  52 + }
  53 + },
  54 + zh: {
  55 + oweFeeCallable: {
  56 + search: {
  57 + title: '查询条件',
  58 + ownerName: '业主名称',
  59 + ownerNamePlaceholder: '请输入业主名称',
  60 + feeName: '费用名称',
  61 + feeNamePlaceholder: '请输入费用名称',
  62 + callableWay: '催缴方式',
  63 + callableWayPlaceholder: '请选择催缴方式',
  64 + staffName: '催缴人',
  65 + staffNamePlaceholder: '请输入催缴人',
  66 + state: '状态',
  67 + statePlaceholder: '请选择状态'
  68 + },
  69 + list: {
  70 + title: '催缴记录',
  71 + register: '登记',
  72 + call: '催缴'
  73 + },
  74 + table: {
  75 + id: '编号',
  76 + ownerName: '业主名称',
  77 + payerObjName: '付费对象',
  78 + feeName: '费用名称',
  79 + amountdOwed: '催缴金额',
  80 + timeRange: '欠费时间段',
  81 + callableWayName: '催缴方式',
  82 + staffName: '催缴人',
  83 + stateName: '状态',
  84 + remark: '说明',
  85 + createTime: '创建时间'
  86 + },
  87 + write: {
  88 + title: '催缴登记',
  89 + roomName: '登记房屋',
  90 + feeName: '催缴费用',
  91 + remark: '催缴说明',
  92 + remarkPlaceholder: '请输入备注',
  93 + success: '登记成功',
  94 + error: '登记失败'
  95 + },
  96 + delete: {
  97 + title: '删除确认',
  98 + confirm: '确定删除欠费催缴记录吗?',
  99 + success: '删除成功',
  100 + error: '删除失败'
  101 + },
  102 + fetchError: '获取数据失败',
  103 + selectRoomFirst: '请先选择房屋'
  104 + }
  105 + }
  106 +}
0 \ No newline at end of file 107 \ No newline at end of file
src/views/fee/oweFeeCallableList.vue 0 → 100644
  1 +<template>
  2 + <div class="owe-fee-callable-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="4" class="tree-container">
  5 + <room-tree-div ref="roomTree" @selectRoom="handleSelectRoom"></room-tree-div>
  6 + </el-col>
  7 + <el-col :span="20">
  8 + <el-card class="search-card">
  9 + <div slot="header" class="flex justify-between">
  10 + <span>{{ $t('oweFeeCallable.search.title') }}</span>
  11 + </div>
  12 + <el-form :inline="true" :model="searchForm" class="search-form text-left">
  13 + <el-form-item :label="$t('oweFeeCallable.search.ownerName')">
  14 + <el-input v-model="searchForm.ownerName"
  15 + :placeholder="$t('oweFeeCallable.search.ownerNamePlaceholder')"></el-input>
  16 + </el-form-item>
  17 + <el-form-item :label="$t('oweFeeCallable.search.feeName')">
  18 + <el-input v-model="searchForm.feeName"
  19 + :placeholder="$t('oweFeeCallable.search.feeNamePlaceholder')"></el-input>
  20 + </el-form-item>
  21 + <el-form-item :label="$t('oweFeeCallable.search.callableWay')">
  22 + <el-select v-model="searchForm.callableWay"
  23 + :placeholder="$t('oweFeeCallable.search.callableWayPlaceholder')" style="width:100%">
  24 + <el-option label="全部" value=""></el-option>
  25 + <el-option label="微信模板消息" value="WECHAT"></el-option>
  26 + <el-option label="短信" value="SMS"></el-option>
  27 + <el-option label="上门催缴" value="PRINT"></el-option>
  28 + </el-select>
  29 + </el-form-item>
  30 + <el-form-item :label="$t('oweFeeCallable.search.staffName')">
  31 + <el-input v-model="searchForm.staffName"
  32 + :placeholder="$t('oweFeeCallable.search.staffNamePlaceholder')"></el-input>
  33 + </el-form-item>
  34 + <el-form-item :label="$t('oweFeeCallable.search.state')">
  35 + <el-select v-model="searchForm.state" :placeholder="$t('oweFeeCallable.search.statePlaceholder')"
  36 + style="width:100%">
  37 + <el-option label="全部" value=""></el-option>
  38 + <el-option label="待催缴" value="W"></el-option>
  39 + <el-option label="催缴完成" value="C"></el-option>
  40 + <el-option label="催缴失败" value="F"></el-option>
  41 + <el-option label="催缴中" value="D"></el-option>
  42 + </el-select>
  43 + </el-form-item>
  44 + <el-form-item>
  45 + <el-button type="primary" @click="handleSearch">{{ $t('common.search') }}</el-button>
  46 + <el-button @click="handleReset">{{ $t('common.reset') }}</el-button>
  47 + </el-form-item>
  48 + </el-form>
  49 + </el-card>
  50 +
  51 + <el-card class="list-card">
  52 + <div slot="header" class="flex justify-between">
  53 + <span>{{ roomName || $t('oweFeeCallable.list.title') }}</span>
  54 + <div style="float: right;">
  55 + <el-button type="primary" size="small" @click="handleWritePrint">{{ $t('oweFeeCallable.list.register')
  56 + }}</el-button>
  57 + <el-button type="primary" size="small" @click="handleAdd">{{ $t('oweFeeCallable.list.call') }}</el-button>
  58 + </div>
  59 + </div>
  60 + <el-table :data="tableData" border style="width: 100%" v-loading="loading">
  61 + <el-table-column prop="ofcId" :label="$t('oweFeeCallable.table.id')" align="center">
  62 + </el-table-column>
  63 + <el-table-column prop="ownerName" :label="$t('oweFeeCallable.table.ownerName')" align="center">
  64 + </el-table-column>
  65 + <el-table-column prop="payerObjName" :label="$t('oweFeeCallable.table.payerObjName')" align="center">
  66 + </el-table-column>
  67 + <el-table-column prop="feeName" :label="$t('oweFeeCallable.table.feeName')" align="center">
  68 + </el-table-column>
  69 + <el-table-column prop="amountdOwed" :label="$t('oweFeeCallable.table.amountdOwed')" align="center">
  70 + </el-table-column>
  71 + <el-table-column :label="$t('oweFeeCallable.table.timeRange')" align="center">
  72 + <template slot-scope="scope">
  73 + {{ scope.row.startTime }} ~ {{ scope.row.endTime }}
  74 + </template>
  75 + </el-table-column>
  76 + <el-table-column prop="callableWayName" :label="$t('oweFeeCallable.table.callableWayName')" align="center">
  77 + </el-table-column>
  78 + <el-table-column prop="staffName" :label="$t('oweFeeCallable.table.staffName')" align="center">
  79 + </el-table-column>
  80 + <el-table-column prop="stateName" :label="$t('oweFeeCallable.table.stateName')" align="center">
  81 + </el-table-column>
  82 + <el-table-column prop="remark" :label="$t('oweFeeCallable.table.remark')" align="center" width="200">
  83 + <template slot-scope="scope">
  84 + <el-tooltip :content="scope.row.remark" placement="top">
  85 + <div class="text-ellipsis">{{ scope.row.remark }}</div>
  86 + </el-tooltip>
  87 + </template>
  88 + </el-table-column>
  89 + <el-table-column prop="createTime" :label="$t('oweFeeCallable.table.createTime')" align="center">
  90 + </el-table-column>
  91 + <el-table-column :label="$t('common.operation')" align="center" width="150">
  92 + <template slot-scope="scope">
  93 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">{{ $t('common.delete')
  94 + }}</el-button>
  95 + </template>
  96 + </el-table-column>
  97 + </el-table>
  98 + <el-pagination @size-change="handleSizeChange" @current-change="handlePageChange"
  99 + :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size"
  100 + layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" class="pagination">
  101 + </el-pagination>
  102 + </el-card>
  103 + </el-col>
  104 + </el-row>
  105 +
  106 + <write-owe-fee-callable ref="writeOweFeeCallable" @success="handleSuccess"></write-owe-fee-callable>
  107 + <delete-owe-fee-callable ref="deleteOweFeeCallable" @success="handleSuccess"></delete-owe-fee-callable>
  108 + </div>
  109 +</template>
  110 +
  111 +<script>
  112 +import { listOweFeeCallable } from '@/api/fee/oweFeeCallableApi'
  113 +import { getCommunityId } from '@/api/community/communityApi'
  114 +import RoomTreeDiv from '@/components/room/roomTreeDiv'
  115 +import WriteOweFeeCallable from '@/components/fee/writeOweFeeCallable'
  116 +import DeleteOweFeeCallable from '@/components/fee/deleteOweFeeCallable'
  117 +
  118 +export default {
  119 + name: 'OweFeeCallableList',
  120 + components: {
  121 + RoomTreeDiv,
  122 + WriteOweFeeCallable,
  123 + DeleteOweFeeCallable
  124 + },
  125 + data() {
  126 + return {
  127 + loading: false,
  128 + roomName: '',
  129 + roomId: '',
  130 + searchForm: {
  131 + ownerName: '',
  132 + feeName: '',
  133 + callableWay: '',
  134 + staffName: '',
  135 + state: '',
  136 + payerObjId: ''
  137 + },
  138 + tableData: [],
  139 + pagination: {
  140 + current: 1,
  141 + size: 10,
  142 + total: 0
  143 + }
  144 + }
  145 + },
  146 + created() {
  147 + this.communityId = getCommunityId()
  148 + this.getList()
  149 + },
  150 + methods: {
  151 + async getList() {
  152 + try {
  153 + this.loading = true
  154 + const params = {
  155 + page: this.pagination.current,
  156 + row: this.pagination.size,
  157 + communityId: this.communityId,
  158 + ...this.searchForm
  159 + }
  160 + const { data, total } = await listOweFeeCallable(params)
  161 + this.tableData = data
  162 + this.pagination.total = total
  163 + } catch (error) {
  164 + this.$message.error(this.$t('oweFeeCallable.fetchError'))
  165 + } finally {
  166 + this.loading = false
  167 + }
  168 + },
  169 + handleSearch() {
  170 + this.pagination.current = 1
  171 + this.getList()
  172 + },
  173 + handleReset() {
  174 + this.searchForm = {
  175 + ownerName: '',
  176 + feeName: '',
  177 + callableWay: '',
  178 + staffName: '',
  179 + state: '',
  180 + payerObjId: this.searchForm.payerObjId
  181 + }
  182 + this.handleSearch()
  183 + },
  184 + handleSelectRoom({ roomId, roomName }) {
  185 + this.roomId = roomId
  186 + this.roomName = roomName
  187 + this.searchForm.payerObjId = roomId
  188 + this.handleSearch()
  189 + },
  190 + handleWritePrint() {
  191 + if (!this.roomId) {
  192 + this.$message.warning(this.$t('oweFeeCallable.selectRoomFirst'))
  193 + return
  194 + }
  195 + this.$refs.writeOweFeeCallable.open({
  196 + roomId: this.roomId,
  197 + roomName: this.roomName
  198 + })
  199 + },
  200 + handleAdd() {
  201 + this.$router.push('/views/fee/addOweFeeCallable')
  202 + },
  203 + handleDelete(row) {
  204 + this.$refs.deleteOweFeeCallable.open(row)
  205 + },
  206 + handleSuccess() {
  207 + this.getList()
  208 + },
  209 + handleSizeChange(val) {
  210 + this.pagination.size = val
  211 + this.getList()
  212 + },
  213 + handlePageChange(val) {
  214 + this.pagination.current = val
  215 + this.getList()
  216 + }
  217 + }
  218 +}
  219 +</script>
  220 +
  221 +<style lang="scss" scoped>
  222 +.owe-fee-callable-container {
  223 + padding: 20px;
  224 +
  225 + .tree-container {
  226 + min-height: 450px;
  227 + overflow-y: auto;
  228 + background: #fff;
  229 + padding: 10px;
  230 + border-radius: 4px;
  231 + }
  232 +
  233 + .search-card {
  234 + margin-bottom: 20px;
  235 +
  236 + .search-form {
  237 + .el-form-item {
  238 + margin-bottom: 0;
  239 + }
  240 + }
  241 + }
  242 +
  243 + .list-card {
  244 + .pagination {
  245 + margin-top: 20px;
  246 + text-align: right;
  247 + }
  248 + }
  249 +
  250 + .text-ellipsis {
  251 + white-space: nowrap;
  252 + overflow: hidden;
  253 + text-overflow: ellipsis;
  254 + }
  255 +}
  256 +</style>
0 \ No newline at end of file 257 \ No newline at end of file
src/views/fee/payFeeBatchLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + payFeeBatch: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + batchId: 'Please enter batch number',
  7 + state: 'Please select status',
  8 + createUserName: 'Please enter creator'
  9 + },
  10 + list: {
  11 + title: 'Fee Cancellation'
  12 + },
  13 + table: {
  14 + batchId: 'Batch Number',
  15 + createUserName: 'Employee',
  16 + createTime: 'Time',
  17 + remark: 'Cancellation Reason',
  18 + noRemark: 'None',
  19 + stateName: 'Audit Status',
  20 + msg: 'Audit Opinion',
  21 + operation: 'Operation'
  22 + },
  23 + state: {
  24 + normal: 'Normal',
  25 + applyCancel: 'Apply for Cancellation',
  26 + auditPass: 'Audit Passed',
  27 + auditFail: 'Audit Failed'
  28 + },
  29 + button: {
  30 + applyCancel: 'Apply for Cancellation',
  31 + audit: 'Audit'
  32 + },
  33 + message: {
  34 + auditSuccess: 'Audit successful'
  35 + }
  36 + },
  37 + applyDeleteFeeBatch: {
  38 + title: 'Fee Cancellation Application',
  39 + form: {
  40 + batchId: 'Batch Number',
  41 + createUserName: 'Employee',
  42 + createTime: 'Creation Time',
  43 + remark: 'Cancellation Reason'
  44 + },
  45 + rules: {
  46 + remarkRequired: 'Cancellation reason cannot be empty'
  47 + },
  48 + placeholder: {
  49 + remark: 'Please enter the cancellation reason'
  50 + },
  51 + message: {
  52 + success: 'Cancellation application submitted successfully'
  53 + }
  54 + },
  55 + audit: {
  56 + title: 'Audit Information',
  57 + form: {
  58 + state: 'Audit Status',
  59 + remark: 'Reason'
  60 + },
  61 + state: {
  62 + agree: 'Agree',
  63 + reject: 'Reject'
  64 + },
  65 + rules: {
  66 + stateRequired: 'Audit status cannot be empty',
  67 + remarkRequired: 'Reason cannot be empty',
  68 + remarkMaxLength: 'Reason cannot exceed 200 characters'
  69 + },
  70 + placeholder: {
  71 + state: 'Please audit',
  72 + remark: 'Please enter the reason (required)'
  73 + }
  74 + }
  75 + },
  76 + zh: {
  77 + payFeeBatch: {
  78 + search: {
  79 + title: '查询条件',
  80 + batchId: '请输入批次号',
  81 + state: '请选择状态',
  82 + createUserName: '请填写创建员工'
  83 + },
  84 + list: {
  85 + title: '取消费用'
  86 + },
  87 + table: {
  88 + batchId: '批次号',
  89 + createUserName: '员工',
  90 + createTime: '时间',
  91 + remark: '取消原因',
  92 + noRemark: '无',
  93 + stateName: '审核状态',
  94 + msg: '审核意见',
  95 + operation: '操作'
  96 + },
  97 + state: {
  98 + normal: '正常',
  99 + applyCancel: '申请取消',
  100 + auditPass: '审核通过',
  101 + auditFail: '审核失败'
  102 + },
  103 + button: {
  104 + applyCancel: '申请取消',
  105 + audit: '审核'
  106 + },
  107 + message: {
  108 + auditSuccess: '审核成功'
  109 + }
  110 + },
  111 + applyDeleteFeeBatch: {
  112 + title: '费用取消申请',
  113 + form: {
  114 + batchId: '批次号',
  115 + createUserName: '员工',
  116 + createTime: '创建时间',
  117 + remark: '取消原因'
  118 + },
  119 + rules: {
  120 + remarkRequired: '取消原因不能为空'
  121 + },
  122 + placeholder: {
  123 + remark: '请填写取消原因'
  124 + },
  125 + message: {
  126 + success: '取消申请提交成功'
  127 + }
  128 + },
  129 + audit: {
  130 + title: '审核信息',
  131 + form: {
  132 + state: '审核状态',
  133 + remark: '原因'
  134 + },
  135 + state: {
  136 + agree: '同意',
  137 + reject: '拒绝'
  138 + },
  139 + rules: {
  140 + stateRequired: '审核状态不能为空',
  141 + remarkRequired: '原因内容不能为空',
  142 + remarkMaxLength: '原因内容不能超过200字'
  143 + },
  144 + placeholder: {
  145 + state: '请审核',
  146 + remark: '请填写原因(必填)'
  147 + }
  148 + }
  149 + }
  150 +}
0 \ No newline at end of file 151 \ No newline at end of file
src/views/fee/payFeeBatchList.vue 0 → 100644
  1 +<template>
  2 + <div class="pay-fee-batch-container animated fadeInRight">
  3 + <el-row :gutter="20">
  4 + <el-col :span="24">
  5 + <el-card class="box-card">
  6 + <div slot="header" class="flex justify-between">
  7 + <span>{{ $t('payFeeBatch.search.title') }}</span>
  8 + </div>
  9 + <div class="search-content">
  10 + <el-row :gutter="20">
  11 + <el-col :span="6">
  12 + <el-input v-model="payFeeBatchInfo.conditions.batchId" :placeholder="$t('payFeeBatch.search.batchId')"
  13 + clearable />
  14 + </el-col>
  15 + <el-col :span="6">
  16 + <el-select v-model="payFeeBatchInfo.conditions.state" :placeholder="$t('payFeeBatch.search.state')"
  17 + style="width:100%">
  18 + <el-option :label="$t('payFeeBatch.state.normal')" value="2006001" />
  19 + <el-option :label="$t('payFeeBatch.state.applyCancel')" value="2007001" />
  20 + <el-option :label="$t('payFeeBatch.state.auditPass')" value="2008001" />
  21 + <el-option :label="$t('payFeeBatch.state.auditFail')" value="2009001" />
  22 + </el-select>
  23 + </el-col>
  24 + <el-col :span="6">
  25 + <el-input v-model="payFeeBatchInfo.conditions.createUserName"
  26 + :placeholder="$t('payFeeBatch.search.createUserName')" clearable />
  27 + </el-col>
  28 + <el-col :span="6">
  29 + <el-button type="primary" @click="_queryPayFeeBatchMethod">
  30 + <i class="el-icon-search"></i>
  31 + {{ $t('common.search') }}
  32 + </el-button>
  33 + <el-button @click="_resetPayFeeBatchMethod">
  34 + <i class="el-icon-refresh"></i>
  35 + {{ $t('common.reset') }}
  36 + </el-button>
  37 + </el-col>
  38 + </el-row>
  39 + </div>
  40 + </el-card>
  41 + </el-col>
  42 + </el-row>
  43 +
  44 + <el-row :gutter="20" style="margin-top:20px">
  45 + <el-col :span="24">
  46 + <el-card class="box-card">
  47 + <div slot="header" class="flex justify-between">
  48 + <span>{{ $t('payFeeBatch.list.title') }}</span>
  49 + </div>
  50 + <el-table :data="payFeeBatchInfo.payFeeBatchs" border style="width:100%">
  51 + <el-table-column prop="batchId" :label="$t('payFeeBatch.table.batchId')" align="center" />
  52 + <el-table-column prop="createUserName" :label="$t('payFeeBatch.table.createUserName')" align="center" />
  53 + <el-table-column prop="createTime" :label="$t('payFeeBatch.table.createTime')" align="center" />
  54 + <el-table-column prop="remark" :label="$t('payFeeBatch.table.remark')" align="center">
  55 + <template slot-scope="scope">
  56 + {{ scope.row.remark || $t('payFeeBatch.table.noRemark') }}
  57 + </template>
  58 + </el-table-column>
  59 + <el-table-column prop="stateName" :label="$t('payFeeBatch.table.stateName')" align="center" />
  60 + <el-table-column prop="msg" :label="$t('payFeeBatch.table.msg')" align="center" />
  61 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  62 + <template slot-scope="scope">
  63 + <el-button v-if="scope.row.state === '2006001'" size="mini" @click="_openApply(scope.row)">
  64 + {{ $t('payFeeBatch.button.applyCancel') }}
  65 + </el-button>
  66 + <el-button v-if="scope.row.state === '2007001'" size="mini" type="primary"
  67 + @click="_openPayFeeBatchAuditModel(scope.row)">
  68 + {{ $t('payFeeBatch.button.audit') }}
  69 + </el-button>
  70 + </template>
  71 + </el-table-column>
  72 + </el-table>
  73 +
  74 + <el-pagination :current-page.sync="pagination.current" :page-sizes="[10, 20, 30, 50]"
  75 + :page-size="pagination.size" :total="pagination.total" layout="total, sizes, prev, pager, next, jumper"
  76 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  77 + </el-card>
  78 + </el-col>
  79 + </el-row>
  80 +
  81 + <apply-delete-fee-batch ref="applyDeleteFeeBatch" @success="handleSuccess" />
  82 + <audit ref="audit" @success="handleSuccess" />
  83 + </div>
  84 +</template>
  85 +
  86 +<script>
  87 +import { listPayFeeBatch, updatePayFeeBatch } from '@/api/fee/payFeeBatchApi'
  88 +import { getCommunityId } from '@/api/community/communityApi'
  89 +import ApplyDeleteFeeBatch from '@/components/fee/applyDeleteFeeBatch'
  90 +import Audit from '@/components/fee/audit'
  91 +
  92 +export default {
  93 + name: 'PayFeeBatchList',
  94 + components: {
  95 + ApplyDeleteFeeBatch,
  96 + Audit
  97 + },
  98 + data() {
  99 + return {
  100 + payFeeBatchInfo: {
  101 + payFeeBatchs: [],
  102 + conditions: {
  103 + communityId: '',
  104 + state: '',
  105 + batchId: '',
  106 + createUserName: ''
  107 + }
  108 + },
  109 + pagination: {
  110 + current: 1,
  111 + size: 10,
  112 + total: 0
  113 + }
  114 + }
  115 + },
  116 + created() {
  117 + this.payFeeBatchInfo.conditions.communityId = getCommunityId()
  118 + this._listPayFeeBatchs(this.pagination.current, this.pagination.size)
  119 + },
  120 + methods: {
  121 + async _listPayFeeBatchs(page, size) {
  122 + try {
  123 + const params = {
  124 + ...this.payFeeBatchInfo.conditions,
  125 + page,
  126 + row: size
  127 + }
  128 + const { data, total } = await listPayFeeBatch(params)
  129 + this.payFeeBatchInfo.payFeeBatchs = data
  130 + this.pagination.total = total
  131 + } catch (error) {
  132 + console.error('获取数据失败:', error)
  133 + }
  134 + },
  135 + _queryPayFeeBatchMethod() {
  136 + this.pagination.current = 1
  137 + this._listPayFeeBatchs(this.pagination.current, this.pagination.size)
  138 + },
  139 + _resetPayFeeBatchMethod() {
  140 + this.payFeeBatchInfo.conditions = {
  141 + ...this.payFeeBatchInfo.conditions,
  142 + state: '',
  143 + batchId: '',
  144 + createUserName: ''
  145 + }
  146 + this._queryPayFeeBatchMethod()
  147 + },
  148 + _openApply(payFee) {
  149 + this.$refs.applyDeleteFeeBatch.open(payFee)
  150 + },
  151 + _openPayFeeBatchAuditModel(payFee) {
  152 + this.payFeeBatchInfo.payFeeBatch = payFee
  153 + this.$refs.audit.open()
  154 + },
  155 + async _auditPayFeeBatchState(auditInfo) {
  156 + try {
  157 + const payFeeBatch = {
  158 + ...this.payFeeBatchInfo.payFeeBatch,
  159 + state: auditInfo.state,
  160 + msg: auditInfo.remark
  161 + }
  162 + await updatePayFeeBatch(payFeeBatch)
  163 + this.$message.success(this.$t('payFeeBatch.message.auditSuccess'))
  164 + this._listPayFeeBatchs(this.pagination.current, this.pagination.size)
  165 + } catch (error) {
  166 + console.error('审核失败:', error)
  167 + }
  168 + },
  169 + handleSuccess() {
  170 + this._listPayFeeBatchs(this.pagination.current, this.pagination.size)
  171 + },
  172 + handleSizeChange(val) {
  173 + this.pagination.size = val
  174 + this._listPayFeeBatchs(this.pagination.current, this.pagination.size)
  175 + },
  176 + handleCurrentChange(val) {
  177 + this.pagination.current = val
  178 + this._listPayFeeBatchs(this.pagination.current, this.pagination.size)
  179 + }
  180 + }
  181 +}
  182 +</script>
  183 +
  184 +<style lang="scss" scoped>
  185 +.pay-fee-batch-container {
  186 + padding: 20px;
  187 +
  188 + .search-content {
  189 + margin-bottom: 20px;
  190 + }
  191 +
  192 + .el-pagination {
  193 + margin-top: 20px;
  194 + text-align: right;
  195 + }
  196 +}
  197 +</style>
0 \ No newline at end of file 198 \ No newline at end of file
src/views/fee/staffFeeManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + staffFeeManage: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + userCode: 'Staff Code',
  7 + userCodePlaceholder: 'Please enter staff code',
  8 + startTime: 'Start Time',
  9 + startTimePlaceholder: 'Please select start time',
  10 + endTime: 'End Time',
  11 + endTimePlaceholder: 'Please select end time'
  12 + },
  13 + list: {
  14 + title: 'Staff Fee Management'
  15 + },
  16 + table: {
  17 + userCode: 'Staff Code',
  18 + userName: 'Staff Name',
  19 + receivableAmount: 'Receivable Amount',
  20 + receivedAmount: 'Received Amount',
  21 + yuan: 'Yuan'
  22 + },
  23 + fetchError: 'Failed to fetch staff fee data'
  24 + }
  25 + },
  26 + zh: {
  27 + staffFeeManage: {
  28 + search: {
  29 + title: '查询条件',
  30 + userCode: '员工编码',
  31 + userCodePlaceholder: '请填写员工编码',
  32 + startTime: '开始时间',
  33 + startTimePlaceholder: '请选择开始时间',
  34 + endTime: '结束时间',
  35 + endTimePlaceholder: '请选择结束时间'
  36 + },
  37 + list: {
  38 + title: '员工收费管理'
  39 + },
  40 + table: {
  41 + userCode: '员工编码',
  42 + userName: '员工名称',
  43 + receivableAmount: '应收金额',
  44 + receivedAmount: '实收金额',
  45 + yuan: '元'
  46 + },
  47 + fetchError: '获取员工收费数据失败'
  48 + }
  49 + }
  50 +}
0 \ No newline at end of file 51 \ No newline at end of file
src/views/fee/staffFeeManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="staff-fee-manage-container animated fadeInRight">
  3 + <el-row :gutter="20" class="mb-20">
  4 + <el-col :span="24">
  5 + <el-card>
  6 + <div slot="header" class="flex justify-between">
  7 + <span>{{ $t('staffFeeManage.search.title') }}</span>
  8 + </div>
  9 + <el-form :inline="true" :model="searchForm" class="demo-form-inline">
  10 + <el-form-item :label="$t('staffFeeManage.search.userCode')">
  11 + <el-input
  12 + v-model="searchForm.userCode"
  13 + :placeholder="$t('staffFeeManage.search.userCodePlaceholder')"
  14 + clearable
  15 + />
  16 + </el-form-item>
  17 + <el-form-item :label="$t('staffFeeManage.search.startTime')">
  18 + <el-date-picker
  19 + v-model="searchForm.startTime"
  20 + type="datetime"
  21 + :placeholder="$t('staffFeeManage.search.startTimePlaceholder')"
  22 + value-format="yyyy-MM-dd HH:mm:ss"
  23 + />
  24 + </el-form-item>
  25 + <el-form-item :label="$t('staffFeeManage.search.endTime')">
  26 + <el-date-picker
  27 + v-model="searchForm.endTime"
  28 + type="datetime"
  29 + :placeholder="$t('staffFeeManage.search.endTimePlaceholder')"
  30 + value-format="yyyy-MM-dd HH:mm:ss"
  31 + />
  32 + </el-form-item>
  33 + <el-form-item>
  34 + <el-button type="primary" @click="handleSearch">
  35 + {{ $t('common.search') }}
  36 + </el-button>
  37 + <el-button @click="handleReset">
  38 + {{ $t('common.reset') }}
  39 + </el-button>
  40 + </el-form-item>
  41 + </el-form>
  42 + </el-card>
  43 + </el-col>
  44 + </el-row>
  45 +
  46 + <el-row :gutter="20">
  47 + <el-col :span="24">
  48 + <el-card>
  49 + <div slot="header" class="flex justify-between">
  50 + <span>{{ $t('staffFeeManage.list.title') }}</span>
  51 + <el-button
  52 + type="primary"
  53 + size="small"
  54 + class="float-right"
  55 + @click="handleExport"
  56 + >
  57 + {{ $t('common.export') }}
  58 + </el-button>
  59 + </div>
  60 + <el-table
  61 + v-loading="loading"
  62 + :data="tableData"
  63 + border
  64 + style="width: 100%"
  65 + >
  66 + <el-table-column
  67 + prop="userId"
  68 + :label="$t('staffFeeManage.table.userCode')"
  69 + align="center"
  70 + />
  71 + <el-table-column
  72 + prop="userName"
  73 + :label="$t('staffFeeManage.table.userName')"
  74 + align="center"
  75 + />
  76 + <el-table-column
  77 + prop="receivableAmount"
  78 + :label="$t('staffFeeManage.table.receivableAmount')"
  79 + align="center"
  80 + >
  81 + <template slot-scope="scope">
  82 + {{ scope.row.receivableAmount }} {{ $t('staffFeeManage.table.yuan') }}
  83 + </template>
  84 + </el-table-column>
  85 + <el-table-column
  86 + prop="receivedAmount"
  87 + :label="$t('staffFeeManage.table.receivedAmount')"
  88 + align="center"
  89 + >
  90 + <template slot-scope="scope">
  91 + {{ scope.row.receivedAmount }} {{ $t('staffFeeManage.table.yuan') }}
  92 + </template>
  93 + </el-table-column>
  94 + </el-table>
  95 + <el-pagination
  96 + :current-page="pagination.current"
  97 + :page-sizes="[10, 20, 30, 50]"
  98 + :page-size="pagination.size"
  99 + layout="total, sizes, prev, pager, next, jumper"
  100 + :total="pagination.total"
  101 + @size-change="handleSizeChange"
  102 + @current-change="handleCurrentChange"
  103 + />
  104 + </el-card>
  105 + </el-col>
  106 + </el-row>
  107 + </div>
  108 +</template>
  109 +
  110 +<script>
  111 +import { getStaffFeeList } from '@/api/fee/staffFeeManageApi'
  112 +import { getCommunityId } from '@/api/community/communityApi'
  113 +
  114 +export default {
  115 + name: 'StaffFeeManageList',
  116 + data() {
  117 + return {
  118 + loading: false,
  119 + searchForm: {
  120 + communityId: '',
  121 + userCode: '',
  122 + startTime: '',
  123 + endTime: ''
  124 + },
  125 + tableData: [],
  126 + pagination: {
  127 + current: 1,
  128 + size: 10,
  129 + total: 0
  130 + }
  131 + }
  132 + },
  133 + created() {
  134 + this.searchForm.communityId = getCommunityId()
  135 + this.getList()
  136 + },
  137 + methods: {
  138 + async getList() {
  139 + try {
  140 + this.loading = true
  141 + const params = {
  142 + page: this.pagination.current,
  143 + row: this.pagination.size,
  144 + ...this.searchForm
  145 + }
  146 + const { data, total } = await getStaffFeeList(params)
  147 + this.tableData = data
  148 + this.pagination.total = total
  149 + } catch (error) {
  150 + this.$message.error(this.$t('staffFeeManage.fetchError'))
  151 + } finally {
  152 + this.loading = false
  153 + }
  154 + },
  155 + handleSearch() {
  156 + this.pagination.current = 1
  157 + this.getList()
  158 + },
  159 + handleReset() {
  160 + this.searchForm = {
  161 + communityId: getCommunityId(),
  162 + userCode: '',
  163 + startTime: '',
  164 + endTime: ''
  165 + }
  166 + this.pagination.current = 1
  167 + this.getList()
  168 + },
  169 + handleExport() {
  170 + // 导出逻辑
  171 + },
  172 + handleSizeChange(val) {
  173 + this.pagination.size = val
  174 + this.getList()
  175 + },
  176 + handleCurrentChange(val) {
  177 + this.pagination.current = val
  178 + this.getList()
  179 + }
  180 + }
  181 +}
  182 +</script>
  183 +
  184 +<style lang="scss" scoped>
  185 +.staff-fee-manage-container {
  186 + padding: 20px;
  187 + .mb-20 {
  188 + margin-bottom: 20px;
  189 + }
  190 + .float-right {
  191 + float: right;
  192 + }
  193 +}
  194 +</style>
0 \ No newline at end of file 195 \ No newline at end of file