Commit a0f584aaadf595efc6081c9de6d1fb8036413ced

Authored by wuxw
1 parent f99ceb4f

费用公摊开发完成

src/api/fee/floorShareApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询楼栋公摊列表
  5 +export function listFloorShareMeter(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/meter.listFloorShareMeter',
  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 saveFloorShareMeter(data) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/meter.saveFloorShareMeter',
  28 + method: 'post',
  29 + data: {
  30 + ...data,
  31 + communityId: getCommunityId()
  32 + }
  33 + }).then(response => {
  34 + const res = response.data
  35 + resolve(res)
  36 + }).catch(error => {
  37 + reject(error)
  38 + })
  39 + })
  40 +}
  41 +
  42 +// 修改楼栋公摊
  43 +export function updateFloorShareMeter(data) {
  44 + return new Promise((resolve, reject) => {
  45 + request({
  46 + url: '/meter.updateFloorShareMeter',
  47 + method: 'post',
  48 + data: {
  49 + ...data,
  50 + communityId: getCommunityId()
  51 + }
  52 + }).then(response => {
  53 + const res = response.data
  54 + resolve(res)
  55 + }).catch(error => {
  56 + reject(error)
  57 + })
  58 + })
  59 +}
  60 +
  61 +// 删除楼栋公摊
  62 +export function deleteFloorShareMeter(data) {
  63 + return new Promise((resolve, reject) => {
  64 + request({
  65 + url: '/meter.deleteFloorShareMeter',
  66 + method: 'post',
  67 + data: {
  68 + ...data,
  69 + communityId: getCommunityId()
  70 + }
  71 + }).then(response => {
  72 + const res = response.data
  73 + resolve(res)
  74 + }).catch(error => {
  75 + reject(error)
  76 + })
  77 + })
  78 +}
  79 +
  80 +// 查询楼栋列表
  81 +export function queryFloors(params) {
  82 + return new Promise((resolve, reject) => {
  83 + request({
  84 + url: '/floor.queryFloors',
  85 + method: 'get',
  86 + params: {
  87 + ...params,
  88 + communityId: getCommunityId()
  89 + }
  90 + }).then(response => {
  91 + const res = response.data
  92 + resolve(res)
  93 + }).catch(error => {
  94 + reject(error)
  95 + })
  96 + })
  97 +}
  98 +
  99 +// 查询费用项列表
  100 +export function listFeeConfigs(params) {
  101 + return new Promise((resolve, reject) => {
  102 + request({
  103 + url: '/feeConfig.listFeeConfigs',
  104 + method: 'get',
  105 + params: {
  106 + ...params,
  107 + communityId: getCommunityId()
  108 + }
  109 + }).then(response => {
  110 + const res = response.data
  111 + resolve(res)
  112 + }).catch(error => {
  113 + reject(error)
  114 + })
  115 + })
  116 +}
0 117 \ No newline at end of file
... ...
src/api/fee/roomFeeImportApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +/**
  5 + * 查询费用导入列表
  6 + * @param {Object} params 查询参数
  7 + * @returns {Promise} Promise对象
  8 + */
  9 +export function queryImportFee(params) {
  10 + return new Promise((resolve, reject) => {
  11 + const queryParams = {
  12 + ...params,
  13 + communityId: getCommunityId()
  14 + }
  15 + request({
  16 + url: '/importFee/queryImportFee',
  17 + method: 'get',
  18 + params: queryParams
  19 + }).then(response => {
  20 + const res = response.data
  21 + resolve({
  22 + data: res.data,
  23 + total: res.total,
  24 + records: res.records
  25 + })
  26 + }).catch(error => {
  27 + reject(error)
  28 + })
  29 + })
  30 +}
  31 +
  32 +/**
  33 + * 导入费用数据
  34 + * @param {FormData} formData 表单数据
  35 + * @returns {Promise} Promise对象
  36 + */
  37 +export function importRoomFeeData(formData) {
  38 + return new Promise((resolve, reject) => {
  39 + request({
  40 + url: '/assetImport/importData',
  41 + method: 'post',
  42 + data: formData,
  43 + headers: {
  44 + 'Content-Type': 'multipart/form-data'
  45 + }
  46 + }).then(response => {
  47 + const res = response.data
  48 + if (res.code === 0) {
  49 + resolve(res.data)
  50 + } else {
  51 + reject(new Error(res.msg))
  52 + }
  53 + }).catch(error => {
  54 + reject(error)
  55 + })
  56 + })
  57 +}
  58 +
  59 +/**
  60 + * 下载导入模板
  61 + * @param {Object} params 查询参数
  62 + * @returns {Promise} Promise对象
  63 + */
  64 +export function downloadImportTemplate(params) {
  65 + return new Promise((resolve, reject) => {
  66 + request({
  67 + url: '/export.exportData',
  68 + method: 'get',
  69 + params: {
  70 + ...params,
  71 + communityId: getCommunityId()
  72 + }
  73 + }).then(response => {
  74 + const res = response.data
  75 + if (res.code === 0) {
  76 + resolve(res.data)
  77 + } else {
  78 + reject(new Error(res.msg))
  79 + }
  80 + }).catch(error => {
  81 + reject(error)
  82 + })
  83 + })
  84 +}
  85 +
  86 +/**
  87 + * 获取费用导入详情
  88 + * @param {String} importFeeId 导入ID
  89 + * @returns {Promise} Promise对象
  90 + */
  91 +export function getImportFeeDetail(importFeeId) {
  92 + return new Promise((resolve, reject) => {
  93 + request({
  94 + url: '/importFee/getImportFeeDetail',
  95 + method: 'get',
  96 + params: {
  97 + importFeeId,
  98 + communityId: getCommunityId()
  99 + }
  100 + }).then(response => {
  101 + const res = response.data
  102 + if (res.code === 0) {
  103 + resolve(res.data)
  104 + } else {
  105 + reject(new Error(res.msg))
  106 + }
  107 + }).catch(error => {
  108 + reject(error)
  109 + })
  110 + })
  111 +}
0 112 \ No newline at end of file
... ...
src/api/fee/shareReadingApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取公摊抄表列表
  5 +export function listFloorShareReading(params) {
  6 + return new Promise((resolve, reject) => {
  7 + const defaultParams = {
  8 + communityId: getCommunityId(),
  9 + page: 1,
  10 + row: 10
  11 + }
  12 + request({
  13 + url: '/meter.listFloorShareReading',
  14 + method: 'get',
  15 + params: { ...defaultParams, ...params }
  16 + }).then(response => {
  17 + const res = response.data
  18 + resolve({
  19 + data: res.data,
  20 + total: res.total
  21 + })
  22 + }).catch(error => {
  23 + reject(error)
  24 + })
  25 + })
  26 +}
  27 +
  28 +// 获取公摊表列表
  29 +export function listFloorShareMeter(params) {
  30 + return new Promise((resolve, reject) => {
  31 + const defaultParams = {
  32 + communityId: getCommunityId(),
  33 + page: 1,
  34 + row: 300
  35 + }
  36 + request({
  37 + url: '/meter.listFloorShareMeter',
  38 + method: 'get',
  39 + params: { ...defaultParams, ...params }
  40 + }).then(response => {
  41 + const res = response.data
  42 + resolve({
  43 + data: res.data,
  44 + total: res.total
  45 + })
  46 + }).catch(error => {
  47 + reject(error)
  48 + })
  49 + })
  50 +}
  51 +
  52 +// 添加公摊抄表
  53 +export function saveFloorShareReading(data) {
  54 + return new Promise((resolve, reject) => {
  55 + request({
  56 + url: '/meter.saveFloorShareReading',
  57 + method: 'post',
  58 + data: {
  59 + ...data,
  60 + communityId: getCommunityId()
  61 + }
  62 + }).then(response => {
  63 + const res = response.data
  64 + if (res.code === 0) {
  65 + resolve(res)
  66 + } else {
  67 + reject(new Error(res.msg))
  68 + }
  69 + }).catch(error => {
  70 + reject(error)
  71 + })
  72 + })
  73 +}
  74 +
  75 +// 审核公摊抄表
  76 +export function auditFloorShareReading(data) {
  77 + return new Promise((resolve, reject) => {
  78 + request({
  79 + url: '/meter.auditFloorShareReading',
  80 + method: 'post',
  81 + data: {
  82 + ...data,
  83 + communityId: getCommunityId()
  84 + }
  85 + }).then(response => {
  86 + const res = response.data
  87 + if (res.code === 0) {
  88 + resolve(res)
  89 + } else {
  90 + reject(new Error(res.msg))
  91 + }
  92 + }).catch(error => {
  93 + reject(error)
  94 + })
  95 + })
  96 +}
  97 +
  98 +// 删除公摊抄表
  99 +export function deleteFloorShareReading(data) {
  100 + return new Promise((resolve, reject) => {
  101 + request({
  102 + url: '/meter.deleteFloorShareReading',
  103 + method: 'post',
  104 + data: {
  105 + ...data,
  106 + communityId: getCommunityId()
  107 + }
  108 + }).then(response => {
  109 + const res = response.data
  110 + if (res.code === 0) {
  111 + resolve(res)
  112 + } else {
  113 + reject(new Error(res.msg))
  114 + }
  115 + }).catch(error => {
  116 + reject(error)
  117 + })
  118 + })
  119 +}
0 120 \ No newline at end of file
... ...
src/api/fee/shareReadingFeeApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +/**
  4 + * 获取公摊费用列表
  5 + * @param {Object} params 查询参数
  6 + * @returns {Promise} Promise对象
  7 + */
  8 +export function listFloorShareFee(params) {
  9 + return new Promise((resolve, reject) => {
  10 + request({
  11 + url: '/meter.listFloorShareFee',
  12 + method: 'get',
  13 + params
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve(res)
  17 + }).catch(error => {
  18 + reject(error)
  19 + })
  20 + })
  21 +}
  22 +
  23 +/**
  24 + * 获取社区ID
  25 + * @returns {Promise} Promise对象
  26 + */
  27 +export function getCommunityId() {
  28 + return new Promise((resolve, reject) => {
  29 + request({
  30 + url: '/community.getCommunityId',
  31 + method: 'get'
  32 + }).then(response => {
  33 + const res = response.data
  34 + resolve(res.communityId)
  35 + }).catch(error => {
  36 + reject(error)
  37 + })
  38 + })
  39 +}
