Commit 9b01bbd3b49b65c684120ac159c6f3b1381fd66b

Authored by wuxw
1 parent 20e526fa

开发完成发票相关功能

src/api/fee/addOwnerInvoiceApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 保存业主发票信息
  4 +export function saveOwnerInvoice(data) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/invoice.saveOwnerInvoice',
  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 +// 查询业主列表
  24 +export function queryOwners(params) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/owner.queryOwners',
  28 + method: 'get',
  29 + params
  30 + }).then(response => {
  31 + const res = response.data
  32 + if (res.code === 0) {
  33 + resolve({
  34 + data: res.data,
  35 + total: res.records
  36 + })
  37 + } else {
  38 + reject(new Error(res.msg || '查询业主失败'))
  39 + }
  40 + }).catch(error => {
  41 + reject(error)
  42 + })
  43 + })
  44 +}
0 45 \ No newline at end of file
... ...
src/api/fee/invoiceApplyApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取发票申请列表
  5 +export function listInvoiceApply(params) {
  6 + return new Promise((resolve, reject) => {
  7 + const communityId = getCommunityId()
  8 + request({
  9 + url: '/invoice.listInvoiceApply',
  10 + method: 'get',
  11 + params: {
  12 + ...params,
  13 + communityId
  14 + }
  15 + }).then(response => {
  16 + const res = response.data
  17 + if (res.code === 0) {
  18 + resolve(res)
  19 + } else {
  20 + reject(new Error(res.msg || '获取发票申请列表失败'))
  21 + }
  22 + }).catch(error => {
  23 + reject(error)
  24 + })
  25 + })
  26 +}
  27 +
  28 +// 审核发票申请
  29 +export function auditInvoiceApply(data) {
  30 + return new Promise((resolve, reject) => {
  31 + const communityId = getCommunityId()
  32 + request({
  33 + url: '/invoice.auditInvoiceApply',
  34 + method: 'post',
  35 + data: {
  36 + ...data,
  37 + communityId
  38 + }
  39 + }).then(response => {
  40 + const res = response.data
  41 + if (res.code === 0) {
  42 + resolve(res)
  43 + } else {
  44 + reject(new Error(res.msg || '审核发票申请失败'))
  45 + }
  46 + }).catch(error => {
  47 + reject(error)
  48 + })
  49 + })
  50 +}
  51 +
  52 +// 删除发票申请
  53 +export function deleteInvoiceApply(data) {
  54 + return new Promise((resolve, reject) => {
  55 + const communityId = getCommunityId()
  56 + request({
  57 + url: '/invoice.deleteInvoiceApply',
  58 + method: 'post',
  59 + data: {
  60 + ...data,
  61 + communityId
  62 + }
  63 + }).then(response => {
  64 + const res = response.data
  65 + if (res.code === 0) {
  66 + resolve(res)
  67 + } else {
  68 + reject(new Error(res.msg || '删除发票申请失败'))
  69 + }
  70 + }).catch(error => {
  71 + reject(error)
  72 + })
  73 + })
  74 +}
  75 +
  76 +// 上传发票照片
  77 +export function uploadInvoicePhoto(data) {
  78 + return new Promise((resolve, reject) => {
  79 + const communityId = getCommunityId()
  80 + request({
  81 + url: '/invoice.uploadInvoicePhoto',
  82 + method: 'post',
  83 + data: {
  84 + ...data,
  85 + communityId
  86 + }
  87 + }).then(response => {
  88 + const res = response.data
  89 + if (res.code === 0) {
  90 + resolve(res)
  91 + } else {
  92 + reject(new Error(res.msg || '上传发票照片失败'))
  93 + }
  94 + }).catch(error => {
  95 + reject(error)
  96 + })
  97 + })
  98 +}
  99 +
  100 +// 写入发票事件
  101 +export function writeInvoiceEvent(data) {
  102 + return new Promise((resolve, reject) => {
  103 + const communityId = getCommunityId()
  104 + request({
  105 + url: '/invoice.writeInvoiceApply',
  106 + method: 'post',
  107 + data: {
  108 + ...data,
  109 + communityId
  110 + }
  111 + }).then(response => {
  112 + const res = response.data
  113 + if (res.code === 0) {
  114 + resolve(res)
  115 + } else {
  116 + reject(new Error(res.msg || '写入发票事件失败'))
  117 + }
  118 + }).catch(error => {
  119 + reject(error)
  120 + })
  121 + })
  122 +}
  123 +
  124 +// 上传图片
  125 +export function uploadImage(formData) {
  126 + return new Promise((resolve, reject) => {
  127 + const communityId = getCommunityId()
  128 + formData.append('communityId', communityId)
  129 +
  130 + request({
  131 + url: 'uploadFile/uploadImage',
  132 + method: 'post',
  133 + data: formData,
  134 + headers: {
  135 + 'Content-Type': 'multipart/form-data'
  136 + }
  137 + }).then(response => {
  138 + const res = response.data
  139 + if (res.code === 0) {
  140 + resolve(res.data)
  141 + } else {
  142 + reject(new Error(res.msg || '上传图片失败'))
  143 + }
  144 + }).catch(error => {
  145 + reject(error)
  146 + })
  147 + })
  148 +}
0 149 \ No newline at end of file
... ...
src/api/fee/invoiceApplyDetailApi.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
  8 + */
  9 +export function getInvoiceApplyDetail(params) {
  10 + return new Promise((resolve, reject) => {
  11 + params.communityId = getCommunityId()
  12 + request({
  13 + url: '/invoice.listInvoiceApply',
  14 + method: 'get',
  15 + params
  16 + }).then(response => {
  17 + const res = response.data
  18 + if (res.code === 0) {
  19 + resolve(res)
  20 + } else {
  21 + reject(new Error(res.msg || '获取发票申请详情失败'))
  22 + }
  23 + }).catch(error => {
  24 + reject(error)
  25 + })
  26 + })
  27 +}
  28 +
  29 +/**
  30 + * 获取发票申请费用明细
  31 + * @param {Object} params
  32 + * @returns
  33 + */
  34 +export function getInvoiceApplyItemList(params) {
  35 + return new Promise((resolve, reject) => {
  36 + params.communityId = getCommunityId()
  37 + request({
  38 + url: '/invoice.listInvoiceApplyItem',
  39 + method: 'get',
  40 + params
  41 + }).then(response => {
  42 + const res = response.data
  43 + if (res.code === 0) {
  44 + resolve(res)
  45 + } else {
  46 + reject(new Error(res.msg || '获取发票申请费用明细失败'))
  47 + }
  48 + }).catch(error => {
  49 + reject(error)
  50 + })
  51 + })
  52 +}
  53 +
  54 +/**
  55 + * 获取发票申请审核记录
  56 + * @param {Object} params
  57 + * @returns
  58 + */
  59 +export function getInvoiceEventList(params) {
  60 + return new Promise((resolve, reject) => {
  61 + params.communityId = getCommunityId()
  62 + request({
  63 + url: '/invoice.listInvoiceEvent',
  64 + method: 'get',
  65 + params
  66 + }).then(response => {
  67 + const res = response.data
  68 + if (res.code === 0) {
  69 + resolve(res)
  70 + } else {
  71 + reject(new Error(res.msg || '获取发票申请审核记录失败'))
  72 + }
  73 + }).catch(error => {
  74 + reject(error)
  75 + })
  76 + })
  77 +}
0 78 \ No newline at end of file
... ...
src/api/fee/ownerApplyInvoiceApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 获取业主发票信息列表
  5 +export function listOwnerInvoice(params) {
  6 + return new Promise((resolve, reject) => {
  7 + const communityId = getCommunityId()
  8 + request({
  9 + url: '/invoice.listOwnerInvoice',
  10 + method: 'get',
  11 + params: {
  12 + ...params,
  13 + communityId
  14 + }
  15 + }).then(response => {
  16 + const res = response.data
  17 +
  18 + resolve(res)
  19 + }).catch(error => {
  20 + reject(error)
  21 + })
  22 + })
  23 +}
  24 +
  25 +// 查询费用明细
  26 +export function queryFeeDetail(params) {
  27 + return new Promise((resolve, reject) => {
  28 + const communityId = getCommunityId()
  29 + request({
  30 + url: '/fee.queryFeeDetail',
  31 + method: 'get',
  32 + params: {
  33 + ...params,
  34 + communityId
  35 + }
  36 + }).then(response => {
  37 + const res = response.data
  38 +
  39 + resolve(res)
  40 + }).catch(error => {
  41 + reject(error)
  42 + })
  43 + })
  44 +}
  45 +
  46 +// 查询账户预存明细
  47 +export function listAccountReceipt(params) {
  48 + return new Promise((resolve, reject) => {
  49 + const communityId = getCommunityId()
  50 + request({
  51 + url: '/receipt.listAccountReceipt',
  52 + method: 'get',
  53 + params: {
  54 + ...params,
  55 + communityId
  56 + }
  57 + }).then(response => {
  58 + const res = response.data
  59 +
  60 + resolve(res)
  61 + }).catch(error => {
  62 + reject(error)
  63 + })
  64 + })
  65 +}
  66 +
  67 +// 保存发票申请
  68 +export function saveInvoiceApply(data) {
  69 + return new Promise((resolve, reject) => {
  70 + const communityId = getCommunityId()
  71 + request({
  72 + url: '/invoice.saveInvoiceApply',
  73 + method: 'post',
  74 + data: {
  75 + ...data,
  76 + communityId
  77 + }
  78 + }).then(response => {
  79 + const res = response.data
  80 + if (res.code === 0) {
  81 + resolve(res)
  82 + } else {
  83 + reject(new Error(res.msg || '保存发票申请失败'))
  84 + }
  85 + }).catch(error => {
  86 + reject(error)
  87 + })
  88 + })
  89 +}
0 90 \ No newline at end of file
... ...
src/api/fee/ownerInvoiceApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询业主发票列表
  5 +export function listOwnerInvoice(params) {
  6 + return new Promise((resolve, reject) => {
  7 + params.communityId = getCommunityId()
  8 + request({
  9 + url: '/invoice.listOwnerInvoice',
  10 + method: 'get',
  11 + params
  12 + }).then(response => {
  13 + const res = response.data
  14 + if (res.code === 0) {
  15 + resolve(res)
  16 + } else {
  17 + reject(new Error(res.msg || '获取发票列表失败'))
  18 + }
  19 + }).catch(error => {
  20 + reject(error)
  21 + })
  22 + })
  23 +}
  24 +
  25 +// 更新业主发票
  26 +export function updateOwnerInvoice(data) {
  27 + return new Promise((resolve, reject) => {
  28 + data.communityId = getCommunityId()
  29 + request({
  30 + url: '/invoice.updateOwnerInvoice',
  31 + method: 'post',
  32 + data
  33 + }).then(response => {
  34 + const res = response.data
  35 + if (res.code === 0) {
  36 + resolve(res)
  37 + } else {
  38 + reject(new Error(res.msg || '更新发票失败'))
  39 + }
  40 + }).catch(error => {
  41 + reject(error)
  42 + })
  43 + })
  44 +}
  45 +
  46 +// 删除业主发票
  47 +export function deleteOwnerInvoice(data) {
  48 + return new Promise((resolve, reject) => {
  49 + data.communityId = getCommunityId()
  50 + request({
  51 + url: '/invoice.deleteOwnerInvoice',
  52 + method: 'post',
  53 + data
  54 + }).then(response => {
  55 + const res = response.data
  56 + if (res.code === 0) {
  57 + resolve(res)
  58 + } else {
  59 + reject(new Error(res.msg || '删除发票失败'))
  60 + }
  61 + }).catch(error => {
  62 + reject(error)
  63 + })
  64 + })
  65 +}
