Commit f99ceb4fd18352b5bea8fce2ae96b1d295819c57

Authored by wuxw
1 parent 43eaaadb

收据优化完成

src/api/fee/feeReceiptApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询收据列表
  5 +export function queryFeeReceipt(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/feeReceipt/queryFeeReceipt',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve(res)
  17 + }).catch(error => {
  18 + reject(error)
  19 + })
  20 + })
  21 +}
  22 +
  23 +// 查询打印页面配置
  24 +export function listFeePrintPage(params) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/feePrintPage.listFeePrintPage',
  28 + method: 'get',
  29 + params: {
  30 + ...params,
  31 + communityId: getCommunityId()
  32 + }
  33 + }).then(response => {
  34 + const res = response.data
  35 + resolve(res)
  36 + }).catch(error => {
  37 + reject(error)
  38 + })
  39 + })
  40 +}
0 41 \ No newline at end of file
... ...
src/api/fee/listApplyRoomDiscountRecordApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询验房跟踪记录列表
  5 +export function queryApplyRoomDiscountRecord(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/applyRoomDiscountRecord/queryApplyRoomDiscountRecord',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + resolve(res)
  17 + }).catch(error => {
  18 + reject(error)
  19 + })
  20 + })
  21 +}
  22 +
  23 +// 添加验房跟踪记录
  24 +export function addApplyRoomDiscountRecord(data) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/applyRoomDiscountRecord/addApplyRoomDiscountRecord',
  28 + method: 'post',
  29 + data: {
  30 + ...data,
  31 + communityId: getCommunityId()
  32 + }
  33 + }).then(response => {
  34 + const res = response.data
  35 + resolve(res)
  36 + }).catch(error => {
  37 + reject(error)
  38 + })
  39 + })
  40 +}
  41 +
  42 +// 删除验房跟踪记录
  43 +export function cutApplyRoomDiscountRecord(params) {
  44 + return new Promise((resolve, reject) => {
  45 + request({
  46 + url: '/applyRoomDiscountRecord/cutApplyRoomDiscountRecord',
  47 + method: 'post',
  48 + data: {
  49 + ...params,
  50 + communityId: getCommunityId()
  51 + }
  52 + }).then(response => {
  53 + const res = response.data
  54 + resolve(res)
  55 + }).catch(error => {
  56 + reject(error)
  57 + })
  58 + })
  59 +}
  60 +
  61 +// 上传图片
  62 +export function uploadImage(data) {
  63 + return new Promise((resolve, reject) => {
  64 + request({
  65 + url: '/uploadImage',
  66 + method: 'post',
  67 + headers: {
  68 + 'Content-Type': 'multipart/form-data'
  69 + },
  70 + data
  71 + }).then(response => {
  72 + const res = response.data
  73 + resolve(res)
  74 + }).catch(error => {
  75 + reject(error)
  76 + })
  77 + })
  78 +}
  79 +
  80 +// 上传视频
  81 +export function uploadVedio(data, config) {
  82 + return new Promise((resolve, reject) => {
  83 + request({
  84 + url: '/uploadVedio',
  85 + method: 'post',
  86 + headers: {
  87 + 'Content-Type': 'multipart/form-data'
  88 + },
  89 + data,
  90 + ...config
  91 + }).then(response => {
  92 + const res = response.data
  93 + resolve(res)
  94 + }).catch(error => {
  95 + reject(error)
  96 + })
  97 + })
  98 +}
0 99 \ No newline at end of file
... ...
src/api/fee/printPayFeeApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询收费单据
  5 +export function queryFeeReceipt(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/feeReceipt/queryFeeReceipt',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + resolve(response.data)
  16 + }).catch(error => {
  17 + reject(error)
  18 + })
  19 + })
  20 +}
  21 +
  22 +// 查询收费单据详情
  23 +export function queryFeeReceiptDetail(params) {
  24 + return new Promise((resolve, reject) => {
  25 + request({
  26 + url: '/feeReceipt/queryFeeReceiptDetail',
  27 + method: 'get',
  28 + params: {
  29 + ...params,
  30 + communityId: getCommunityId()
  31 + }
  32 + }).then(response => {
  33 + resolve(response.data)
  34 + }).catch(error => {
  35 + reject(error)
  36 + })
  37 + })
  38 +}
  39 +
  40 +// 查询打印规范
  41 +export function queryFeePrintSpec(params) {
  42 + return new Promise((resolve, reject) => {
  43 + request({
  44 + url: '/feePrintSpec/queryFeePrintSpec',
  45 + method: 'get',
  46 + params: {
  47 + ...params,
  48 + communityId: getCommunityId()
  49 + }
  50 + }).then(response => {
  51 + resolve(response.data)
  52 + }).catch(error => {
  53 + reject(error)
  54 + })
  55 + })
  56 +}
0 57 \ No newline at end of file
... ...
src/api/fee/printPayFeeBangTaiApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 查询缴费收据
  4 +export function queryFeeReceipt(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/feeReceipt/queryFeeReceipt',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + resolve(res)
  13 + }).catch(error => {
  14 + reject(error)
  15 + })
  16 + })
  17 +}
  18 +
  19 +// 查询缴费收据详情
  20 +export function queryFeeReceiptDetail(params) {
  21 + return new Promise((resolve, reject) => {
  22 + request({
  23 + url: '/feeReceipt/queryFeeReceiptDetail',
  24 + method: 'get',
  25 + params
  26 + }).then(response => {
  27 + const res = response.data
  28 + resolve(res)
  29 + }).catch(error => {
  30 + reject(error)
  31 + })
  32 + })
  33 +}
  34 +
  35 +// 查询打印规范
  36 +export function queryFeePrintSpec(params) {
  37 + return new Promise((resolve, reject) => {
  38 + request({
  39 + url: '/feePrintSpec/queryFeePrintSpec',
  40 + method: 'get',
  41 + params
  42 + }).then(response => {
  43 + const res = response.data
  44 + resolve(res)
  45 + }).catch(error => {
  46 + reject(error)
  47 + })
  48 + })
  49 +}
0 50 \ No newline at end of file
... ...
src/api/fee/printSmallPayFeeApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询收据信息
  5 +export function queryFeeReceipt(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/feeReceipt/queryFeeReceipt',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + resolve(response.data)
  16 + }).catch(error => {
  17 + reject(error)
  18 + })
  19 + })
  20 +}
  21 +
  22 +// 查询收据详情
  23 +export function queryFeeReceiptDetail(params) {
  24 + return new Promise((resolve, reject) => {
  25 + request({
  26 + url: '/feeReceipt/queryFeeReceiptDetail',
  27 + method: 'get',
  28 + params: {
  29 + ...params,
  30 + communityId: getCommunityId()
  31 + }
  32 + }).then(response => {
  33 + resolve(response.data)
  34 + }).catch(error => {
  35 + reject(error)
  36 + })
  37 + })
  38 +}
  39 +
  40 +// 查询打印规格
  41 +export function queryFeePrintSpec(params) {
  42 + return new Promise((resolve, reject) => {
  43 + request({
  44 + url: '/feePrintSpec/queryFeePrintSpec',
  45 + method: 'get',
  46 + params: {
  47 + ...params,
  48 + communityId: getCommunityId()
  49 + }
  50 + }).then(response => {
  51 + resolve(response.data)
  52 + }).catch(error => {
  53 + reject(error)
  54 + })
  55 + })
  56 +}
  57 +
  58 +// 查询打印机列表
  59 +export function listMachinePrinter(params) {
  60 + return new Promise((resolve, reject) => {
  61 + request({
  62 + url: '/printer/listMachinePrinter',
  63 + method: 'get',
  64 + params: {
  65 + ...params,
  66 + communityId: getCommunityId()
  67 + }
  68 + }).then(response => {
  69 + resolve(response.data)
  70 + }).catch(error => {
  71 + reject(error)
  72 + })
  73 + })
  74 +}
  75 +
  76 +// 提交云打印
  77 +export function printPayFeeDetail(data) {
  78 + return new Promise((resolve, reject) => {
  79 + request({
  80 + url: '/print/printPayFeeDetail',
  81 + method: 'post',
  82 + data: {
  83 + ...data,
  84 + communityId: getCommunityId()
  85 + }
  86 + }).then(response => {
  87 + resolve(response.data)
  88 + }).catch(error => {
  89 + reject(error)
  90 + })
  91 + })
  92 +}
0 93 \ No newline at end of file
... ...
src/components/fee/applyRoomDiscountRecord.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('applyRoomDiscountRecord.title')" :visible.sync="visible" width="60%"
  3 + :before-close="handleClose">
  4 + <el-form :model="form" label-width="120px" label-position="left">
  5 + <el-form-item :label="$t('applyRoomDiscountRecord.room')">
  6 + <el-input v-model="form.roomName" disabled></el-input>
  7 + </el-form-item>
  8 + <el-form-item :label="$t('applyRoomDiscountRecord.status')">
  9 + <el-input v-model="form.stateName" disabled></el-input>
  10 + </el-form-item>
  11 + <el-form-item :label="$t('applyRoomDiscountRecord.isViolation')" prop="isTrue" required>
  12 + <el-select v-model="form.isTrue" style="width:100%">
  13 + <el-option :value="null" :label="$t('applyRoomDiscountRecord.selectViolation')" disabled></el-option>
  14 + <el-option :value="true" :label="$t('common.yes')"></el-option>
  15 + <el-option :value="false" :label="$t('common.no')"></el-option>
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item :label="$t('applyRoomDiscountRecord.remark')" prop="remark" required>
  19 + <el-input type="textarea" v-model="form.remark"
  20 + :placeholder="$t('applyRoomDiscountRecord.remarkPlaceholder')"></el-input>
  21 + </el-form-item>
  22 + <el-form-item :label="$t('applyRoomDiscountRecord.uploadImages')">
  23 + <upload-image-url ref="uploadImageUrl" />
  24 + </el-form-item>
  25 + <el-form-item :label="$t('applyRoomDiscountRecord.uploadVideo')">
  26 + <upload-vedio ref="uploadVedio" />
  27 + </el-form-item>
  28 + </el-form>
  29 + <span slot="footer" class="dialog-footer">
  30 + <el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
  31 + <el-button type="primary" @click="saveApplyRoomDiscountRecordInfo">{{ $t('common.save') }}</el-button>
  32 + </span>
  33 + </el-dialog>
  34 +</template>
  35 +
  36 +<script>
  37 +import { addApplyRoomDiscountRecord } from '@/api/fee/listApplyRoomDiscountRecordApi'
  38 +import UploadImageUrl from '@/components/upload/UploadImageUrl'
  39 +import UploadVedio from './uploadVedio'
  40 +import { getCommunityId } from '@/api/community/communityApi'
  41 +
  42 +export default {
  43 + name: 'ApplyRoomDiscountRecord',
  44 + components: {
  45 + UploadImageUrl,
  46 + UploadVedio
  47 + },
  48 + props: {
  49 + callBackFunction: {
  50 + type: String,
  51 + default: ''
  52 + }
  53 + },
  54 + data() {
  55 + return {
  56 + visible: false,
  57 + form: {
  58 + ardId: '',
  59 + roomName: '',
  60 + state: '',
  61 + stateName: '',
  62 + remark: '',
  63 + roomId: '',
  64 + photos: [],
  65 + videoName: '',
  66 + url: '',
  67 + detailType: '1001',
  68 + isTrue: null,
  69 + communityId: ''
  70 + }
  71 + }
  72 + },
  73 + methods: {
  74 + open(params) {
  75 + this.resetForm()
  76 + this.form.ardId = params[0]
  77 + this.form.state = params[1]
  78 + this.form.stateName = params[2]
  79 + this.form.roomId = params[3]
  80 + this.form.roomName = params[4]
  81 + this.form.communityId = getCommunityId()
  82 + this.visible = true
  83 + },
  84 + resetForm() {
  85 + this.form = {
  86 + ardId: '',
  87 + roomName: '',
  88 + state: '',
  89 + stateName: '',
  90 + remark: '',
  91 + roomId: '',
  92 + photos: [],
  93 + videoName: '',
  94 + url: '',
  95 + detailType: '1001',
  96 + isTrue: null,
  97 + communityId: ''
  98 + }
  99 + this.$refs.uploadImageUrl.clear()
  100 + this.$refs.uploadVedio.clear()
  101 + },
  102 + handleClose() {
  103 + this.visible = false
  104 + },
  105 + validateForm() {
  106 + if (this.form.isTrue === null) {
  107 + this.$message.error(this.$t('applyRoomDiscountRecord.violationRequired'))
  108 + return false
  109 + }
  110 + if (!this.form.remark) {
  111 + this.$message.error(this.$t('applyRoomDiscountRecord.remarkRequired'))
  112 + return false
  113 + }
  114 + return true
  115 + },
  116 + async saveApplyRoomDiscountRecordInfo() {
  117 + if (!this.validateForm()) return
  118 +
  119 + try {
  120 + this.form.photos = this.$refs.uploadImageUrl.getPhotos()
  121 + this.form.videoName = this.$refs.uploadVedio.getVideoName()
  122 +
  123 + const res = await addApplyRoomDiscountRecord(this.form)
  124 + if (res.code === 0) {
  125 + this.$message.success(this.$t('common.saveSuccess'))
  126 + this.handleClose()
  127 + this.$emit('success')
  128 + } else {
  129 + this.$message.error(res.msg)
  130 + }
  131 + } catch (error) {
  132 + this.$message.error(this.$t('common.saveFailed'))
  133 + }
  134 + }
  135 + }
  136 +}
  137 +</script>