0 40 \ No newline at end of file
... ...
src/components/fee/addFloorShare.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('floorShare.addTitle')" :visible.sync="dialogVisible" width="50%" @close="handleClose">
  3 + <el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="right">
  4 + <el-form-item :label="$t('floorShare.floor')" prop="floorNum">
  5 + <el-input v-model="form.floorNum" disabled />
  6 + </el-form-item>
  7 +
  8 + <el-form-item :label="$t('floorShare.meterType')" prop="meterType">
  9 + <el-select v-model="form.meterType" style="width:100%">
  10 + <el-option v-for="item in meterTypes" :key="item.value" :label="item.label" :value="item.value" />
  11 + </el-select>
  12 + </el-form-item>
  13 +
  14 + <el-form-item :label="$t('floorShare.meterNum')">
  15 + <el-input v-model="form.meterNum" :placeholder="$t('floorShare.meterNumPlaceholder')" />
  16 + <span class="tip-text">{{ $t('floorShare.meterNumTip') }}</span>
  17 + </el-form-item>
  18 +
  19 + <el-form-item :label="$t('floorShare.feeItem')" prop="configId">
  20 + <el-select v-model="form.configId" style="width:100%">
  21 + <el-option v-for="item in feeConfigs" :key="item.configId" :label="item.feeName" :value="item.configId" />
  22 + </el-select>
  23 + </el-form-item>
  24 +
  25 + <el-form-item :label="$t('floorShare.feePrice')" prop="sharePrice">
  26 + <el-input v-model.number="form.sharePrice" type="number" />
  27 + </el-form-item>
  28 +
  29 + <el-form-item :label="$t('floorShare.shareType')" prop="shareType">
  30 + <el-select v-model="form.shareType" style="width:100%" @change="handleShareTypeChange">
  31 + <el-option v-for="item in shareTypes" :key="item.value" :label="item.label" :value="item.value" />
  32 + </el-select>
  33 + </el-form-item>
  34 +
  35 + <el-form-item v-if="form.shareType === '3003'" :label="$t('floorShare.formula')" prop="formulaValue">
  36 + <el-input v-model="form.formulaValue" />
  37 + </el-form-item>
  38 + </el-form>
  39 +
  40 + <div slot="footer" class="dialog-footer">
  41 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  42 + <el-button type="primary" @click="handleSubmit">{{ $t('common.save') }}</el-button>
  43 + </div>
  44 + </el-dialog>
  45 +</template>
  46 +
  47 +<script>
  48 +import { saveFloorShareMeter, listFeeConfigs } from '@/api/fee/floorShareApi'
  49 +import { getCommunityId } from '@/api/community/communityApi'
  50 +
  51 +export default {
  52 + name: 'AddFloorShare',
  53 + data() {
  54 + return {
  55 + dialogVisible: false,
  56 + form: {
  57 + floorId: '',
  58 + floorNum: '',
  59 + meterType: '',
  60 + meterNum: '',
  61 + shareType: '',
  62 + formulaValue: '',
  63 + configId: '',
  64 + sharePrice: '',
  65 + communityId: ''
  66 + },
  67 + feeConfigs: [],
  68 + meterTypes: [
  69 + { value: 'D', label: this.$t('floorShare.electricMeter') },
  70 + { value: 'S', label: this.$t('floorShare.waterMeter') }
  71 + ],
  72 + shareTypes: [
  73 + { value: '1001', label: this.$t('floorShare.byArea') },
  74 + { value: '2002', label: this.$t('floorShare.byHouse') },
  75 + { value: '3003', label: this.$t('floorShare.byFormula') }
  76 + ],
  77 + rules: {
  78 + meterType: [
  79 + { required: true, message: this.$t('floorShare.meterTypeRequired'), trigger: 'blur' }
  80 + ],
  81 + configId: [
  82 + { required: true, message: this.$t('floorShare.feeItemRequired'), trigger: 'blur' }
  83 + ],
  84 + sharePrice: [
  85 + { required: true, message: this.$t('floorShare.feePriceRequired'), trigger: 'blur' },
  86 + { type: 'number', message: this.$t('floorShare.feePriceNumber'), trigger: 'blur' }
  87 + ],
  88 + shareType: [
  89 + { required: true, message: this.$t('floorShare.shareTypeRequired'), trigger: 'blur' }
  90 + ],
  91 + formulaValue: [
  92 + { required: true, message: this.$t('floorShare.formulaRequired'), trigger: 'blur' }
  93 + ]
  94 + }
  95 + }
  96 + },
  97 + methods: {
  98 + open(data) {
  99 + this.form = {
  100 + ...this.form,
  101 + floorId: data.floorId,
  102 + floorNum: data.floorNum,
  103 + communityId: getCommunityId()
  104 + }
  105 + this.loadFeeConfigs()
  106 + this.dialogVisible = true
  107 + this.$nextTick(() => {
  108 + this.$refs.form && this.$refs.form.clearValidate()
  109 + })
  110 + },
  111 + async loadFeeConfigs() {
  112 + try {
  113 + const params = {
  114 + page: 1,
  115 + row: 500,
  116 + communityId: this.form.communityId,
  117 + feeTypeCd: '888800010017',
  118 + isDefault: 'F',
  119 + state: 'Y'
  120 + }
  121 + const { feeConfigs } = await listFeeConfigs(params)
  122 + this.feeConfigs = feeConfigs
  123 + } catch (error) {
  124 + this.$message.error(this.$t('floorShare.fetchFeeConfigError'))
  125 + }
  126 + },
  127 + handleShareTypeChange(value) {
  128 + if (value !== '3003') {
  129 + this.form.formulaValue = ''
  130 + }
  131 + },
  132 + handleSubmit() {
  133 + this.$refs.form.validate(async valid => {
  134 + if (valid) {
  135 + try {
  136 + await saveFloorShareMeter(this.form)
  137 + this.$message.success(this.$t('floorShare.addSuccess'))
  138 + this.dialogVisible = false
  139 + this.$emit('success')
  140 + } catch (error) {
  141 + this.$message.error(error.message || this.$t('floorShare.addFailed'))
  142 + }
  143 + }
  144 + })
  145 + },
  146 + handleClose() {
  147 + this.$refs.form.resetFields()
  148 + }
  149 + }
  150 +}
  151 +</script>
  152 +
  153 +<style lang="scss" scoped>
  154 +.tip-text {
  155 + font-size: 12px;
  156 + color: #909399;
  157 + margin-left: 10px;
  158 +}
  159 +</style>
0 160 \ No newline at end of file
... ...
src/components/fee/addShareReading.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('shareReading.add.title')" :visible.sync="dialogVisible" width="50%" @close="handleClose">
  3 + <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  4 + <el-form-item :label="$t('shareReading.add.meter')" prop="fsmId">
  5 + <el-select v-model="form.fsmId" :placeholder="$t('shareReading.add.selectMeter')" style="width:100%"
  6 + @change="handleMeterChange">
  7 + <el-option v-for="(item, index) in floorShares" :key="index" :label="`${item.floorNum}栋-${item.meterTypeName}`"
  8 + :value="item.fsmId" />
  9 + </el-select>
  10 + </el-form-item>
  11 + <el-form-item :label="$t('shareReading.add.preDegrees')" prop="preDegrees">
  12 + <el-input v-model="form.preDegrees" :placeholder="$t('shareReading.add.inputPreDegrees')" />
  13 + </el-form-item>
  14 + <el-form-item :label="$t('shareReading.add.curDegrees')" prop="curDegrees">
  15 + <el-input v-model="form.curDegrees" :placeholder="$t('shareReading.add.inputCurDegrees')" />
  16 + </el-form-item>
  17 + <el-form-item :label="$t('shareReading.add.preReadingTime')" prop="preReadingTime">
  18 + <el-date-picker v-model="form.preReadingTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
  19 + :placeholder="$t('shareReading.add.selectPreTime')" style="width:100%" />
  20 + </el-form-item>
  21 + <el-form-item :label="$t('shareReading.add.curReadingTime')" prop="curReadingTime">
  22 + <el-date-picker v-model="form.curReadingTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
  23 + :placeholder="$t('shareReading.add.selectCurTime')" style="width:100%" />
  24 + </el-form-item>
  25 + <el-form-item :label="$t('shareReading.add.remark')">
  26 + <el-input v-model="form.remark" type="textarea" :placeholder="$t('shareReading.add.inputRemark')" :rows="3" />
  27 + </el-form-item>
  28 + </el-form>
  29 + <span slot="footer" class="dialog-footer">
  30 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  31 + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button>
  32 + </span>
  33 + </el-dialog>
  34 +</template>
  35 +
  36 +<script>
  37 +import { getCommunityId } from '@/api/community/communityApi'
  38 +import { saveFloorShareReading, listFloorShareMeter } from '@/api/fee/shareReadingApi'
  39 +
  40 +export default {
  41 + name: 'AddShareReading',
  42 + data() {
  43 + return {
  44 + dialogVisible: false,
  45 + floorShares: [],
  46 + form: {
  47 + fsmId: '',
  48 + preDegrees: '',
  49 + curDegrees: '',
  50 + preReadingTime: '',
  51 + curReadingTime: '',
  52 + remark: '',
  53 + communityId: ''
  54 + },
  55 + rules: {
  56 + fsmId: [
  57 + { required: true, message: this.$t('shareReading.validate.selectMeter'), trigger: 'change' }
  58 + ],
  59 + preDegrees: [
  60 + { required: true, message: this.$t('shareReading.validate.inputPreDegrees'), trigger: 'blur' },
  61 + { pattern: /^\d+(\.\d+)?$/, message: this.$t('shareReading.validate.number'), trigger: 'blur' }
  62 + ],
  63 + curDegrees: [
  64 + { required: true, message: this.$t('shareReading.validate.inputCurDegrees'), trigger: 'blur' },
  65 + { pattern: /^\d+(\.\d+)?$/, message: this.$t('shareReading.validate.number'), trigger: 'blur' }
  66 + ],
  67 + preReadingTime: [
  68 + { required: true, message: this.$t('shareReading.validate.selectPreTime'), trigger: 'change' }
  69 + ],
  70 + curReadingTime: [
  71 + { required: true, message: this.$t('shareReading.validate.selectCurTime'), trigger: 'change' }
  72 + ]
  73 + }
  74 + }
  75 + },
  76 + methods: {
  77 + open() {
  78 + this.dialogVisible = true
  79 + this.getFloorShares()
  80 + },
  81 + async getFloorShares() {
  82 + try {
  83 + const params = {
  84 + page: 1,
  85 + row: 300,
  86 + communityId: getCommunityId()
  87 + }
  88 + const { data } = await listFloorShareMeter(params)
  89 + this.floorShares = data
  90 + } catch (error) {
  91 + console.error('获取公摊表列表失败:', error)
  92 + }
  93 + },
  94 + handleMeterChange(val) {
  95 + const selectedMeter = this.floorShares.find(item => item.fsmId === val)
  96 + if (selectedMeter) {
  97 + this.form.preDegrees = selectedMeter.curDegree
  98 + this.form.preReadingTime = selectedMeter.curReadingTime
  99 + }
  100 + },
  101 + handleSubmit() {
  102 + this.$refs.form.validate(async valid => {
  103 + if (valid) {
  104 + try {
  105 + this.form.communityId = getCommunityId()
  106 + await saveFloorShareReading(this.form)
  107 + this.$message.success(this.$t('shareReading.message.addSuccess'))
  108 + this.dialogVisible = false
  109 + this.$emit('success')
  110 + } catch (error) {
  111 + console.error('添加公摊抄表失败:', error)
  112 + }
  113 + }
  114 + })
  115 + },
  116 + handleClose() {
  117 + this.$refs.form.resetFields()
  118 + this.form = {
  119 + fsmId: '',
  120 + preDegrees: '',
  121 + curDegrees: '',
  122 + preReadingTime: '',
  123 + curReadingTime: '',
  124 + remark: '',
  125 + communityId: ''
  126 + }
  127 + }
  128 + }
  129 +}
  130 +</script>