0 66 \ No newline at end of file
... ...
src/components/fee/audit.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('audit.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="closeDialog"
  7 + >
  8 + <el-form label-width="120px">
  9 + <el-form-item :label="$t('audit.status')">
  10 + <el-select
  11 + v-model="form.state"
  12 + :placeholder="$t('audit.statusRequired')"
  13 + style="width: 100%"
  14 + >
  15 + <el-option
  16 + :label="$t('audit.approve')"
  17 + value="1100"
  18 + />
  19 + <el-option
  20 + :label="$t('audit.reject')"
  21 + value="1200"
  22 + />
  23 + </el-select>
  24 + </el-form-item>
  25 + <el-form-item :label="$t('audit.reason')">
  26 + <el-input
  27 + v-model="form.remark"
  28 + type="textarea"
  29 + :rows="3"
  30 + :placeholder="$t('audit.reasonRequired')"
  31 + />
  32 + </el-form-item>
  33 + </el-form>
  34 + <div slot="footer">
  35 + <el-button @click="closeDialog">{{ $t('common.cancel') }}</el-button>
  36 + <el-button
  37 + type="primary"
  38 + @click="submitAudit"
  39 + >{{ $t('common.submit') }}</el-button>
  40 + </div>
  41 + </el-dialog>
  42 +</template>
  43 +
  44 +<script>
  45 +export default {
  46 + name: 'AuditDialog',
  47 + data() {
  48 + return {
  49 + visible: false,
  50 + form: {
  51 + state: '',
  52 + remark: ''
  53 + }
  54 + }
  55 + },
  56 + watch: {
  57 + 'form.state'(val) {
  58 + if (val === '1100') {
  59 + this.form.remark = this.$t('audit.approve')
  60 + } else if (val === '1200') {
  61 + this.form.remark = this.$t('audit.reject') + ': '
  62 + }
  63 + }
  64 + },
  65 + methods: {
  66 + open() {
  67 + this.form = {
  68 + state: '',
  69 + remark: ''
  70 + }
  71 + this.visible = true
  72 + },
  73 + closeDialog() {
  74 + this.visible = false
  75 + },
  76 + submitAudit() {
  77 + if (!this.form.state) {
  78 + this.$message.warning(this.$t('audit.statusRequired'))
  79 + return
  80 + }
  81 + if (!this.form.remark) {
  82 + this.$message.warning(this.$t('audit.reasonRequired'))
  83 + return
  84 + }
  85 + this.$emit('success', this.form)
  86 + this.closeDialog()
  87 + }
  88 + }
  89 +}
  90 +</script>
0 91 \ No newline at end of file
... ...
src/components/fee/deleteInvoiceApply.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('deleteInvoiceApply.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + @close="closeDialog"
  7 + >
  8 + <div align="center">
  9 + <p>{{ $t('deleteInvoiceApply.confirmMessage') }}</p>
  10 + </div>
  11 + <div slot="footer">
  12 + <el-button @click="closeDialog">{{ $t('common.cancel') }}</el-button>
  13 + <el-button
  14 + type="primary"
  15 + @click="deleteInvoiceApply"
  16 + >{{ $t('common.confirmDelete') }}</el-button>
  17 + </div>
  18 + </el-dialog>
  19 +</template>
  20 +
  21 +<script>
  22 +import { deleteInvoiceApply as deleteApi } from '@/api/fee/invoiceApplyApi'
  23 +import { getCommunityId } from '@/api/community/communityApi'
  24 +
  25 +export default {
  26 + name: 'DeleteInvoiceApply',
  27 + data() {
  28 + return {
  29 + visible: false,
  30 + applyData: {}
  31 + }
  32 + },
  33 + methods: {
  34 + open(row) {
  35 + this.applyData = { ...row }
  36 + this.visible = true
  37 + },
  38 + closeDialog() {
  39 + this.visible = false
  40 + },
  41 + async deleteInvoiceApply() {
  42 + try {
  43 + const params = {
  44 + ...this.applyData,
  45 + communityId: getCommunityId()
  46 + }
  47 + await deleteApi(params)
  48 + this.$emit('success')
  49 + this.$message.success(this.$t('common.deleteSuccess'))
  50 + this.closeDialog()
  51 + } catch (error) {
  52 + this.$message.error(error.message)
  53 + }
  54 + }
  55 + }
  56 +}
  57 +</script>
0 58 \ No newline at end of file
... ...
src/components/fee/deleteOwnerInvoice.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('confirmDeleteTitle')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + >
  7 + <p>{{ $t('confirmDeleteContent') }}</p>
  8 + <div slot="footer">
  9 + <el-button @click="visible = false">{{ $t('cancel') }}</el-button>
  10 + <el-button type="danger" @click="handleDelete">{{ $t('confirmDelete') }}</el-button>
  11 + </div>
  12 + </el-dialog>
  13 +</template>
  14 +
  15 +<script>
  16 +import { deleteOwnerInvoice } from '@/api/fee/ownerInvoiceApi'
  17 +
  18 +export default {
  19 + name: 'DeleteOwnerInvoice',
  20 + data() {
  21 + return {
  22 + visible: false,
  23 + currentData: null
  24 + }
  25 + },
  26 + methods: {
  27 + open(data) {
  28 + this.currentData = data
  29 + this.visible = true
  30 + },
  31 + async handleDelete() {
  32 + try {
  33 + await deleteOwnerInvoice({ oiId: this.currentData.oiId })
  34 + this.$message.success(this.$t('common.deleteSuccess'))
  35 + this.visible = false
  36 + this.$emit('success')
  37 + } catch (error) {
  38 + this.$message.error(error.message || this.$t('common.deleteFailed'))
  39 + }
  40 + }
  41 + }
  42 +}
  43 +</script>
0 44 \ No newline at end of file
... ...
src/components/fee/editOwnerInvoice.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('editTitle')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="resetForm"
  7 + >
  8 + <el-form ref="form" :model="formData" label-width="150px">
  9 + <el-form-item :label="$t('invoiceType')" prop="invoiceType" required>
  10 + <el-select
  11 + v-model="formData.invoiceType"
  12 + :placeholder="$t('requiredInvoiceType')"
  13 + style="width: 100%"
  14 + >
  15 + <el-option value="1001" :label="$t('personal')" />
  16 + <el-option value="2002" :label="$t('enterprise')" />
  17 + </el-select>
  18 + </el-form-item>
  19 +
  20 + <el-form-item :label="$t('invoiceHeader')" prop="invoiceName" required>
  21 + <el-input
  22 + v-model="formData.invoiceName"
  23 + :placeholder="$t('requiredInvoiceHeader')"
  24 + />
  25 + </el-form-item>
  26 +
  27 + <el-form-item :label="$t('taxpayerId')" prop="invoiceNum" required>
  28 + <el-input
  29 + v-model="formData.invoiceNum"
  30 + :placeholder="$t('requiredTaxpayerId')"
  31 + />
  32 + </el-form-item>
  33 +
  34 + <el-form-item :label="$t('address')" prop="invoiceAddress" required>
  35 + <el-input
  36 + v-model="formData.invoiceAddress"
  37 + :placeholder="$t('requiredAddress')"
  38 + />
  39 + </el-form-item>
  40 +
  41 + <el-form-item :label="$t('phone')" prop="invoiceLink" required>
  42 + <el-input
  43 + v-model="formData.invoiceLink"
  44 + :placeholder="$t('requiredPhone')"
  45 + />
  46 + </el-form-item>
  47 +
  48 + <el-form-item :label="$t('bankAccount')" prop="invoiceAccount" required>
  49 + <el-input
  50 + v-model="formData.invoiceAccount"
  51 + :placeholder="$t('requiredBankAccount')"
  52 + />
  53 + </el-form-item>
  54 +
  55 + <el-form-item :label="$t('remark')" prop="remark">
  56 + <el-input
  57 + v-model="formData.remark"
  58 + type="textarea"
  59 + :placeholder="$t('optionalRemark')"
  60 + />
  61 + </el-form-item>
  62 + </el-form>
  63 +
  64 + <div slot="footer">
  65 + <el-button @click="visible = false">{{ $t('cancel') }}</el-button>
  66 + <el-button type="primary" @click="handleSave">{{ $t('save') }}</el-button>
  67 + </div>
  68 + </el-dialog>
  69 +</template>
  70 +
  71 +<script>
  72 +import { updateOwnerInvoice } from '@/api/fee/ownerInvoiceApi'
  73 +
  74 +export default {
  75 + name: 'EditOwnerInvoice',
  76 + data() {
  77 + return {
  78 + visible: false,
  79 + formData: {
  80 + oiId: '',
  81 + ownerId: '',
  82 + ownerName: '',
  83 + invoiceType: '',
  84 + invoiceName: '',
  85 + invoiceNum: '',
  86 + invoiceAddress: '',
  87 + invoiceLink: '',
  88 + invoiceAccount: '',
  89 + remark: ''
  90 + }
  91 + }
  92 + },
  93 + methods: {
  94 + open(data) {
  95 + Object.assign(this.formData, data)
  96 + this.visible = true
  97 + },
  98 + resetForm() {
  99 + this.$refs.form.resetFields()
  100 + Object.keys(this.formData).forEach(key => {
  101 + this.formData[key] = ''
  102 + })
  103 + },
  104 + async handleSave() {
  105 + try {
  106 + await updateOwnerInvoice(this.formData)
  107 + this.$message.success(this.$t('common.saveSuccess'))
  108 + this.visible = false
  109 + this.$emit('success')
  110 + } catch (error) {
  111 + this.$message.error(error.message || this.$t('common.saveFailed'))
  112 + }
  113 + }
  114 + }
  115 +}
  116 +</script>
0 117 \ No newline at end of file
... ...
src/components/fee/invoiceApplyDetailEvent.vue 0 → 100644
  1 +<template>
  2 + <div class="invoice-apply-detail-event">
  3 + <el-table :data="events" border style="width: 100%">
  4 + <el-table-column prop="eventTypeName" :label="$t('invoiceApplyDetailEvent.type')" align="center"></el-table-column>
  5 + <el-table-column prop="createUserName" :label="$t('invoiceApplyDetailEvent.operator')" align="center"></el-table-column>
  6 + <el-table-column prop="remark" :label="$t('invoiceApplyDetailEvent.remark')" align="center"></el-table-column>
  7 + <el-table-column prop="createTime" :label="$t('invoiceApplyDetailEvent.time')" align="center"></el-table-column>
  8 + </el-table>
  9 +
  10 + <el-pagination
  11 + @size-change="handleSizeChange"
  12 + @current-change="handleCurrentChange"
  13 + :current-page="page.current"
  14 + :page-sizes="[10, 20, 30, 50]"
  15 + :page-size="page.size"
  16 + layout="total, sizes, prev, pager, next, jumper"
  17 + :total="page.total">
  18 + </el-pagination>
  19 + </div>
  20 +</template>
  21 +
  22 +<script>
  23 +import { getInvoiceEventList } from '@/api/fee/invoiceApplyDetailApi'
  24 +
  25 +export default {
  26 + name: 'InvoiceApplyDetailEvent',
  27 + props: {
  28 + applyId: {
  29 + type: String,
  30 + required: true
  31 + }
  32 + },
  33 + data() {
  34 + return {
  35 + events: [],
  36 + page: {
  37 + current: 1,
  38 + size: 10,
  39 + total: 0
  40 + }
  41 + }
  42 + },
  43 + watch: {
  44 + applyId: {
  45 + immediate: true,
  46 + handler() {
  47 + if (this.applyId) {
  48 + this.loadData()
  49 + }
  50 + }
  51 + }
  52 + },
  53 + methods: {
  54 + async loadData() {
  55 + try {
  56 + const params = {
  57 + page: this.page.current,
  58 + row: this.page.size,
  59 + applyId: this.applyId
  60 + }
  61 + const res = await getInvoiceEventList(params)
  62 + if (res.code === 0) {
  63 + this.events = res.data
  64 + this.page.total = res.total
  65 + }
  66 + } catch (error) {
  67 + this.$message.error(this.$t('invoiceApplyDetailEvent.fetchError'))
  68 + }
  69 + },
  70 + handleSizeChange(val) {
  71 + this.page.size = val
  72 + this.loadData()
  73 + },
  74 + handleCurrentChange(val) {
  75 + this.page.current = val
  76 + this.loadData()
  77 + }
  78 + }
  79 +}
  80 +</script>
  81 +
  82 +<style scoped>
  83 +.invoice-apply-detail-event {
  84 + margin-top: 20px;
  85 +}
  86 +</style>
0 87 \ No newline at end of file
... ...
src/components/fee/invoiceApplyDetailFee.vue 0 → 100644
  1 +<template>
  2 + <div class="invoice-apply-detail-fee">
  3 + <el-table :data="fees" border style="width: 100%">
  4 + <el-table-column prop="itemTypeName" :label="$t('invoiceApplyDetailFee.type')" align="center"></el-table-column>
  5 + <el-table-column prop="itemName" :label="$t('invoiceApplyDetailFee.name')" align="center"></el-table-column>
  6 + <el-table-column prop="itemAmount" :label="$t('invoiceApplyDetailFee.amount')" align="center"></el-table-column>
  7 + <el-table-column prop="payTime" :label="$t('invoiceApplyDetailFee.payTime')" align="center"></el-table-column>
  8 + <el-table-column prop="remark" :label="$t('invoiceApplyDetailFee.remark')" align="center"></el-table-column>
  9 + <el-table-column :label="$t('invoiceApplyDetailFee.paymentId')" align="center">
  10 + <template slot-scope="scope">
  11 + {{ scope.row.itemObjId }}
  12 + (<a href="javascript:void(0)" v-if="scope.row.itemType === '2002'" @click="viewFeeDetail(scope.row)">{{ $t('common.view') }}</a>
  13 + <a href="javascript:void(0)" v-else @click="viewAcctDetail(scope.row)">{{ $t('common.view') }}</a>)
  14 + </template>
  15 + </el-table-column>
  16 + </el-table>
  17 +
  18 + <el-pagination
  19 + @size-change="handleSizeChange"
  20 + @current-change="handleCurrentChange"
  21 + :current-page="page.current"
  22 + :page-sizes="[10, 20, 30, 50]"
  23 + :page-size="page.size"
  24 + layout="total, sizes, prev, pager, next, jumper"
  25 + :total="page.total">
  26 + </el-pagination>
  27 + </div>
  28 +</template>
  29 +
  30 +<script>
  31 +import { getInvoiceApplyItemList } from '@/api/fee/invoiceApplyDetailApi'
  32 +
  33 +export default {
  34 + name: 'InvoiceApplyDetailFee',
  35 + props: {
  36 + applyId: {
  37 + type: String,
  38 + required: true
  39 + },
  40 + ownerId: {
  41 + type: String,
  42 + default: ''
  43 + }
  44 + },
  45 + data() {
  46 + return {
  47 + fees: [],
  48 + page: {
  49 + current: 1,
  50 + size: 10,
  51 + total: 0
  52 + }
  53 + }
  54 + },
  55 + watch: {
  56 + applyId: {
  57 + immediate: true,
  58 + handler() {
  59 + if (this.applyId) {
  60 + this.loadData()
  61 + }
  62 + }
  63 + }
  64 + },
  65 + methods: {
  66 + async loadData() {
  67 + try {
  68 + const params = {
  69 + page: this.page.current,
  70 + row: this.page.size,
  71 + applyId: this.applyId
  72 + }
  73 + const res = await getInvoiceApplyItemList(params)
  74 + if (res.code === 0) {
  75 + this.fees = res.data.map(item => ({
  76 + ...item,
  77 + itemTypeName: item.itemType === '2002' ? this.$t('invoiceApplyDetailFee.feeType') : this.$t('invoiceApplyDetailFee.accountType')
  78 + }))
  79 + this.page.total = res.total
  80 + }
  81 + } catch (error) {
  82 + this.$message.error(this.$t('invoiceApplyDetailFee.fetchError'))
  83 + }
  84 + },
  85 + handleSizeChange(val) {
  86 + this.page.size = val
  87 + this.loadData()
  88 + },
  89 + handleCurrentChange(val) {
  90 + this.page.current = val
  91 + this.loadData()
  92 + },
  93 + viewFeeDetail(fee) {
  94 + this.$router.push({ path: '/fee/feeDetail', query: { detailId: fee.itemObjId }})
  95 + },
  96 + viewAcctDetail(fee) {
  97 + this.$router.push({ path: '/owner/ownerDetail', query: { ownerId: fee.ownerId, currentTab: 'ownerDetailAccountReceipt' }})
  98 + }
  99 + }
  100 +}
  101 +</script>
  102 +
  103 +<style scoped>
  104 +.invoice-apply-detail-fee {
  105 + margin-top: 20px;
  106 +}
  107 +</style>
0 108 \ No newline at end of file
... ...
src/components/fee/searchOwnerInvoice.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('searchOwnerInvoice.title')" :visible.sync="visible" width="80%" @close="handleClose">
  3 + <div>
  4 + <el-row :gutter="20">
  5 + <el-col :span="9">
  6 + <el-input v-model="searchForm.roomName" :placeholder="$t('searchOwnerInvoice.placeholderRoom')" clearable
  7 + @keyup.enter.native="handleSearch" />
  8 + </el-col>
  9 + <el-col :span="9">
  10 + <el-input v-model="searchForm.ownerNameLike" :placeholder="$t('searchOwnerInvoice.placeholderOwner')" clearable
  11 + @keyup.enter.native="handleSearch" />
  12 + </el-col>
  13 + <el-col :span="6">
  14 + <el-button type="primary" @click="handleSearch">
  15 + {{ $t('searchOwnerInvoice.search') }}
  16 + </el-button>
  17 + </el-col>
  18 + </el-row>
  19 +
  20 + <el-table v-loading="loading" :data="ownerInvoices" border style="width: 100%; margin-top: 20px">
  21 + <el-table-column prop="ownerName" :label="$t('searchOwnerInvoice.ownerName')" align="center" />
  22 + <el-table-column prop="invoiceType" :label="$t('searchOwnerInvoice.invoiceType')" align="center">
  23 + <template slot-scope="scope">
  24 + {{ scope.row.invoiceType === '1001' ? $t('searchOwnerInvoice.personal') : $t('searchOwnerInvoice.company') }}
  25 + </template>
  26 + </el-table-column>
  27 + <el-table-column prop="invoiceName" :label="$t('searchOwnerInvoice.invoiceTitle')" align="center" />
  28 + <el-table-column prop="invoiceNum" :label="$t('searchOwnerInvoice.taxpayerId')" align="center" />
  29 + <el-table-column prop="invoiceAddress" :label="$t('searchOwnerInvoice.addressPhone')" align="center" />
  30 + <el-table-column :label="$t('searchOwnerInvoice.operation')" align="center">
  31 + <template slot-scope="scope">
  32 + <el-button type="primary" size="small" @click="handleSelect(scope.row)">
  33 + {{ $t('searchOwnerInvoice.select') }}
  34 + </el-button>
  35 + </template>
  36 + </el-table-column>
  37 + </el-table>
  38 +
  39 + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size"
  40 + layout="total, sizes, prev, pager, next" :total="pagination.total" @size-change="handleSizeChange"
  41 + @current-change="handlePageChange" />
  42 + </div>
  43 + </el-dialog>
  44 +</template>
  45 +
  46 +<script>
  47 +import { listOwnerInvoice } from '@/api/fee/ownerApplyInvoiceApi'
  48 +
  49 +export default {
  50 + name: 'SearchOwnerInvoice',
  51 + props: {
  52 + visible: {
  53 + type: Boolean,
  54 + default: false
  55 + }
  56 + },
  57 + data() {
  58 + return {
  59 + loading: false,
  60 + searchForm: {
  61 + roomName: '',
  62 + ownerNameLike: ''
  63 + },
  64 + ownerInvoices: [],
  65 + pagination: {
  66 + current: 1,
  67 + size: 10,
  68 + total: 0
  69 + }
  70 + }
  71 + },
  72 + methods: {
  73 + open() {
  74 + this.visible = true
  75 + this.loadData()
  76 + },
  77 + handleClose() {
  78 + this.$emit('update:visible', false)
  79 + },
  80 + async loadData() {
  81 + this.loading = true
  82 + try {
  83 + const params = {
  84 + page: this.pagination.current,
  85 + row: this.pagination.size,
  86 + ownerNameLike: this.searchForm.ownerNameLike,
  87 + roomName: this.searchForm.roomName
  88 + }
  89 +
  90 + const res = await listOwnerInvoice(params)
  91 + this.ownerInvoices = res.data
  92 + this.pagination.total = res.records
  93 + } catch (error) {
  94 + console.error('Failed to load owner invoices:', error)
  95 + this.$message.error(this.$t('common.loadFailed'))
  96 + } finally {
  97 + this.loading = false
  98 + }
  99 + },
  100 + handleSearch() {
  101 + this.pagination.current = 1
  102 + this.loadData()
  103 + },
  104 + handleSizeChange(size) {
  105 + this.pagination.size = size
  106 + this.loadData()
  107 + },
  108 + handlePageChange(page) {
  109 + this.pagination.current = page
  110 + this.loadData()
  111 + },
  112 + handleSelect(owner) {
  113 + this.$emit('select', owner)
  114 + this.handleClose()
  115 + }
  116 + }
  117 +}
  118 +</script>
  119 +
  120 +<style scoped>
  121 +.el-row {
  122 + margin-bottom: 20px;
  123 +}
  124 +</style>
0 125 \ No newline at end of file
... ...
src/components/fee/uploadInvoicePhoto.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('uploadInvoicePhoto.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="closeDialog"
  7 + >
  8 + <el-form label-width="120px">
  9 + <el-form-item :label="$t('uploadInvoicePhoto.invoiceCode')">
  10 + <el-input
  11 + v-model="form.invoiceCode"
  12 + :placeholder="$t('uploadInvoicePhoto.codeRequired')"
  13 + />
  14 + </el-form-item>
  15 + <el-form-item :label="$t('uploadInvoicePhoto.invoice')">
  16 + <upload-image-url
  17 + ref="uploadImage"
  18 + @notifyUploadCoverImage="handleImageUpload"
  19 + />
  20 + </el-form-item>
  21 + </el-form>
  22 + <div slot="footer">
  23 + <el-button @click="closeDialog">{{ $t('common.cancel') }}</el-button>
  24 + <el-button
  25 + type="primary"
  26 + @click="saveUploadInvoicePhoto"
  27 + >{{ $t('common.save') }}</el-button>
  28 + </div>
  29 + </el-dialog>
  30 +</template>
  31 +
  32 +<script>
  33 +import { uploadInvoicePhoto } from '@/api/fee/invoiceApplyApi'
  34 +import { getCommunityId } from '@/api/community/communityApi'
  35 +import UploadImageUrl from '@/components/upload/UploadImageUrl'
  36 +
  37 +export default {
  38 + name: 'UploadInvoicePhoto',
  39 + components: {
  40 + UploadImageUrl
  41 + },
  42 + data() {
  43 + return {
  44 + visible: false,
  45 + form: {
  46 + applyId: '',
  47 + invoiceCode: '',
  48 + photos: []
  49 + }
  50 + }
  51 + },
  52 + methods: {
  53 + open(row) {
  54 + this.form = {
  55 + applyId: row.applyId,
  56 + invoiceCode: '',
  57 + photos: []
  58 + }
  59 + this.$refs.uploadImage.clearImages()
  60 + this.visible = true
  61 + },
  62 + closeDialog() {
  63 + this.visible = false
  64 + },
  65 + handleImageUpload(photos) {
  66 + this.form.photos = photos
  67 + },
  68 + async saveUploadInvoicePhoto() {
  69 + if (!this.form.invoiceCode) {
  70 + this.$message.warning(this.$t('uploadInvoicePhoto.codeRequired'))
  71 + return
  72 + }
  73 + if (this.form.photos.length === 0) {
  74 + this.$message.warning(this.$t('uploadInvoicePhoto.photoRequired'))
  75 + return
  76 + }
  77 +
  78 + try {
  79 + const params = {
  80 + ...this.form,
  81 + communityId: getCommunityId()
  82 + }
  83 + await uploadInvoicePhoto(params)
  84 + this.$emit('success')
  85 + this.$message.success(this.$t('common.saveSuccess'))
  86 + this.closeDialog()
  87 + } catch (error) {
  88 + this.$message.error(error.message)
  89 + }
  90 + }
  91 + }
  92 +}
  93 +</script>
0 94 \ No newline at end of file
... ...
src/components/fee/wirteInvoiceEvent.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('wirteInvoiceEvent.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="closeDialog"
  7 + >
  8 + <el-form label-width="120px">
  9 + <el-form-item :label="$t('wirteInvoiceEvent.type')">
  10 + <el-select
  11 + v-model="form.eventType"
  12 + :placeholder="$t('wirteInvoiceEvent.typeRequired')"
  13 + style="width: 100%"
  14 + >
  15 + <el-option
  16 + :label="$t('wirteInvoiceEvent.receive')"
  17 + value="4004"
  18 + />
  19 + <el-option
  20 + :label="$t('wirteInvoiceEvent.register')"
  21 + value="5005"
  22 + />
  23 + </el-select>
  24 + </el-form-item>
  25 + <el-form-item :label="$t('wirteInvoiceEvent.remark')">
  26 + <el-input
  27 + v-model="form.remark"
  28 + type="textarea"
  29 + :rows="3"
  30 + :placeholder="$t('wirteInvoiceEvent.remarkRequired')"
  31 + />
  32 + </el-form-item>
  33 + </el-form>
  34 + <div slot="footer">
  35 + <el-button @click="closeDialog">{{ $t('common.cancel') }}</el-button>
  36 + <el-button
  37 + type="primary"
  38 + @click="saveEvent"
  39 + >{{ $t('common.save') }}</el-button>
  40 + </div>
  41 + </el-dialog>
  42 +</template>
  43 +
  44 +<script>
  45 +import { writeInvoiceEvent } from '@/api/fee/invoiceApplyApi'
  46 +import { getCommunityId } from '@/api/community/communityApi'
  47 +
  48 +export default {
  49 + name: 'WirteInvoiceEvent',
  50 + data() {
  51 + return {
  52 + visible: false,
  53 + form: {
  54 + applyId: '',
  55 + eventType: '',
  56 + remark: ''
  57 + }
  58 + }
  59 + },
  60 + methods: {
  61 + open(row) {
  62 + this.form = {
  63 + applyId: row.applyId,
  64 + eventType: '',
  65 + remark: ''
  66 + }
  67 + this.visible = true
  68 + },
  69 + closeDialog() {
  70 + this.visible = false
  71 + },
  72 + async saveEvent() {
  73 + if (!this.form.eventType) {
  74 + this.$message.warning(this.$t('wirteInvoiceEvent.typeRequired'))
  75 + return
  76 + }
  77 + if (!this.form.remark) {
  78 + this.$message.warning(this.$t('wirteInvoiceEvent.remarkRequired'))
  79 + return
  80 + }
  81 +
  82 + try {
  83 + const params = {
  84 + ...this.form,
  85 + communityId: getCommunityId()
  86 + }
  87 + await writeInvoiceEvent(params)
  88 + this.$emit('success')
  89 + this.$message.success(this.$t('common.saveSuccess'))
  90 + this.closeDialog()
  91 + } catch (error) {
  92 + this.$message.error(error.message)
  93 + }
  94 + }
  95 + }
  96 +}
  97 +</script>
0 98 \ No newline at end of file
... ...
src/i18n/index.js
... ... @@ -139,6 +139,10 @@ import { messages as ownerCommitteeManageMessages } from &#39;../views/owner/ownerCo
139 139 import { messages as addOwnerCommitteeMessages } from '../views/owner/addOwnerCommitteeLang'
140 140 import { messages as editOwnerCommitteeMessages } from '../views/owner/editOwnerCommitteeLang'
141 141 import { messages as ownerCommitteeDetailMessages } from '../views/owner/ownerCommitteeDetailLang'
  142 +import { messages as ownerInvoiceMessages } from '../views/fee/ownerInvoiceLang'
  143 +import { messages as invoiceApplyMessages } from '../views/fee/invoiceApplyLang'
  144 +import { messages as ownerApplyInvoiceMessages } from '../views/fee/ownerApplyInvoiceLang'
  145 +import { messages as invoiceApplyDetailMessages } from '../views/fee/invoiceApplyDetailLang'
142 146  
143 147 Vue.use(VueI18n)
144 148  
... ... @@ -282,6 +286,10 @@ const messages = {
282 286 ...addOwnerCommitteeMessages.en,
283 287 ...editOwnerCommitteeMessages.en,
284 288 ...ownerCommitteeDetailMessages.en,
  289 + ...ownerInvoiceMessages.en,
  290 + ...invoiceApplyMessages.en,
  291 + ...ownerApplyInvoiceMessages.en,
  292 + ...invoiceApplyDetailMessages.en,
285 293 },
286 294 zh: {
287 295 ...loginMessages.zh,
... ... @@ -421,6 +429,10 @@ const messages = {
421 429 ...addOwnerCommitteeMessages.zh,
422 430 ...editOwnerCommitteeMessages.zh,
423 431 ...ownerCommitteeDetailMessages.zh,
  432 + ...ownerInvoiceMessages.zh,
  433 + ...invoiceApplyMessages.zh,
  434 + ...ownerApplyInvoiceMessages.zh,
  435 + ...invoiceApplyDetailMessages.zh,
424 436 }
425 437 }
426 438  
... ...
src/router/index.js
... ... @@ -682,10 +682,35 @@ const routes = [
682 682 component: () => import('@/views/owner/editOwnerCommitteeList.vue')
683 683 },
684 684 {
685   - path:'/views/owner/ownerCommitteeDetail',
686   - name:'/views/owner/ownerCommitteeDetail',
  685 + path: '/views/owner/ownerCommitteeDetail',
  686 + name: '/views/owner/ownerCommitteeDetail',
687 687 component: () => import('@/views/owner/ownerCommitteeDetailList.vue')
  688 + },
  689 + {
  690 + path: '/pages/fee/ownerInvoice',
  691 + name: '/pages/fee/ownerInvoice',
  692 + component: () => import('@/views/fee/ownerInvoiceList.vue')
  693 + },
  694 + {
  695 + path: '/views/fee/addOwnerInvoice',
  696 + name: '/views/fee/addOwnerInvoice',
  697 + component: () => import('@/views/fee/addOwnerInvoiceList.vue')
  698 + },
  699 + {
  700 + path:'/pages/fee/invoiceApply',
  701 + name:'/pages/fee/invoiceApply',
  702 + component: () => import('@/views/fee/invoiceApplyList.vue')
688 703 },
  704 + {
  705 + path:'/views/fee/ownerApplyInvoice',
  706 + name:'/views/fee/ownerApplyInvoice',
  707 + component: () => import('@/views/fee/ownerApplyInvoiceList.vue')
  708 + },
  709 + {
  710 + path:'/views/fee/invoiceApplyDetail',
  711 + name:'/views/fee/invoiceApplyDetail',
  712 + component: () => import('@/views/fee/invoiceApplyDetailList.vue')
  713 + },
689 714 // 其他子路由可以在这里添加
690 715 ]
691 716 },
... ...
src/views/fee/addOwnerInvoiceList.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-card shadow="never">
  4 + <div slot="header" class="clearfix">
  5 + <span>{{ $t('addOwnerInvoice.title') }}</span>
  6 + <el-button class="float-right" type="text" @click="$router.go(-1)">
  7 + {{ $t('addOwnerInvoice.back') }}
  8 + </el-button>
  9 + </div>
  10 +
  11 + <el-form ref="form" :model="formData" :rules="rules" label-width="150px" label-position="right">
  12 + <el-form-item :label="$t('addOwnerInvoice.owner')" prop="ownerName">
  13 + <el-input v-model="formData.ownerName" :placeholder="$t('addOwnerInvoice.placeholder.owner')" disabled
  14 + style="width: 70%" />
  15 + <el-button type="primary" style="margin-left: 10px" @click="openOwnerDialog">
  16 + {{ $t('addOwnerInvoice.selectOwner') }}
  17 + </el-button>
  18 + </el-form-item>
  19 +
  20 + <el-form-item :label="$t('addOwnerInvoice.invoiceType')" prop="invoiceType">
  21 + <el-select v-model="formData.invoiceType" :placeholder="$t('addOwnerInvoice.placeholder.invoiceType')"
  22 + style="width: 100%">
  23 + <el-option v-for="item in invoiceTypes" :key="item.value" :label="item.label" :value="item.value" />
  24 + </el-select>
  25 + </el-form-item>
  26 +
  27 + <el-form-item :label="$t('addOwnerInvoice.invoiceTitle')" prop="invoiceName">
  28 + <el-input v-model="formData.invoiceName" :placeholder="$t('addOwnerInvoice.placeholder.invoiceTitle')"
  29 + style="width: 100%" />
  30 + </el-form-item>
  31 +
  32 + <el-form-item :label="$t('addOwnerInvoice.taxNumber')" prop="invoiceNum">
  33 + <el-input v-model="formData.invoiceNum" :placeholder="$t('addOwnerInvoice.placeholder.taxNumber')"
  34 + style="width: 100%" />
  35 + </el-form-item>
  36 +
  37 + <el-form-item :label="$t('addOwnerInvoice.address')" prop="invoiceAddress">
  38 + <el-input v-model="formData.invoiceAddress" :placeholder="$t('addOwnerInvoice.placeholder.address')"
  39 + style="width: 100%" />
  40 + </el-form-item>
  41 +
  42 + <el-form-item :label="$t('addOwnerInvoice.phone')" prop="invoiceLink">
  43 + <el-input v-model="formData.invoiceLink" :placeholder="$t('addOwnerInvoice.placeholder.phone')"
  44 + style="width: 100%" />
  45 + </el-form-item>
  46 +
  47 + <el-form-item :label="$t('addOwnerInvoice.bankAccount')" prop="invoiceAccount">
  48 + <el-input v-model="formData.invoiceAccount" :placeholder="$t('addOwnerInvoice.placeholder.bankAccount')"
  49 + style="width: 100%" />
  50 + </el-form-item>
  51 +
  52 + <el-form-item :label="$t('addOwnerInvoice.remarks')">
  53 + <el-input v-model="formData.remark" type="textarea" :rows="3"
  54 + :placeholder="$t('addOwnerInvoice.placeholder.remarks')" style="width: 100%" />
  55 + </el-form-item>
  56 +
  57 + <el-form-item>
  58 + <el-button type="primary" @click="submitForm">
  59 + {{ $t('addOwnerInvoice.save') }}
  60 + </el-button>
  61 + </el-form-item>
  62 + </el-form>
  63 + </el-card>
  64 +
  65 + <search-owner ref="searchOwner" :visible.sync="showOwnerDialog" @chooseOwner="handleOwnerSelect" />
  66 + </div>
  67 +</template>
  68 +
  69 +<script>
  70 +import { saveOwnerInvoice } from '@/api/fee/addOwnerInvoiceApi'
  71 +import SearchOwner from '@/components/owner/SearchOwner'
  72 +
  73 +export default {
  74 + name: 'AddOwnerInvoice',
  75 + components: { SearchOwner },
  76 + data() {
  77 + return {
  78 + showOwnerDialog: false,
  79 + formData: {
  80 + oiId: '',
  81 + ownerId: '',
  82 + ownerName: '',
  83 + invoiceType: '',
  84 + invoiceName: '',
  85 + invoiceNum: '',
  86 + invoiceAddress: '',
  87 + invoiceLink: '',
  88 + invoiceAccount: '',
  89 + remark: ''
  90 + },
  91 + invoiceTypes: [
  92 + { value: '1001', label: this.$t('addOwnerInvoice.personal') },
  93 + { value: '2002', label: this.$t('addOwnerInvoice.enterprise') }
  94 + ],
  95 + rules: {
  96 + ownerName: [
  97 + { required: true, message: this.$t('addOwnerInvoice.validation.ownerRequired'), trigger: 'blur' }
  98 + ],
  99 + invoiceType: [
  100 + { required: true, message: this.$t('addOwnerInvoice.validation.typeRequired'), trigger: 'change' }
  101 + ],
  102 + invoiceName: [
  103 + { required: true, message: this.$t('addOwnerInvoice.validation.titleRequired'), trigger: 'blur' }
  104 + ],
  105 + invoiceNum: [
  106 + { required: true, message: this.$t('addOwnerInvoice.validation.taxNumberRequired'), trigger: 'blur' }
  107 + ],
  108 + invoiceAddress: [
  109 + { required: true, message: this.$t('addOwnerInvoice.validation.addressRequired'), trigger: 'blur' }
  110 + ],
  111 + invoiceLink: [
  112 + { required: true, message: this.$t('addOwnerInvoice.validation.phoneRequired'), trigger: 'blur' }
  113 + ],
  114 + invoiceAccount: [
  115 + { required: true, message: this.$t('addOwnerInvoice.validation.bankAccountRequired'), trigger: 'blur' }
  116 + ]
  117 + }
  118 + }
  119 + },
  120 + methods: {
  121 + openOwnerDialog() {
  122 + this.$refs.searchOwner.open()
  123 + },
  124 + handleOwnerSelect(owner) {
  125 + this.formData.ownerId = owner.ownerId
  126 + this.formData.ownerName = owner.name
  127 + },
  128 + submitForm() {
  129 + this.$refs.form.validate(async valid => {
  130 + if (valid) {
  131 + try {
  132 + const payload = {
  133 + ...this.formData,
  134 + communityId: this.getCommunityId()
  135 + }
  136 +
  137 + await saveOwnerInvoice(payload)
  138 + this.$message.success(this.$t('common.saveSuccess'))
  139 + this.$router.go(-1)
  140 + } catch (error) {
  141 + console.error('保存失败:', error)
  142 + this.$message.error(error.message || this.$t('common.saveFailed'))
  143 + }
  144 + }
  145 + })
  146 + }
  147 + }
  148 +}
  149 +</script>
  150 +
  151 +<style lang="scss" scoped>
  152 +.app-container {
  153 + padding: 20px;
  154 +}
  155 +
  156 +.float-right {
  157 + float: right;
  158 +}
  159 +
  160 +.el-form {
  161 + max-width: 800px;
  162 + margin: 0 auto;
  163 +}
  164 +
  165 +.el-select,
  166 +.el-input {
  167 + width: 100%;
  168 +}
  169 +</style>
0 170 \ No newline at end of file
... ...
src/views/fee/invoiceApplyDetailLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + invoiceApplyDetail: {
  4 + title: 'Invoice Details',
  5 + back: 'Back',
  6 + applyId: 'Apply ID:',
  7 + invoiceType: 'Invoice Type:',
  8 + personal: 'Personal',
  9 + company: 'Company',
  10 + ownerName: 'Owner Name:',
  11 + createUserName: 'Applicant:',
  12 + invoiceName: 'Invoice Title:',
  13 + invoiceNum: 'Taxpayer ID:',
  14 + invoiceAddress: 'Address/Phone:',
  15 + invoiceAmount: 'Apply Amount:',
  16 + stateName: 'Status:',
  17 + createTime: 'Apply Time:',
  18 + invoiceCode: 'Invoice Code:',
  19 + feeTab: 'Fee Details',
  20 + eventTab: 'Audit Records',
  21 + imageTab: 'View Invoice',
  22 + notUploaded: 'Not uploaded',
  23 + noInvoice: 'No Invoice',
  24 + downloadInvoice: 'Download Invoice',
  25 + fetchError: 'Failed to fetch invoice details'
  26 + },
  27 + invoiceApplyDetailFee: {
  28 + type: 'Type',
  29 + name: 'Name',
  30 + amount: 'Amount',
  31 + payTime: 'Pay Time',
  32 + remark: 'Remark',
  33 + paymentId: 'Payment ID',
  34 + feeType: 'Fee',
  35 + accountType: 'Account',
  36 + fetchError: 'Failed to fetch fee details'
  37 + },
  38 + invoiceApplyDetailEvent: {
  39 + type: 'Type',
  40 + operator: 'Operator',
  41 + remark: 'Remark',
  42 + time: 'Time',
  43 + fetchError: 'Failed to fetch audit records'
  44 + },
  45 + },
  46 + zh: {
  47 + invoiceApplyDetail: {
  48 + title: '发票详情',
  49 + back: '返回',
  50 + applyId: '编号:',
  51 + invoiceType: '发票类型:',
  52 + personal: '个人',
  53 + company: '企业',
  54 + ownerName: '业主名称:',
  55 + createUserName: '申请人:',
  56 + invoiceName: '发票名头:',
  57 + invoiceNum: '纳税人识别号:',
  58 + invoiceAddress: '地址、电话:',
  59 + invoiceAmount: '申请金额:',
  60 + stateName: '审核状态:',
  61 + createTime: '申请时间:',
  62 + invoiceCode: '发票编号:',
  63 + feeTab: '开票明细',
  64 + eventTab: '审核记录',
  65 + imageTab: '查看发票',
  66 + notUploaded: '未上传',
  67 + noInvoice: '没有发票',
  68 + downloadInvoice: '下载发票',
  69 + fetchError: '获取发票详情失败'
  70 + },
  71 + invoiceApplyDetailFee: {
  72 + type: '类型',
  73 + name: '名称',
  74 + amount: '金额',
  75 + payTime: '缴费时间',
  76 + remark: '备注',
  77 + paymentId: '缴费ID',
  78 + feeType: '费用',
  79 + accountType: '账户',
  80 + fetchError: '获取费用明细失败'
  81 + },
  82 + invoiceApplyDetailEvent: {
  83 + type: '类型',
  84 + operator: '操作人',
  85 + remark: '说明',
  86 + time: '时间',
  87 + fetchError: '获取审核记录失败'
  88 + },
  89 + }
  90 +}