0 138 \ No newline at end of file
... ...
src/components/fee/deleteApplyRoomDiscountRecord.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('deleteApplyRoomDiscountRecord.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + :before-close="handleClose"
  7 + >
  8 + <div class="delete-content">
  9 + <i class="el-icon-warning" style="color: #e6a23c; font-size: 24px;"></i>
  10 + <span style="margin-left: 10px;">{{ $t('deleteApplyRoomDiscountRecord.confirmText') }}</span>
  11 + </div>
  12 + <span slot="footer" class="dialog-footer">
  13 + <el-button @click="handleClose">{{ $t('common.cancel') }}</el-button>
  14 + <el-button type="primary" @click="confirmDelete" :loading="loading">{{ $t('common.confirm') }}</el-button>
  15 + </span>
  16 + </el-dialog>
  17 +</template>
  18 +
  19 +<script>
  20 +import { cutApplyRoomDiscountRecord } from '@/api/fee/listApplyRoomDiscountRecordApi'
  21 +import { getCommunityId } from '@/api/community/communityApi'
  22 +
  23 +export default {
  24 + name: 'DeleteApplyRoomDiscountRecord',
  25 + data() {
  26 + return {
  27 + visible: false,
  28 + loading: false,
  29 + record: null
  30 + }
  31 + },
  32 + methods: {
  33 + open(record) {
  34 + this.record = record
  35 + this.visible = true
  36 + },
  37 + handleClose() {
  38 + this.visible = false
  39 + },
  40 + async confirmDelete() {
  41 + if (!this.record) return
  42 +
  43 + this.loading = true
  44 + try {
  45 + const params = {
  46 + ardrId: this.record.ardrId,
  47 + communityId: getCommunityId()
  48 + }
  49 +
  50 + const res = await cutApplyRoomDiscountRecord(params)
  51 + if (res.code === 0) {
  52 + this.$message.success(this.$t('common.deleteSuccess'))
  53 + this.handleClose()
  54 + this.$emit('success')
  55 + } else {
  56 + this.$message.error(res.msg)
  57 + }
  58 + } catch (error) {
  59 + this.$message.error(this.$t('common.deleteFailed'))
  60 + } finally {
  61 + this.loading = false
  62 + }
  63 + }
  64 + }
  65 +}
  66 +</script>
  67 +
  68 +<style lang="scss" scoped>
  69 +.delete-content {
  70 + display: flex;
  71 + align-items: center;
  72 + justify-content: center;
  73 + padding: 20px 0;
  74 +}
  75 +</style>
0 76 \ No newline at end of file
... ...
src/components/fee/uploadImageUrl.vue
1 1 <template>
2 2 <div class="upload-image-container">
3   - <div v-for="(image, index) in photos" :key="index" class="image-item">
4   - <img :src="getImageUrl(image)" @error="handleImageError" />
5   - <span class="remove-icon" @click="removeImage(index)">
6   - <i class="el-icon-delete"></i>
7   - </span>
  3 + <div class="image-preview">
  4 + <div v-for="(image, index) in photos" :key="index" class="image-item">
  5 + <el-image
  6 + :src="image.url"
  7 + fit="cover"
  8 + :preview-src-list="previewList"
  9 + style="width: 100px; height: 100px"
  10 + ></el-image>
  11 + <i class="el-icon-delete delete-icon" @click="removeImage(index)"></i>
  12 + </div>
  13 + <div v-if="photos.length < maxCount" class="upload-btn" @click="triggerUpload">
  14 + <i class="el-icon-plus"></i>
  15 + </div>
8 16 </div>
9   -
10   - <div v-if="photos.length < imageCount" class="upload-btn" @click="triggerUpload">
11   - <i class="el-icon-plus"></i>
12   - </div>
13   -
14   - <input
  17 + <input
  18 + type="file"
15 19 ref="fileInput"
16   - type="file"
17   - accept="image/*"
18   - style="display: none"
  20 + accept="image/*"
  21 + style="display: none"
19 22 @change="handleFileChange"
  23 + multiple
20 24 />
21 25 </div>
22 26 </template>
23 27  
24 28 <script>
25   -import { uploadFile } from '@/api/common'
  29 +import { uploadImage } from '@/api/fee/listApplyRoomDiscountRecordApi'
  30 +import { getCommunityId } from '@/api/community/communityApi'
26 31  
27 32 export default {
28 33 name: 'UploadImageUrl',
29 34 props: {
30   - imageCount: {
  35 + maxCount: {
31 36 type: Number,
32 37 default: 99
33 38 }
34 39 },
35 40 data() {
36 41 return {
37   - photos: [], // 用于显示的图片数组
38   - photosUrl: [] // 实际存储的图片信息数组
  42 + photos: [],
  43 + previewList: []
39 44 }
40 45 },
41 46 methods: {
... ... @@ -46,66 +51,47 @@ export default {
46 51 const files = event.target.files
47 52 if (!files || files.length === 0) return
48 53  
49   - const file = files[0]
50   - if (file.size > 2 * 1024 * 1024) {
51   - this.$message.error(this.$t('uploadImage.validate.sizeLimit'))
  54 + if (files.length + this.photos.length > this.maxCount) {
  55 + this.$message.error(this.$t('uploadImage.maxCountError', { count: this.maxCount }))
52 56 return
53 57 }
54 58  
55   - try {
56   - // 显示本地预览
57   - const reader = new FileReader()
58   - reader.onload = (e) => {
59   - this.photos.push(e.target.result)
  59 + for (let i = 0; i < files.length; i++) {
  60 + const file = files[i]
  61 + if (file.size > 2 * 1024 * 1024) {
  62 + this.$message.error(this.$t('uploadImage.sizeError')))
  63 + continue
60 64 }
61   - reader.readAsDataURL(file)
62 65  
63   - // 上传到服务器
64   - const formData = new FormData()
65   - formData.append('uploadFile', file)
66   - formData.append('communityId', this.$store.getters.communityId)
  66 + try {
  67 + const formData = new FormData()
  68 + formData.append('uploadFile', file)
  69 + formData.append('communityId', getCommunityId())
67 70  
68   - const { data } = await uploadFile(formData)
69   - this.photosUrl.push(data)
70   - this.$emit('change', this.photosUrl)
71   - } catch (error) {
72   - this.$message.error(this.$t('uploadImage.message.uploadFailed'))
73   - } finally {
74   - event.target.value = null
  71 + const res = await uploadImage(formData)
  72 + if (res.code === 0) {
  73 + this.photos.push({
  74 + fileId: res.data.fileId,
  75 + url: res.data.url
  76 + })
  77 + this.previewList.push(res.data.url)
  78 + }
  79 + } catch (error) {
  80 + this.$message.error(this.$t('uploadImage.uploadFailed')))
  81 + }
75 82 }
  83 + event.target.value = null
76 84 },
77 85 removeImage(index) {
78 86 this.photos.splice(index, 1)
79   - this.photosUrl.splice(index, 1)
80   - this.$emit('change', this.photosUrl)
  87 + this.previewList.splice(index, 1)
81 88 },
82   - handleImageError(e) {
83   - e.target.src = '/img/noPhoto.jpg'
84   - },
85   - getImageUrl(image) {
86   - if (typeof image === 'string') {
87   - if (image.startsWith('http') || image.startsWith('https') || image.startsWith('data:')) {
88   - return image
89   - }
90   - return `/callComponent/download/getFile/file?fileId=${image}&communityId=-1&time=${new Date().getTime()}`
91   - }
92   - return image.url || '/img/noPhoto.jpg'
  89 + getPhotos() {
  90 + return this.photos.map(item => item.fileId)
93 91 },
94 92 clear() {
95 93 this.photos = []
96   - this.photosUrl = []
97   - },
98   - setImages(images) {
99   - this.photos = []
100   - this.photosUrl = []
101   - images.forEach(image => {
102   - if (image.startsWith('data:')) {
103   - this.photos.push(image)
104   - return
105   - }
106   - this.photosUrl.push({ fileId: image, url: image })
107   - this.photos.push(this.getImageUrl(image))
108   - })
  94 + this.previewList = []
109 95 }
110 96 }
111 97 }
... ... @@ -113,57 +99,45 @@ export default {
113 99  
114 100 <style lang="scss" scoped>
115 101 .upload-image-container {
116   - display: flex;
117   - flex-wrap: wrap;
118   - gap: 10px;
  102 + .image-preview {
  103 + display: flex;
  104 + flex-wrap: wrap;
  105 + gap: 10px;
119 106  
120   - .image-item {
121   - position: relative;
122   - width: 100px;
123   - height: 100px;
124   - border: 1px dashed #dcdfe6;
125   - border-radius: 4px;
126   - overflow: hidden;
  107 + .image-item {
  108 + position: relative;
  109 + width: 100px;
  110 + height: 100px;
127 111  
128   - img {
129   - width: 100%;
130   - height: 100%;
131   - object-fit: cover;
  112 + .delete-icon {
  113 + position: absolute;
  114 + top: -10px;
  115 + right: -10px;
  116 + color: #f56c6c;
  117 + cursor: pointer;
  118 + font-size: 16px;
  119 + background: white;
  120 + border-radius: 50%;
  121 + padding: 2px;
  122 + }
132 123 }
133 124  
134   - .remove-icon {
135   - position: absolute;
136   - top: 0;
137   - right: 0;
138   - color: #f56c6c;
139   - background: rgba(255, 255, 255, 0.7);
140   - padding: 4px;
  125 + .upload-btn {
  126 + width: 100px;
  127 + height: 100px;
  128 + border: 1px dashed #dcdfe6;
  129 + display: flex;
  130 + justify-content: center;
  131 + align-items: center;
141 132 cursor: pointer;
142   - font-size: 16px;
  133 + color: #909399;
  134 + font-size: 24px;
143 135  
144 136 &:hover {
145   - color: #f78989;
  137 + border-color: #409eff;
  138 + color: #409eff;
146 139 }
147 140 }
148 141 }
149   -
150   - .upload-btn {
151   - width: 100px;
152   - height: 100px;
153   - border: 1px dashed #dcdfe6;
154   - border-radius: 4px;
155   - display: flex;
156   - justify-content: center;
157   - align-items: center;
158   - cursor: pointer;
159   - color: #8c939d;
160   - font-size: 28px;
161   - background-color: #f5f7fa;
162   -
163   - &:hover {
164   - border-color: #409eff;
165   - color: #409eff;
166   - }
167   - }
168 142 }
169 143 </style>
170 144 \ No newline at end of file
... ...
src/components/fee/uploadVedio.vue 0 → 100644
  1 +<template>
  2 + <div class="upload-vedio-container">
  3 + <div v-if="progress > 0" class="progress-container">
  4 + <el-progress :percentage="progress" :stroke-width="2"></el-progress>
  5 + <div class="file-name">{{ fileName }}</div>
  6 + </div>
  7 +
  8 + <el-button
  9 + type="primary"
  10 + size="small"
  11 + @click="triggerUpload"
  12 + :disabled="uploading"
  13 + >
  14 + <i class="el-icon-upload"></i>
  15 + {{ $t('uploadVedio.uploadButton') }}
  16 + </el-button>
  17 + <input
  18 + type="file"
  19 + ref="fileInput"
  20 + accept="video/*"
  21 + style="display: none"
  22 + @change="handleFileChange"
  23 + />
  24 + </div>
  25 +</template>
  26 +
  27 +<script>
  28 +import { uploadVedio } from '@/api/fee/listApplyRoomDiscountRecordApi'
  29 +import { getCommunityId } from '@/api/community/communityApi'
  30 +
  31 +export default {
  32 + name: 'UploadVedio',
  33 + data() {
  34 + return {
  35 + fileName: '',
  36 + realFileName: '',
  37 + progress: 0,
  38 + uploading: false
  39 + }
  40 + },
  41 + methods: {
  42 + triggerUpload() {
  43 + this.$refs.fileInput.click()
  44 + },
  45 + async handleFileChange(event) {
  46 + const file = event.target.files[0]
  47 + if (!file) return
  48 +
  49 + if (file.size > 500 * 1024 * 1024) {
  50 + this.$message.error(this.$t('uploadVedio.sizeError'))
  51 + return
  52 + }
  53 +
  54 + this.fileName = file.name
  55 + this.uploading = true
  56 + this.progress = 0
  57 +
  58 + try {
  59 + const formData = new FormData()
  60 + formData.append('uploadFile', file)
  61 + formData.append('communityId', getCommunityId())
  62 +
  63 + const res = await uploadVedio(formData, {
  64 + onUploadProgress: (progressEvent) => {
  65 + this.progress = Math.round((progressEvent.loaded * 90) / progressEvent.total)
  66 + }
  67 + })
  68 +
  69 + if (res.code === 0) {
  70 + this.progress = 100
  71 + this.realFileName = res.data.realFileName
  72 + this.$message.success(this.$t('uploadVedio.uploadSuccess'))
  73 + }
  74 + } catch (error) {
  75 + this.$message.error(this.$t('uploadVedio.uploadFailed'))
  76 + } finally {
  77 + this.uploading = false
  78 + event.target.value = null
  79 + }
  80 + },
  81 + getVideoName() {
  82 + return this.realFileName
  83 + },
  84 + clear() {
  85 + this.fileName = ''
  86 + this.realFileName = ''
  87 + this.progress = 0
  88 + this.uploading = false
  89 + }
  90 + }
  91 +}
  92 +</script>
  93 +
  94 +<style lang="scss" scoped>
  95 +.upload-vedio-container {
  96 + .progress-container {
  97 + margin-bottom: 10px;
  98 +
  99 + .file-name {
  100 + font-size: 12px;
  101 + color: #606266;
  102 + margin-top: 5px;
  103 + word-break: break-all;
  104 + }
  105 + }
  106 +}
  107 +</style>
0 108 \ No newline at end of file
... ...
src/i18n/feeI18n.js
... ... @@ -5,6 +5,11 @@ import { messages as returnPayFeeManageMessages } from &#39;../views/fee/returnPayFe
5 5 import { messages as feeDiscountManageMessages } from '../views/fee/feeDiscountManageLang'
6 6 import { messages as applyRoomDiscountManageMessages } from '../views/fee/applyRoomDiscountManageLang'
7 7 import { messages as discountTypeMessages } from '../views/fee/discountTypeLang'
  8 +import { messages as listApplyRoomDiscountRecordMessages } from '../views/fee/listApplyRoomDiscountRecordLang'
  9 +import { messages as feeReceiptMessages } from '../views/fee/feeReceiptLang'
  10 +import { messages as printPayFeeMessages } from '../views/fee/printPayFeeLang'
  11 +import { messages as printPayFeeBangTaiMessages } from '../views/fee/printPayFeeBangTaiLang'
  12 +import { messages as printSmallPayFeeMessages } from '../views/fee/printSmallPayFeeLang'
8 13  
9 14 export const messages = {
10 15 en: {
... ... @@ -15,6 +20,11 @@ export const messages = {
15 20 ...feeDiscountManageMessages.en,
16 21 ...applyRoomDiscountManageMessages.en,
17 22 ...discountTypeMessages.en,
  23 + ...listApplyRoomDiscountRecordMessages.en,
  24 + ...feeReceiptMessages.en,
  25 + ...printPayFeeMessages.en,
  26 + ...printPayFeeBangTaiMessages.en,
  27 + ...printSmallPayFeeMessages.en,
18 28 },
19 29 zh: {
20 30 ...contractCreateFeeMessages.zh,
... ... @@ -24,5 +34,10 @@ export const messages = {
24 34 ...feeDiscountManageMessages.zh,
25 35 ...applyRoomDiscountManageMessages.zh,
26 36 ...discountTypeMessages.zh,
  37 + ...listApplyRoomDiscountRecordMessages.zh,
  38 + ...feeReceiptMessages.zh,
  39 + ...printPayFeeMessages.zh,
  40 + ...printPayFeeBangTaiMessages.zh,
  41 + ...printSmallPayFeeMessages.zh,
27 42 }
28 43 }
29 44 \ No newline at end of file
... ...
src/router/feeRouter.js
... ... @@ -30,8 +30,19 @@ export default [
30 30 component: () => import('@/views/fee/applyRoomDiscountManageList.vue')
31 31 },
32 32 {
33   - path:'/views/fee/discountType',
34   - name:'/views/fee/discountType',
  33 + path: '/views/fee/discountType',
  34 + name: '/views/fee/discountType',
35 35 component: () => import('@/views/fee/discountTypeList.vue')
36   - },
  36 + },
  37 + {
  38 + path: '/views/fee/listApplyRoomDiscountRecord',
  39 + name: '/views/fee/listApplyRoomDiscountRecord',
  40 + component: () => import('@/views/fee/listApplyRoomDiscountRecordList.vue')
  41 + },
  42 + {
  43 + path: '/pages/property/feeReceipt',
  44 + name: '/pages/property/feeReceipt',
  45 + component: () => import('@/views/fee/feeReceiptList.vue')
  46 + },
  47 +
37 48 ]
38 49 \ No newline at end of file
... ...
src/router/index.js
... ... @@ -15,7 +15,7 @@ import scmRouter from &#39;./scmRouter&#39;
15 15 import userRouter from './userRouter'
16 16 import systemRouter from './systemRouter'
17 17 import communityRouter from './communityRouter'
18   -import workRouter from './workRouter'
  18 +import workRouter from './workRouter'
19 19 import feeRouter from './feeRouter'
20 20  
21 21 Vue.use(VueRouter)
... ... @@ -31,13 +31,13 @@ const routes = [
31 31 name: '/views/index/index',
32 32 component: () => import('@/views/index/index.vue')
33 33 },
34   -
  34 +
35 35 {
36 36 path: '/pages/frame/changeStaffPwd',
37 37 name: '/pages/frame/changeStaffPwd',
38 38 component: () => import('@/views/user/login/updatePwd.vue')
39 39 },
40   -
  40 +
41 41 {
42 42  
43 43 path: '/pages/frame/orgManage',
... ... @@ -68,7 +68,7 @@ const routes = [
68 68 component: () => import('@/views/staff/staffDetailList.vue')
69 69 },
70 70  
71   -
  71 +
72 72 {
73 73 path: '/pages/log/mqttLog',
74 74 name: '/pages/log/mqttLog',
... ... @@ -84,7 +84,7 @@ const routes = [
84 84 name: '/pages/frame/registerProtocol',
85 85 component: () => import('@/views/system/registerProtocolList.vue')
86 86 },
87   -
  87 +
88 88 {
89 89 path: '/views/report/reportCustomComponentRelManage',
90 90 name: '/views/report/reportCustomComponentRelManage',
... ... @@ -100,7 +100,7 @@ const routes = [
100 100 name: '/views/report/reportCustomComponentFooterManage',
101 101 component: () => import('@/views/report/reportCustomComponentFooterManageList.vue')
102 102 },
103   -
  103 +
104 104 {
105 105 path: '/pages/common/communityManage',
106 106 name: '/pages/common/communityManage',
... ... @@ -166,7 +166,7 @@ const routes = [
166 166 name: '/pages/owner/adminOwner',
167 167 component: () => import('@/views/owner/adminOwnerList.vue')
168 168 },
169   -
  169 +
170 170 {
171 171 path: '/pages/owner/adminAuthOwner',
172 172 name: '/pages/owner/adminAuthOwner',
... ... @@ -187,7 +187,7 @@ const routes = [
187 187 name: '/pages/fee/adminFeeConfig',
188 188 component: () => import('@/views/fee/adminFeeConfigList.vue')
189 189 },
190   -
  190 +
191 191 {
192 192 path: '/pages/fee/adminRoomFee',
193 193 name: '/pages/fee/adminRoomFee',
... ... @@ -566,7 +566,7 @@ const routes = [
566 566 name: '/views/fee/feeConfigDetail',
567 567 component: () => import('@/views/fee/feeConfigDetailList.vue')
568 568 },
569   -
  569 +
570 570 {
571 571 path: '/pages/property/carCreateFee',
572 572 name: '/pages/property/carCreateFee',
... ... @@ -621,7 +621,7 @@ const routes = [
621 621 path: '/pages/property/repairForceFinishManage',
622 622 name: '/pages/property/repairForceFinishManage',
623 623 component: () => import('@/views/work/repairForceFinishManageList.vue')
624   - },
  624 + },
625 625 {
626 626 path: '/pages/property/locationManage',
627 627 name: '/pages/property/locationManage',
... ... @@ -679,6 +679,21 @@ const routes = [
679 679 name: '/views/contract/printContract',
680 680 component: () => import('@/views/contract/printContractList.vue')
681 681 },
  682 + {
  683 + path: '/pages/property/printPayFee',
  684 + name: '/pages/property/printPayFee',
  685 + component: () => import('@/views/fee/printPayFeeList.vue')
  686 + },
  687 + {
  688 + path: '/pages/property/printPayFeeBangTai',
  689 + name: '/pages/property/printPayFeeBangTai',
  690 + component: () => import('@/views/fee/printPayFeeBangTaiList.vue')
  691 + },
  692 + {
  693 + path:'/pages/property/printSmallPayFee',
  694 + name:'/pages/property/printSmallPayFee',
  695 + component: () => import('@/views/fee/printSmallPayFeeList.vue')
  696 + },
682 697 ]
683 698  
684 699 const router = new VueRouter({
... ...
src/views/fee/applyRoomDiscountManageList.vue
... ... @@ -231,7 +231,7 @@ export default {
231 231 },
232 232 handleOpenRecord(row) {
233 233 this.$router.push({
234   - path: '/pages/property/listApplyRoomDiscountRecord',
  234 + path: '/views/fee/listApplyRoomDiscountRecord',
235 235 query: {
236 236 ardId: row.ardId,
237 237 roomId: row.roomId,
... ...
src/views/fee/discountTypeLang.js
... ... @@ -43,16 +43,6 @@ export const messages = {
43 43 },
44 44 fetchError: 'Failed to fetch discount types'
45 45 },
46   - common: {
47   - search: 'Search',
48   - add: 'Add',
49   - edit: 'Edit',
50   - delete: 'Delete',
51   - back: 'Back',
52   - cancel: 'Cancel',
53   - confirm: 'Confirm',
54   - operation: 'Operation'
55   - }
56 46 },
57 47 zh: {
58 48 discountType: {
... ... @@ -98,15 +88,5 @@ export const messages = {
98 88 },
99 89 fetchError: '获取优惠类型失败'
100 90 },
101   - common: {
102   - search: '查询',
103   - add: '添加',
104   - edit: '修改',
105   - delete: '删除',
106   - back: '返回',
107   - cancel: '取消',
108   - confirm: '确认',
109   - operation: '操作'
110   - }
111 91 }
112 92 }
113 93 \ No newline at end of file
... ...
src/views/fee/feeReceiptLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + feeReceipt: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + receiptId: 'Please enter receipt ID',
  7 + roomId: 'Please enter room/carport info, format: building-unit-room, e.g. 1-1-1',
  8 + objType: 'Please select fee type',
  9 + all: 'All',
  10 + houseFee: 'House Fee',
  11 + parkingFee: 'Parking Fee',
  12 + startTime: 'Please select start time',
  13 + endTime: 'Please select end time'
  14 + },
  15 + list: {
  16 + title: 'Receipt Information'
  17 + },
  18 + table: {
  19 + receiptId: 'Receipt ID',
  20 + receiptCode: 'Receipt Code',
  21 + objType: 'Fee Type',
  22 + feeName: 'Fee Item',
  23 + roomName: 'Room',
  24 + payObjName: 'Owner',
  25 + carNum: 'Carport',
  26 + amount: 'Total Amount(¥)',
  27 + createTime: 'Payment Time'
  28 + },
  29 + button: {
  30 + reprint: 'Reprint Receipt',
  31 + reprintSmall: 'Reprint Small Receipt'
  32 + },
  33 + fetchError: 'Failed to fetch receipt data'
  34 + }
  35 + },
  36 + zh: {
  37 + feeReceipt: {
  38 + search: {
  39 + title: '查询条件',
  40 + receiptId: '请输入收据ID',
  41 + roomId: '请输入房屋或车位信息,格式为楼栋-单元-房屋,如1-1-1',
  42 + objType: '请选择收费类型',
  43 + all: '全部',
  44 + houseFee: '房屋费',
  45 + parkingFee: '车位费',
  46 + startTime: '请选择开始时间',
  47 + endTime: '请选择结束时间'
  48 + },
  49 + list: {
  50 + title: '收据信息'
  51 + },
  52 + table: {
  53 + receiptId: '收据ID',
  54 + receiptCode: '收据编号',
  55 + objType: '费用类型',
  56 + feeName: '费用项',
  57 + roomName: '房屋',
  58 + payObjName: '业主',
  59 + carNum: '车位',
  60 + amount: '总金额(单位:元)',
  61 + createTime: '缴费时间'
  62 + },
  63 + button: {
  64 + reprint: '补打收据',
  65 + reprintSmall: '补打小票'
  66 + },
  67 + fetchError: '获取收据数据失败'
  68 + }
  69 + }
  70 +}
0 71 \ No newline at end of file
... ...
src/views/fee/feeReceiptList.vue 0 → 100644
  1 +<template>
  2 + <div class="fee-receipt-container">
  3 + <!-- 查询条件 -->
  4 + <el-card class="search-wrapper">
  5 + <div slot="header" class="flex justify-between">
  6 + <span>{{ $t('feeReceipt.search.title') }}</span>
  7 + <el-button type="text" style="float: right; padding: 3px 0" @click="_moreCondition">
  8 + {{ feeReceiptManageInfo.moreCondition ? $t('common.hide') : $t('common.more') }}
  9 + </el-button>
  10 + </div>
  11 + <el-row :gutter="20">
  12 + <el-col :span="6">
  13 + <el-input v-model="feeReceiptManageInfo.conditions.receiptId" :placeholder="$t('feeReceipt.search.receiptId')"
  14 + clearable />
  15 + </el-col>
  16 + <el-col :span="8">
  17 + <el-input v-model="feeReceiptManageInfo.conditions.roomId" :placeholder="$t('feeReceipt.search.roomId')"
  18 + clearable />
  19 + </el-col>
  20 + <el-col :span="6">
  21 + <el-select v-model="feeReceiptManageInfo.conditions.objType" :placeholder="$t('feeReceipt.search.objType')"
  22 + style="width:100%">
  23 + <el-option :label="$t('feeReceipt.search.all')" value="" />
  24 + <el-option :label="$t('feeReceipt.search.houseFee')" value="3333" />
  25 + <el-option :label="$t('feeReceipt.search.parkingFee')" value="6666" />
  26 + </el-select>
  27 + </el-col>
  28 + <el-col :span="4">
  29 + <el-button type="primary" @click="_queryFeeReceiptMethod">
  30 + <i class="el-icon-search"></i>
  31 + {{ $t('common.search') }}
  32 + </el-button>
  33 + <el-button @click="_resetFeeReceiptMethod">
  34 + <i class="el-icon-refresh"></i>
  35 + {{ $t('common.reset') }}
  36 + </el-button>
  37 + </el-col>
  38 + </el-row>
  39 +
  40 + <el-row v-show="feeReceiptManageInfo.moreCondition" :gutter="20" style="margin-top:20px">
  41 + <el-col :span="6">
  42 + <el-date-picker v-model="feeReceiptManageInfo.conditions.qstartTime" type="date"
  43 + :placeholder="$t('feeReceipt.search.startTime')" style="width:100%" />
  44 + </el-col>
  45 + <el-col :span="6">
  46 + <el-date-picker v-model="feeReceiptManageInfo.conditions.qendTime" type="date"
  47 + :placeholder="$t('feeReceipt.search.endTime')" style="width:100%" />
  48 + </el-col>
  49 + </el-row>
  50 + </el-card>
  51 +
  52 + <!-- 收据信息 -->
  53 + <el-card class="list-wrapper">
  54 + <div slot="header" class="flex justify-between">
  55 + <span>{{ $t('feeReceipt.list.title') }}</span>
  56 + </div>
  57 +
  58 + <el-table v-loading="loading" :data="feeReceiptManageInfo.feeReceipts" border style="width: 100%">
  59 + <el-table-column prop="receiptId" :label="$t('feeReceipt.table.receiptId')" align="center" />
  60 + <el-table-column prop="receiptCode" :label="$t('feeReceipt.table.receiptCode')" align="center">
  61 + <template slot-scope="scope">
  62 + {{ scope.row.receiptCode || '-' }}
  63 + </template>
  64 + </el-table-column>
  65 + <el-table-column prop="objType" :label="$t('feeReceipt.table.objType')" align="center">
  66 + <template slot-scope="scope">
  67 + {{ scope.row.objType === '3333' ? $t('feeReceipt.search.houseFee') : $t('feeReceipt.search.parkingFee') }}
  68 + </template>
  69 + </el-table-column>
  70 + <el-table-column prop="feeName" :label="$t('feeReceipt.table.feeName')" align="center" />
  71 + <el-table-column prop="roomName" :label="$t('feeReceipt.table.roomName')" align="center" />
  72 + <el-table-column prop="payObjName" :label="$t('feeReceipt.table.payObjName')" align="center" />
  73 + <el-table-column prop="carNum" :label="$t('feeReceipt.table.carNum')" align="center" />
  74 + <el-table-column prop="amount" :label="$t('feeReceipt.table.amount')" align="center" />
  75 + <el-table-column prop="createTime" :label="$t('feeReceipt.table.createTime')" align="center" />
  76 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  77 + <template slot-scope="scope">
  78 + <el-button size="mini" type="primary" @click="_printFeeReceipt(scope.row)">
  79 + {{ $t('feeReceipt.button.reprint') }}
  80 + </el-button>
  81 + <el-button size="mini" type="success" @click="_printFeeSmallReceipt(scope.row)">
  82 + {{ $t('feeReceipt.button.reprintSmall') }}
  83 + </el-button>
  84 + </template>
  85 + </el-table-column>
  86 + </el-table>
  87 +
  88 + <el-pagination :current-page.sync="page.current" :page-sizes="[10, 20, 30, 50]" :page-size="page.size"
  89 + :total="page.total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
  90 + @current-change="handleCurrentChange" />
  91 + </el-card>
  92 + </div>
  93 +</template>
  94 +
  95 +<script>
  96 +import { queryFeeReceipt, listFeePrintPage } from '@/api/fee/feeReceiptApi'
  97 +import { getCommunityId } from '@/api/community/communityApi'
  98 +
  99 +export default {
  100 + name: 'FeeReceiptList',
  101 + data() {
  102 + return {
  103 + loading: false,
  104 + feeReceiptManageInfo: {
  105 + feeReceipts: [],
  106 + moreCondition: false,
  107 + printUrl: '/print.html#/pages/property/printPayFee',
  108 + conditions: {
  109 + objType: '',
  110 + storeName: '',
  111 + objId: '',
  112 + month: '',
  113 + qstartTime: '',
  114 + qendTime: '',
  115 + type: '',
  116 + roomId: '',
  117 + communityId: getCommunityId(),
  118 + receiptId: '',
  119 + page: 1,
  120 + row: 10
  121 + }
  122 + },
  123 + page: {
  124 + current: 1,
  125 + size: 10,
  126 + total: 0
  127 + }
  128 + }
  129 + },
  130 + created() {
  131 + this._listFeeReceipts()
  132 + this._listFeePrintPages()
  133 + },
  134 + methods: {
  135 + async _listFeeReceipts() {
  136 + try {
  137 + this.loading = true
  138 + const params = {
  139 + ...this.feeReceiptManageInfo.conditions,
  140 + page: this.page.current,
  141 + row: this.page.size
  142 + }
  143 + const { data, total } = await queryFeeReceipt(params)
  144 + this.feeReceiptManageInfo.feeReceipts = data
  145 + this.page.total = total
  146 + } catch (error) {
  147 + this.$message.error(this.$t('feeReceipt.fetchError'))
  148 + } finally {
  149 + this.loading = false
  150 + }
  151 + },
  152 + async _listFeePrintPages() {
  153 + try {
  154 + const params = {
  155 + page: 1,
  156 + row: 1,
  157 + state: 'T',
  158 + communityId: getCommunityId()
  159 + }
  160 + const { data } = await listFeePrintPage(params)
  161 + if (data && data.length > 0) {
  162 + this.feeReceiptManageInfo.printUrl = data[0].url
  163 + }
  164 + } catch (error) {
  165 + console.error('请求失败:', error)
  166 + }
  167 + },
  168 + _queryFeeReceiptMethod() {
  169 + this.page.current = 1
  170 + this._listFeeReceipts()
  171 + },
  172 + _resetFeeReceiptMethod() {
  173 + this.feeReceiptManageInfo.conditions = {
  174 + ...this.feeReceiptManageInfo.conditions,
  175 + objType: '',
  176 + roomId: '',
  177 + receiptId: '',
  178 + qstartTime: '',
  179 + qendTime: ''
  180 + }
  181 + this._listFeeReceipts()
  182 + },
  183 + _printFeeReceipt(receipt) {
  184 + window.open(`${this.feeReceiptManageInfo.printUrl}?receiptId=${receipt.receiptId}`)
  185 + },
  186 + _printFeeSmallReceipt(receipt) {
  187 + window.open(`/smallPrint.html#/pages/property/printSmallPayFee?receiptIds=${receipt.receiptId}`)
  188 + },
  189 + _moreCondition() {
  190 + this.feeReceiptManageInfo.moreCondition = !this.feeReceiptManageInfo.moreCondition
  191 + },
  192 + handleSizeChange(val) {
  193 + this.page.size = val
  194 + this._listFeeReceipts()
  195 + },
  196 + handleCurrentChange(val) {
  197 + this.page.current = val
  198 + this._listFeeReceipts()
  199 + }
  200 + }
  201 +}
  202 +</script>
  203 +
  204 +<style lang="scss" scoped>
  205 +.fee-receipt-container {
  206 + padding: 20px;
  207 +
  208 + .search-wrapper {
  209 + margin-bottom: 20px;
  210 +
  211 + .el-row {
  212 + margin-bottom: 20px;
  213 + }
  214 + }
  215 +
  216 + .list-wrapper {
  217 + .el-pagination {
  218 + margin-top: 20px;
  219 + text-align: right;
  220 + }
  221 + }
  222 +}
  223 +</style>
0 224 \ No newline at end of file
... ...
src/views/fee/listApplyRoomDiscountRecordLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + listApplyRoomDiscountRecord: {
  4 + title: 'Decoration Tracking Record',
  5 + id: 'ID',
  6 + room: 'Room',
  7 + operator: 'Operator',
  8 + createTime: 'Create Time',
  9 + status: 'Status',
  10 + isViolation: 'Is Violation',
  11 + remark: 'Remark',
  12 + fetchError: 'Failed to fetch records'
  13 + },
  14 + applyRoomDiscountRecord: {
  15 + title: 'Inspection Tracking',
  16 + room: 'Room',
  17 + status: 'Status',
  18 + isViolation: 'Is Violation',
  19 + selectViolation: 'Please select violation status',
  20 + remark: 'Remark',
  21 + remarkPlaceholder: 'Please enter remarks',
  22 + uploadImages: 'Upload Images',
  23 + uploadVideo: 'Upload Video',
  24 + violationRequired: 'Violation status is required',
  25 + remarkRequired: 'Remark is required'
  26 + },
  27 + deleteApplyRoomDiscountRecord: {
  28 + title: 'Confirm Operation',
  29 + confirmText: 'Are you sure to delete this inspection record?'
  30 + },
  31 + uploadImage: {
  32 + maxCountError: 'Maximum {count} images can be uploaded',
  33 + sizeError: 'Image size cannot exceed 2MB',
  34 + uploadFailed: 'Image upload failed'
  35 + },
  36 + uploadVedio: {
  37 + uploadButton: 'Upload Video',
  38 + sizeError: 'Video size cannot exceed 500MB',
  39 + uploadSuccess: 'Video uploaded successfully',
  40 + uploadFailed: 'Video upload failed'
  41 + }
  42 + },
  43 + zh: {
  44 + listApplyRoomDiscountRecord: {
  45 + title: '装修跟踪记录',
  46 + id: 'ID',
  47 + room: '房屋',
  48 + operator: '操作人员',
  49 + createTime: '创建时间',
  50 + status: '状态',
  51 + isViolation: '是否违规',
  52 + remark: '备注',
  53 + fetchError: '获取记录失败'
  54 + },
  55 + applyRoomDiscountRecord: {
  56 + title: '验房跟踪',
  57 + room: '房屋',
  58 + status: '状态',
  59 + isViolation: '是否违规',
  60 + selectViolation: '请选择是否违规',
  61 + remark: '备注',
  62 + remarkPlaceholder: '请填写备注',
  63 + uploadImages: '上传图片',
  64 + uploadVideo: '上传视频',
  65 + violationRequired: '是否违规不能为空',
  66 + remarkRequired: '备注不能为空'
  67 + },
  68 + deleteApplyRoomDiscountRecord: {
  69 + title: '请确认您的操作',
  70 + confirmText: '确定删除验房记录吗?'
  71 + },
  72 + uploadImage: {
  73 + maxCountError: '最多只能上传{count}张图片',
  74 + sizeError: '图片大小不能超过2MB',
  75 + uploadFailed: '图片上传失败'
  76 + },
  77 + uploadVedio: {
  78 + uploadButton: '上传视频',
  79 + sizeError: '视频大小不能超过500MB',
  80 + uploadSuccess: '视频上传成功',
  81 + uploadFailed: '视频上传失败'
  82 + }
  83 + }
  84 +}
0 85 \ No newline at end of file
... ...
src/views/fee/listApplyRoomDiscountRecordList.vue 0 → 100644
  1 +<template>
  2 + <div class="list-apply-room-discount-record-container">
  3 + <el-card class="box-card">
  4 + <div slot="header" class="clearfix">
  5 + <div class="card-header">
  6 + <span>{{ listApplyRoomDiscountRecordsInfo.conditions.roomName }}</span>
  7 + <span class="header-title">{{ $t('listApplyRoomDiscountRecord.title') }}</span>
  8 + <div class="header-tools">
  9 + <el-button type="primary" size="small" @click="_openAddModal(listApplyRoomDiscountRecordsInfo.applyRoomDiscount)">
  10 + <i class="el-icon-plus"></i>
  11 + {{ $t('common.add') }}
  12 + </el-button>
  13 + <el-button type="primary" size="small" style="margin-left:10px" @click="_goBack()">
  14 + <i class="el-icon-close"></i>
  15 + {{ $t('common.back') }}
  16 + </el-button>
  17 + </div>
  18 + </div>
  19 + </div>
  20 +
  21 + <el-table
  22 + :data="listApplyRoomDiscountRecordsInfo.listApplyRoomDiscountRecords"
  23 + border
  24 + style="width: 100%"
  25 + v-loading="loading"
  26 + >
  27 + <el-table-column prop="ardrId" :label="$t('listApplyRoomDiscountRecord.id')" align="center" />
  28 + <el-table-column prop="roomName" :label="$t('listApplyRoomDiscountRecord.room')" align="center" />
  29 + <el-table-column prop="createUserName" :label="$t('listApplyRoomDiscountRecord.operator')" align="center" />
  30 + <el-table-column prop="createTime" :label="$t('listApplyRoomDiscountRecord.createTime')" align="center" />
  31 + <el-table-column prop="stateName" :label="$t('listApplyRoomDiscountRecord.status')" align="center" />
  32 + <el-table-column prop="isTrueName" :label="$t('listApplyRoomDiscountRecord.isViolation')" align="center" />
  33 + <el-table-column prop="remark" :label="$t('listApplyRoomDiscountRecord.remark')" align="center" />
  34 + <el-table-column :label="$t('common.operation')" align="center" width="200">
  35 + <template slot-scope="scope">
  36 + <el-button-group>
  37 + <el-button size="mini" @click="_openApplyRoomDiscountRecordDetailsModel(scope.row)">
  38 + {{ $t('common.viewDetails') }}
  39 + </el-button>
  40 + <el-button size="mini" type="danger" @click="_openDeleteApplyRoomDiscountRecordModel(scope.row)">
  41 + {{ $t('common.delete') }}
  42 + </el-button>
  43 + </el-button-group>
  44 + </template>
  45 + </el-table-column>
  46 + </el-table>
  47 +
  48 + <el-pagination
  49 + @size-change="handleSizeChange"
  50 + @current-change="handleCurrentChange"
  51 + :current-page="page.current"
  52 + :page-sizes="[10, 20, 30, 50]"
  53 + :page-size="page.size"
  54 + layout="total, sizes, prev, pager, next, jumper"
  55 + :total="listApplyRoomDiscountRecordsInfo.total"
  56 + class="pagination"
  57 + >
  58 + </el-pagination>
  59 + </el-card>
  60 +
  61 + <apply-room-discount-record ref="applyRoomDiscountRecord" @success="handleSuccess" />
  62 + <delete-apply-room-discount-record ref="deleteApplyRoomDiscountRecord" @success="handleSuccess" />
  63 + </div>
  64 +</template>
  65 +
  66 +<script>
  67 +import { queryApplyRoomDiscountRecord } from '@/api/fee/listApplyRoomDiscountRecordApi'
  68 +import ApplyRoomDiscountRecord from '@/components/fee/applyRoomDiscountRecord'
  69 +import DeleteApplyRoomDiscountRecord from '@/components/fee/deleteApplyRoomDiscountRecord'
  70 +import { getCommunityId } from '@/api/community/communityApi'
  71 +
  72 +export default {
  73 + name: 'ListApplyRoomDiscountRecord',
  74 + components: {
  75 + ApplyRoomDiscountRecord,
  76 + DeleteApplyRoomDiscountRecord
  77 + },
  78 + data() {
  79 + return {
  80 + loading: false,
  81 + listApplyRoomDiscountRecordsInfo: {
  82 + listApplyRoomDiscountRecords: [],
  83 + applyRoomDiscount: [],
  84 + total: 0,
  85 + records: 1,
  86 + conditions: {
  87 + ardId: '',
  88 + roomId: '',
  89 + roomName: '',
  90 + stateName: '',
  91 + state: '',
  92 + communityId: ''
  93 + }
  94 + },
  95 + page: {
  96 + current: 1,
  97 + size: 10
  98 + }
  99 + }
  100 + },
  101 + created() {
  102 + this.listApplyRoomDiscountRecordsInfo.conditions.communityId = getCommunityId()
  103 + this.listApplyRoomDiscountRecordsInfo.conditions.ardId = this.$route.query.ardId
  104 + this.listApplyRoomDiscountRecordsInfo.conditions.state = this.$route.query.state
  105 + this.listApplyRoomDiscountRecordsInfo.conditions.stateName = this.$route.query.stateName
  106 + this.listApplyRoomDiscountRecordsInfo.conditions.roomId = this.$route.query.roomId
  107 + this.listApplyRoomDiscountRecordsInfo.conditions.roomName = this.$route.query.roomName
  108 + this._listApplyRoomDiscountRecords(this.page.current, this.page.size)
  109 + },
  110 + methods: {
  111 + async _listApplyRoomDiscountRecords(page, size) {
  112 + try {
  113 + this.loading = true
  114 + const params = {
  115 + page: page,
  116 + row: size,
  117 + ...this.listApplyRoomDiscountRecordsInfo.conditions
  118 + }
  119 + const { data, total } = await queryApplyRoomDiscountRecord(params)
  120 + this.listApplyRoomDiscountRecordsInfo.listApplyRoomDiscountRecords = data
  121 + this.listApplyRoomDiscountRecordsInfo.total = total
  122 + } catch (error) {
  123 + this.$message.error(this.$t('listApplyRoomDiscountRecord.fetchError'))
  124 + } finally {
  125 + this.loading = false
  126 + }
  127 + },
  128 + _openAddModal(applyRoomDiscount) {
  129 + applyRoomDiscount.push(this.listApplyRoomDiscountRecordsInfo.conditions.ardId)
  130 + applyRoomDiscount.push(this.listApplyRoomDiscountRecordsInfo.conditions.state)
  131 + applyRoomDiscount.push(this.listApplyRoomDiscountRecordsInfo.conditions.stateName)
  132 + applyRoomDiscount.push(this.listApplyRoomDiscountRecordsInfo.conditions.roomId)
  133 + applyRoomDiscount.push(this.listApplyRoomDiscountRecordsInfo.conditions.roomName)
  134 + this.$refs.applyRoomDiscountRecord.open(applyRoomDiscount)
  135 + },
  136 + _openDeleteApplyRoomDiscountRecordModel(applyRoomDiscountRecord) {
  137 + this.$refs.deleteApplyRoomDiscountRecord.open(applyRoomDiscountRecord)
  138 + },
  139 + _openApplyRoomDiscountRecordDetailsModel(applyRoomDiscountRecord) {
  140 + this.$router.push({
  141 + path: '/views/fee/listApplyRoomDiscountRecordDetails',
  142 + query: {
  143 + ardrId: applyRoomDiscountRecord.ardrId,
  144 + roomName: applyRoomDiscountRecord.roomName,
  145 + state: applyRoomDiscountRecord.state
  146 + }
  147 + })
  148 + },
  149 + handleSuccess() {
  150 + this._listApplyRoomDiscountRecords(this.page.current, this.page.size)
  151 + },
  152 + handleSizeChange(val) {
  153 + this.page.size = val
  154 + this._listApplyRoomDiscountRecords(this.page.current, val)
  155 + },
  156 + handleCurrentChange(val) {
  157 + this.page.current = val
  158 + this._listApplyRoomDiscountRecords(val, this.page.size)
  159 + },
  160 + _goBack() {
  161 + this.$router.go(-1)
  162 + }
  163 + }
  164 +}
  165 +</script>
  166 +
  167 +<style lang="scss" scoped>
  168 +.list-apply-room-discount-record-container {
  169 + padding: 20px;
  170 +
  171 + .box-card {
  172 + margin-bottom: 20px;
  173 +
  174 + .card-header {
  175 + display: flex;
  176 + align-items: center;
  177 + justify-content: space-between;
  178 +
  179 + .header-title {
  180 + margin-left: 10px;
  181 + font-weight: bold;
  182 + }
  183 +
  184 + .header-tools {
  185 + display: flex;
  186 + align-items: center;
  187 + }
  188 + }
  189 + }
  190 +
  191 + .pagination {
  192 + margin-top: 20px;
  193 + text-align: right;
  194 + }
  195 +}
  196 +</style>
0 197 \ No newline at end of file
... ...
src/views/fee/printPayFeeBangTaiLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + printPayFeeBangTai: {
  4 + applyForm: 'Application Form',
  5 + receiptForm: 'Receipt Form',
  6 + receiptNum: 'Receipt No.',
  7 + owner: 'Owner: ',
  8 + paymentTime: 'Payment Time: ',
  9 + serialNum: 'No.',
  10 + feeItem: 'Fee Item',
  11 + houseCar: 'House/Car',
  12 + feeRange: 'Fee Range',
  13 + cycle: 'Cycle',
  14 + unitPrice: 'Unit Price/Fixed Fee',
  15 + areaUsage: 'Area/Usage',
  16 + paymentMethod: 'Payment Method',
  17 + amount: 'Amount',
  18 + discountAmount: 'Discount Amount',
  19 + remark: 'Remark',
  20 + to: ' to ',
  21 + none: 'None',
  22 + capitalRMB: 'Capital RMB',
  23 + departmentHead: 'Department Head',
  24 + handler: 'Handler',
  25 + financialCollection: 'Financial Collection',
  26 + customerConfirmation: 'Customer Confirmation',
  27 + print: 'Print',
  28 + cancel: 'Cancel'
  29 + }
  30 + },
  31 + zh: {
  32 + printPayFeeBangTai: {
  33 + applyForm: '申请单',
  34 + receiptForm: '收据单',
  35 + receiptNum: '单号',
  36 + owner: '业主:',
  37 + paymentTime: '缴费时间:',
  38 + serialNum: '编号',
  39 + feeItem: '收费项目',
  40 + houseCar: '房屋/车辆',
  41 + feeRange: '收费范围',
  42 + cycle: '周期',
  43 + unitPrice: '单价/固定费',
  44 + areaUsage: '面积/用量',
  45 + paymentMethod: '支付方式',
  46 + amount: '金额',
  47 + discountAmount: '优惠金额',
  48 + remark: '备注',
  49 + to: '至',
  50 + none: '无',
  51 + capitalRMB: '大写人民币',
  52 + departmentHead: '部门负责人',
  53 + handler: '经办人',
  54 + financialCollection: '财务收款',
  55 + customerConfirmation: '客户确认',
  56 + print: '打印',
  57 + cancel: '取消'
  58 + }
  59 + }
  60 +}
0 61 \ No newline at end of file
... ...
src/views/fee/printPayFeeBangTaiList.vue 0 → 100644
  1 +<template>
  2 + <div class="print-pay-fee-container">
  3 + <el-row>
  4 + <el-col :span="24">
  5 + <div class="text-center">
  6 + <div style="color:#000;font-size:36px">{{ printPayFeeInfo.communityName }} {{ printPayFeeInfo.apply ==
  7 + 'Y' ? $t('printPayFeeBangTai.applyForm') : $t('printPayFeeBangTai.receiptForm')}}</div>
  8 + <span style="color:#000;font-size:20px">
  9 + <span>{{ $t('printPayFeeBangTai.receiptNum') }}</span>:{{ printPayFeeInfo.receiptNum }}
  10 + </span>
  11 + </div>
  12 + <div style="color:#000;font-size:20px;margin-left:20px">
  13 + <div class="float-left">
  14 + <span>{{ $t('printPayFeeBangTai.owner') }}</span>{{ printPayFeeInfo.payObjName }}
  15 + </div>
  16 + <div class="float-right text-right">
  17 + <span>{{ $t('printPayFeeBangTai.paymentTime') }}</span>{{ printPayFeeInfo.feeTime }}
  18 + </div>
  19 + </div>
  20 + <table class="table vc-table-border" style="color:#000;font-size:20px">
  21 + <thead>
  22 + <tr>
  23 + <th scope="col" class="text-center" width="80px">{{ $t('printPayFeeBangTai.serialNum') }}</th>
  24 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.feeItem') }}</th>
  25 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.houseCar') }}</th>
  26 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.feeRange') }}</th>
  27 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.cycle') }}</th>
  28 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.unitPrice') }}</th>
  29 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.areaUsage') }}</th>
  30 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.paymentMethod') }}</th>
  31 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.amount') }}</th>
  32 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.discountAmount') }}</th>
  33 + <th scope="col" class="text-center">{{ $t('printPayFeeBangTai.remark') }}</th>
  34 + </tr>
  35 + </thead>
  36 + <tbody class="vc-table-border" style="color:#000;font-size:20px">
  37 + <tr v-for="(item, index) in printPayFeeInfo.fees" class="vc-table-border" :key="index">
  38 + <th scope="row" class="text-center">{{ index + 1 }}</th>
  39 + <td class="text-center">{{ item.feeName }}</td>
  40 + <td class="text-center">{{ item.objName }}</td>
  41 + <td class="text-center" v-if="item.preDegrees">
  42 + {{ formatDate(item.startTime) }}<span>{{ $t('printPayFeeBangTai.to') }}</span>{{ formatDate(item.endTime) }}<br>
  43 + {{ item.preDegrees }} {{ $t('printPayFeeBangTai.to') }} {{ item.curDegrees }}
  44 + </td>
  45 + <td class="text-center" v-else-if="item.feeTypeCd == '888800010006'">
  46 + {{ $t('printPayFeeBangTai.none') }}
  47 + </td>
  48 + <td class="text-center" v-else>
  49 + {{ formatDate(item.startTime) }}<span>{{ $t('printPayFeeBangTai.to') }}</span>{{ formatDate(item.endTime) }}
  50 + </td>
  51 + <td class="text-center">{{ item.cycle }}</td>
  52 + <td class="text-center">{{ item.squarePrice }}</td>
  53 + <td class="text-center">{{ item.area }}</td>
  54 + <td class="text-center">{{ item.primeRate }}</td>
  55 + <td class="text-center">{{ item.amount }}</td>
  56 + <td class="text-center">{{ item.discountPrice }}</td>
  57 + <td class="text-center" width="200px">{{ item.remark }}</td>
  58 + </tr>
  59 + <tr>
  60 + <td colspan="3" class="text-center">
  61 + <span>{{ $t('printPayFeeBangTai.capitalRMB') }}</span>(元)
  62 + </td>
  63 + <td colspan="4" class="text-center">{{ changeNumMoneyToChinese(printPayFeeInfo.amount) }}</td>
  64 + <td colspan="1" class="text-center">{{ computeSumArea() }}</td>
  65 + <td colspan="3" class="text-center">{{ printPayFeeInfo.amount }}</td>
  66 + </tr>
  67 + <tr height="60px">
  68 + <td colspan="6">
  69 + <div style="max-width: 600px;">
  70 + <div v-html="printPayFeeInfo.content"></div>
  71 + </div>
  72 + </td>
  73 + <td colspan="5">
  74 + <img :src="printPayFeeInfo.qrImg" width="100px" height="100px">
  75 + </td>
  76 + </tr>
  77 + </tbody>
  78 + </table>
  79 + </el-col>
  80 + </el-row>
  81 + <el-row>
  82 + <el-col :span="20">
  83 + <div class="row" style="color:#000;font-size:20px;margin-left: 10px;">
  84 + <div class="float-left" style="width: 20%;">
  85 + <span>{{ $t('printPayFeeBangTai.departmentHead') }}</span>:
  86 + </div>
  87 + <div class="float-left" style="width: 20%;">
  88 + <span>{{ $t('printPayFeeBangTai.handler') }}</span>:{{ userInfo.name }}
  89 + </div>
  90 + <div class="float-left" style="width: 20%;">
  91 + <span>{{ $t('printPayFeeBangTai.financialCollection') }}</span>:
  92 + </div>
  93 + <div class="float-left" style="width: 20%;">
  94 + <span>{{ $t('printPayFeeBangTai.customerConfirmation') }}</span>:
  95 + </div>
  96 + </div>
  97 + </el-col>
  98 + <el-col :span="4" id="print-btn">
  99 + <el-button type="primary" class="float-right" @click="printPurchaseApplyDiv">
  100 + <i class="el-icon-printer"></i>&nbsp;{{ $t('printPayFeeBangTai.print') }}
  101 + </el-button>
  102 + <el-button type="warning" class="float-right" style="margin-right:20px;" @click="closePage">
  103 + {{ $t('printPayFeeBangTai.cancel') }}
  104 + </el-button>
  105 + </el-col>
  106 + </el-row>
  107 + </div>
  108 +</template>
  109 +
  110 +<script>
  111 +import { queryFeeReceipt, queryFeeReceiptDetail, queryFeePrintSpec } from '@/api/fee/printPayFeeBangTaiApi'
  112 +import { getCommunityId } from '@/api/community/communityApi'
  113 +import { getUserId,getUserName } from '@/api/user/userApi'
  114 +
  115 +export default {
  116 + name: 'PrintPayFeeBangTaiList',
  117 + data() {
  118 + return {
  119 + printPayFeeInfo: {
  120 + communityName: '',
  121 + receiptId: '',
  122 + receiptIds: '',
  123 + roomName: '',
  124 + amount: 0.00,
  125 + fees: [],
  126 + feeTime: '',
  127 + wechatName: '',
  128 + content: '',
  129 + qrImg: '',
  130 + payObjName: '',
  131 + feeReceipt: [],
  132 + apply: 'N',
  133 + receiptNum: '',
  134 + merge: ''
  135 + },
  136 + printFlag: '0',
  137 + userInfo: {},
  138 + communityId: ''
  139 + }
  140 + },
  141 + created() {
  142 + this.initData()
  143 + this.loadData()
  144 + },
  145 + methods: {
  146 + initData() {
  147 + this.printPayFeeInfo.receiptId = this.$route.query.receiptId
  148 + this.printPayFeeInfo.receiptIds = this.$route.query.receiptIds
  149 + this.printPayFeeInfo.apply = this.$route.query.apply || 'N'
  150 + this.printPayFeeInfo.merge = this.$route.query.merge
  151 + this.communityId = getCommunityId()
  152 + this.userInfo = {
  153 + userId: getUserId(),
  154 + name: getUserName()
  155 + }
  156 + },
  157 + async loadData() {
  158 + this.printPayFeeInfo.communityName = this.$store.getters.currentCommunity.name
  159 + await this.loadReceipt()
  160 + await this.loadPrintSpec()
  161 + },
  162 + async loadReceipt() {
  163 + try {
  164 + const params = {
  165 + page: 1,
  166 + row: 30,
  167 + receiptId: this.printPayFeeInfo.receiptId,
  168 + receiptIds: this.printPayFeeInfo.receiptIds,
  169 + communityId: this.communityId
  170 + }
  171 + const { data } = await queryFeeReceipt(params)
  172 + const _feeReceipt = data
  173 + let _amount = 0
  174 + _feeReceipt.forEach(item => {
  175 + _amount += parseFloat(item.amount)
  176 + })
  177 +
  178 + this.printPayFeeInfo.amount = _amount.toFixed(2)
  179 + this.printPayFeeInfo.roomName = _feeReceipt[0].objName
  180 + this.printPayFeeInfo.feeTime = _feeReceipt[0].createTime
  181 + this.printPayFeeInfo.payObjName = _feeReceipt[0].payObjName
  182 + this.printPayFeeInfo.feeReceipt = _feeReceipt
  183 + this.printPayFeeInfo.receiptNum = _feeReceipt[0].receiptCode
  184 + await this.loadReceiptDetail()
  185 + } catch (error) {
  186 + console.error('Failed to load receipt:', error)
  187 + }
  188 + },
  189 + async loadReceiptDetail() {
  190 + try {
  191 + const params = {
  192 + page: 1,
  193 + row: 100,
  194 + receiptId: this.printPayFeeInfo.receiptId,
  195 + receiptIds: this.printPayFeeInfo.receiptIds,
  196 + communityId: this.communityId,
  197 + mergeFee: this.printPayFeeInfo.merge
  198 + }
  199 + const { data } = await queryFeeReceiptDetail(params)
  200 + const _feeReceiptDetails = data
  201 + this.printPayFeeInfo.receiptNum = this.printPayFeeInfo.receiptNum + "(" + _feeReceiptDetails[0].payOrderId + ")"
  202 + _feeReceiptDetails.forEach(item => {
  203 + this.printPayFeeInfo.feeReceipt.forEach(im => {
  204 + if (item.receiptId == im.receiptId) {
  205 + item.objName = im.objName
  206 + item.feeTypeCd = im.feeTypeCd
  207 + }
  208 + })
  209 + })
  210 + this.printPayFeeInfo.fees = _feeReceiptDetails
  211 + } catch (error) {
  212 + console.error('Failed to load receipt detail:', error)
  213 + }
  214 + },
  215 + async loadPrintSpec() {
  216 + try {
  217 + const params = {
  218 + page: 1,
  219 + row: 1,
  220 + specCd: 2020,
  221 + communityId: this.communityId
  222 + }
  223 + const { data } = await queryFeePrintSpec(params)
  224 + if (data.length > 0) {
  225 + this.printPayFeeInfo.content = data[0].content
  226 + this.printPayFeeInfo.qrImg = data[0].qrImg
  227 + if (data[0].printName) {
  228 + this.printPayFeeInfo.communityName = data[0].printName
  229 + }
  230 + }
  231 + } catch (error) {
  232 + console.error('Failed to load print spec:', error)
  233 + }
  234 + },
  235 + computeSumArea() {
  236 + let _sum = 0.00
  237 + this.printPayFeeInfo.fees.forEach(item => {
  238 + if (item.area && this.isNumber(item.area)) {
  239 + _sum += parseFloat(item.area)
  240 + }
  241 + })
  242 + return _sum.toFixed(2)
  243 + },
  244 + isNumber(value) {
  245 + return !isNaN(parseFloat(value)) && isFinite(value)
  246 + },
  247 + formatDate(date) {
  248 + if (!date) return ''
  249 + return this.$moment(date).format('YYYY-MM-DD')
  250 + },
  251 + changeNumMoneyToChinese(num) {
  252 + // 这里实现数字转中文大写金额的逻辑
  253 + // 由于实现较复杂,建议使用第三方库或自行实现
  254 + return num // 暂时返回原值
  255 + },
  256 + printPurchaseApplyDiv() {
  257 + this.printFlag = '1'
  258 + document.getElementById("print-btn").style.display = "none"
  259 + window.print()
  260 + window.opener = null
  261 + window.close()
  262 + },
  263 + closePage() {
  264 + window.opener = null
  265 + window.close()
  266 + }
  267 + }
  268 +}
  269 +</script>
  270 +
  271 +<style scoped>
  272 +.print-pay-fee-container {
  273 + padding: 20px;
  274 +}
  275 +
  276 +.text-center {
  277 + text-align: center;
  278 +}
  279 +
  280 +.float-left {
  281 + float: left;
  282 +}
  283 +
  284 +.float-right {
  285 + float: right;
  286 +}
  287 +
  288 +.vc-table-border {
  289 + border: 1px solid #000;
  290 + border-collapse: collapse;
  291 +}
  292 +
  293 +.vc-table-border th,
  294 +.vc-table-border td {
  295 + border: 1px solid #000;
  296 + padding: 5px;
  297 +}
  298 +
  299 +#print-btn {
  300 + text-align: right;
  301 + margin-top: 20px;
  302 +}
  303 +</style>
0 304 \ No newline at end of file
... ...
src/views/fee/printPayFeeLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + printPayFee: {
  4 + apply: 'Application',
  5 + receipt: 'Receipt',
  6 + refund: 'Refund',
  7 + owner: 'Owner',
  8 + receiptNum: 'Receipt No.',
  9 + payTime: 'Payment Time',
  10 + serialNumber: 'No.',
  11 + feeItem: 'Fee Item',
  12 + houseCar: 'House/Vehicle',
  13 + feeRange: 'Fee Range',
  14 + to: 'to',
  15 + none: 'None',
  16 + unitPrice: 'Unit Price',
  17 + areaUsage: 'Area/Usage',
  18 + paymentMethod: 'Payment Method',
  19 + receivableActual: 'Receivable/Actual',
  20 + yuan: 'yuan',
  21 + discountAmount: 'Discount Amount',
  22 + remark: 'Remark',
  23 + capitalRMB: 'Capital RMB',
  24 + accountDeduction: 'Account Deduction',
  25 + departmentHead: 'Department Head:',
  26 + operator: 'Operator',
  27 + financeReceipt: 'Finance Receipt:',
  28 + customerConfirm: 'Customer Confirm:'
  29 + }
  30 + },
  31 + zh: {
  32 + printPayFee: {
  33 + apply: '申请单',
  34 + receipt: '收据单',
  35 + refund: '退款单',
  36 + owner: '业主',
  37 + receiptNum: '单号',
  38 + payTime: '缴费时间',
  39 + serialNumber: '编号',
  40 + feeItem: '收费项目',
  41 + houseCar: '房屋/车辆',
  42 + feeRange: '收费范围',
  43 + to: '至',
  44 + none: '无',
  45 + unitPrice: '单价',
  46 + areaUsage: '面积/用量',
  47 + paymentMethod: '支付方式',
  48 + receivableActual: '应收/实收',
  49 + yuan: '元',
  50 + discountAmount: '优惠金额',
  51 + remark: '备注',
  52 + capitalRMB: '大写人民币(元)',
  53 + accountDeduction: '账户扣款',
  54 + departmentHead: '部门负责人:',
  55 + operator: '经办人:',
  56 + financeReceipt: '财务收款:',
  57 + customerConfirm: '客户确认:'
  58 + }
  59 + }
  60 +}
0 61 \ No newline at end of file
... ...
src/views/fee/printPayFeeList.vue 0 → 100644
  1 +<template>
  2 + <div class="print-pay-fee-container">
  3 + <el-card class="box-card">
  4 + <div class="text-center">
  5 + <div style="color:#000;font-size:26px">{{ printPayFeeInfo.communityName }}
  6 + <span v-if="printPayFeeInfo.apply === 'Y'">{{ $t('printPayFee.apply') }}</span>
  7 + <span v-if="printPayFeeInfo.apply === 'N'">{{ $t('printPayFee.receipt') }}</span>
  8 + <span v-if="printPayFeeInfo.apply === 'R'">{{ $t('printPayFee.refund') }}</span>
  9 + </div>
  10 + </div>
  11 + <div style="color:#000;font-size:18px;margin-left:20px">
  12 + <div class="float-left">
  13 + {{ $t('printPayFee.owner') }}:{{ printPayFeeInfo.payObjName }}
  14 + </div>
  15 + <div class="float-right text-right">
  16 + <span>{{ $t('printPayFee.receiptNum') }}{{ printPayFeeInfo.receiptNum }}</span>
  17 + <br />
  18 + <span>{{ $t('printPayFee.payTime') }}:{{ printPayFeeInfo.feeTime }}</span>
  19 + </div>
  20 + </div>
  21 + <el-table :data="printPayFeeInfo.fees" border style="width: 100%;color:#000;font-size:20px">
  22 + <el-table-column prop="index" :label="$t('printPayFee.serialNumber')" width="80" align="center">
  23 + <template slot-scope="scope">
  24 + {{ scope.$index + 1 }}
  25 + </template>
  26 + </el-table-column>
  27 + <el-table-column prop="feeName" :label="$t('printPayFee.feeItem')" align="center"></el-table-column>
  28 + <el-table-column prop="objName" :label="$t('printPayFee.houseCar')" align="center"></el-table-column>
  29 + <el-table-column :label="$t('printPayFee.feeRange')" align="center">
  30 + <template slot-scope="scope">
  31 + <div v-if="scope.row.preDegrees">
  32 + {{ dateFormat(scope.row.startTime) }}
  33 + <span>{{ $t('printPayFee.to') }}</span>
  34 + {{ dateFormat(scope.row.endTime) }}<br />
  35 + {{ scope.row.preDegrees }} {{ $t('printPayFee.to') }} {{ scope.row.curDegrees }}
  36 + </div>
  37 + <div v-else-if="scope.row.feeTypeCd === '888800010006'">
  38 + {{ $t('printPayFee.none') }}
  39 + </div>
  40 + <div v-else>
  41 + {{ dateFormat(scope.row.startTime) }}{{ $t('printPayFee.to') }}{{ dateFormat(scope.row.endTime) }}
  42 + </div>
  43 + </template>
  44 + </el-table-column>
  45 + <el-table-column :label="$t('printPayFee.unitPrice')" align="center">
  46 + <template slot-scope="scope">
  47 + {{ scope.row.squarePrice }}<br />{{ scope.row.units }}
  48 + </template>
  49 + </el-table-column>
  50 + <el-table-column prop="area" :label="$t('printPayFee.areaUsage')" align="center"></el-table-column>
  51 + <el-table-column prop="primeRate" :label="$t('printPayFee.paymentMethod')" align="center"></el-table-column>
  52 + <el-table-column :label="$t('printPayFee.receivableActual')" align="center">
  53 + <template slot-scope="scope">
  54 + {{ scope.row.receivableAmount }}/{{ scope.row.amount }}{{ $t('printPayFee.yuan') }}
  55 + <span v-if="scope.row.amount < 0">({{ $t('printPayFee.refund') }})</span>
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column prop="discountPrice" :label="$t('printPayFee.discountAmount')" align="center">
  59 + <template slot-scope="scope">
  60 + {{ scope.row.discountPrice || 0 }}
  61 + </template>
  62 + </el-table-column>
  63 + <el-table-column prop="remark" :label="$t('printPayFee.remark')" width="200" align="center"></el-table-column>
  64 + </el-table>
  65 +
  66 + <el-row>
  67 + <el-col :span="3" class="text-center">{{ $t('printPayFee.capitalRMB') }}</el-col>
  68 + <el-col :span="2" class="text-center">{{ changeNumMoneyToChinese(printPayFeeInfo.amount) }}</el-col>
  69 + <el-col :span="1" class="text-center">{{ printPayFeeInfo.amount }}</el-col>
  70 + <el-col :span="2" class="text-center">{{ $t('printPayFee.accountDeduction') }}</el-col>
  71 + <el-col :span="2" class="text-center">{{ printPayFeeInfo.acctAmount }}</el-col>
  72 + </el-row>
  73 +
  74 + <el-row v-if="printPayFeeInfo.content || printPayFeeInfo.qrImg">
  75 + <el-col :span="6">
  76 + <div style="max-width: 600px;" v-html="printPayFeeInfo.content"></div>
  77 + </el-col>
  78 + <el-col :span="4">
  79 + <img v-if="printPayFeeInfo.qrImg" :src="printPayFeeInfo.qrImg" width="100px" height="100px">
  80 + </el-col>
  81 + </el-row>
  82 +
  83 + <el-row style="color:#000;font-size:18px;margin-left: 10px;">
  84 + <el-col :span="6">
  85 + {{ $t('printPayFee.departmentHead') }}
  86 + </el-col>
  87 + <el-col :span="6">
  88 + {{ $t('printPayFee.operator') }}:{{ userInfo.name }}
  89 + </el-col>
  90 + <el-col :span="6">
  91 + {{ $t('printPayFee.financeReceipt') }}
  92 + </el-col>
  93 + <el-col :span="6">
  94 + {{ $t('printPayFee.customerConfirm') }}
  95 + </el-col>
  96 + </el-row>
  97 +
  98 + <el-row id="print-btn">
  99 + <el-col :span="24">
  100 + <el-button type="primary" class="float-right" @click="handlePrint">
  101 + <i class="el-icon-printer"></i>&nbsp;{{ $t('common.print') }}
  102 + </el-button>
  103 + <el-button type="warning" class="float-right" style="margin-right:20px;" @click="handleClose">
  104 + {{ $t('common.cancel') }}
  105 + </el-button>
  106 + </el-col>
  107 + </el-row>
  108 + </el-card>
  109 + </div>
  110 +</template>
  111 +
  112 +<script>
  113 +import { getCommunityId } from '@/api/community/communityApi'
  114 +import { queryFeeReceipt, queryFeeReceiptDetail, queryFeePrintSpec } from '@/api/fee/printPayFeeApi'
  115 +import { getUserId,getUserName } from '@/api/user/userApi'
  116 +
  117 +export default {
  118 + name: 'PrintPayFeeList',
  119 + data() {
  120 + return {
  121 + printPayFeeInfo: {
  122 + communityName: '',
  123 + receiptId: '',
  124 + receiptIds: '',
  125 + detailIds: '',
  126 + roomName: '',
  127 + amount: 0.00,
  128 + fees: [],
  129 + feeTime: '',
  130 + wechatName: '',
  131 + content: '',
  132 + qrImg: '',
  133 + payObjName: '',
  134 + feeReceipt: [],
  135 + apply: 'N',
  136 + receiptNum: '',
  137 + acctAmount: 0
  138 + },
  139 + userInfo: {},
  140 + printFlag: '0',
  141 + communityId: ''
  142 + }
  143 + },
  144 + created() {
  145 + this.communityId = getCommunityId()
  146 + this.userInfo = {
  147 + userId: getUserId(),
  148 + name: getUserName()
  149 + }
  150 + this.initData()
  151 + },
  152 + methods: {
  153 + initData() {
  154 + this.printPayFeeInfo.receiptId = this.$route.query.receiptId
  155 + this.printPayFeeInfo.receiptIds = this.$route.query.receiptIds
  156 + this.printPayFeeInfo.detailIds = this.$route.query.detailIds
  157 + this.printPayFeeInfo.apply = this.$route.query.apply
  158 + this.printPayFeeInfo.merge = this.$route.query.merge
  159 + this.printPayFeeInfo.communityName = this.$route.query.communityName || ''
  160 +
  161 + this.loadReceipt()
  162 + this.loadPrintSpec()
  163 + },
  164 + loadReceipt() {
  165 + const params = {
  166 + page: 1,
  167 + row: 30,
  168 + receiptId: this.printPayFeeInfo.receiptId,
  169 + receiptIds: this.printPayFeeInfo.receiptIds,
  170 + detailIds: this.printPayFeeInfo.detailIds,
  171 + communityId: this.communityId
  172 + }
  173 +
  174 + queryFeeReceipt(params).then(response => {
  175 + const feeReceipt = response.data
  176 + let amount = 0
  177 + feeReceipt.forEach(item => {
  178 + amount += parseFloat(item.amount)
  179 + })
  180 +
  181 + this.printPayFeeInfo.amount = amount.toFixed(2)
  182 + this.printPayFeeInfo.roomName = feeReceipt[0].objName
  183 + this.printPayFeeInfo.feeTime = feeReceipt[0].createTime
  184 + this.printPayFeeInfo.payObjName = feeReceipt[0].payObjName
  185 + this.printPayFeeInfo.feeReceipt = feeReceipt
  186 + this.printPayFeeInfo.receiptNum = feeReceipt[0].receiptCode
  187 +
  188 + this.loadReceiptDetail()
  189 + })
  190 + },
  191 + loadReceiptDetail() {
  192 + const params = {
  193 + page: 1,
  194 + row: 100,
  195 + receiptId: this.printPayFeeInfo.receiptId,
  196 + receiptIds: this.printPayFeeInfo.receiptIds,
  197 + detailIds: this.printPayFeeInfo.detailIds,
  198 + communityId: this.communityId,
  199 + mergeFee: this.printPayFeeInfo.merge
  200 + }
  201 +
  202 + queryFeeReceiptDetail(params).then(response => {
  203 + const feeReceiptDetails = response.data
  204 + this.printPayFeeInfo.receiptNum = this.printPayFeeInfo.receiptNum + "(" + feeReceiptDetails[0].payOrderId + ")"
  205 + if (feeReceiptDetails[0].amount < 0) {
  206 + this.printPayFeeInfo.apply = 'R'
  207 + }
  208 + let acctAmount = 0.0
  209 + feeReceiptDetails.forEach(item => {
  210 + acctAmount = acctAmount + parseFloat(item.acctAmount)
  211 + this.printPayFeeInfo.feeReceipt.forEach(im => {
  212 + if (item.receiptId == im.receiptId) {
  213 + item.objName = im.objName
  214 + item.feeTypeCd = im.feeTypeCd
  215 + }
  216 + })
  217 + })
  218 + this.printPayFeeInfo.fees = feeReceiptDetails
  219 + this.printPayFeeInfo.acctAmount = acctAmount.toFixed(2)
  220 + })
  221 + },
  222 + loadPrintSpec() {
  223 + const params = {
  224 + page: 1,
  225 + row: 1,
  226 + specCd: 2020,
  227 + communityId: this.communityId
  228 + }
  229 +
  230 + queryFeePrintSpec(params).then(response => {
  231 + const data = response.data
  232 + if (data.length > 0) {
  233 + this.printPayFeeInfo.content = data[0].content
  234 + this.printPayFeeInfo.qrImg = data[0].qrImg
  235 + if (data[0].printName) {
  236 + this.printPayFeeInfo.communityName = data[0].printName
  237 + }
  238 + }
  239 + })
  240 + },
  241 + dateFormat(date) {
  242 + if (!date) return ''
  243 + return this.$moment(date).format('YYYY-MM-DD')
  244 + },
  245 + changeNumMoneyToChinese(num) {
  246 + // 这里实现数字转中文大写金额的逻辑
  247 + // 原vc.changeNumMoneyToChinese的实现
  248 + return num // 实际应该返回转换后的中文大写金额
  249 + },
  250 + handlePrint() {
  251 + this.printFlag = '1'
  252 + document.getElementById("print-btn").style.display = "none"
  253 + window.print()
  254 + window.close()
  255 + },
  256 + handleClose() {
  257 + window.close()
  258 + }
  259 + }
  260 +}
  261 +</script>
  262 +
  263 +<style scoped>
  264 +.print-pay-fee-container {
  265 + padding: 20px;
  266 +}
  267 +
  268 +.float-left {
  269 + float: left;
  270 +}
  271 +
  272 +.float-right {
  273 + float: right;
  274 +}
  275 +
  276 +.text-center {
  277 + text-align: center;
  278 +}
  279 +
  280 +.text-right {
  281 + text-align: right;
  282 +}
  283 +
  284 +.el-row {
  285 + width: 100%;
  286 + margin-bottom: 20px;
  287 +}
  288 +
  289 +.el-col {
  290 + width: 100%;
  291 +}
  292 +</style>
0 293 \ No newline at end of file
... ...
src/views/fee/printSmallPayFeeLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + printSmallPayFee: {
  4 + receipt: 'Payment Receipt',
  5 + receiptNum: 'Receipt No:',
  6 + orderNum: 'Order No:',
  7 + roomNum: 'Room No',
  8 + owner: 'Owner',
  9 + time: 'Time',
  10 + feeItem: 'Fee Item',
  11 + feeRange: 'Fee Range',
  12 + to: 'to',
  13 + unitPrice: 'Unit Price/Fixed Fee:',
  14 + areaUsage: 'Area/Usage:',
  15 + paymentMethod: 'Payment Method:',
  16 + meterReading: 'Meter Reading:',
  17 + amount: 'Amount:',
  18 + refund: '(Refund)',
  19 + discount: 'Discount Amount:',
  20 + remark: 'Remark',
  21 + total: 'Total',
  22 + issuer: 'Issuer',
  23 + print: 'Print',
  24 + cloudPrint: 'Cloud Print',
  25 + cancel: 'Cancel',
  26 + submit: 'Submit',
  27 + printer: 'Printer',
  28 + printerRequired: 'Required, please select printer',
  29 + quantity: 'Quantity',
  30 + quantityRequired: 'Required, please enter quantity',
  31 + noFee: 'No fee included',
  32 + submitSuccess: 'Submitted successfully',
  33 + submitFailed: 'Submission failed'
  34 + }
  35 + },
  36 + zh: {
  37 + printSmallPayFee: {
  38 + receipt: '缴费收据单',
  39 + receiptNum: '收据号:',
  40 + orderNum: '订单号:',
  41 + roomNum: '房号',
  42 + owner: '业主',
  43 + time: '时间',
  44 + feeItem: '收费项目',
  45 + feeRange: '收费范围',
  46 + to: '至',
  47 + unitPrice: '单价/固定费:',
  48 + areaUsage: '面积/用量:',
  49 + paymentMethod: '支付方式:',
  50 + meterReading: '表读数:',
  51 + amount: '金额:',
  52 + refund: '(退费)',
  53 + discount: '优惠金额:',
  54 + remark: '备注',
  55 + total: '总计',
  56 + issuer: '开票人',
  57 + print: '打印',
  58 + cloudPrint: '云打印',
  59 + cancel: '取消',
  60 + submit: '提交',
  61 + printer: '打印机',
  62 + printerRequired: '必填,请选择打印机',
  63 + quantity: '数量',
  64 + quantityRequired: '必填,请填写数量',
  65 + noFee: '未包含费用',
  66 + submitSuccess: '提交成功',
  67 + submitFailed: '提交失败'
  68 + }
  69 + }
  70 +}
0 71 \ No newline at end of file
... ...
src/views/fee/printSmallPayFeeList.vue 0 → 100644
  1 +<template>
  2 + <div class="printSmallPayFee-container">
  3 +
  4 + <div class="print_container">
  5 + <div style="color:#000;font-size:32px" class="text-center">
  6 + <span>{{ $t('printSmallPayFee.receipt') }}</span>
  7 + </div>
  8 + <span>**************************</span>
  9 + <div class="section2" style="font-size: 12px; margin-left: 5px;">
  10 + <div>
  11 + <span>{{ $t('printSmallPayFee.receiptNum') }}</span>{{ printSmallPayFeeInfo.receiptNum }}
  12 + </div>
  13 + <div>
  14 + <span>{{ $t('printSmallPayFee.orderNum') }}</span>{{ printSmallPayFeeInfo.payOrderId }}
  15 + </div>
  16 + <div>
  17 + <span>{{ $t('printSmallPayFee.roomNum') }}</span>: {{ printSmallPayFeeInfo.roomName }}
  18 + </div>
  19 + <div>
  20 + <span>{{ $t('printSmallPayFee.owner') }}</span>: {{ printSmallPayFeeInfo.payObjName }}
  21 + </div>
  22 + <div>
  23 + <span>{{ $t('printSmallPayFee.time') }}</span>: {{ printSmallPayFeeInfo.feeTime }}
  24 + </div>
  25 + </div>
  26 + <span>**************************</span>
  27 + <div class="section2" style="font-size: 12px; margin-left: 5px;"
  28 + v-for="(item, index) in printSmallPayFeeInfo.fees" :key="index">
  29 + <div>
  30 + <span>{{ $t('printSmallPayFee.feeItem') }}</span>: {{ item.feeName }}
  31 + </div>
  32 + <div>
  33 + <span>{{ $t('printSmallPayFee.feeRange') }}</span>: {{ formatDate(item.startTime) }}
  34 + <span>{{ $t('printSmallPayFee.to') }}</span>{{ formatDate(item.endTime) }}
  35 + </div>
  36 + <div>
  37 + <span>{{ $t('printSmallPayFee.unitPrice') }}</span>{{ item.squarePrice }}
  38 + </div>
  39 + <div>
  40 + <span>{{ $t('printSmallPayFee.areaUsage') }}</span>{{ item.area }}
  41 + </div>
  42 + <div>
  43 + <span>{{ $t('printSmallPayFee.paymentMethod') }}</span>{{ item.primeRate }}
  44 + </div>
  45 + <div v-if="item.preDegrees">
  46 + <span>{{ $t('printSmallPayFee.meterReading') }}</span>{{ item.preDegrees }} {{ $t('printSmallPayFee.to') }}
  47 + {{ item.curDegrees }}
  48 + </div>
  49 + <div>
  50 + <span>{{ $t('printSmallPayFee.amount') }}</span>
  51 + <span>{{ formatAmount(item.amount) }}</span>
  52 + </div>
  53 + <div>
  54 + <span>{{ $t('printSmallPayFee.discount') }}</span> {{ item.discountPrice || 0 }}
  55 + </div>
  56 + <div>
  57 + <span>{{ $t('printSmallPayFee.remark') }}</span>: {{ item.remark }}
  58 + </div>
  59 + <span>**************************</span>
  60 + </div>
  61 + <div class="section5" style="font-size: 12px; margin-left: 5px;">
  62 + <div>
  63 + <span>{{ $t('printSmallPayFee.total') }}</span>: {{ printSmallPayFeeInfo.amount }}
  64 + </div>
  65 + <div>
  66 + <span>{{ $t('printSmallPayFee.issuer') }}</span>: {{ userInfo.name }}
  67 + </div>
  68 + <div v-html="printSmallPayFeeInfo.content"></div>
  69 + <div><img :src="printSmallPayFeeInfo.qrImg" width="100px" height="100px"></div>
  70 + </div>
  71 + <span>**************************</span>
  72 + </div>
  73 +
  74 + <div id="print-btn" class="action-buttons">
  75 + <el-button type="primary" class="float-right" @click="handlePrint">
  76 + <i class="el-icon-printer"></i>&nbsp;{{ $t('printSmallPayFee.print') }}
  77 + </el-button>
  78 + <el-button class="float-right margin-right" @click="handleCloudPrint">
  79 + <i class="el-icon-cloudy"></i>&nbsp;{{ $t('printSmallPayFee.cloudPrint') }}
  80 + </el-button>
  81 + <el-button class="float-right margin-right" @click="handleClose">
  82 + <span>{{ $t('printSmallPayFee.cancel') }}</span>
  83 + </el-button>
  84 + </div>
  85 +
  86 +
  87 + <el-dialog :title="$t('printSmallPayFee.cloudPrint')" :visible.sync="cloudPrintVisible" width="50%">
  88 + <div class="cloud-print-content">
  89 + <el-form label-width="120px">
  90 + <el-form-item :label="$t('printSmallPayFee.printer')">
  91 + <el-select v-model="printSmallPayFeeInfo.machineId" style="width:100%"
  92 + :placeholder="$t('printSmallPayFee.printerRequired')">
  93 + <el-option v-for="(item, index) in printSmallPayFeeInfo.machines" :key="index"
  94 + :label="`${item.machineName}(${item.machineCode})`" :value="item.machineId" />
  95 + </el-select>
  96 + </el-form-item>
  97 + <el-form-item :label="$t('printSmallPayFee.quantity')">
  98 + <el-input v-model="printSmallPayFeeInfo.quantity" type="number"
  99 + :placeholder="$t('printSmallPayFee.quantityRequired')" />
  100 + </el-form-item>
  101 + </el-form>
  102 + </div>
  103 + <span slot="footer" class="dialog-footer">
  104 + <el-button @click="cloudPrintVisible = false">{{ $t('printSmallPayFee.cancel') }}</el-button>
  105 + <el-button type="primary" @click="handleSubmitCloudPrint">{{ $t('printSmallPayFee.submit') }}</el-button>
  106 + </span>
  107 + </el-dialog>
  108 + </div>
  109 +</template>
  110 +
  111 +<script>
  112 +import {
  113 + queryFeeReceipt,
  114 + queryFeeReceiptDetail,
  115 + queryFeePrintSpec,
  116 + listMachinePrinter,
  117 + printPayFeeDetail
  118 +} from '@/api/fee/printSmallPayFeeApi'
  119 +import { getCommunityId } from '@/api/community/communityApi'
  120 +import { getUserId,getUserName } from '@/api/user/userApi'
  121 +
  122 +export default {
  123 + name: 'PrintSmallPayFeeList',
  124 + data() {
  125 + return {
  126 + printSmallPayFeeInfo: {
  127 + communityName: '',
  128 + receiptId: '',
  129 + receiptIds: '',
  130 + receiptNum: '',
  131 + payOrderId: '',
  132 + roomName: '',
  133 + amount: 0.00,
  134 + fees: [],
  135 + feeTime: '',
  136 + wechatName: '',
  137 + content: '',
  138 + qrImg: '',
  139 + payObjName: '',
  140 + machineId: '',
  141 + quantity: '1',
  142 + machines: []
  143 + },
  144 + cloudPrintVisible: false,
  145 + userInfo: {}
  146 + }
  147 + },
  148 + created() {
  149 + this.printSmallPayFeeInfo.receiptId = this.$route.query.receiptId
  150 + this.printSmallPayFeeInfo.receiptIds = this.$route.query.receiptIds
  151 + this.printSmallPayFeeInfo.communityName = getCommunityId().name
  152 + this.loadData()
  153 + this.userInfo = {
  154 + userId: getUserId(),
  155 + name: getUserName()
  156 + }
  157 + },
  158 + methods: {
  159 + async loadData() {
  160 + await this.loadReceipt()
  161 + await this.loadReceiptDetail()
  162 + await this.loadPrintSpec()
  163 + },
  164 + async loadReceipt() {
  165 + try {
  166 + const params = {
  167 + page: 1,
  168 + row: 30,
  169 + receiptId: this.printSmallPayFeeInfo.receiptId,
  170 + receiptIds: this.printSmallPayFeeInfo.receiptIds,
  171 + communityId: getCommunityId()
  172 + }
  173 + const { data } = await queryFeeReceipt(params)
  174 + const _feeReceipt = data
  175 + let _amount = 0
  176 + _feeReceipt.forEach(item => {
  177 + _amount += parseFloat(item.amount)
  178 + })
  179 + this.printSmallPayFeeInfo.amount = _amount.toFixed(2)
  180 + this.printSmallPayFeeInfo.roomName = _feeReceipt[0].objName
  181 + this.printSmallPayFeeInfo.feeTime = _feeReceipt[0].createTime
  182 + this.printSmallPayFeeInfo.payObjName = _feeReceipt[0].payObjName
  183 + this.printSmallPayFeeInfo.receiptNum = _feeReceipt[0].receiptCode
  184 + } catch (error) {
  185 + console.error('Failed to load receipt:', error)
  186 + }
  187 + },
  188 + async loadReceiptDetail() {
  189 + try {
  190 + const params = {
  191 + page: 1,
  192 + row: 100,
  193 + receiptId: this.printSmallPayFeeInfo.receiptId,
  194 + receiptIds: this.printSmallPayFeeInfo.receiptIds,
  195 + communityId: getCommunityId(),
  196 + orderBy: 'start_time'
  197 + }
  198 + const { data } = await queryFeeReceiptDetail(params)
  199 + this.printSmallPayFeeInfo.payOrderId = data[0].payOrderId
  200 + this.printSmallPayFeeInfo.fees = data
  201 + } catch (error) {
  202 + console.error('Failed to load receipt detail:', error)
  203 + }
  204 + },
  205 + async loadPrintSpec() {
  206 + try {
  207 + const params = {
  208 + page: 1,
  209 + row: 1,
  210 + specCd: 2020,
  211 + communityId: getCommunityId()
  212 + }
  213 + const { data } = await queryFeePrintSpec(params)
  214 + if (data.length > 0) {
  215 + this.printSmallPayFeeInfo.content = data[0].content
  216 + this.printSmallPayFeeInfo.qrImg = data[0].qrImg
  217 + }
  218 + } catch (error) {
  219 + console.error('Failed to load print spec:', error)
  220 + }
  221 + },
  222 + formatDate(date) {
  223 + if (!date) return ''
  224 + return new Date(date).toLocaleDateString()
  225 + },
  226 + formatAmount(amount) {
  227 + if (amount < 0) {
  228 + return amount + this.$t('printSmallPayFee.refund')
  229 + }
  230 + return amount
  231 + },
  232 + handlePrint() {
  233 + window.print()
  234 + },
  235 + handleCloudPrint() {
  236 + this.listMachinePrinter()
  237 + this.cloudPrintVisible = true
  238 + },
  239 + async listMachinePrinter() {
  240 + try {
  241 + const params = {
  242 + page: 1,
  243 + row: 100,
  244 + communityId: getCommunityId()
  245 + }
  246 + const { data } = await listMachinePrinter(params)
  247 + this.printSmallPayFeeInfo.machines = data
  248 + if (this.printSmallPayFeeInfo.machines && this.printSmallPayFeeInfo.machines.length > 0) {
  249 + this.printSmallPayFeeInfo.machineId = this.printSmallPayFeeInfo.machines[0].machineId
  250 + }
  251 + } catch (error) {
  252 + console.error('Failed to list machine printer:', error)
  253 + }
  254 + },
  255 + async handleSubmitCloudPrint() {
  256 + try {
  257 + const detailIds = this.printSmallPayFeeInfo.fees.map(item => item.detailId)
  258 + if (detailIds.length < 1) {
  259 + this.$message.error(this.$t('printSmallPayFee.noFee'))
  260 + return
  261 + }
  262 + const data = {
  263 + communityId: getCommunityId(),
  264 + machineId: this.printSmallPayFeeInfo.machineId,
  265 + quantity: this.printSmallPayFeeInfo.quantity,
  266 + detailId: detailIds.join(',')
  267 + }
  268 + await printPayFeeDetail(data)
  269 + this.cloudPrintVisible = false
  270 + this.$message.success(this.$t('printSmallPayFee.submitSuccess'))
  271 + } catch (error) {
  272 + console.error('Failed to submit cloud print:', error)
  273 + this.$message.error(error.message || this.$t('printSmallPayFee.submitFailed'))
  274 + }
  275 + },
  276 + handleClose() {
  277 + this.$router.go(-1)
  278 + }
  279 + }
  280 +}
  281 +</script>
  282 +
  283 +<style scoped>
  284 +.printSmallPayFee-container {
  285 + padding: 20px;
  286 +}
  287 +
  288 +.print_container {
  289 + padding: 20px;
  290 + background: #fff;
  291 +}
  292 +
  293 +.text-center {
  294 + text-align: center;
  295 +}
  296 +
  297 +.section2,
  298 +.section5 {
  299 + margin-bottom: 15px;
  300 +}
  301 +
  302 +.action-buttons {
  303 + margin-top: 20px;
  304 + text-align: right;
  305 +}
  306 +
  307 +.float-right {
  308 + float: right;
  309 +}
  310 +
  311 +.margin-right {
  312 + margin-right: 10px;
  313 +}
  314 +
  315 +.cloud-print-content {
  316 + padding: 20px;
  317 +}
  318 +</style>
0 319 \ No newline at end of file
... ...