0 131 \ No newline at end of file
... ...
src/components/fee/auditShareReading.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('shareReading.audit.title')"
  4 + :visible.sync="dialogVisible"
  5 + width="40%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  9 + <el-form-item :label="$t('shareReading.audit.status')" prop="state">
  10 + <el-select
  11 + v-model="form.state"
  12 + :placeholder="$t('shareReading.audit.selectStatus')"
  13 + style="width:100%"
  14 + >
  15 + <el-option
  16 + :label="$t('shareReading.status.passed')"
  17 + value="C"
  18 + />
  19 + <el-option
  20 + :label="$t('shareReading.status.rejected')"
  21 + value="F"
  22 + />
  23 + </el-select>
  24 + </el-form-item>
  25 + <el-form-item :label="$t('shareReading.audit.remark')">
  26 + <el-input
  27 + v-model="form.auditRemark"
  28 + type="textarea"
  29 + :placeholder="$t('shareReading.audit.inputRemark')"
  30 + :rows="3"
  31 + />
  32 + </el-form-item>
  33 + </el-form>
  34 + <span slot="footer" class="dialog-footer">
  35 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  36 + <el-button type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</el-button>
  37 + </span>
  38 + </el-dialog>
  39 +</template>
  40 +
  41 +<script>
  42 +import { getCommunityId } from '@/api/community/communityApi'
  43 +import { auditFloorShareReading } from '@/api/fee/shareReadingApi'
  44 +
  45 +export default {
  46 + name: 'AuditShareReading',
  47 + data() {
  48 + return {
  49 + dialogVisible: false,
  50 + form: {
  51 + readingId: '',
  52 + state: '',
  53 + auditRemark: '',
  54 + communityId: ''
  55 + },
  56 + rules: {
  57 + state: [
  58 + { required: true, message: this.$t('shareReading.validate.selectStatus'), trigger: 'change' }
  59 + ]
  60 + }
  61 + }
  62 + },
  63 + methods: {
  64 + open(data) {
  65 + this.dialogVisible = true
  66 + this.form.readingId = data.readingId
  67 + this.form.communityId = getCommunityId()
  68 + },
  69 + handleSubmit() {
  70 + this.$refs.form.validate(async valid => {
  71 + if (valid) {
  72 + try {
  73 + await auditFloorShareReading(this.form)
  74 + this.$message.success(this.$t('shareReading.message.auditSuccess'))
  75 + this.dialogVisible = false
  76 + this.$emit('success')
  77 + } catch (error) {
  78 + console.error('审核公摊抄表失败:', error)
  79 + }
  80 + }
  81 + })
  82 + },
  83 + handleClose() {
  84 + this.$refs.form.resetFields()
  85 + this.form = {
  86 + readingId: '',
  87 + state: '',
  88 + auditRemark: '',
  89 + communityId: ''
  90 + }
  91 + }
  92 + }
  93 +}
  94 +</script>
0 95 \ No newline at end of file
... ...
src/components/fee/deleteFloorShare.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('floorShare.deleteConfirm')"
  4 + :visible.sync="dialogVisible"
  5 + width="30%"
  6 + center
  7 + >
  8 + <div class="text-center">
  9 + <p>{{ $t('floorShare.deleteMessage') }}</p>
  10 + </div>
  11 + <div slot="footer" class="dialog-footer">
  12 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  13 + <el-button type="primary" @click="handleConfirm" :loading="loading">{{ $t('common.confirm') }}</el-button>
  14 + </div>
  15 + </el-dialog>
  16 +</template>
  17 +
  18 +<script>
  19 +import { deleteFloorShareMeter } from '@/api/fee/floorShareApi'
  20 +import { getCommunityId } from '@/api/community/communityApi'
  21 +
  22 +export default {
  23 + name: 'DeleteFloorShare',
  24 + data() {
  25 + return {
  26 + dialogVisible: false,
  27 + loading: false,
  28 + form: {
  29 + fsmId: '',
  30 + communityId: ''
  31 + }
  32 + }
  33 + },
  34 + methods: {
  35 + open(data) {
  36 + this.form = {
  37 + fsmId: data.fsmId,
  38 + communityId: getCommunityId()
  39 + }
  40 + this.dialogVisible = true
  41 + },
  42 + async handleConfirm() {
  43 + this.loading = true
  44 + try {
  45 + await deleteFloorShareMeter(this.form)
  46 + this.$message.success(this.$t('floorShare.deleteSuccess'))
  47 + this.dialogVisible = false
  48 + this.$emit('success')
  49 + } catch (error) {
  50 + this.$message.error(error.message || this.$t('floorShare.deleteFailed'))
  51 + } finally {
  52 + this.loading = false
  53 + }
  54 + }
  55 + }
  56 +}
  57 +</script>
  58 +
  59 +<style lang="scss" scoped>
  60 +.text-center {
  61 + text-align: center;
  62 + font-size: 16px;
  63 + margin-bottom: 20px;
  64 +}
  65 +
  66 +.dialog-footer {
  67 + text-align: center;
  68 +}
  69 +</style>
0 70 \ No newline at end of file
... ...
src/components/fee/deleteShareReading.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('shareReading.delete.title')"
  4 + :visible.sync="dialogVisible"
  5 + width="30%"
  6 + @close="handleClose"
  7 + >
  8 + <div class="text-center">
  9 + <p>{{ $t('shareReading.delete.confirm') }}</p>
  10 + </div>
  11 + <span slot="footer" class="dialog-footer">
  12 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  13 + <el-button type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</el-button>
  14 + </span>
  15 + </el-dialog>
  16 +</template>
  17 +
  18 +<script>
  19 +import { getCommunityId } from '@/api/community/communityApi'
  20 +import { deleteFloorShareReading } from '@/api/fee/shareReadingApi'
  21 +
  22 +export default {
  23 + name: 'DeleteShareReading',
  24 + data() {
  25 + return {
  26 + dialogVisible: false,
  27 + readingId: '',
  28 + communityId: ''
  29 + }
  30 + },
  31 + methods: {
  32 + open(data) {
  33 + this.dialogVisible = true
  34 + this.readingId = data.readingId
  35 + this.communityId = getCommunityId()
  36 + },
  37 + async handleConfirm() {
  38 + try {
  39 + await deleteFloorShareReading({
  40 + readingId: this.readingId,
  41 + communityId: this.communityId
  42 + })
  43 + this.$message.success(this.$t('shareReading.message.deleteSuccess'))
  44 + this.dialogVisible = false
  45 + this.$emit('success')
  46 + } catch (error) {
  47 + console.error('删除公摊抄表失败:', error)
  48 + }
  49 + },
  50 + handleClose() {
  51 + this.readingId = ''
  52 + this.communityId = ''
  53 + }
  54 + }
  55 +}
  56 +</script>
0 57 \ No newline at end of file
... ...
src/components/fee/editFloorShare.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('floorShare.editTitle')" :visible.sync="dialogVisible" width="50%" @close="handleClose">
  3 + <el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="right">
  4 + <el-form-item :label="$t('floorShare.floor')" prop="floorNum">
  5 + <el-input v-model="form.floorNum" disabled />
  6 + </el-form-item>
  7 +
  8 + <el-form-item :label="$t('floorShare.meterType')" prop="meterType">
  9 + <el-select v-model="form.meterType" style="width:100%">
  10 + <el-option v-for="item in meterTypes" :key="item.value" :label="item.label" :value="item.value" />
  11 + </el-select>
  12 + </el-form-item>
  13 +
  14 + <el-form-item :label="$t('floorShare.meterNum')">
  15 + <el-input v-model="form.meterNum" :placeholder="$t('floorShare.meterNumPlaceholder')" />
  16 + </el-form-item>
  17 +
  18 + <el-form-item :label="$t('floorShare.feeItem')" prop="configId">
  19 + <el-select v-model="form.configId" style="width:100%">
  20 + <el-option v-for="item in feeConfigs" :key="item.configId" :label="item.feeName" :value="item.configId" />
  21 + </el-select>
  22 + </el-form-item>
  23 +
  24 + <el-form-item :label="$t('floorShare.feePrice')" prop="sharePrice">
  25 + <el-input v-model.number="form.sharePrice" type="number" />
  26 + </el-form-item>
  27 +
  28 + <el-form-item :label="$t('floorShare.shareType')" prop="shareType">
  29 + <el-select v-model="form.shareType" style="width:100%" @change="handleShareTypeChange">
  30 + <el-option v-for="item in shareTypes" :key="item.value" :label="item.label" :value="item.value" />
  31 + </el-select>
  32 + </el-form-item>
  33 +
  34 + <el-form-item v-if="form.shareType === '3003'" :label="$t('floorShare.formula')" prop="formulaValue">
  35 + <el-input v-model="form.formulaValue" />
  36 + </el-form-item>
  37 + </el-form>
  38 +
  39 + <div slot="footer" class="dialog-footer">
  40 + <el-button @click="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
  41 + <el-button type="primary" @click="handleSubmit">{{ $t('common.save') }}</el-button>
  42 + </div>
  43 + </el-dialog>
  44 +</template>
  45 +
  46 +<script>
  47 +import { updateFloorShareMeter, listFeeConfigs } from '@/api/fee/floorShareApi'
  48 +import { getCommunityId } from '@/api/community/communityApi'
  49 +
  50 +export default {
  51 + name: 'EditFloorShare',
  52 + data() {
  53 + return {
  54 + dialogVisible: false,
  55 + form: {
  56 + fsmId: '',
  57 + floorId: '',
  58 + floorNum: '',
  59 + meterType: '',
  60 + meterNum: '',
  61 + shareType: '',
  62 + formulaValue: '',
  63 + configId: '',
  64 + sharePrice: '',
  65 + communityId: ''
  66 + },
  67 + feeConfigs: [],
  68 + meterTypes: [
  69 + { value: 'D', label: this.$t('floorShare.electricMeter') },
  70 + { value: 'S', label: this.$t('floorShare.waterMeter') }
  71 + ],
  72 + shareTypes: [
  73 + { value: '1001', label: this.$t('floorShare.byArea') },
  74 + { value: '2002', label: this.$t('floorShare.byHouse') },
  75 + { value: '3003', label: this.$t('floorShare.byFormula') }
  76 + ],
  77 + rules: {
  78 + meterType: [
  79 + { required: true, message: this.$t('floorShare.meterTypeRequired'), trigger: 'blur' }
  80 + ],
  81 + configId: [
  82 + { required: true, message: this.$t('floorShare.feeItemRequired'), trigger: 'blur' }
  83 + ],
  84 + sharePrice: [
  85 + { required: true, message: this.$t('floorShare.feePriceRequired'), trigger: 'blur' },
  86 + ],
  87 + shareType: [
  88 + { required: true, message: this.$t('floorShare.shareTypeRequired'), trigger: 'blur' }
  89 + ],
  90 + formulaValue: [
  91 + { required: true, message: this.$t('floorShare.formulaRequired'), trigger: 'blur' }
  92 + ],
  93 + fsmId: [
  94 + { required: true, message: this.$t('floorShare.idRequired'), trigger: 'blur' }
  95 + ]
  96 + }
  97 + }
  98 + },
  99 + methods: {
  100 + open(data) {
  101 + this.form = {
  102 + ...this.form,
  103 + ...data,
  104 + communityId: getCommunityId()
  105 + }
  106 + this.loadFeeConfigs()
  107 + this.dialogVisible = true
  108 + this.$nextTick(() => {
  109 + this.$refs.form && this.$refs.form.clearValidate()
  110 + })
  111 + },
  112 + async loadFeeConfigs() {
  113 + try {
  114 + const params = {
  115 + page: 1,
  116 + row: 500,
  117 + communityId: this.form.communityId,
  118 + feeTypeCd: '888800010017',
  119 + isDefault: 'F',
  120 + state: 'Y'
  121 + }
  122 + const { feeConfigs } = await listFeeConfigs(params)
  123 + this.feeConfigs = feeConfigs
  124 + } catch (error) {
  125 + this.$message.error(this.$t('floorShare.fetchFeeConfigError'))
  126 + }
  127 + },
  128 + handleShareTypeChange(value) {
  129 + if (value !== '3003') {
  130 + this.form.formulaValue = ''
  131 + }
  132 + },
  133 + handleSubmit() {
  134 + this.$refs.form.validate(async valid => {
  135 + if (valid) {
  136 + try {
  137 + await updateFloorShareMeter(this.form)
  138 + this.$message.success(this.$t('floorShare.editSuccess'))
  139 + this.dialogVisible = false
  140 + this.$emit('success')
  141 + } catch (error) {
  142 + this.$message.error(error.message || this.$t('floorShare.editFailed'))
  143 + }
  144 + }
  145 + })
  146 + },
  147 + handleClose() {
  148 + this.$refs.form.resetFields()
  149 + }
  150 + }
  151 +}
  152 +</script>
  153 +
  154 +<style lang="scss" scoped>
  155 +.dialog-footer {
  156 + text-align: right;
  157 +}
  158 +</style>