0 91 \ No newline at end of file
... ...
src/views/fee/invoiceApplyDetailList.vue 0 → 100644
  1 +<template>
  2 + <div class="invoice-apply-detail">
  3 + <el-card class="box-card">
  4 + <div class="card-header">
  5 + <div class="text-title">{{ $t('invoiceApplyDetail.title') }}</div>
  6 + <div>
  7 + <el-button type="primary" size="small" style="margin-left:10px" @click="goBack">
  8 + {{ $t('invoiceApplyDetail.back') }}
  9 + </el-button>
  10 + </div>
  11 + </div>
  12 +
  13 + <!-- 业主信息 -->
  14 + <div class="margin-top">
  15 + <el-row :gutter="20">
  16 + <el-col :span="24">
  17 + <el-row :gutter="20">
  18 + <el-col :span="6">
  19 + <div class="form-group">
  20 + <label class="col-form-label">{{ $t('invoiceApplyDetail.applyId') }}</label>
  21 + <label>{{ invoiceApplyDetailInfo.applyId }}</label>
  22 + </div>
  23 + </el-col>
  24 + <el-col :span="6">
  25 + <div class="form-group">
  26 + <label class="col-form-label">{{ $t('invoiceApplyDetail.invoiceType') }}</label>
  27 + <label>{{ invoiceApplyDetailInfo.invoiceType === '1001' ? $t('invoiceApplyDetail.personal') :
  28 + $t('invoiceApplyDetail.company') }}</label>
  29 + </div>
  30 + </el-col>
  31 + <el-col :span="6">
  32 + <div class="form-group">
  33 + <label class="col-form-label">{{ $t('invoiceApplyDetail.ownerName') }}</label>
  34 + <label>{{ invoiceApplyDetailInfo.ownerName }}</label>
  35 + </div>
  36 + </el-col>
  37 + <el-col :span="6">
  38 + <div class="form-group">
  39 + <label class="col-form-label">{{ $t('invoiceApplyDetail.createUserName') }}</label>
  40 + <label>{{ invoiceApplyDetailInfo.createUserName }}({{ invoiceApplyDetailInfo.applyTel }})</label>
  41 + </div>
  42 + </el-col>
  43 + </el-row>
  44 + <el-row :gutter="20">
  45 + <el-col :span="6">
  46 + <div class="form-group">
  47 + <label class="col-form-label">{{ $t('invoiceApplyDetail.invoiceName') }}</label>
  48 + <label>{{ invoiceApplyDetailInfo.invoiceName }}</label>
  49 + </div>
  50 + </el-col>
  51 + <el-col :span="6">
  52 + <div class="form-group">
  53 + <label class="col-form-label">{{ $t('invoiceApplyDetail.invoiceNum') }}</label>
  54 + <label>{{ invoiceApplyDetailInfo.invoiceNum }}</label>
  55 + </div>
  56 + </el-col>
  57 + <el-col :span="6">
  58 + <div class="form-group">
  59 + <label class="col-form-label">{{ $t('invoiceApplyDetail.invoiceAddress') }}</label>
  60 + <label>{{ invoiceApplyDetailInfo.invoiceAddress }}</label>
  61 + </div>
  62 + </el-col>
  63 + <el-col :span="6">
  64 + <div class="form-group">
  65 + <label class="col-form-label">{{ $t('invoiceApplyDetail.invoiceAmount') }}</label>
  66 + <label>{{ invoiceApplyDetailInfo.invoiceAmount }}</label>
  67 + </div>
  68 + </el-col>
  69 + </el-row>
  70 + <el-row :gutter="20">
  71 + <el-col :span="6">
  72 + <div class="form-group">
  73 + <label class="col-form-label">{{ $t('invoiceApplyDetail.stateName') }}</label>
  74 + <label>{{ invoiceApplyDetailInfo.stateName }}</label>
  75 + </div>
  76 + </el-col>
  77 + <el-col :span="6">
  78 + <div class="form-group">
  79 + <label class="col-form-label">{{ $t('invoiceApplyDetail.createTime') }}</label>
  80 + <label>{{ invoiceApplyDetailInfo.createTime }}</label>
  81 + </div>
  82 + </el-col>
  83 + <el-col :span="6">
  84 + <div class="form-group">
  85 + <label class="col-form-label">{{ $t('invoiceApplyDetail.invoiceCode') }}</label>
  86 + <label>{{ invoiceApplyDetailInfo.invoiceCode || $t('invoiceApplyDetail.notUploaded') }}</label>
  87 + </div>
  88 + </el-col>
  89 + </el-row>
  90 + </el-col>
  91 + </el-row>
  92 + </div>
  93 + <el-divider></el-divider>
  94 +
  95 + <el-tabs v-model="invoiceApplyDetailInfo._currentTab" @tab-click="handleTabClick">
  96 + <el-tab-pane :label="$t('invoiceApplyDetail.feeTab')" name="invoiceApplyDetailFee">
  97 + <invoice-apply-detail-fee v-if="invoiceApplyDetailInfo._currentTab === 'invoiceApplyDetailFee'"
  98 + :apply-id="invoiceApplyDetailInfo.applyId"
  99 + :owner-id="invoiceApplyDetailInfo.ownerId"></invoice-apply-detail-fee>
  100 + </el-tab-pane>
  101 + <el-tab-pane :label="$t('invoiceApplyDetail.eventTab')" name="invoiceApplyDetailEvent">
  102 + <invoice-apply-detail-event v-if="invoiceApplyDetailInfo._currentTab === 'invoiceApplyDetailEvent'"
  103 + :apply-id="invoiceApplyDetailInfo.applyId"></invoice-apply-detail-event>
  104 + </el-tab-pane>
  105 + <el-tab-pane :label="$t('invoiceApplyDetail.imageTab')" name="invoiceApplyDetailJpg">
  106 + <div v-if="invoiceApplyDetailInfo.urls && invoiceApplyDetailInfo.urls.length > 0">
  107 + <div v-for="(item, index) in invoiceApplyDetailInfo.urls" :key="index">
  108 + <a target="_blank" :href="item">{{ $t('invoiceApplyDetail.downloadInvoice') }}</a>
  109 + </div>
  110 + </div>
  111 + <div class="padding" v-else>
  112 + {{ $t('invoiceApplyDetail.noInvoice') }}
  113 + </div>
  114 + </el-tab-pane>
  115 + </el-tabs>
  116 + </el-card>
  117 + </div>
  118 +</template>
  119 +
  120 +<script>
  121 +import { getInvoiceApplyDetail } from '@/api/fee/invoiceApplyDetailApi'
  122 +import InvoiceApplyDetailFee from '@/components/fee/invoiceApplyDetailFee'
  123 +import InvoiceApplyDetailEvent from '@/components/fee/invoiceApplyDetailEvent'
  124 +
  125 +export default {
  126 + name: 'InvoiceApplyDetailList',
  127 + components: {
  128 + InvoiceApplyDetailFee,
  129 + InvoiceApplyDetailEvent
  130 + },
  131 + data() {
  132 + return {
  133 + invoiceApplyDetailInfo: {
  134 + applyId: '',
  135 + invoiceType: '',
  136 + ownerName: '',
  137 + createUserName: '',
  138 + invoiceName: '',
  139 + invoiceNum: '',
  140 + invoiceAddress: '',
  141 + invoiceAmount: '',
  142 + stateName: '',
  143 + createTime: '',
  144 + invoiceCode: '',
  145 + urls: [],
  146 + _currentTab: 'invoiceApplyDetailFee'
  147 + }
  148 + }
  149 + },
  150 + created() {
  151 + this.invoiceApplyDetailInfo.applyId = this.$route.query.applyId
  152 + if (this.invoiceApplyDetailInfo.applyId) {
  153 + this.loadInvoiceApplyInfo()
  154 + }
  155 + },
  156 + methods: {
  157 + async loadInvoiceApplyInfo() {
  158 + try {
  159 + const params = {
  160 + page: 1,
  161 + row: 1,
  162 + applyId: this.invoiceApplyDetailInfo.applyId
  163 + }
  164 + const res = await getInvoiceApplyDetail(params)
  165 + if (res.code === 0 && res.data && res.data.length > 0) {
  166 + Object.assign(this.invoiceApplyDetailInfo, res.data[0])
  167 + }
  168 + } catch (error) {
  169 + this.$message.error(this.$t('invoiceApplyDetail.fetchError'))
  170 + }
  171 + },
  172 + handleTabClick(tab) {
  173 + this.invoiceApplyDetailInfo._currentTab = tab.name
  174 + },
  175 + goBack() {
  176 + this.$router.go(-1)
  177 + }
  178 + }
  179 +}
  180 +</script>
  181 +
  182 +<style scoped>
  183 +.invoice-apply-detail {
  184 + padding: 20px;
  185 +}
  186 +
  187 +.card-header {
  188 + display: flex;
  189 + justify-content: space-between;
  190 + align-items: center;
  191 +}
  192 +
  193 +.text-title {
  194 + font-size: 18px;
  195 + font-weight: bold;
  196 +}
  197 +
  198 +.margin-top {
  199 + margin-top: 20px;
  200 +}
  201 +
  202 +.padding {
  203 + padding: 20px;
  204 +}
  205 +
  206 +.form-group {
  207 + margin-bottom: 15px;
  208 +}
  209 +
  210 +.col-form-label {
  211 + margin-right: 10px;
  212 +}</style>