0 159 \ No newline at end of file
... ...
src/components/fee/importRoomFee.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('importRoomFee.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form
  9 + ref="form"
  10 + :model="form"
  11 + label-width="120px"
  12 + label-position="right"
  13 + >
  14 + <el-form-item
  15 + :label="$t('importRoomFee.form.feeTypeCd')"
  16 + prop="feeTypeCd"
  17 + :rules="[
  18 + { required: true, message: $t('importRoomFee.rules.feeTypeCdRequired'), trigger: 'change' }
  19 + ]"
  20 + >
  21 + <el-select
  22 + v-model="form.feeTypeCd"
  23 + :placeholder="$t('importRoomFee.placeholder.feeTypeCd')"
  24 + style="width:100%"
  25 + @change="handleFeeTypeChange"
  26 + >
  27 + <el-option
  28 + v-for="item in feeTypeCds"
  29 + :key="item.statusCd"
  30 + :label="item.name"
  31 + :value="item.statusCd"
  32 + />
  33 + </el-select>
  34 + </el-form-item>
  35 +
  36 + <el-form-item
  37 + :label="$t('importRoomFee.form.objType')"
  38 + prop="objType"
  39 + :rules="[
  40 + { required: true, message: $t('importRoomFee.rules.objTypeRequired'), trigger: 'change' }
  41 + ]"
  42 + >
  43 + <el-select
  44 + v-model="form.objType"
  45 + :placeholder="$t('importRoomFee.placeholder.objType')"
  46 + style="width:100%"
  47 + >
  48 + <el-option
  49 + :label="$t('importRoomFee.options.room')"
  50 + value="3333"
  51 + />
  52 + <el-option
  53 + :label="$t('importRoomFee.options.parking')"
  54 + value="6666"
  55 + />
  56 + </el-select>
  57 + </el-form-item>
  58 +
  59 + <el-form-item
  60 + :label="$t('importRoomFee.form.file')"
  61 + prop="file"
  62 + :rules="[
  63 + { required: true, message: $t('importRoomFee.rules.fileRequired'), trigger: 'change' }
  64 + ]"
  65 + >
  66 + <el-upload
  67 + ref="upload"
  68 + :auto-upload="false"
  69 + :limit="1"
  70 + :on-change="handleFileChange"
  71 + :on-remove="handleFileRemove"
  72 + accept=".xls,.xlsx"
  73 + >
  74 + <el-button size="small" type="primary">
  75 + {{ $t('importRoomFee.button.selectFile') }}
  76 + </el-button>
  77 + <div slot="tip" class="el-upload__tip">
  78 + {{ $t('importRoomFee.tips.fileFormat') }}
  79 + </div>
  80 + </el-upload>
  81 + </el-form-item>
  82 +
  83 + <el-form-item :label="$t('importRoomFee.form.template')">
  84 + <span>
  85 + {{ $t('importRoomFee.tips.downloadTemplate1') }}
  86 + <el-link type="primary" @click="handleDownloadTemplate">
  87 + {{ $t('importRoomFee.tips.downloadTemplate2') }}
  88 + </el-link>
  89 + {{ $t('importRoomFee.tips.downloadTemplate3') }}
  90 + </span>
  91 + </el-form-item>
  92 + </el-form>
  93 +
  94 + <div slot="footer" class="dialog-footer">
  95 + <el-button @click="visible = false">
  96 + {{ $t('common.cancel') }}
  97 + </el-button>
  98 + <el-button
  99 + type="primary"
  100 + :loading="loading"
  101 + @click="handleSubmit"
  102 + >
  103 + {{ $t('common.confirm') }}
  104 + </el-button>
  105 + </div>
  106 + </el-dialog>
  107 +</template>
  108 +
  109 +<script>
  110 +import { importRoomFeeData, downloadImportTemplate } from '@/api/fee/roomFeeImportApi'
  111 +import { getDict } from '@/api/community/communityApi'
  112 +import { getCommunityId } from '@/api/community/communityApi'
  113 +
  114 +export default {
  115 + name: 'ImportRoomFee',
  116 + data() {
  117 + return {
  118 + visible: false,
  119 + loading: false,
  120 + form: {
  121 + feeTypeCd: '',
  122 + objType: '3333',
  123 + file: null
  124 + },
  125 + feeTypeCds: []
  126 + }
  127 + },
  128 + methods: {
  129 + open() {
  130 + this.visible = true
  131 + this.getDictData()
  132 + },
  133 + async getDictData() {
  134 + try {
  135 + const data = await getDict('pay_fee_config', 'fee_type_cd')
  136 + this.feeTypeCds = data
  137 + } catch (error) {
  138 + console.error('获取字典数据失败:', error)
  139 + }
  140 + },
  141 + handleFeeTypeChange(value) {
  142 + this.form.feeTypeCd = value
  143 + },
  144 + handleFileChange(file) {
  145 + this.form.file = file.raw
  146 + },
  147 + handleFileRemove() {
  148 + this.form.file = null
  149 + },
  150 + async handleSubmit() {
  151 + try {
  152 + this.$refs.form.validate(async valid => {
  153 + if (!valid) return
  154 + if (!this.form.file) {
  155 + this.$message.warning(this.$t('importRoomFee.rules.fileRequired'))
  156 + return
  157 + }
  158 +
  159 + this.loading = true
  160 + const formData = new FormData()
  161 + formData.append('uploadFile', this.form.file)
  162 + formData.append('communityId', getCommunityId())
  163 + formData.append('feeTypeCd', this.form.feeTypeCd)
  164 + formData.append('objType', this.form.objType)
  165 + formData.append('importAdapt', 'importRoomFee')
  166 +
  167 + await importRoomFeeData(formData)
  168 + this.$message.success(this.$t('importRoomFee.message.importSuccess'))
  169 + this.$emit('success')
  170 + this.visible = false
  171 + })
  172 + } catch (error) {
  173 + console.error('导入失败:', error)
  174 + this.$message.error(error.message || this.$t('importRoomFee.message.importFailed'))
  175 + } finally {
  176 + this.loading = false
  177 + }
  178 + },
  179 + async handleDownloadTemplate() {
  180 + try {
  181 + const params = {
  182 + communityId: getCommunityId(),
  183 + objType: this.form.objType,
  184 + pagePath: 'exportFeeImportTemplate'
  185 + }
  186 + await downloadImportTemplate(params)
  187 + this.$message.success(this.$t('importRoomFee.message.downloadSuccess'))
  188 + } catch (error) {
  189 + console.error('下载模板失败:', error)
  190 + this.$message.error(this.$t('importRoomFee.message.downloadFailed'))
  191 + }
  192 + },
  193 + handleClose() {
  194 + this.$refs.form.resetFields()
  195 + this.$refs.upload.clearFiles()
  196 + this.form.file = null
  197 + }
  198 + }
  199 +}
  200 +</script>
  201 +
  202 +<style scoped>
  203 +.el-upload__tip {
  204 + margin-top: 7px;
  205 + color: #606266;
  206 + font-size: 12px;
  207 +}
  208 +</style>
0 209 \ No newline at end of file
... ...
src/i18n/commonLang.js
... ... @@ -52,6 +52,7 @@ export const messages = {
52 52 year:'Year',
53 53 month:'Month',
54 54 examine:'Examine',
  55 + audit:'Audit',
55 56 }
56 57 },
57 58 zh: {
... ... @@ -107,6 +108,7 @@ export const messages = {
107 108 year:'年',
108 109 month:'月',
109 110 examine:'审核',
  111 + audit:'审核',
110 112 }
111 113 }
112 114 }
113 115 \ No newline at end of file
... ...
src/i18n/feeI18n.js
... ... @@ -10,6 +10,10 @@ import { messages as feeReceiptMessages } from &#39;../views/fee/feeReceiptLang&#39;
10 10 import { messages as printPayFeeMessages } from '../views/fee/printPayFeeLang'
11 11 import { messages as printPayFeeBangTaiMessages } from '../views/fee/printPayFeeBangTaiLang'
12 12 import { messages as printSmallPayFeeMessages } from '../views/fee/printSmallPayFeeLang'
  13 +import { messages as floorShareMessages } from '../views/fee/floorShareLang'
  14 +import { messages as shareReadingMessages } from '../views/fee/shareReadingLang'
  15 +import { messages as roomFeeImportMessages } from '../views/fee/roomFeeImportLang'
  16 +import { messages as shareReadingFeeMessages } from '../views/fee/shareReadingFeeLang'