0 213 \ No newline at end of file
... ...
src/views/fee/invoiceApplyLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + invoiceApply: {
  4 + searchTitle: 'Search Conditions',
  5 + title: 'Invoice Application',
  6 + apply: 'Apply',
  7 + table: {
  8 + id: 'ID',
  9 + invoiceType: 'Invoice Type',
  10 + ownerName: 'Owner Name',
  11 + applicant: 'Applicant',
  12 + invoiceName: 'Invoice Title',
  13 + taxNumber: 'Taxpayer ID',
  14 + addressPhone: 'Address & Phone',
  15 + amount: 'Amount',
  16 + invoiceCode: 'Invoice Code',
  17 + status: 'Status',
  18 + applyTime: 'Apply Time'
  19 + },
  20 + states: {
  21 + all: 'All',
  22 + pendingReview: 'Pending Review',
  23 + pendingUpload: 'Pending Upload',
  24 + reviewFailed: 'Review Failed',
  25 + pendingReceive: 'Pending Receive',
  26 + received: 'Received'
  27 + },
  28 + personal: 'Personal',
  29 + company: 'Company',
  30 + notUploaded: 'Not Uploaded',
  31 + openInvoice: 'Open Invoice',
  32 + uploadInvoice: 'Upload Invoice',
  33 + reUpload: 'Re-upload',
  34 + verify: 'Verify',
  35 + register: 'Register',
  36 + auditSuccess: 'Audit successful',
  37 + invoiceCodePlaceholder: 'Please enter invoice number',
  38 + invoiceTypePlaceholder: 'Please select invoice type',
  39 + ownerNamePlaceholder: 'Please enter owner name',
  40 + applicantPlaceholder: 'Please enter applicant',
  41 + phonePlaceholder: 'Please enter applicant phone'
  42 + },
  43 + deleteInvoiceApply: {
  44 + title: 'Confirm Operation',
  45 + confirmMessage: 'Confirm to delete this invoice application?'
  46 + },
  47 + uploadInvoicePhoto: {
  48 + title: 'Upload Invoice',
  49 + invoiceCode: 'Invoice Code',
  50 + invoice: 'Invoice',
  51 + codeRequired: 'Required, please enter invoice code',
  52 + photoRequired: 'Please select invoice photo'
  53 + },
  54 + wirteInvoiceEvent: {
  55 + title: 'Register/Verify',
  56 + type: 'Type',
  57 + remark: 'Remark',
  58 + typeRequired: 'Required, please select type',
  59 + remarkRequired: 'Required, please enter remark',
  60 + receive: 'Receive',
  61 + register: 'Register'
  62 + },
  63 + audit: {
  64 + title: 'Audit Information',
  65 + status: 'Audit Status',
  66 + reason: 'Reason',
  67 + statusRequired: 'Please select audit status',
  68 + reasonRequired: 'Required, please enter reason',
  69 + approve: 'Approve',
  70 + reject: 'Reject'
  71 + },
  72 + uploadImage: {
  73 + sizeLimit: 'Image size cannot exceed 2MB'
  74 + }
  75 + },
  76 + zh: {
  77 + invoiceApply: {
  78 + searchTitle: '查询条件',
  79 + title: '申请发票',
  80 + apply: '申请',
  81 + table: {
  82 + id: '编号',
  83 + invoiceType: '发票类型',
  84 + ownerName: '业主名称',
  85 + applicant: '申请人',
  86 + invoiceName: '发票名头',
  87 + taxNumber: '纳税人识别号',
  88 + addressPhone: '地址、电话',
  89 + amount: '申请金额',
  90 + invoiceCode: '发票号',
  91 + status: '审核状态',
  92 + applyTime: '申请时间'
  93 + },
  94 + states: {
  95 + all: '全部',
  96 + pendingReview: '待审核',
  97 + pendingUpload: '待上传',
  98 + reviewFailed: '审核失败',
  99 + pendingReceive: '带领用',
  100 + received: '已领用'
  101 + },
  102 + personal: '个人',
  103 + company: '企业',
  104 + notUploaded: '未上传',
  105 + openInvoice: '开票',
  106 + uploadInvoice: '上传发票',
  107 + reUpload: '重新上传',
  108 + verify: '核销',
  109 + register: '登记',
  110 + auditSuccess: '审核成功',
  111 + invoiceCodePlaceholder: '请填写发票号',
  112 + invoiceTypePlaceholder: '请选择发票类型',
  113 + ownerNamePlaceholder: '请填写业主名称',
  114 + applicantPlaceholder: '请填写申请人',
  115 + phonePlaceholder: '请填写申请人电话'
  116 + },
  117 + deleteInvoiceApply: {
  118 + title: '请确认您的操作',
  119 + confirmMessage: '确定删除申请发票'
  120 + },
  121 + uploadInvoicePhoto: {
  122 + title: '上传发票',
  123 + invoiceCode: '发票编号',
  124 + invoice: '发票',
  125 + codeRequired: '必填,请填写发票编号',
  126 + photoRequired: '请选择发票'
  127 + },
  128 + wirteInvoiceEvent: {
  129 + title: '登记核销',
  130 + type: '类型',
  131 + remark: '说明',
  132 + typeRequired: '必填,请选择类型',
  133 + remarkRequired: '必填,请填写说明',
  134 + receive: '领用',
  135 + register: '登记'
  136 + },
  137 + audit: {
  138 + title: '审核信息',
  139 + status: '审核状态',
  140 + reason: '原因',
  141 + statusRequired: '请审核',
  142 + reasonRequired: '必填,请填写原因',
  143 + approve: '同意',
  144 + reject: '拒绝'
  145 + },
  146 + uploadImage: {
  147 + sizeLimit: '图片大小不能超过 2MB'
  148 + }
  149 + }
  150 +}
0 151 \ No newline at end of file
... ...
src/views/fee/invoiceApplyList.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-row :gutter="20">
  4 + <el-col :span="4">
  5 + <el-card class="border-radius">
  6 + <div class="treeview attendance-staff">
  7 + <ul class="list-group text-center border-radius">
  8 + <li v-for="(item, index) in states" :key="index" @click="swatchState(item)" :class="{
  9 + 'vc-node-selected': conditions.state === item.state
  10 + }" class="list-group-item node-orgTree">
  11 + {{ $t(`invoiceApply.states.${item.stateName}`) }}
  12 + </li>
  13 + </ul>
  14 + </div>
  15 + </el-card>
  16 + </el-col>
  17 + <el-col :span="20">
  18 + <el-row>
  19 + <el-col :span="24">
  20 + <el-card>
  21 + <div slot="header" class="text-left">
  22 + <span>{{ $t('invoiceApply.searchTitle') }}</span>
  23 + </div>
  24 + <el-row :gutter="20">
  25 + <el-col :span="4">
  26 + <el-input v-model="conditions.invoiceCode" :placeholder="$t('invoiceApply.invoiceCodePlaceholder')" />
  27 + </el-col>
  28 + <el-col :span="4">
  29 + <el-select v-model="conditions.invoiceType" :placeholder="$t('invoiceApply.invoiceTypePlaceholder')">
  30 + <el-option label="请选择发票类型" value="" />
  31 + <el-option label="个人" value="1001" />
  32 + <el-option label="企业" value="2002" />
  33 + </el-select>
  34 + </el-col>
  35 + <el-col :span="4">
  36 + <el-input v-model="conditions.ownerName" :placeholder="$t('invoiceApply.ownerNamePlaceholder')" />
  37 + </el-col>
  38 + <el-col :span="4">
  39 + <el-input v-model="conditions.createUserName" :placeholder="$t('invoiceApply.applicantPlaceholder')" />
  40 + </el-col>
  41 + <el-col :span="4">
  42 + <el-input v-model="conditions.applyTel" :placeholder="$t('invoiceApply.phonePlaceholder')" />
  43 + </el-col>
  44 + <el-col :span="4">
  45 + <el-button type="primary" @click="_queryInvoiceApplyMethod">
  46 + <i class="el-icon-search" />
  47 + <span>{{ $t('common.search') }}</span>
  48 + </el-button>
  49 + </el-col>
  50 + </el-row>
  51 + </el-card>
  52 + </el-col>
  53 + </el-row>
  54 + <el-row>
  55 + <el-col :span="24">
  56 + <el-card>
  57 + <div slot="header" class="flex justify-between">
  58 + <span>{{ $t('invoiceApply.title') }}</span>
  59 + <el-button type="primary" size="small" class="float-right" @click="_invoiceApply">
  60 + {{ $t('invoiceApply.apply') }}
  61 + </el-button>
  62 + </div>
  63 + <el-table :data="invoiceApplys" border style="width: 100%">
  64 + <el-table-column prop="applyId" :label="$t('invoiceApply.table.id')" align="center" />
  65 + <el-table-column prop="invoiceType" :label="$t('invoiceApply.table.invoiceType')" align="center">
  66 + <template slot-scope="scope">
  67 + {{ scope.row.invoiceType === '1001' ? $t('invoiceApply.personal') : $t('invoiceApply.company') }}
  68 + </template>
  69 + </el-table-column>
  70 + <el-table-column prop="ownerName" :label="$t('invoiceApply.table.ownerName')" align="center" />
  71 + <el-table-column :label="$t('invoiceApply.table.applicant')" align="center">
  72 + <template slot-scope="scope">
  73 + {{ scope.row.createUserName }}({{ scope.row.applyTel }})
  74 + </template>
  75 + </el-table-column>
  76 + <el-table-column prop="invoiceName" :label="$t('invoiceApply.table.invoiceName')" align="center" />
  77 + <el-table-column prop="invoiceNum" :label="$t('invoiceApply.table.taxNumber')" align="center" />
  78 + <el-table-column prop="invoiceAddress" :label="$t('invoiceApply.table.addressPhone')" align="center" />
  79 + <el-table-column prop="invoiceAmount" :label="$t('invoiceApply.table.amount')" align="center" />
  80 + <el-table-column prop="invoiceCode" :label="$t('invoiceApply.table.invoiceCode')" align="center">
  81 + <template slot-scope="scope">
  82 + {{ scope.row.invoiceCode || $t('invoiceApply.notUploaded') }}
  83 + </template>
  84 + </el-table-column>
  85 + <el-table-column prop="stateName" :label="$t('invoiceApply.table.status')" align="center" />
  86 + <el-table-column prop="createTime" :label="$t('invoiceApply.table.applyTime')" align="center" />
  87 + <el-table-column :label="$t('common.operation')" align="center" width="220">
  88 + <template slot-scope="scope">
  89 + <el-button v-if="scope.row.state === 'W'" size="mini" @click="_openInvoiceAuditModel(scope.row)">
  90 + {{ $t('common.audit') }}
  91 + </el-button>
  92 + <template v-else>
  93 + <el-button v-if="hasPlugin('invoice') && scope.row.state !== 'W'" size="mini"
  94 + @click="_toOpenInvoicePlugin(scope.row)">
  95 + {{ $t('invoiceApply.openInvoice') }}
  96 + </el-button>
  97 + <div v-else>
  98 + <el-button v-if="['U', 'G', 'C'].includes(scope.row.state)" size="mini"
  99 + @click="_openUploadInvoicePhoto(scope.row)">
  100 + {{ scope.row.state === 'U' ? $t('invoiceApply.uploadInvoice') : $t('invoiceApply.reUpload') }}
  101 + </el-button>
  102 + <el-button v-if="['U', 'G', 'C'].includes(scope.row.state)" size="mini"
  103 + @click="_openUserGetInvoice(scope.row)">
  104 + {{ scope.row.state === 'G' ? $t('invoiceApply.verify') : $t('invoiceApply.register') }}
  105 + </el-button>
  106 + </div>
  107 + </template>
  108 + <el-button size="mini" type="danger" @click="_openDeleteInvoiceApplyModel(scope.row)">
  109 + {{ $t('common.delete') }}
  110 + </el-button>
  111 + <el-button size="mini" @click="_openInvoiceApplyDetail(scope.row)">
  112 + {{ $t('common.detail') }}
  113 + </el-button>
  114 + </template>
  115 + </el-table-column>
  116 + </el-table>
  117 + <el-pagination :current-page="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  118 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  119 + @current-change="handleCurrentChange" />
  120 + </el-card>
  121 + </el-col>
  122 + </el-row>
  123 + </el-col>
  124 + </el-row>
  125 +
  126 + <delete-invoice-apply ref="deleteDialog" @success="listInvoiceApplys" />
  127 + <upload-invoice-photo ref="uploadPhotoDialog" @success="listInvoiceApplys" />
  128 + <wirte-invoice-event ref="writeEventDialog" @success="listInvoiceApplys" />
  129 + <audit-dialog ref="auditDialog" @success="notifyAuditInfo" />
  130 + </div>
  131 +</template>
  132 +
  133 +<script>
  134 +import { listInvoiceApply, auditInvoiceApply } from '@/api/fee/invoiceApplyApi'
  135 +import { getCommunityId } from '@/api/community/communityApi'
  136 +import DeleteInvoiceApply from '@/components/fee/deleteInvoiceApply'
  137 +import UploadInvoicePhoto from '@/components/fee/uploadInvoicePhoto'
  138 +import WirteInvoiceEvent from '@/components/fee/wirteInvoiceEvent'
  139 +import AuditDialog from '@/components/fee/audit'
  140 +
  141 +export default {
  142 + name: 'InvoiceApplyList',
  143 + components: {
  144 + DeleteInvoiceApply,
  145 + UploadInvoicePhoto,
  146 + WirteInvoiceEvent,
  147 + AuditDialog
  148 + },
  149 + data() {
  150 + return {
  151 + states: [
  152 + { stateName: 'all', state: '' },
  153 + { stateName: 'pendingReview', state: 'W' },
  154 + { stateName: 'pendingUpload', state: 'U' },
  155 + { stateName: 'reviewFailed', state: 'F' },
  156 + { stateName: 'pendingReceive', state: 'G' },
  157 + { stateName: 'received', state: 'C' }
  158 + ],
  159 + conditions: {
  160 + invoiceCode: '',
  161 + invoiceType: '',
  162 + ownerName: '',
  163 + applyTel: '',
  164 + createUserName: '',
  165 + state: ''
  166 + },
  167 + invoiceApplys: [],
  168 + page: {
  169 + current: 1,
  170 + size: 10,
  171 + total: 0
  172 + },
  173 + auditData: {}
  174 + }
  175 + },
  176 + created() {
  177 + this.communityId = getCommunityId()
  178 + this.listInvoiceApplys()
  179 + },
  180 + methods: {
  181 + async listInvoiceApplys() {
  182 + try {
  183 + const params = {
  184 + ...this.conditions,
  185 + page: this.page.current,
  186 + row: this.page.size,
  187 + communityId: this.communityId
  188 + }
  189 + const res = await listInvoiceApply(params)
  190 + this.invoiceApplys = res.data
  191 + this.page.total = res.total
  192 + } catch (error) {
  193 + this.$message.error(this.$t('common.fetchError'))
  194 + }
  195 + },
  196 + handleSizeChange(size) {
  197 + this.page.size = size
  198 + this.listInvoiceApplys()
  199 + },
  200 + handleCurrentChange(current) {
  201 + this.page.current = current
  202 + this.listInvoiceApplys()
  203 + },
  204 + swatchState(item) {
  205 + this.conditions.state = item.state
  206 + this.listInvoiceApplys()
  207 + },
  208 + _queryInvoiceApplyMethod() {
  209 + this.page.current = 1
  210 + this.listInvoiceApplys()
  211 + },
  212 + _invoiceApply() {
  213 + this.$router.push('/views/fee/ownerApplyInvoice')
  214 + },
  215 + _openInvoiceApplyDetail(row) {
  216 + this.$router.push(`/views/fee/invoiceApplyDetail?applyId=${row.applyId}`)
  217 + },
  218 + _openInvoiceAuditModel(row) {
  219 + this.auditData = { ...row }
  220 + this.$refs.auditDialog.open()
  221 + },
  222 + async notifyAuditInfo(auditInfo) {
  223 + try {
  224 + const params = {
  225 + ...this.auditData,
  226 + state: auditInfo.state,
  227 + remark: auditInfo.remark,
  228 + communityId: this.communityId
  229 + }
  230 + await auditInvoiceApply(params)
  231 + this.$message.success(this.$t('invoiceApply.auditSuccess'))
  232 + this.listInvoiceApplys()
  233 + } catch (error) {
  234 + this.$message.error(error.message)
  235 + }
  236 + },
  237 + _openUploadInvoicePhoto(row) {
  238 + this.$refs.uploadPhotoDialog.open(row)
  239 + },
  240 + _openUserGetInvoice(row) {
  241 + this.$refs.writeEventDialog.open(row)
  242 + },
  243 + _toOpenInvoicePlugin(row) {
  244 + const userInfo = this.$store.getters.userInfo
  245 + this.$router.push({
  246 + path: '/plugin',
  247 + query: {
  248 + pluginType: 'invoice',
  249 + orderNo: row.applyId,
  250 + staffName: userInfo.name,
  251 + communityId: this.communityId
  252 + }
  253 + })
  254 + },
  255 + _openDeleteInvoiceApplyModel(row) {
  256 + this.$refs.deleteDialog.open(row)
  257 + },
  258 + hasPlugin(pluginName) {
  259 + // 实际项目中需要根据业务逻辑实现
  260 + console.log(pluginName)
  261 + return true
  262 + }
  263 + }
  264 +}
  265 +</script>
  266 +
  267 +<style scoped>
  268 +.border-radius {
  269 + border-radius: 4px;
  270 +}
  271 +
  272 +.list-group {
  273 + padding: 0;
  274 + margin: 0;
  275 + list-style: none;
  276 +}
  277 +
  278 +.list-group-item {
  279 + padding: 10px 15px;
  280 + margin-bottom: -1px;
  281 + background-color: #fff;
  282 + border: 1px solid #ddd;
  283 + cursor: pointer;
  284 +}
  285 +
  286 +.list-group-item:hover,
  287 +.vc-node-selected {
  288 + background-color: #f5f7fa;
  289 + color: #409EFF;
  290 +}
  291 +
  292 +.float-right {
  293 + float: right;
  294 +}
  295 +</style>