13 17  
14 18 export const messages = {
15 19 en: {
... ... @@ -25,6 +29,10 @@ export const messages = {
25 29 ...printPayFeeMessages.en,
26 30 ...printPayFeeBangTaiMessages.en,
27 31 ...printSmallPayFeeMessages.en,
  32 + ...floorShareMessages.en,
  33 + ...shareReadingMessages.en,
  34 + ...roomFeeImportMessages.en,
  35 + ...shareReadingFeeMessages.en,
28 36 },
29 37 zh: {
30 38 ...contractCreateFeeMessages.zh,
... ... @@ -39,5 +47,9 @@ export const messages = {
39 47 ...printPayFeeMessages.zh,
40 48 ...printPayFeeBangTaiMessages.zh,
41 49 ...printSmallPayFeeMessages.zh,
  50 + ...floorShareMessages.zh,
  51 + ...shareReadingMessages.zh,
  52 + ...roomFeeImportMessages.zh,
  53 + ...shareReadingFeeMessages.zh,
42 54 }
43 55 }
44 56 \ No newline at end of file
... ...
src/router/feeRouter.js
... ... @@ -44,5 +44,25 @@ export default [
44 44 name: '/pages/property/feeReceipt',
45 45 component: () => import('@/views/fee/feeReceiptList.vue')
46 46 },
  47 + {
  48 + path: '/pages/fee/floorShare',
  49 + name: '/pages/fee/floorShare',
  50 + component: () => import('@/views/fee/floorShareList.vue')
  51 + },
  52 + {
  53 + path: '/pages/fee/shareReading',
  54 + name: '/pages/fee/shareReading',
  55 + component: () => import('@/views/fee/shareReadingList.vue')
  56 + },
  57 + {
  58 + path:'/pages/property/roomFeeImport',
  59 + name:'/pages/property/roomFeeImport',
  60 + component: () => import('@/views/fee/roomFeeImportList.vue')
  61 + },
  62 + {
  63 + path:'/views/fee/shareReadingFee',
  64 + name:'/views/fee/shareReadingFee',
  65 + component: () => import('@/views/fee/shareReadingFeeList.vue')
  66 + },
47 67  
48 68 ]
49 69 \ No newline at end of file
... ...
src/views/fee/floorShareLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + floorShare: {
  4 + title: 'Floor Share',
  5 + id: 'ID',
  6 + floor: 'Floor',
  7 + meterType: 'Meter Type',
  8 + meterNum: 'Meter Number',
  9 + shareType: 'Share Type',
  10 + formula: 'Formula',
  11 + feeItem: 'Fee Item',
  12 + feePrice: 'Fee Price',
  13 + degree: 'Degree',
  14 + readingTime: 'Reading Time',
  15 + createTime: 'Create Time',
  16 + addTitle: 'Add Floor Share',
  17 + editTitle: 'Edit Floor Share',
  18 + deleteConfirm: 'Confirm Operation',
  19 + deleteMessage: 'Are you sure to delete this floor share?',
  20 + fetchError: 'Failed to fetch floor share data',
  21 + fetchFloorError: 'Failed to fetch floor data',
  22 + fetchFeeConfigError: 'Failed to fetch fee config data',
  23 + addSuccess: 'Floor share added successfully',
  24 + addFailed: 'Failed to add floor share',
  25 + editSuccess: 'Floor share updated successfully',
  26 + editFailed: 'Failed to update floor share',
  27 + deleteSuccess: 'Floor share deleted successfully',
  28 + deleteFailed: 'Failed to delete floor share',
  29 + meterTypeRequired: 'Please select meter type',
  30 + feeItemRequired: 'Please select fee item',
  31 + feePriceRequired: 'Please enter fee price',
  32 + feePriceNumber: 'Fee price must be a number',
  33 + shareTypeRequired: 'Please select share type',
  34 + formulaRequired: 'Please enter formula',
  35 + idRequired: 'ID is required',
  36 + meterNumPlaceholder: 'Optional, please enter meter number',
  37 + meterNumTip: 'Enter 1 if no meter number',
  38 + electricMeter: 'Electric Meter',
  39 + waterMeter: 'Water Meter',
  40 + byArea: 'By Area',
  41 + byHouse: 'By House',
  42 + byFormula: 'By Formula'
  43 + }
  44 + },
  45 + zh: {
  46 + floorShare: {
  47 + title: '楼栋公摊',
  48 + id: '编号',
  49 + floor: '楼栋',
  50 + meterType: '表类型',
  51 + meterNum: '表编号',
  52 + shareType: '公摊方式',
  53 + formula: '公式',
  54 + feeItem: '费用项',
  55 + feePrice: '费用单价',
  56 + degree: '度数',
  57 + readingTime: '抄表时间',
  58 + createTime: '创建时间',
  59 + addTitle: '添加楼栋公摊',
  60 + editTitle: '修改楼栋公摊',
  61 + deleteConfirm: '请确认您的操作',
  62 + deleteMessage: '确定删除楼栋公摊吗?',
  63 + fetchError: '获取楼栋公摊数据失败',
  64 + fetchFloorError: '获取楼栋数据失败',
  65 + fetchFeeConfigError: '获取费用项数据失败',
  66 + addSuccess: '楼栋公摊添加成功',
  67 + addFailed: '楼栋公摊添加失败',
  68 + editSuccess: '楼栋公摊修改成功',
  69 + editFailed: '楼栋公摊修改失败',
  70 + deleteSuccess: '楼栋公摊删除成功',
  71 + deleteFailed: '楼栋公摊删除失败',
  72 + meterTypeRequired: '请选择表类型',
  73 + feeItemRequired: '请选择费用项',
  74 + feePriceRequired: '请输入费用单价',
  75 + feePriceNumber: '费用单价必须为数字',
  76 + shareTypeRequired: '请选择公摊方式',
  77 + formulaRequired: '请输入公式',
  78 + idRequired: '编号不能为空',
  79 + meterNumPlaceholder: '选填,请填写表编号',
  80 + meterNumTip: '没有表号请填写1',
  81 + electricMeter: '电表',
  82 + waterMeter: '水表',
  83 + byArea: '按面积',
  84 + byHouse: '按户',
  85 + byFormula: '按公式'
  86 + }
  87 + }
  88 +}
0 89 \ No newline at end of file
... ...
src/views/fee/floorShareList.vue 0 → 100644
  1 +<template>
  2 + <div class="floor-share-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="4">
  5 + <select-community-floor ref="selectCommunityFloor" @floor-change="handleFloorChange" />
  6 + </el-col>
  7 + <el-col :span="20">
  8 + <el-card class="box-card">
  9 + <div slot="header" class="clearfix">
  10 + <span>{{ $t('floorShare.title') }}</span>
  11 + <el-button v-if="floorShareInfo.conditions.floorId" type="primary" size="small" style="float: right;"
  12 + @click="openAddFloorShareModal">
  13 + {{ $t('common.add') }}
  14 + </el-button>
  15 + </div>
  16 +
  17 + <el-table :data="floorShareInfo.floorShares" border style="width: 100%" v-loading="loading">
  18 + <el-table-column prop="fsmId" :label="$t('floorShare.id')" align="center" />
  19 + <el-table-column prop="floorNum" :label="$t('floorShare.floor')" align="center" />
  20 + <el-table-column prop="meterTypeName" :label="$t('floorShare.meterType')" align="center" />
  21 + <el-table-column prop="meterNum" :label="$t('floorShare.meterNum')" align="center" />
  22 + <el-table-column prop="shareTypeName" :label="$t('floorShare.shareType')" align="center" />
  23 + <el-table-column prop="formulaValue" :label="$t('floorShare.formula')" align="center">
  24 + <template slot-scope="scope">
  25 + {{ scope.row.formulaValue || '-' }}
  26 + </template>
  27 + </el-table-column>
  28 + <el-table-column prop="configName" :label="$t('floorShare.feeItem')" align="center" />
  29 + <el-table-column prop="sharePrice" :label="$t('floorShare.feePrice')" align="center" />
  30 + <el-table-column prop="curDegree" :label="$t('floorShare.degree')" align="center" />
  31 + <el-table-column prop="curReadingTime" :label="$t('floorShare.readingTime')" align="center" />
  32 + <el-table-column prop="createTime" :label="$t('floorShare.createTime')" align="center" />
  33 + <el-table-column :label="$t('common.operation')" align="center" width="180">
  34 + <template slot-scope="scope">
  35 + <el-button size="mini" type="primary" @click="openEditFloorShareModal(scope.row)">
  36 + {{ $t('common.edit') }}
  37 + </el-button>
  38 + <el-button size="mini" type="danger" @click="openDeleteFloorShareModal(scope.row)">
  39 + {{ $t('common.delete') }}
  40 + </el-button>
  41 + </template>
  42 + </el-table-column>
  43 + </el-table>
  44 +
  45 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  46 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  47 + @current-change="handleCurrentChange" />
  48 + </el-card>
  49 + </el-col>
  50 + </el-row>
  51 +
  52 + <add-floor-share ref="addFloorShare" @success="handleSuccess" />
  53 + <edit-floor-share ref="editFloorShare" @success="handleSuccess" />
  54 + <delete-floor-share ref="deleteFloorShare" @success="handleSuccess" />
  55 + </div>
  56 +</template>
  57 +
  58 +<script>
  59 +import { listFloorShareMeter } from '@/api/fee/floorShareApi'
  60 +import SelectCommunityFloor from '@/components/report/selectCommunityFloor'
  61 +import AddFloorShare from '@/components/fee/addFloorShare'
  62 +import EditFloorShare from '@/components/fee/editFloorShare'
  63 +import DeleteFloorShare from '@/components/fee/deleteFloorShare'
  64 +import { getCommunityId } from '@/api/community/communityApi'
  65 +
  66 +export default {
  67 + name: 'FloorShareList',
  68 + components: {
  69 + SelectCommunityFloor,
  70 + AddFloorShare,
  71 + EditFloorShare,
  72 + DeleteFloorShare
  73 + },
  74 + data() {
  75 + return {
  76 + loading: false,
  77 + floorShareInfo: {
  78 + floorShares: [],
  79 + conditions: {
  80 + floorId: '',
  81 + communityId: ''
  82 + }
  83 + },
  84 + page: {
  85 + current: 1,
  86 + size: 10,
  87 + total: 0
  88 + }
  89 + }
  90 + },
  91 + created() {
  92 + this.floorShareInfo.conditions.communityId = getCommunityId()
  93 + setTimeout(() => {
  94 + this.$refs.selectCommunityFloor.open({
  95 + callBack: this.handleFloorChange
  96 + })
  97 + }, 1000)
  98 + },
  99 + methods: {
  100 + async listFloorShares() {
  101 + try {
  102 + this.loading = true
  103 + const params = {
  104 + page: this.page.current,
  105 + row: this.page.size,
  106 + floorId: this.floorShareInfo.conditions.floorId,
  107 + communityId: this.floorShareInfo.conditions.communityId
  108 + }
  109 + const { data, total } = await listFloorShareMeter(params)
  110 + this.floorShareInfo.floorShares = data
  111 + this.page.total = total
  112 + } catch (error) {
  113 + this.$message.error(this.$t('floorShare.fetchError'))
  114 + } finally {
  115 + this.loading = false
  116 + }
  117 + },
  118 + handleFloorChange(floor) {
  119 + this.floorShareInfo.conditions.floorId = floor.floorId
  120 + this.floorShareInfo.conditions.floorNum = floor.floorNum
  121 + this.page.current = 1
  122 + this.listFloorShares()
  123 + },
  124 + openAddFloorShareModal() {
  125 + this.$refs.addFloorShare.open({
  126 + floorId: this.floorShareInfo.conditions.floorId,
  127 + floorNum: this.floorShareInfo.conditions.floorNum
  128 + })
  129 + },
  130 + openEditFloorShareModal(row) {
  131 + this.$refs.editFloorShare.open(row)
  132 + },
  133 + openDeleteFloorShareModal(row) {
  134 + this.$refs.deleteFloorShare.open(row)
  135 + },
  136 + handleSuccess() {
  137 + this.listFloorShares()
  138 + },
  139 + handleSizeChange(val) {
  140 + this.page.size = val
  141 + this.listFloorShares()
  142 + },
  143 + handleCurrentChange(val) {
  144 + this.page.current = val
  145 + this.listFloorShares()
  146 + }
  147 + }
  148 +}
  149 +</script>
  150 +
  151 +<style lang="scss" scoped>
  152 +.floor-share-container {
  153 + padding: 20px;
  154 +
  155 + .box-card {
  156 + margin-bottom: 20px;
  157 + }
  158 +
  159 + .el-pagination {
  160 + margin-top: 20px;
  161 + text-align: right;
  162 + }
  163 +}
  164 +</style>