0 296 \ No newline at end of file
... ...
src/views/fee/ownerApplyInvoiceLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + ownerApplyInvoice: {
  4 + title: 'Invoice Application',
  5 + invoiceTitle: 'Invoice Title',
  6 + taxpayerId: 'Taxpayer ID',
  7 + invoiceType: 'Invoice Type',
  8 + addressPhone: 'Address & Phone',
  9 + owner: 'Owner',
  10 + content: 'Content',
  11 + personal: 'Personal',
  12 + enterprise: 'Enterprise',
  13 + paidFees: 'Paid Fees',
  14 + accountDeposit: 'Account Deposit',
  15 + feeName: 'Fee Item',
  16 + payer: 'Payer',
  17 + receivablePaid: 'Receivable/Paid',
  18 + paymentMethod: 'Payment Method',
  19 + paymentPeriod: 'Payment Period',
  20 + paymentTime: 'Payment Time',
  21 + cashier: 'Cashier',
  22 + status: 'Status',
  23 + remark: 'Remark',
  24 + accountName: 'Account Name',
  25 + accountType: 'Account Type',
  26 + depositAmount: 'Deposit Amount',
  27 + depositMethod: 'Deposit Method',
  28 + totalAmount: 'Total Amount',
  29 + depositTime: 'Deposit Time',
  30 + back: 'Back',
  31 + select: 'Select',
  32 + submit: 'Submit',
  33 + required: 'Required',
  34 + chooseInvoiceTitle: 'Please select invoice title',
  35 + chooseTaxId: 'Please select taxpayer ID',
  36 + chooseInvoiceType: 'Please select invoice type',
  37 + chooseAddressPhone: 'Please enter address and phone',
  38 + chooseContent: 'Please select content',
  39 + noFeeSelected: 'No paid fees selected',
  40 + noDepositSelected: 'No account deposit selected'
  41 + },
  42 + searchOwnerInvoice: {
  43 + title: 'Select Owner',
  44 + ownerName: 'Owner Name',
  45 + invoiceType: 'Invoice Type',
  46 + invoiceTitle: 'Invoice Title',
  47 + taxpayerId: 'Taxpayer ID',
  48 + addressPhone: 'Address & Phone',
  49 + operation: 'Operation',
  50 + search: 'Search',
  51 + placeholderRoom: 'Enter room number (building-unit-room)',
  52 + placeholderOwner: 'Enter owner name',
  53 + select: 'Select',
  54 + personal: 'Personal',
  55 + company: 'Company'
  56 + }
  57 + },
  58 + zh: {
  59 + ownerApplyInvoice: {
  60 + title: '发票申请',
  61 + invoiceTitle: '发票抬头',
  62 + taxpayerId: '纳税人识别号',
  63 + invoiceType: '发票类型',
  64 + addressPhone: '地址、电话',
  65 + owner: '业主',
  66 + content: '开票内容',
  67 + personal: '个人',
  68 + enterprise: '企业',
  69 + paidFees: '已缴费费用',
  70 + accountDeposit: '账户预存',
  71 + feeName: '费用项',
  72 + payer: '收费对象',
  73 + receivablePaid: '应收/实收',
  74 + paymentMethod: '缴费方式',
  75 + paymentPeriod: '缴费起始段',
  76 + paymentTime: '缴费时间',
  77 + cashier: '收银员',
  78 + status: '状态',
  79 + remark: '备注',
  80 + accountName: '账户名称',
  81 + accountType: '账户类型',
  82 + depositAmount: '预存金额',
  83 + depositMethod: '预存方式',
  84 + totalAmount: '总金额',
  85 + depositTime: '预存时间',
  86 + back: '返回',
  87 + select: '选择',
  88 + submit: '提交',
  89 + required: '必填',
  90 + chooseInvoiceTitle: '请选择发票抬头',
  91 + chooseTaxId: '请选择纳税人识别号',
  92 + chooseInvoiceType: '请选择发票类型',
  93 + chooseAddressPhone: '请输入地址、电话',
  94 + chooseContent: '请选择开票内容',
  95 + noFeeSelected: '未选择已缴费费用',
  96 + noDepositSelected: '未选择账户预存'
  97 + },
  98 + searchOwnerInvoice: {
  99 + title: '选择业主',
  100 + ownerName: '业主名称',
  101 + invoiceType: '发票类型',
  102 + invoiceTitle: '发票名头',
  103 + taxpayerId: '纳税人识别号',
  104 + addressPhone: '地址、电话',
  105 + operation: '操作',
  106 + search: '查询',
  107 + placeholderRoom: '输入房屋编号楼栋-单元-房屋',
  108 + placeholderOwner: '输入业主名称',
  109 + select: '选择',
  110 + personal: '个人',
  111 + company: '公司'
  112 + }
  113 + }
  114 +}
0 115 \ No newline at end of file
... ...
src/views/fee/ownerApplyInvoiceList.vue 0 → 100644
  1 +<template>
  2 + <div class="owner-apply-invoice-container">
  3 + <el-card>
  4 + <div slot="header" class="clearfix flex justify-between">
  5 + <h3>{{ $t('ownerApplyInvoice.title') }}</h3>
  6 + <div class="header-actions">
  7 + <el-button type="primary" size="small" @click="goBack">
  8 + {{ $t('ownerApplyInvoice.back') }}
  9 + </el-button>
  10 + </div>
  11 + </div>
  12 +
  13 + <el-form ref="form" :model="formData" label-width="150px">
  14 + <el-row :gutter="20">
  15 + <el-col :span="8">
  16 + <el-form-item :label="$t('ownerApplyInvoice.invoiceTitle')" required>
  17 + <el-input v-model="formData.invoiceName" :placeholder="$t('ownerApplyInvoice.chooseInvoiceTitle')"
  18 + disabled />
  19 + </el-form-item>
  20 + </el-col>
  21 + <el-col :span="4" class="text-left">
  22 + <el-button type="primary" size="small" style="margin-left: 150px; margin-bottom: 20px"
  23 + @click="openSelectOwner">
  24 + {{ $t('ownerApplyInvoice.select') }}
  25 + </el-button></el-col>
  26 + <el-col :span="12">
  27 + <el-form-item :label="$t('ownerApplyInvoice.taxpayerId')" required>
  28 + <el-input v-model="formData.invoiceNum" :placeholder="$t('ownerApplyInvoice.chooseTaxId')" disabled />
  29 + </el-form-item>
  30 + </el-col>
  31 + </el-row>
  32 +
  33 + <el-row :gutter="20">
  34 + <el-col :span="12">
  35 + <el-form-item :label="$t('ownerApplyInvoice.invoiceType')" required>
  36 + <el-select v-model="formData.invoiceType" :placeholder="$t('ownerApplyInvoice.chooseInvoiceType')"
  37 + style="width:100%">
  38 + <el-option :label="$t('ownerApplyInvoice.personal')" value="1001" />
  39 + <el-option :label="$t('ownerApplyInvoice.enterprise')" value="2002" />
  40 + </el-select>
  41 + </el-form-item>
  42 + </el-col>
  43 + <el-col :span="12">
  44 + <el-form-item :label="$t('ownerApplyInvoice.addressPhone')" required>
  45 + <el-input v-model="formData.invoiceAddress" :placeholder="$t('ownerApplyInvoice.chooseAddressPhone')" />
  46 + </el-form-item>
  47 + </el-col>
  48 + </el-row>
  49 +
  50 + <el-row :gutter="20">
  51 + <el-col :span="12">
  52 + <el-form-item :label="$t('ownerApplyInvoice.owner')" required>
  53 + <el-input v-model="formData.ownerName" :placeholder="$t('ownerApplyInvoice.chooseContent')" disabled />
  54 + </el-form-item>
  55 + </el-col>
  56 + <el-col :span="12">
  57 + <el-form-item :label="$t('ownerApplyInvoice.content')" required>
  58 + <el-select v-model="formData.invoiceFlag" :placeholder="$t('ownerApplyInvoice.chooseContent')"
  59 + style="width:100%" @change="handleContentChange">
  60 + <el-option :label="$t('ownerApplyInvoice.paidFees')" value="FEE" />
  61 + <el-option :label="$t('ownerApplyInvoice.accountDeposit')" value="ACCT" />
  62 + </el-select>
  63 + </el-form-item>
  64 + </el-col>
  65 + </el-row>
  66 + </el-form>
  67 +
  68 +
  69 + </el-card>
  70 +
  71 + <!-- 已缴费费用 -->
  72 + <el-card v-if="formData.invoiceFlag === 'FEE'" class="mt-20">
  73 + <div slot="header" class="flex justify-between">
  74 + <h3>{{ $t('ownerApplyInvoice.paidFees') }}</h3>
  75 + </div>
  76 +
  77 + <el-table :data="feeDetails" border style="width: 100%">
  78 + <el-table-column type="selection" width="55" align="center" @select-all="handleFeeSelectAll" />
  79 + <el-table-column prop="feeName" :label="$t('ownerApplyInvoice.feeName')" align="center" />
  80 + <el-table-column prop="payerObjName" :label="$t('ownerApplyInvoice.payer')" align="center" />
  81 + <el-table-column :label="$t('ownerApplyInvoice.receivablePaid')" align="center">
  82 + <template slot-scope="scope">
  83 + {{ scope.row.receivableAmount }}/{{ scope.row.receivedAmount }}
  84 + </template>
  85 + </el-table-column>
  86 + <el-table-column prop="primeRateName" :label="$t('ownerApplyInvoice.paymentMethod')" align="center" />
  87 + <el-table-column :label="$t('ownerApplyInvoice.paymentPeriod')" align="center">
  88 + <template slot-scope="scope">
  89 + {{ formatDate(scope.row.startTime) }}~<br>
  90 + {{ formatDate(scope.row.endTime) }}
  91 + </template>
  92 + </el-table-column>
  93 + <el-table-column prop="createTime" :label="$t('ownerApplyInvoice.paymentTime')" align="center" />
  94 + <el-table-column prop="cashierName" :label="$t('ownerApplyInvoice.cashier')" align="center">
  95 + <template slot-scope="scope">
  96 + {{ scope.row.cashierName || '-' }}
  97 + </template>
  98 + </el-table-column>
  99 + <el-table-column prop="stateName" :label="$t('ownerApplyInvoice.status')" align="center" />
  100 + <el-table-column prop="remark" :label="$t('ownerApplyInvoice.remark')" align="center" />
  101 + </el-table>
  102 + </el-card>
  103 +
  104 + <!-- 账户预存 -->
  105 + <el-card v-if="formData.invoiceFlag === 'ACCT'" class="mt-20">
  106 + <div slot="" class="flex justify-between">
  107 + <h3>{{ $t('ownerApplyInvoice.accountDeposit') }}</h3>
  108 + </div>
  109 +
  110 + <el-table :data="acctDetails" border style="width: 100%">
  111 + <el-table-column type="selection" width="55" align="center" @select-all="handleAcctSelectAll" />
  112 + <el-table-column prop="acctName" :label="$t('ownerApplyInvoice.accountName')" align="center" />
  113 + <el-table-column prop="acctTypeName" :label="$t('ownerApplyInvoice.accountType')" align="center" />
  114 + <el-table-column prop="ownerName" :label="$t('ownerApplyInvoice.owner')" align="center" />
  115 + <el-table-column prop="receivedAmount" :label="$t('ownerApplyInvoice.depositAmount')" align="center" />
  116 + <el-table-column prop="primeRateName" :label="$t('ownerApplyInvoice.depositMethod')" align="center" />
  117 + <el-table-column prop="amount" :label="$t('ownerApplyInvoice.totalAmount')" align="center" />
  118 + <el-table-column prop="createTime" :label="$t('ownerApplyInvoice.depositTime')" align="center" />
  119 + </el-table>
  120 + </el-card>
  121 +
  122 + <div class="mt-20 text-right">
  123 + <el-button type="primary" @click="handleSubmit">
  124 + {{ $t('ownerApplyInvoice.submit') }}
  125 + </el-button>
  126 + </div>
  127 +
  128 + <search-owner-invoice ref="searchOwnerInvoice" :visible.sync="showSearchDialog" @select="handleOwnerSelect" />
  129 + </div>
  130 +</template>
  131 +
  132 +<script>
  133 +import { queryFeeDetail, listAccountReceipt, saveInvoiceApply } from '@/api/fee/ownerApplyInvoiceApi'
  134 +import SearchOwnerInvoice from '@/components/fee/searchOwnerInvoice'
  135 +import { formatDate } from '@/utils/dateUtil'
  136 +
  137 +export default {
  138 + name: 'OwnerApplyInvoiceList',
  139 + components: {
  140 + SearchOwnerInvoice
  141 + },
  142 + data() {
  143 + return {
  144 + showSearchDialog: false,
  145 + formData: {
  146 + oiId: '',
  147 + ownerId: '',
  148 + ownerName: '',
  149 + invoiceType: '',
  150 + invoiceName: '',
  151 + invoiceAddress: '',
  152 + invoiceNum: '',
  153 + detailIds: [],
  154 + feeDetails: [],
  155 + invoiceFlag: 'FEE',
  156 + arIds: [],
  157 + acctDetails: []
  158 + },
  159 + feeDetails: [],
  160 + acctDetails: []
  161 + }
  162 + },
  163 + methods: {
  164 + formatDate,
  165 + goBack() {
  166 + this.$router.go(-1)
  167 + },
  168 + openSelectOwner() {
  169 + this.showSearchDialog = true
  170 + this.$nextTick(() => {
  171 + this.$refs.searchOwnerInvoice.open()
  172 + })
  173 + },
  174 + handleOwnerSelect(owner) {
  175 + Object.assign(this.formData, owner)
  176 + this.formData.detailIds = []
  177 + this.formData.arIds = []
  178 +
  179 + if (this.formData.invoiceFlag === 'FEE') {
  180 + this.loadFeeDetails()
  181 + } else {
  182 + this.loadAcctDetails()
  183 + }
  184 + },
  185 + async loadFeeDetails() {
  186 + try {
  187 + const params = {
  188 + page: 1,
  189 + row: 50,
  190 + ownerId: this.formData.ownerId,
  191 + openInvoice: 'N'
  192 + }
  193 +
  194 + const res = await queryFeeDetail(params)
  195 + this.feeDetails = res.feeDetails || []
  196 +
  197 + // 默认全选
  198 + this.formData.detailIds = this.feeDetails.map(item => item.detailId)
  199 + } catch (error) {
  200 + console.error('Failed to load fee details:', error)
  201 + this.$message.error(this.$t('common.loadFailed'))
  202 + }
  203 + },
  204 + async loadAcctDetails() {
  205 + try {
  206 + const params = {
  207 + page: 1,
  208 + row: 50,
  209 + ownerId: this.formData.ownerId
  210 + }
  211 +
  212 + const res = await listAccountReceipt(params)
  213 + this.acctDetails = res.data || []
  214 +
  215 + // 默认全选
  216 + this.formData.arIds = this.acctDetails.map(item => item.arId)
  217 + } catch (error) {
  218 + console.error('Failed to load account details:', error)
  219 + this.$message.error(this.$t('common.loadFailed'))
  220 + }
  221 + },
  222 + handleContentChange() {
  223 + if (!this.formData.ownerId) return
  224 +
  225 + this.formData.detailIds = []
  226 + this.formData.arIds = []
  227 +
  228 + if (this.formData.invoiceFlag === 'FEE') {
  229 + this.loadFeeDetails()
  230 + } else {
  231 + this.loadAcctDetails()
  232 + }
  233 + },
  234 + handleFeeSelectAll(selection) {
  235 + this.formData.detailIds = selection.length
  236 + ? this.feeDetails.map(item => item.detailId)
  237 + : []
  238 + },
  239 + handleAcctSelectAll(selection) {
  240 + this.formData.arIds = selection.length
  241 + ? this.acctDetails.map(item => item.arId)
  242 + : []
  243 + },
  244 + async handleSubmit() {
  245 + try {
  246 + // 验证表单
  247 + if (!this.formData.ownerId) {
  248 + this.$message.warning(this.$t('ownerApplyInvoice.chooseContent'))
  249 + return
  250 + }
  251 +
  252 + if (this.formData.invoiceFlag === 'FEE' && !this.formData.detailIds.length) {
  253 + this.$message.warning(this.$t('ownerApplyInvoice.noFeeSelected'))
  254 + return
  255 + }
  256 +
  257 + if (this.formData.invoiceFlag === 'ACCT' && !this.formData.arIds.length) {
  258 + this.$message.warning(this.$t('ownerApplyInvoice.noDepositSelected'))
  259 + return
  260 + }
  261 +
  262 + // 准备提交数据
  263 + const submitData = {
  264 + ...this.formData,
  265 + detailIds: this.formData.invoiceFlag === 'FEE' ? this.formData.detailIds : [],
  266 + arIds: this.formData.invoiceFlag === 'ACCT' ? this.formData.arIds : []
  267 + }
  268 +
  269 + // 调用API
  270 + await saveInvoiceApply(submitData)
  271 + this.$message.success(this.$t('common.submitSuccess'))
  272 + this.goBack()
  273 + } catch (error) {
  274 + console.error('Failed to submit invoice application:', error)
  275 + this.$message.error(error.message || this.$t('common.submitFailed'))
  276 + }
  277 + }
  278 + }
  279 +}
  280 +</script>
  281 +
  282 +<style scoped>
  283 +.owner-apply-invoice-container {
  284 + padding: 20px;
  285 +}
  286 +
  287 +.mt-20 {
  288 + margin-top: 20px;
  289 +}
  290 +
  291 +.text-right {
  292 + text-align: right;
  293 +}
  294 +
  295 +.header-actions {}
  296 +</style>