0 165 \ No newline at end of file
... ...
src/views/fee/roomFeeImportLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + roomFeeImport: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + importFeeId: 'Please enter import ID',
  7 + feeTypeCd: 'Please select fee type'
  8 + },
  9 + table: {
  10 + title: 'Fee Import',
  11 + importFeeId: 'Import ID',
  12 + feeTypeCd: 'Fee Type',
  13 + createTime: 'Create Time',
  14 + remark: 'Remark'
  15 + },
  16 + button: {
  17 + import: 'Import Fee'
  18 + },
  19 + fetchError: 'Failed to fetch fee import data'
  20 + },
  21 + importRoomFee: {
  22 + title: 'Fee Import',
  23 + form: {
  24 + feeTypeCd: 'Fee Type',
  25 + objType: 'Fee Object',
  26 + file: 'Select File',
  27 + template: 'Download Template'
  28 + },
  29 + placeholder: {
  30 + feeTypeCd: 'Required, please select fee type',
  31 + objType: 'Required, please select fee object'
  32 + },
  33 + options: {
  34 + room: 'Room',
  35 + parking: 'Parking'
  36 + },
  37 + rules: {
  38 + feeTypeCdRequired: 'Fee type is required',
  39 + objTypeRequired: 'Fee object is required',
  40 + fileRequired: 'File is required'
  41 + },
  42 + tips: {
  43 + fileFormat: 'Only .xls, .xlsx files can be uploaded',
  44 + downloadTemplate1: 'Please download',
  45 + downloadTemplate2: 'import template',
  46 + downloadTemplate3: 'to prepare data before uploading'
  47 + },
  48 + button: {
  49 + selectFile: 'Select File'
  50 + },
  51 + message: {
  52 + importSuccess: 'Import successfully',
  53 + importFailed: 'Import failed',
  54 + downloadSuccess: 'Download template successfully',
  55 + downloadFailed: 'Download template failed'
  56 + }
  57 + }
  58 + },
  59 + zh: {
  60 + roomFeeImport: {
  61 + search: {
  62 + title: '查询条件',
  63 + importFeeId: '请输入导入ID',
  64 + feeTypeCd: '请选择费用类型'
  65 + },
  66 + table: {
  67 + title: '费用导入',
  68 + importFeeId: '导入ID',
  69 + feeTypeCd: '费用类型',
  70 + createTime: '创建时间',
  71 + remark: '备注'
  72 + },
  73 + button: {
  74 + import: '费用导入'
  75 + },
  76 + fetchError: '获取费用导入数据失败'
  77 + },
  78 + importRoomFee: {
  79 + title: '费用导入',
  80 + form: {
  81 + feeTypeCd: '费用类型',
  82 + objType: '费用对象',
  83 + file: '选择文件',
  84 + template: '下载模板'
  85 + },
  86 + placeholder: {
  87 + feeTypeCd: '必填,请选择费用类型',
  88 + objType: '必填,请选择费用对象'
  89 + },
  90 + options: {
  91 + room: '房屋',
  92 + parking: '车位车辆'
  93 + },
  94 + rules: {
  95 + feeTypeCdRequired: '费用类型不能为空',
  96 + objTypeRequired: '费用对象不能为空',
  97 + fileRequired: '文件不能为空'
  98 + },
  99 + tips: {
  100 + fileFormat: '只能上传.xls, .xlsx格式文件',
  101 + downloadTemplate1: '请先下载',
  102 + downloadTemplate2: '导入模板',
  103 + downloadTemplate3: '准备数据后上传导入'
  104 + },
  105 + button: {
  106 + selectFile: '选择文件'
  107 + },
  108 + message: {
  109 + importSuccess: '导入成功',
  110 + importFailed: '导入失败',
  111 + downloadSuccess: '下载模板成功',
  112 + downloadFailed: '下载模板失败'
  113 + }
  114 + }
  115 + }
  116 +}
0 117 \ No newline at end of file
... ...
src/views/fee/roomFeeImportList.vue 0 → 100644
  1 +<template>
  2 + <div class="room-fee-import-container animated fadeInRight">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-card">
  5 + <div slot="header" class="flex justify-between">
  6 + <span>{{ $t('roomFeeImport.search.title') }}</span>
  7 + </div>
  8 + <el-row :gutter="20">
  9 + <el-col :span="6">
  10 + <el-input
  11 + v-model="searchForm.importFeeId"
  12 + :placeholder="$t('roomFeeImport.search.importFeeId')"
  13 + clearable
  14 + />
  15 + </el-col>
  16 + <el-col :span="6">
  17 + <el-select
  18 + v-model="searchForm.feeTypeCd"
  19 + :placeholder="$t('roomFeeImport.search.feeTypeCd')"
  20 + style="width:100%"
  21 + >
  22 + <el-option
  23 + v-for="item in filteredFeeTypeCds"
  24 + :key="item.statusCd"
  25 + :label="item.name"
  26 + :value="item.statusCd"
  27 + />
  28 + </el-select>
  29 + </el-col>
  30 + <el-col :span="6">
  31 + <el-button type="primary" @click="handleSearch">
  32 + {{ $t('common.search') }}
  33 + </el-button>
  34 + </el-col>
  35 + </el-row>
  36 + </el-card>
  37 +
  38 + <!-- 费用导入列表 -->
  39 + <el-card class="table-card">
  40 + <div slot="header" class="flex justify-between">
  41 + <span>{{ $t('roomFeeImport.table.title') }}</span>
  42 + <el-button
  43 + type="primary"
  44 + style="float: right;"
  45 + @click="handleImport"
  46 + >
  47 + {{ $t('roomFeeImport.button.import') }}
  48 + </el-button>
  49 + </div>
  50 +
  51 + <el-table
  52 + :data="tableData"
  53 + border
  54 + style="width: 100%"
  55 + v-loading="loading"
  56 + >
  57 + <el-table-column
  58 + prop="importFeeId"
  59 + :label="$t('roomFeeImport.table.importFeeId')"
  60 + align="center"
  61 + />
  62 + <el-table-column
  63 + prop="feeTypeCdName"
  64 + :label="$t('roomFeeImport.table.feeTypeCd')"
  65 + align="center"
  66 + />
  67 + <el-table-column
  68 + prop="createTime"
  69 + :label="$t('roomFeeImport.table.createTime')"
  70 + align="center"
  71 + />
  72 + <el-table-column
  73 + prop="remark"
  74 + :label="$t('roomFeeImport.table.remark')"
  75 + align="center"
  76 + />
  77 + <el-table-column
  78 + :label="$t('common.operation')"
  79 + align="center"
  80 + width="150"
  81 + >
  82 + <template slot-scope="scope">
  83 + <el-button
  84 + size="mini"
  85 + @click="handleDetail(scope.row)"
  86 + >
  87 + {{ $t('common.detail') }}
  88 + </el-button>
  89 + </template>
  90 + </el-table-column>
  91 + </el-table>
  92 +
  93 + <el-pagination
  94 + :current-page="pagination.current"
  95 + :page-sizes="[10, 20, 30, 50]"
  96 + :page-size="pagination.size"
  97 + :total="pagination.total"
  98 + layout="total, sizes, prev, pager, next, jumper"
  99 + @size-change="handleSizeChange"
  100 + @current-change="handleCurrentChange"
  101 + />
  102 + </el-card>
  103 +
  104 + <!-- 导入组件 -->
  105 + <import-room-fee ref="importRoomFee" @success="handleSuccess" />
  106 + </div>
  107 +</template>
  108 +
  109 +<script>
  110 +import { queryImportFee } from '@/api/fee/roomFeeImportApi'
  111 +import { getDict } from '@/api/community/communityApi'
  112 +import ImportRoomFee from '@/components/fee/importRoomFee'
  113 +
  114 +export default {
  115 + name: 'RoomFeeImportList',
  116 + components: {
  117 + ImportRoomFee
  118 + },
  119 + data() {
  120 + return {
  121 + loading: false,
  122 + searchForm: {
  123 + importFeeId: '',
  124 + feeTypeCd: ''
  125 + },
  126 + tableData: [],
  127 + feeTypeCds: [],
  128 + pagination: {
  129 + current: 1,
  130 + size: 10,
  131 + total: 0
  132 + }
  133 + }
  134 + },
  135 + created() {
  136 + this.getDictData()
  137 + this.getList()
  138 + },
  139 + computed: {
  140 + filteredFeeTypeCds() {
  141 + return this.feeTypeCds.filter(item => item.statusCd !== '888800010008')
  142 + }
  143 + },
  144 + methods: {
  145 + async getDictData() {
  146 + try {
  147 + const data = await getDict('pay_fee_config', 'fee_type_cd')
  148 + this.feeTypeCds = data
  149 + } catch (error) {
  150 + console.error('获取字典数据失败:', error)
  151 + }
  152 + },
  153 + async getList() {
  154 + try {
  155 + this.loading = true
  156 + const params = {
  157 + page: this.pagination.current,
  158 + row: this.pagination.size,
  159 + importFeeId: this.searchForm.importFeeId,
  160 + feeTypeCd: this.searchForm.feeTypeCd
  161 + }
  162 + const { data, total } = await queryImportFee(params)
  163 + this.tableData = data
  164 + this.pagination.total = total
  165 + } catch (error) {
  166 + this.$message.error(this.$t('roomFeeImport.fetchError'))
  167 + } finally {
  168 + this.loading = false
  169 + }
  170 + },
  171 + handleSearch() {
  172 + this.pagination.current = 1
  173 + this.getList()
  174 + },
  175 + handleSizeChange(val) {
  176 + this.pagination.size = val
  177 + this.getList()
  178 + },
  179 + handleCurrentChange(val) {
  180 + this.pagination.current = val
  181 + this.getList()
  182 + },
  183 + handleImport() {
  184 + this.$refs.importRoomFee.open()
  185 + },
  186 + handleDetail(row) {
  187 + this.$router.push({
  188 + path: '/fee/roomFeeImportDetail',
  189 + query: { importFeeId: row.importFeeId }
  190 + })
  191 + },
  192 + handleSuccess() {
  193 + this.getList()
  194 + }
  195 + }
  196 +}
  197 +</script>
  198 +
  199 +<style lang="scss" scoped>
  200 +.room-fee-import-container {
  201 + padding: 20px;
  202 +
  203 + .search-card {
  204 + margin-bottom: 20px;
  205 + }
  206 +
  207 + .table-card {
  208 + margin-bottom: 20px;
  209 + }
  210 +}
  211 +</style>
0 212 \ No newline at end of file
... ...
src/views/fee/shareReadingFeeLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + shareReadingFee: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + roomName: 'Please enter room number',
  7 + ownerName: 'Please enter owner name'
  8 + },
  9 + list: {
  10 + title: 'Shared Reading Fees'
  11 + },
  12 + table: {
  13 + roomName: 'Room',
  14 + ownerName: 'Owner',
  15 + feeName: 'Fee Name',
  16 + degrees: 'Shared Degrees',
  17 + amount: 'Shared Amount',
  18 + remark: 'Remark'
  19 + },
  20 + fetchError: 'Failed to fetch shared reading fees'
  21 + }
  22 + },
  23 + zh: {
  24 + shareReadingFee: {
  25 + search: {
  26 + title: '查询条件',
  27 + roomName: '请输入房屋编号',
  28 + ownerName: '请输入业主姓名'
  29 + },
  30 + list: {
  31 + title: '公摊费用'
  32 + },
  33 + table: {
  34 + roomName: '房屋',
  35 + ownerName: '业主',
  36 + feeName: '费用名称',
  37 + degrees: '公摊度数',
  38 + amount: '公摊金额',
  39 + remark: '说明'
  40 + },
  41 + fetchError: '获取公摊费用失败'
  42 + }
  43 + }
  44 +}
0 45 \ No newline at end of file
... ...
src/views/fee/shareReadingFeeList.vue 0 → 100644
  1 +<template>
  2 + <div class="share-reading-fee-container">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-card">
  5 + <div slot="header" class="flex justify-between">
  6 + <span>{{ $t('shareReadingFee.search.title') }}</span>
  7 + </div>
  8 + <el-row :gutter="20">
  9 + <el-col :span="6">
  10 + <el-input v-model="searchForm.roomName" :placeholder="$t('shareReadingFee.search.roomName')" clearable
  11 + @keyup.enter.native="handleSearch" />
  12 + </el-col>
  13 + <el-col :span="6">
  14 + <el-input v-model="searchForm.ownerNameLike" :placeholder="$t('shareReadingFee.search.ownerName')" clearable
  15 + @keyup.enter.native="handleSearch" />
  16 + </el-col>
  17 + <el-col :span="4">
  18 + <el-button type="primary" @click="handleSearch">
  19 + {{ $t('common.search') }}
  20 + </el-button>
  21 + </el-col>
  22 + </el-row>
  23 + </el-card>
  24 +
  25 + <!-- 数据列表 -->
  26 + <el-card class="list-card">
  27 + <div slot="header" class="flex justify-between">
  28 + <span>{{ $t('shareReadingFee.list.title') }}</span>
  29 + <el-button style="float: right; padding: 3px 0" type="text" @click="goBack">
  30 + {{ $t('common.back') }}
  31 + </el-button>
  32 + </div>
  33 +
  34 + <el-table v-loading="loading" :data="tableData" border style="width: 100%">
  35 + <el-table-column prop="roomName" :label="$t('shareReadingFee.table.roomName')" align="center" />
  36 + <el-table-column prop="ownerName" :label="$t('shareReadingFee.table.ownerName')" align="center" />
  37 + <el-table-column prop="feeName" :label="$t('shareReadingFee.table.feeName')" align="center" />
  38 + <el-table-column prop="degrees" :label="$t('shareReadingFee.table.degrees')" align="center" />
  39 + <el-table-column prop="amount" :label="$t('shareReadingFee.table.amount')" align="center" />
  40 + <el-table-column prop="remark" :label="$t('shareReadingFee.table.remark')" align="center" />
  41 + <el-table-column :label="$t('common.operation')" align="center" width="150">
  42 + <template slot-scope="scope">
  43 + <el-button size="mini" type="primary" @click="handleDetail(scope.row)">
  44 + {{ $t('common.detail') }}
  45 + </el-button>
  46 + </template>
  47 + </el-table-column>
  48 + </el-table>
  49 +
  50 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  51 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  52 + @current-change="handleCurrentChange" />
  53 + </el-card>
  54 + </div>
  55 +</template>
  56 +
  57 +<script>
  58 +import { listFloorShareFee } from '@/api/fee/shareReadingFeeApi'
  59 +import { getCommunityId } from '@/api/community/communityApi'
  60 +
  61 +export default {
  62 + name: 'ShareReadingFeeList',
  63 + data() {
  64 + return {
  65 + loading: false,
  66 + searchForm: {
  67 + readingId: this.$route.query.readingId || '',
  68 + roomName: '',
  69 + ownerNameLike: '',
  70 + communityId: ''
  71 + },
  72 + tableData: [],
  73 + page: {
  74 + current: 1,
  75 + size: 10,
  76 + total: 0
  77 + }
  78 + }
  79 + },
  80 + created() {
  81 + this.searchForm.communityId = getCommunityId()
  82 + this.getList()
  83 + },
  84 + methods: {
  85 + async getList() {
  86 + try {
  87 + this.loading = true
  88 + const params = {
  89 + page: this.page.current,
  90 + row: this.page.size,
  91 + ...this.searchForm
  92 + }
  93 + const { data, total } = await listFloorShareFee(params)
  94 + this.tableData = data
  95 + this.page.total = total
  96 + } catch (error) {
  97 + this.$message.error(this.$t('shareReadingFee.fetchError'))
  98 + } finally {
  99 + this.loading = false
  100 + }
  101 + },
  102 + handleSearch() {
  103 + this.page.current = 1
  104 + this.getList()
  105 + },
  106 + handleSizeChange(val) {
  107 + this.page.size = val
  108 + this.getList()
  109 + },
  110 + handleCurrentChange(val) {
  111 + this.page.current = val
  112 + this.getList()
  113 + },
  114 + handleDetail(row) {
  115 + this.$router.push({
  116 + path: '/fee/feeDetail',
  117 + query: { feeId: row.feeId }
  118 + })
  119 + },
  120 + goBack() {
  121 + this.$router.go(-1)
  122 + }
  123 + }
  124 +}
  125 +</script>
  126 +
  127 +<style lang="scss" scoped>
  128 +.share-reading-fee-container {
  129 + padding: 20px;
  130 +
  131 + .search-card {
  132 + margin-bottom: 20px;
  133 + }
  134 +
  135 + .list-card {
  136 + margin-bottom: 20px;
  137 + }
  138 +
  139 + .el-pagination {
  140 + margin-top: 20px;
  141 + text-align: right;
  142 + }
  143 +}
  144 +</style>
0 145 \ No newline at end of file
... ...
src/views/fee/shareReadingLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + shareReading: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + selectMeter: 'Please select shared meter',
  7 + startTime: 'Please select start time',
  8 + endTime: 'Please select end time'
  9 + },
  10 + list: {
  11 + title: 'Shared Meter Reading',
  12 + addReading: 'Add Reading'
  13 + },
  14 + table: {
  15 + id: 'ID',
  16 + name: 'Name',
  17 + preDegrees: 'Previous Degrees',
  18 + curDegrees: 'Current Degrees',
  19 + degrees: 'Degrees',
  20 + preReadingTime: 'Previous Reading Time',
  21 + curReadingTime: 'Current Reading Time',
  22 + reader: 'Reader',
  23 + status: 'Status',
  24 + remark: 'Remark',
  25 + auditor: 'Auditor',
  26 + auditOpinion: 'Audit Opinion',
  27 + shareProgress: 'Share Progress',
  28 + readingTime: 'Reading Time',
  29 + shareFee: 'Share Fee',
  30 + operation: 'Operation'
  31 + },
  32 + status: {
  33 + waiting: 'Waiting',
  34 + passed: 'Passed',
  35 + rejected: 'Rejected',
  36 + all: 'All'
  37 + },
  38 + add: {
  39 + title: 'Add Shared Reading',
  40 + meter: 'Shared Meter',
  41 + selectMeter: 'Please select shared meter',
  42 + preDegrees: 'Previous Degrees',
  43 + inputPreDegrees: 'Please input previous degrees',
  44 + curDegrees: 'Current Degrees',
  45 + inputCurDegrees: 'Please input current degrees',
  46 + preReadingTime: 'Previous Reading Time',
  47 + selectPreTime: 'Please select previous reading time',
  48 + curReadingTime: 'Current Reading Time',
  49 + selectCurTime: 'Please select current reading time',
  50 + remark: 'Remark',
  51 + inputRemark: 'Please input remark (optional)'
  52 + },
  53 + audit: {
  54 + title: 'Audit Shared Reading',
  55 + status: 'Status',
  56 + selectStatus: 'Please select status',
  57 + remark: 'Remark',
  58 + inputRemark: 'Please input remark'
  59 + },
  60 + delete: {
  61 + title: 'Delete Confirmation',
  62 + confirm: 'Are you sure to delete this shared reading?'
  63 + },
  64 + validate: {
  65 + selectMeter: 'Please select shared meter',
  66 + inputPreDegrees: 'Please input previous degrees',
  67 + inputCurDegrees: 'Please input current degrees',
  68 + selectPreTime: 'Please select previous reading time',
  69 + selectCurTime: 'Please select current reading time',
  70 + selectStatus: 'Please select audit status',
  71 + number: 'Please input valid number'
  72 + },
  73 + message: {
  74 + addSuccess: 'Add shared reading successfully',
  75 + auditSuccess: 'Audit shared reading successfully',
  76 + deleteSuccess: 'Delete shared reading successfully'
  77 + }
  78 + }
  79 + },
  80 + zh: {
  81 + shareReading: {
  82 + search: {
  83 + title: '查询条件',
  84 + selectMeter: '请选择公摊表',
  85 + startTime: '请选择开始时间',
  86 + endTime: '请选择结束时间'
  87 + },
  88 + list: {
  89 + title: '公摊抄表',
  90 + addReading: '抄表'
  91 + },
  92 + table: {
  93 + id: '编号',
  94 + name: '名称',
  95 + preDegrees: '上期度数',
  96 + curDegrees: '本期度数',
  97 + degrees: '度数',
  98 + preReadingTime: '上期读表时间',
  99 + curReadingTime: '本期读表时间',
  100 + reader: '抄表人',
  101 + status: '状态',
  102 + remark: '说明',
  103 + auditor: '审核人',
  104 + auditOpinion: '审核意见',
  105 + shareProgress: '公摊进度',
  106 + readingTime: '抄表时间',
  107 + shareFee: '公摊费用',
  108 + operation: '操作'
  109 + },
  110 + status: {
  111 + waiting: '待审核',
  112 + passed: '审核通过',
  113 + rejected: '审核不通过',
  114 + all: '全部'
  115 + },
  116 + add: {
  117 + title: '公摊抄表',
  118 + meter: '公摊表',
  119 + selectMeter: '请选择公摊表',
  120 + preDegrees: '上期度数',
  121 + inputPreDegrees: '请填写上期度数',
  122 + curDegrees: '本期度数',
  123 + inputCurDegrees: '请填写本期度数',
  124 + preReadingTime: '上期读表时间',
  125 + selectPreTime: '请选择上期读表时间',
  126 + curReadingTime: '本期读表时间',
  127 + selectCurTime: '请选择本期读表时间',
  128 + remark: '备注',
  129 + inputRemark: '请填写备注(选填)'
  130 + },
  131 + audit: {
  132 + title: '公摊审核',
  133 + status: '审核',
  134 + selectStatus: '请选择审核',
  135 + remark: '说明',
  136 + inputRemark: '请填写说明'
  137 + },
  138 + delete: {
  139 + title: '删除确认',
  140 + confirm: '确定删除公摊抄表?'
  141 + },
  142 + validate: {
  143 + selectMeter: '请选择公摊表',
  144 + inputPreDegrees: '请填写上期度数',
  145 + inputCurDegrees: '请填写本期度数',
  146 + selectPreTime: '请选择上期读表时间',
  147 + selectCurTime: '请选择本期读表时间',
  148 + selectStatus: '请选择审核状态',
  149 + number: '请输入有效数字'
  150 + },
  151 + message: {
  152 + addSuccess: '添加公摊抄表成功',
  153 + auditSuccess: '审核公摊抄表成功',
  154 + deleteSuccess: '删除公摊抄表成功'
  155 + }
  156 + }
  157 + }
  158 +}