0 297 \ No newline at end of file
... ...
src/views/fee/ownerInvoiceLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + searchCondition: 'Search Conditions',
  4 + ownerNamePlaceholder: 'Enter owner name',
  5 + invoiceTypePlaceholder: 'Select invoice type',
  6 + personal: 'Personal',
  7 + enterprise: 'Enterprise',
  8 + invoiceHeaderPlaceholder: 'Enter invoice header',
  9 + search: 'Search',
  10 + invoiceHeader: 'Invoice Header',
  11 + add: 'Add',
  12 + serialNumber: 'Serial Number',
  13 + ownerName: 'Owner Name',
  14 + invoiceType: 'Invoice Type',
  15 + taxpayerId: 'Taxpayer ID',
  16 + address: 'Address',
  17 + phone: 'Phone',
  18 + bankAccount: 'Bank Account',
  19 + remark: 'Remark',
  20 + operation: 'Operation',
  21 + modify: 'Modify',
  22 + delete: 'Delete',
  23 + confirmDeleteTitle: 'Confirm Deletion',
  24 + confirmDeleteContent: 'Are you sure to delete this invoice header?',
  25 + cancel: 'Cancel',
  26 + confirmDelete: 'Confirm Delete',
  27 + editTitle: 'Edit',
  28 + requiredInvoiceType: 'Invoice type is required',
  29 + requiredInvoiceHeader: 'Invoice header is required',
  30 + requiredTaxpayerId: 'Taxpayer ID is required',
  31 + requiredAddress: 'Address is required',
  32 + requiredPhone: 'Phone is required',
  33 + requiredBankAccount: 'Bank account is required',
  34 + optionalRemark: 'Remark (optional)',
  35 + save: 'Save',
  36 + addOwnerInvoice: {
  37 + title: 'Add Invoice',
  38 + back: 'Back',
  39 + owner: 'Owner',
  40 + selectOwner: 'Select Owner',
  41 + invoiceType: 'Invoice Type',
  42 + invoiceTitle: 'Invoice Title',
  43 + taxNumber: 'Taxpayer ID',
  44 + address: 'Address',
  45 + phone: 'Phone',
  46 + bankAccount: 'Bank Account',
  47 + remarks: 'Remarks',
  48 + save: 'Save',
  49 + required: 'Required',
  50 + personal: 'Personal',
  51 + enterprise: 'Enterprise',
  52 + placeholder: {
  53 + owner: 'Please select owner',
  54 + invoiceType: 'Please select invoice type',
  55 + invoiceTitle: 'Please enter invoice title',
  56 + taxNumber: 'Please enter taxpayer ID',
  57 + address: 'Please enter address',
  58 + phone: 'Please enter phone number',
  59 + bankAccount: 'Please enter bank account',
  60 + remarks: 'Optional remarks'
  61 + },
  62 + validation: {
  63 + ownerRequired: 'Owner is required',
  64 + typeRequired: 'Invoice type is required',
  65 + titleRequired: 'Invoice title is required',
  66 + taxNumberRequired: 'Taxpayer ID is required',
  67 + addressRequired: 'Address is required',
  68 + phoneRequired: 'Phone is required',
  69 + bankAccountRequired: 'Bank account is required'
  70 + }
  71 + },
  72 + },
  73 + zh: {
  74 + searchCondition: '查询条件',
  75 + ownerNamePlaceholder: '请输入业主名称',
  76 + invoiceTypePlaceholder: '请选择发票类型',
  77 + personal: '个人',
  78 + enterprise: '企业',
  79 + invoiceHeaderPlaceholder: '请输入发票名头',
  80 + search: '查询',
  81 + invoiceHeader: '发票抬头',
  82 + add: '添加',
  83 + serialNumber: '编号',
  84 + ownerName: '业主名称',
  85 + invoiceType: '发票类型',
  86 + taxpayerId: '纳税人识别号',
  87 + address: '地址',
  88 + phone: '电话',
  89 + bankAccount: '开户行及账号',
  90 + remark: '备注',
  91 + operation: '操作',
  92 + modify: '修改',
  93 + delete: '删除',
  94 + confirmDeleteTitle: '请确认您的操作',
  95 + confirmDeleteContent: '确定删除发票抬头',
  96 + cancel: '点错了',
  97 + confirmDelete: '确认删除',
  98 + editTitle: '修改',
  99 + requiredInvoiceType: '必填,请选择发票类型',
  100 + requiredInvoiceHeader: '必填,请填写发票名头',
  101 + requiredTaxpayerId: '必填,请填写纳税人识别号',
  102 + requiredAddress: '必填,请填写地址',
  103 + requiredPhone: '必填,请填写电话',
  104 + requiredBankAccount: '必填,请填写开户行及账号',
  105 + optionalRemark: '选填,请填写备注',
  106 + save: '保存',
  107 + addOwnerInvoice: {
  108 + title: '添加发票抬头',
  109 + back: '返回',
  110 + owner: '业主',
  111 + selectOwner: '选择业主',
  112 + invoiceType: '发票类型',
  113 + invoiceTitle: '发票名头',
  114 + taxNumber: '纳税人识别号',
  115 + address: '地址',
  116 + phone: '电话',
  117 + bankAccount: '开户行及账号',
  118 + remarks: '备注',
  119 + save: '保存',
  120 + required: '必填',
  121 + personal: '个人',
  122 + enterprise: '企业',
  123 + placeholder: {
  124 + owner: '请选择业主',
  125 + invoiceType: '请选择发票类型',
  126 + invoiceTitle: '请填写发票名头',
  127 + taxNumber: '请填写纳税人识别号',
  128 + address: '请填写地址',
  129 + phone: '请填写电话',
  130 + bankAccount: '请填写开户行及账号',
  131 + remarks: '选填备注'
  132 + },
  133 + validation: {
  134 + ownerRequired: '业主不能为空',
  135 + typeRequired: '发票类型不能为空',
  136 + titleRequired: '发票名头不能为空',
  137 + taxNumberRequired: '纳税人识别号不能为空',
  138 + addressRequired: '地址不能为空',
  139 + phoneRequired: '电话不能为空',
  140 + bankAccountRequired: '开户行及账号不能为空'
  141 + }
  142 + },
  143 + }
  144 +}
0 145 \ No newline at end of file
... ...
src/views/fee/ownerInvoiceList.vue 0 → 100644
  1 +<template>
  2 + <div class="owner-invoice-container">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-card">
  5 + <div slot="header" class="clearfix">
  6 + <span>{{ $t('searchCondition') }}</span>
  7 + </div>
  8 +
  9 + <el-row :gutter="20">
  10 + <el-col :span="6">
  11 + <el-input v-model="searchForm.ownerName" :placeholder="$t('ownerNamePlaceholder')" />
  12 + </el-col>
  13 +
  14 + <el-col :span="6">
  15 + <el-select v-model="searchForm.invoiceType" :placeholder="$t('invoiceTypePlaceholder')" style="width: 100%">
  16 + <el-option value="" :label="$t('invoiceTypePlaceholder')" />
  17 + <el-option value="1001" :label="$t('personal')" />
  18 + <el-option value="2002" :label="$t('enterprise')" />
  19 + </el-select>
  20 + </el-col>
  21 +
  22 + <el-col :span="6">
  23 + <el-input v-model="searchForm.invoiceName" :placeholder="$t('invoiceHeaderPlaceholder')" />
  24 + </el-col>
  25 +
  26 + <el-col :span="6">
  27 + <el-button type="primary" @click="fetchData">{{ $t('search') }}</el-button>
  28 + </el-col>
  29 + </el-row>
  30 + </el-card>
  31 +
  32 + <!-- 发票抬头列表 -->
  33 + <el-card class="list-card">
  34 + <div slot="header" class="clearfix">
  35 + <span>{{ $t('invoiceHeader') }}</span>
  36 + <el-button type="primary" size="small" style="float: right" @click="handleAdd">
  37 + {{ $t('add') }}
  38 + </el-button>
  39 + </div>
  40 +
  41 + <el-table :data="tableData" border style="width: 100%" v-loading="loading">
  42 + <el-table-column prop="oiId" :label="$t('serialNumber')" align="center" min-width="80" />
  43 +
  44 + <el-table-column prop="ownerName" :label="$t('ownerName')" align="center" min-width="120" />
  45 +
  46 + <el-table-column prop="invoiceType" :label="$t('invoiceType')" align="center" min-width="100">
  47 + <template slot-scope="scope">
  48 + {{ scope.row.invoiceType === '1001' ? $t('personal') : $t('enterprise') }}
  49 + </template>
  50 + </el-table-column>
  51 +
  52 + <el-table-column prop="invoiceName" :label="$t('invoiceHeader')" align="center" min-width="150" />
  53 +
  54 + <el-table-column prop="invoiceNum" :label="$t('taxpayerId')" align="center" min-width="150" />
  55 +
  56 + <el-table-column prop="invoiceAddress" :label="$t('address')" align="center" min-width="150" />
  57 +
  58 + <el-table-column prop="invoiceLink" :label="$t('phone')" align="center" min-width="120" />
  59 +
  60 + <el-table-column prop="invoiceAccount" :label="$t('bankAccount')" align="center" min-width="200" />
  61 +
  62 + <el-table-column prop="remark" :label="$t('remark')" align="center" min-width="150" />
  63 +
  64 + <el-table-column :label="$t('operation')" align="center" fixed="right" width="180">
  65 + <template slot-scope="scope">
  66 + <el-button size="mini" type="primary" @click="handleEdit(scope.row)">
  67 + {{ $t('modify') }}
  68 + </el-button>
  69 + <el-button size="mini" type="danger" @click="handleDelete(scope.row)">
  70 + {{ $t('delete') }}
  71 + </el-button>
  72 + </template>
  73 + </el-table-column>
  74 + </el-table>
  75 +
  76 + <!-- 分页 -->
  77 + <el-pagination background layout="total, sizes, prev, pager, next" :current-page="pagination.current"
  78 + :page-size="pagination.size" :total="pagination.total" @size-change="handleSizeChange"
  79 + @current-change="handlePageChange" style="margin-top: 20px; text-align: right;" />
  80 + </el-card>
  81 +
  82 + <!-- 编辑组件 -->
  83 + <edit-owner-invoice ref="editDialog" @success="fetchData" />
  84 +
  85 + <!-- 删除组件 -->
  86 + <delete-owner-invoice ref="deleteDialog" @success="fetchData" />
  87 + </div>
  88 +</template>
  89 +
  90 +<script>
  91 +import { listOwnerInvoice } from '@/api/fee/ownerInvoiceApi'
  92 +import EditOwnerInvoice from '@/components/fee/editOwnerInvoice'
  93 +import DeleteOwnerInvoice from '@/components/fee/deleteOwnerInvoice'
  94 +
  95 +export default {
  96 + name: 'OwnerInvoiceList',
  97 + components: {
  98 + EditOwnerInvoice,
  99 + DeleteOwnerInvoice
  100 + },
  101 + data() {
  102 + return {
  103 + loading: false,
  104 + searchForm: {
  105 + ownerName: '',
  106 + invoiceType: '',
  107 + invoiceName: ''
  108 + },
  109 + tableData: [],
  110 + pagination: {
  111 + current: 1,
  112 + size: 10,
  113 + total: 0
  114 + }
  115 + }
  116 + },
  117 + created() {
  118 + this.fetchData()
  119 + },
  120 + methods: {
  121 + async fetchData() {
  122 + this.loading = true
  123 + try {
  124 + const params = {
  125 + ...this.searchForm,
  126 + page: this.pagination.current,
  127 + row: this.pagination.size
  128 + }
  129 +
  130 + const response = await listOwnerInvoice(params)
  131 + this.tableData = response.data
  132 + this.pagination.total = response.total
  133 + } catch (error) {
  134 + this.$message.error(error.message || this.$t('common.fetchError'))
  135 + } finally {
  136 + this.loading = false
  137 + }
  138 + },
  139 +
  140 + handleAdd() {
  141 + this.$router.push('/views/fee/addOwnerInvoice')
  142 + },
  143 +
  144 + handleEdit(row) {
  145 + this.$refs.editDialog.open({ ...row })
  146 + },
  147 +
  148 + handleDelete(row) {
  149 + this.$refs.deleteDialog.open(row)
  150 + },
  151 +
  152 + handlePageChange(page) {
  153 + this.pagination.current = page
  154 + this.fetchData()
  155 + },
  156 +
  157 + handleSizeChange(size) {
  158 + this.pagination.size = size
  159 + this.fetchData()
  160 + }
  161 + }
  162 +}
  163 +</script>
  164 +
  165 +<style lang="scss" scoped>
  166 +.owner-invoice-container {
  167 + padding: 20px;
  168 +
  169 + .search-card,
  170 + .list-card {
  171 + margin-bottom: 20px;
  172 + }
  173 +
  174 + .clearfix {
  175 + display: flex;
  176 + justify-content: space-between;
  177 + align-items: center;
  178 + }
  179 +}
  180 +</style>
0 181 \ No newline at end of file
... ...