0 159 \ No newline at end of file
... ...
src/views/fee/shareReadingList.vue 0 → 100644
  1 +<template>
  2 + <div class="share-reading-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="4">
  5 + <el-card class="tree-card">
  6 + <ul class="state-list">
  7 + <li v-for="(item, index) in shareReadingInfo.states" :key="index" @click="swatchShareState(item)"
  8 + :class="{ 'active-state': shareReadingInfo.conditions.state === item.statusCd }">
  9 + {{ item.name }}
  10 + </li>
  11 + </ul>
  12 + </el-card>
  13 + </el-col>
  14 + <el-col :span="20">
  15 + <el-card>
  16 + <div slot="header" class="clearfix">
  17 + <span>{{ $t('shareReading.search.title') }}</span>
  18 + </div>
  19 + <div class="search-wrapper">
  20 + <el-row :gutter="20">
  21 + <el-col :span="6">
  22 + <el-select v-model="shareReadingInfo.conditions.fsmId"
  23 + :placeholder="$t('shareReading.search.selectMeter')" style="width:100%">
  24 + <el-option v-for="(item, index) in shareReadingInfo.floorShares" :key="index"
  25 + :label="`${item.floorNum}栋-${item.meterTypeName}`" :value="item.fsmId" />
  26 + </el-select>
  27 + </el-col>
  28 + <el-col :span="6">
  29 + <el-date-picker v-model="shareReadingInfo.conditions.startTime" type="datetime"
  30 + :placeholder="$t('shareReading.search.startTime')" style="width:100%" />
  31 + </el-col>
  32 + <el-col :span="6">
  33 + <el-date-picker v-model="shareReadingInfo.conditions.endTime" type="datetime"
  34 + :placeholder="$t('shareReading.search.endTime')" style="width:100%" />
  35 + </el-col>
  36 + <el-col :span="2">
  37 + <el-button type="primary" @click="_queryShareReadingMethod">
  38 + {{ $t('common.search') }}
  39 + </el-button>
  40 + </el-col>
  41 + </el-row>
  42 + </div>
  43 + </el-card>
  44 +
  45 + <el-card class="mt-20">
  46 + <div slot="header" class="clearfix">
  47 + <span>{{ $t('shareReading.list.title') }}</span>
  48 + <el-button type="primary" size="small" style="float:right" @click="_openAddShareReadingModal">
  49 + {{ $t('shareReading.list.addReading') }}
  50 + </el-button>
  51 + </div>
  52 + <el-table :data="shareReadingInfo.shareReadings" border style="width:100%" v-loading="loading">
  53 + <el-table-column prop="readingId" :label="$t('shareReading.table.id')" align="center" />
  54 + <el-table-column prop="title" :label="$t('shareReading.table.name')" align="center" />
  55 + <el-table-column prop="preDegrees" :label="$t('shareReading.table.preDegrees')" align="center" />
  56 + <el-table-column prop="curDegrees" :label="$t('shareReading.table.curDegrees')" align="center" />
  57 + <el-table-column :label="$t('shareReading.table.degrees')" align="center">
  58 + <template slot-scope="scope">
  59 + {{ scope.row.curDegrees - scope.row.preDegrees }}
  60 + </template>
  61 + </el-table-column>
  62 + <el-table-column prop="preReadingTime" :label="$t('shareReading.table.preReadingTime')" align="center" />
  63 + <el-table-column prop="curReadingTime" :label="$t('shareReading.table.curReadingTime')" align="center" />
  64 + <el-table-column prop="createStaffName" :label="$t('shareReading.table.reader')" align="center" />
  65 + <el-table-column :label="$t('shareReading.table.status')" align="center">
  66 + <template slot-scope="scope">
  67 + <span v-if="scope.row.state === 'W'">{{ $t('shareReading.status.waiting') }}</span>
  68 + <span v-else-if="scope.row.state === 'C'">{{ $t('shareReading.status.passed') }}</span>
  69 + <span v-else>{{ $t('shareReading.status.rejected') }}</span>
  70 + </template>
  71 + </el-table-column>
  72 + <el-table-column prop="remark" :label="$t('shareReading.table.remark')" align="center">
  73 + <template slot-scope="scope">
  74 + {{ scope.row.remark || '-' }}
  75 + </template>
  76 + </el-table-column>
  77 + <el-table-column prop="auditStaffName" :label="$t('shareReading.table.auditor')" align="center">
  78 + <template slot-scope="scope">
  79 + {{ scope.row.auditStaffName || '-' }}
  80 + </template>
  81 + </el-table-column>
  82 + <el-table-column prop="stateMsg" :label="$t('shareReading.table.auditOpinion')" align="center">
  83 + <template slot-scope="scope">
  84 + {{ scope.row.stateMsg || '-' }}
  85 + </template>
  86 + </el-table-column>
  87 + <el-table-column prop="shareMsg" :label="$t('shareReading.table.shareProgress')" align="center">
  88 + <template slot-scope="scope">
  89 + {{ scope.row.shareMsg || '-' }}
  90 + </template>
  91 + </el-table-column>
  92 + <el-table-column prop="createTime" :label="$t('shareReading.table.readingTime')" align="center" />
  93 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  94 + <template slot-scope="scope">
  95 + <el-button v-if="scope.row.state === 'W' && hasPrivilege('502025032678040006')" size="mini"
  96 + type="primary" @click="_openAuditShareReadingModel(scope.row)">
  97 + {{ $t('common.audit') }}
  98 + </el-button>
  99 + <el-button v-if="scope.row.state === 'W'" size="mini" type="danger"
  100 + @click="_openDeleteShareReadingModel(scope.row)">
  101 + {{ $t('common.delete') }}
  102 + </el-button>
  103 + <el-button size="mini" type="success" @click="_openShareFee(scope.row)">
  104 + {{ $t('shareReading.table.shareFee') }}
  105 + </el-button>
  106 + </template>
  107 + </el-table-column>
  108 + </el-table>
  109 + <el-pagination class="mt-20" :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]"
  110 + :page-size="page.size" :total="page.total" layout="total, sizes, prev, pager, next, jumper"
  111 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  112 + </el-card>
  113 + </el-col>
  114 + </el-row>
  115 +
  116 + <add-share-reading ref="addShareReading" @success="handleSuccess" />
  117 + <audit-share-reading ref="auditShareReading" @success="handleSuccess" />
  118 + <delete-share-reading ref="deleteShareReading" @success="handleSuccess" />
  119 + </div>
  120 +</template>
  121 +
  122 +<script>
  123 +import { getDict, getCommunityId } from '@/api/community/communityApi'
  124 +import { listFloorShareReading, listFloorShareMeter } from '@/api/fee/shareReadingApi'
  125 +import AddShareReading from '@/components/fee/addShareReading'
  126 +import AuditShareReading from '@/components/fee/auditShareReading'
  127 +import DeleteShareReading from '@/components/fee/deleteShareReading'
  128 +
  129 +export default {
  130 + name: 'ShareReadingList',
  131 + components: {
  132 + AddShareReading,
  133 + AuditShareReading,
  134 + DeleteShareReading
  135 + },
  136 + data() {
  137 + return {
  138 + loading: false,
  139 + shareReadingInfo: {
  140 + shareReadings: [],
  141 + states: [],
  142 + floorShares: [],
  143 + conditions: {
  144 + fsmId: '',
  145 + state: '',
  146 + communityId: '',
  147 + startTime: '',
  148 + endTime: '',
  149 + page: 1,
  150 + row: 10
  151 + }
  152 + },
  153 + page: {
  154 + current: 1,
  155 + size: 10,
  156 + total: 0
  157 + }
  158 + }
  159 + },
  160 + created() {
  161 + this.initData()
  162 + },
  163 + methods: {
  164 + async initData() {
  165 + try {
  166 + this.loading = true
  167 + this.shareReadingInfo.conditions.communityId = getCommunityId()
  168 +
  169 + // 获取状态字典
  170 + const states = await getDict('floor_share_reading', 'state')
  171 + this.shareReadingInfo.states = [{ statusCd: '', name: this.$t('common.all') }, ...states]
  172 +
  173 + await this._listFloorShares()
  174 + await this._listShareReadings()
  175 + } catch (error) {
  176 + console.error('初始化数据失败:', error)
  177 + } finally {
  178 + this.loading = false
  179 + }
  180 + },
  181 + async _listShareReadings() {
  182 + try {
  183 + this.loading = true
  184 + const params = {
  185 + ...this.shareReadingInfo.conditions,
  186 + page: this.page.current,
  187 + row: this.page.size
  188 + }
  189 + const { data, total } = await listFloorShareReading(params)
  190 + this.shareReadingInfo.shareReadings = data
  191 + this.page.total = total
  192 + } catch (error) {
  193 + console.error('获取公摊抄表列表失败:', error)
  194 + } finally {
  195 + this.loading = false
  196 + }
  197 + },
  198 + async _listFloorShares() {
  199 + try {
  200 + const params = {
  201 + page: 1,
  202 + row: 300,
  203 + communityId: getCommunityId()
  204 + }
  205 + const { data } = await listFloorShareMeter(params)
  206 + this.shareReadingInfo.floorShares = data
  207 + } catch (error) {
  208 + console.error('获取公摊表列表失败:', error)
  209 + }
  210 + },
  211 + _openAddShareReadingModal() {
  212 + this.$refs.addShareReading.open()
  213 + },
  214 + _openAuditShareReadingModel(row) {
  215 + this.$refs.auditShareReading.open(row)
  216 + },
  217 + _openDeleteShareReadingModel(row) {
  218 + this.$refs.deleteShareReading.open(row)
  219 + },
  220 + _queryShareReadingMethod() {
  221 + this.page.current = 1
  222 + this._listShareReadings()
  223 + },
  224 + swatchShareState(state) {
  225 + this.shareReadingInfo.conditions.state = state.statusCd
  226 + this._queryShareReadingMethod()
  227 + },
  228 + _openShareFee(reading) {
  229 + this.$router.push(`/views/fee/shareReadingFee?readingId=${reading.readingId}`)
  230 + },
  231 + handleSuccess() {
  232 + this._listShareReadings()
  233 + },
  234 + handleSizeChange(val) {
  235 + this.page.size = val
  236 + this._listShareReadings()
  237 + },
  238 + handleCurrentChange(val) {
  239 + this.page.current = val
  240 + this._listShareReadings()
  241 + },
  242 +
  243 + }
  244 +}
  245 +</script>
  246 +
  247 +<style lang="scss" scoped>
  248 +.share-reading-container {
  249 + padding: 20px;
  250 +
  251 + .tree-card {
  252 + height: 100%;
  253 +
  254 + .state-list {
  255 + list-style: none;
  256 + padding: 0;
  257 + margin: 0;
  258 +
  259 + li {
  260 + padding: 10px;
  261 + text-align: center;
  262 + cursor: pointer;
  263 + border-radius: 4px;
  264 + margin-bottom: 5px;
  265 +
  266 + &:hover {
  267 + background-color: #f5f7fa;
  268 + }
  269 +
  270 + &.active-state {
  271 + background-color: #409eff;
  272 + color: #fff;
  273 + }
  274 + }
  275 + }
  276 + }
  277 +
  278 + .mt-20 {
  279 + margin-top: 20px;
  280 + }
  281 +
  282 + .search-wrapper {
  283 + margin-bottom: 20px;
  284 + }
  285 +}
  286 +</style>
0 287 \ No newline at end of file
... ...