Commit 10d3499d1187318a4b2a02d42ab69d95107f31a8

Authored by wuxw
1 parent f99fb9c6

开发完成 admin 台账功能

src/api/fee/adminOweFeeDetailApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 查询欠费明细
  4 +export function queryAdminOweFeeDetail(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/fee.queryAdminOweFeeDetail',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + if (res.code === 0) {
  13 + resolve({
  14 + data: res.data,
  15 + total: res.total
  16 + })
  17 + } else {
  18 + reject(new Error(res.msg || '查询欠费明细失败'))
  19 + }
  20 + }).catch(error => {
  21 + reject(error)
  22 + })
  23 + })
  24 +}
  25 +
  26 +// 查询小区楼层树
  27 +export function queryCommunityFloorTree(params) {
  28 + return new Promise((resolve, reject) => {
  29 + request({
  30 + url: '/community.queryCommunityFloorTree',
  31 + method: 'get',
  32 + params
  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 +}
0 \ No newline at end of file 45 \ No newline at end of file
src/api/fee/adminPayFeeDetailApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 查询缴费明细列表
  4 +export function queryAdminPayFeeDetail(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/fee.queryAdminPayFeeDetail',
  8 + method: 'get',
  9 + params
  10 + }).then(response => {
  11 + const res = response.data
  12 + console.log(res)
  13 + if (res.code == 0) {
  14 + resolve(res)
  15 + } else {
  16 + reject(new Error(res.msg || '查询缴费明细失败'))
  17 + }
  18 + }).catch(error => {
  19 + reject(error)
  20 + })
  21 + })
  22 +}
  23 +
  24 +// 导出缴费明细
  25 +export function exportFeeData(params) {
  26 + return new Promise((resolve, reject) => {
  27 + request({
  28 + url: '/export.exportData',
  29 + method: 'get',
  30 + params
  31 + }).then(response => {
  32 + const res = response.data
  33 + if (res.code === 0) {
  34 + resolve(res)
  35 + } else {
  36 + reject(new Error(res.msg || '导出缴费明细失败'))
  37 + }
  38 + }).catch(error => {
  39 + reject(error)
  40 + })
  41 + })
  42 +}
  43 +
  44 +// 获取管理员小区列表
  45 +export function listAdminCommunitys(params) {
  46 + return new Promise((resolve, reject) => {
  47 + request({
  48 + url: '/community.listAdminCommunitys',
  49 + method: 'get',
  50 + params
  51 + }).then(response => {
  52 + const res = response.data
  53 + if (res.code === 0) {
  54 + resolve({
  55 + data: res.data,
  56 + total: res.total
  57 + })
  58 + } else {
  59 + reject(new Error(res.msg || '获取小区列表失败'))
  60 + }
  61 + }).catch(error => {
  62 + reject(error)
  63 + })
  64 + })
  65 +}
0 \ No newline at end of file 66 \ No newline at end of file
src/api/report/communityFeeSummaryApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取费用汇总数据
  4 +export function getCommunityFeeSummary(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/admin.getCommunityFeeSummary',
  8 + method: 'get',
  9 + params
  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 listAdminCommunitys(params) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/community.listAdminCommunitys',
  28 + method: 'get',
  29 + params
  30 + }).then(response => {
  31 + const res = response.data
  32 + if (res.code === 0) {
  33 + resolve(res)
  34 + } else {
  35 + reject(new Error(res.msg || '获取管理员社区列表失败'))
  36 + }
  37 + }).catch(error => {
  38 + reject(error)
  39 + })
  40 + })
  41 +}
  42 +
  43 +// 导出数据
  44 +export function exportCommunityFeeSummary(params) {
  45 + return new Promise((resolve, reject) => {
  46 + request({
  47 + url: '/export.exportData',
  48 + method: 'get',
  49 + params: {
  50 + ...params,
  51 + pagePath: 'communityFeeSummary'
  52 + }
  53 + }).then(response => {
  54 + const res = response.data
  55 + if (res.code === 0) {
  56 + resolve(res)
  57 + } else {
  58 + reject(new Error(res.msg || '导出费用汇总数据失败'))
  59 + }
  60 + }).catch(error => {
  61 + reject(error)
  62 + })
  63 + })
  64 +}
0 \ No newline at end of file 65 \ No newline at end of file
src/api/report/operationalAnalysisApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获取运营分析数据
  4 +export function getCommunityOperationalAnalysis(params) {
  5 + return new Promise((resolve, reject) => {
  6 + request({
  7 + url: '/admin.getCommunityOperationalAnalysis',
  8 + method: 'get',
  9 + params
  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 listAdminCommunitys(params) {
  25 + return new Promise((resolve, reject) => {
  26 + request({
  27 + url: '/community.listAdminCommunitys',
  28 + method: 'get',
  29 + params
  30 + }).then(response => {
  31 + const res = response.data
  32 + if (res.code === 0) {
  33 + resolve(res)
  34 + } else {
  35 + reject(new Error(res.msg || '获取小区列表失败'))
  36 + }
  37 + }).catch(error => {
  38 + reject(error)
  39 + })
  40 + })
  41 +}
0 \ No newline at end of file 42 \ No newline at end of file
src/components/fee/SelectAdminCommunity.vue 0 → 100644
  1 +<template>
  2 + <el-card>
  3 + <div class="community-selector">
  4 + <el-scrollbar style="height: 650px">
  5 + <ul class="community-list">
  6 + <li
  7 + v-for="(item, index) in communitys"
  8 + :key="index"
  9 + class="community-item"
  10 + :class="{ 'selected': communityId === item.communityId }"
  11 + @click="handleSelectCommunity(item)"
  12 + >
  13 + {{ item.name }}
  14 + </li>
  15 + </ul>
  16 + </el-scrollbar>
  17 + </div>
  18 + </el-card>
  19 +</template>
  20 +
  21 +<script>
  22 +import { listAdminCommunitys } from '@/api/fee/adminPayFeeDetailApi'
  23 +
  24 +export default {
  25 + name: 'SelectAdminCommunity',
  26 + data() {
  27 + return {
  28 + communitys: [
  29 + { name: this.$t('adminPayFeeDetail.allCommunities'), communityId: '' }
  30 + ],
  31 + communityId: ''
  32 + }
  33 + },
  34 + created() {
  35 + this.loadCommunities()
  36 + },
  37 + methods: {
  38 + async loadCommunities() {
  39 + try {
  40 + const params = {
  41 + page: 1,
  42 + row: 100
  43 + }
  44 + const { data } = await listAdminCommunitys(params)
  45 + this.communitys = [...this.communitys, ...data]
  46 + } catch (error) {
  47 + this.$message.error(this.$t('adminPayFeeDetail.fetchCommunityError'))
  48 + }
  49 + },
  50 + handleSelectCommunity(community) {
  51 + this.communityId = community.communityId
  52 + this.$emit('changeCommunity', community)
  53 + }
  54 + }
  55 +}
  56 +</script>
  57 +
  58 +<style lang="scss" scoped>
  59 +.community-selector {
  60 + .community-list {
  61 + list-style: none;
  62 + padding: 0;
  63 + margin: 0;
  64 +
  65 + .community-item {
  66 + padding: 12px 15px;
  67 + cursor: pointer;
  68 + text-align: center;
  69 + border-bottom: 1px solid #ebeef5;
  70 + transition: all 0.3s;
  71 +
  72 + &:hover {
  73 + background-color: #f5f7fa;
  74 + }
  75 +
  76 + &.selected {
  77 + background-color: #409eff;
  78 + color: white;
  79 + }
  80 + }
  81 + }
  82 +}
  83 +</style>
0 \ No newline at end of file 84 \ No newline at end of file
src/components/fee/communityFloorTree.vue 0 → 100644
  1 +<template>
  2 + <el-card class="tree-card">
  3 + <el-tree ref="tree" :data="treeData" node-key="id" :props="defaultProps" :highlight-current="true"
  4 + :expand-on-click-node="false" @node-click="handleNodeClick" />
  5 + </el-card>
  6 +</template>
  7 +
  8 +<script>
  9 +import { queryCommunityFloorTree } from '@/api/fee/adminOweFeeDetailApi'
  10 +
  11 +export default {
  12 + name: 'CommunityFloorTree',
  13 + data() {
  14 + return {
  15 + treeData: [],
  16 + defaultProps: {
  17 + children: 'children',
  18 + label: 'text'
  19 + },
  20 + callName: ''
  21 + }
  22 + },
  23 + mounted() {
  24 + this.initCommunityFloorTree()
  25 + },
  26 + methods: {
  27 + async initCommunityFloorTree() {
  28 + await this.loadCommunityFloorTree()
  29 + },
  30 + async loadCommunityFloorTree() {
  31 + try {
  32 + // 这里应该是从API获取树形数据
  33 + const {data} = await queryCommunityFloorTree()
  34 + console.log(data)
  35 + // 模拟数据
  36 + this.treeData = data
  37 + } catch (error) {
  38 + console.error(error)
  39 + }
  40 + },
  41 + handleNodeClick(data) {
  42 + if (data.id.startsWith('c_')) {
  43 + this.$emit('chooseFloor', {
  44 + communityName: data.communityName,
  45 + communityId: data.communityId,
  46 + floorNum: '',
  47 + floorId: ''
  48 + })
  49 + } else if (data.id.startsWith('f_')) {
  50 + this.$emit('chooseFloor', {
  51 + communityName:'',
  52 + communityId: '',
  53 + floorNum: data.floorNum,
  54 + floorId: data.floorId
  55 + })
  56 + }
  57 + }
  58 + }
  59 +}
  60 +</script>
  61 +
  62 +<style scoped>
  63 +.tree-card {
  64 + height: 100%;
  65 +}
  66 +</style>
0 \ No newline at end of file 67 \ No newline at end of file
src/components/report/SelectAdminCommunity.vue 0 → 100644
  1 +<template>
  2 + <div class="select-admin-community">
  3 + <el-card class="border-radius">
  4 + <div class="treeview attendance-staff" style="height: 650px;">
  5 + <ul class="list-group text-center border-radius">
  6 + <li
  7 + v-for="(item, index) in communityList"
  8 + :key="index"
  9 + class="list-group-item node-orgTree"
  10 + :class="{ 'vc-node-selected': selectedCommunityId === item.communityId }"
  11 + @click="handleCommunityClick(item)"
  12 + >
  13 + {{ item.name }}
  14 + </li>
  15 + </ul>
  16 + </div>
  17 + </el-card>
  18 + </div>
  19 +</template>
  20 +
  21 +<script>
  22 +import { listAdminCommunitys } from '@/api/report/communityFeeSummaryApi'
  23 +
  24 +export default {
  25 + name: 'SelectAdminCommunity',
  26 + data() {
  27 + return {
  28 + communityList: [
  29 + { name: this.$t('communityFeeSummary.allCommunities'), communityId: '' }
  30 + ],
  31 + selectedCommunityId: ''
  32 + }
  33 + },
  34 + created() {
  35 + this.loadAdminCommunities()
  36 + },
  37 + methods: {
  38 + async loadAdminCommunities() {
  39 + try {
  40 + const params = {
  41 + _uid: '123mlkdinkldldijdhuudjdjkkd',
  42 + page: 1,
  43 + row: 100
  44 + }
  45 +
  46 + const { data } = await listAdminCommunitys(params)
  47 + this.communityList = [...this.communityList, ...data]
  48 + } catch (error) {
  49 + console.error('Failed to load communities:', error)
  50 + }
  51 + },
  52 + handleCommunityClick(community) {
  53 + this.selectedCommunityId = community.communityId
  54 + this.$emit('changeCommunity', community)
  55 + }
  56 + }
  57 +}
  58 +</script>
  59 +
  60 +<style lang="scss" scoped>
  61 +.select-admin-community {
  62 + .border-radius {
  63 + border-radius: 4px;
  64 + }
  65 +
  66 + .treeview {
  67 + overflow-y: auto;
  68 + }
  69 +
  70 + .list-group {
  71 + padding-left: 0;
  72 + margin-bottom: 0;
  73 +
  74 + &-item {
  75 + position: relative;
  76 + display: block;
  77 + padding: 10px 15px;
  78 + margin-bottom: -1px;
  79 + background-color: #fff;
  80 + border: 1px solid #ddd;
  81 + cursor: pointer;
  82 +
  83 + &:first-child {
  84 + border-top-left-radius: 4px;
  85 + border-top-right-radius: 4px;
  86 + }
  87 +
  88 + &:last-child {
  89 + margin-bottom: 0;
  90 + border-bottom-right-radius: 4px;
  91 + border-bottom-left-radius: 4px;
  92 + }
  93 +
  94 + &.vc-node-selected {
  95 + background-color: #409EFF;
  96 + color: #fff;
  97 + border-color: #409EFF;
  98 + }
  99 +
  100 + &:hover:not(.vc-node-selected) {
  101 + background-color: #f5f5f5;
  102 + }
  103 + }
  104 + }
  105 +}
  106 +</style>
0 \ No newline at end of file 107 \ No newline at end of file
src/i18n/commonLang.js
@@ -31,7 +31,8 @@ export const messages = { @@ -31,7 +31,8 @@ export const messages = {
31 female: 'Female', 31 female: 'Female',
32 male: 'Male', 32 male: 'Male',
33 all: 'All', 33 all: 'All',
34 - refresh: 'Refresh' 34 + refresh: 'Refresh',
  35 + export: 'Export'
35 } 36 }
36 }, 37 },
37 zh: { 38 zh: {
@@ -66,7 +67,8 @@ export const messages = { @@ -66,7 +67,8 @@ export const messages = {
66 female: '女', 67 female: '女',
67 male: '男', 68 male: '男',
68 all: '全部', 69 all: '全部',
69 - refresh: '刷新' 70 + refresh: '刷新',
  71 + export: '导出'
70 } 72 }
71 } 73 }
72 } 74 }
73 \ No newline at end of file 75 \ No newline at end of file
src/i18n/index.js
@@ -84,6 +84,10 @@ import { messages as adminInoutMessages } from &#39;../views/iot/adminInoutLang&#39; @@ -84,6 +84,10 @@ import { messages as adminInoutMessages } from &#39;../views/iot/adminInoutLang&#39;
84 import { messages as adminCarInoutMessages } from '../views/iot/adminCarInoutLang' 84 import { messages as adminCarInoutMessages } from '../views/iot/adminCarInoutLang'
85 import { messages as adminChargeOrderMessages } from '../views/iot/adminChargeOrderLang' 85 import { messages as adminChargeOrderMessages } from '../views/iot/adminChargeOrderLang'
86 import { messages as adminMeterRechargeMessages } from '../views/iot/adminMeterRechargeLang' 86 import { messages as adminMeterRechargeMessages } from '../views/iot/adminMeterRechargeLang'
  87 +import { messages as operationalAnalysisMessages } from '../views/report/operationalAnalysisLang'
  88 +import { messages as communityFeeSummaryMessages } from '../views/report/communityFeeSummaryLang'
  89 +import { messages as adminPayFeeDetailMessages } from '../views/fee/adminPayFeeDetailLang'
  90 +import { messages as adminOweFeeDetailMessages } from '../views/fee/adminOweFeeDetailLang'
87 91
88 Vue.use(VueI18n) 92 Vue.use(VueI18n)
89 93
@@ -172,6 +176,10 @@ const messages = { @@ -172,6 +176,10 @@ const messages = {
172 ...adminCarInoutMessages.en, 176 ...adminCarInoutMessages.en,
173 ...adminChargeOrderMessages.en, 177 ...adminChargeOrderMessages.en,
174 ...adminMeterRechargeMessages.en, 178 ...adminMeterRechargeMessages.en,
  179 + ...operationalAnalysisMessages.en,
  180 + ...communityFeeSummaryMessages.en,
  181 + ...adminPayFeeDetailMessages.en,
  182 + ...adminOweFeeDetailMessages.en,
175 }, 183 },
176 zh: { 184 zh: {
177 ...loginMessages.zh, 185 ...loginMessages.zh,
@@ -256,6 +264,10 @@ const messages = { @@ -256,6 +264,10 @@ const messages = {
256 ...adminCarInoutMessages.zh, 264 ...adminCarInoutMessages.zh,
257 ...adminChargeOrderMessages.zh, 265 ...adminChargeOrderMessages.zh,
258 ...adminMeterRechargeMessages.zh, 266 ...adminMeterRechargeMessages.zh,
  267 + ...operationalAnalysisMessages.zh,
  268 + ...communityFeeSummaryMessages.zh,
  269 + ...adminPayFeeDetailMessages.zh,
  270 + ...adminOweFeeDetailMessages.zh,
259 } 271 }
260 } 272 }
261 273
src/router/index.js
@@ -397,15 +397,35 @@ const routes = [ @@ -397,15 +397,35 @@ const routes = [
397 component: () => import('@/views/iot/adminCarInoutList.vue') 397 component: () => import('@/views/iot/adminCarInoutList.vue')
398 }, 398 },
399 { 399 {
400 - path:'/pages/iot/adminChargeOrder',  
401 - name:'/pages/iot/adminChargeOrder', 400 + path: '/pages/iot/adminChargeOrder',
  401 + name: '/pages/iot/adminChargeOrder',
402 component: () => import('@/views/iot/adminChargeOrderList.vue') 402 component: () => import('@/views/iot/adminChargeOrderList.vue')
403 - },  
404 - {  
405 - path:'/pages/iot/adminMeterRecharge',  
406 - name:'/pages/iot/adminMeterRecharge',  
407 - component: () => import('@/views/iot/adminMeterRechargeList.vue')  
408 - }, 403 + },
  404 + {
  405 + path: '/pages/iot/adminMeterRecharge',
  406 + name: '/pages/iot/adminMeterRecharge',
  407 + component: () => import('@/views/iot/adminMeterRechargeList.vue')
  408 + },
  409 + {
  410 + path: '/pages/report/operationalAnalysis',
  411 + name: '/pages/report/operationalAnalysis',
  412 + component: () => import('@/views/report/operationalAnalysisList.vue')
  413 + },
  414 + {
  415 + path: '/pages/report/communityFeeSummary',
  416 + name: '/pages/report/communityFeeSummary',
  417 + component: () => import('@/views/report/communityFeeSummaryList.vue')
  418 + },
  419 + {
  420 + path: '/pages/fee/adminPayFeeDetail',
  421 + name: '/pages/fee/adminPayFeeDetail',
  422 + component: () => import('@/views/fee/adminPayFeeDetailList.vue')
  423 + },
  424 + {
  425 + path: '/pages/fee/adminOweFeeDetail',
  426 + name: '/pages/fee/adminOweFeeDetail',
  427 + component: () => import('@/views/fee/adminOweFeeDetailList.vue')
  428 + },
409 // 其他子路由可以在这里添加 429 // 其他子路由可以在这里添加
410 ] 430 ]
411 }, 431 },
src/views/fee/adminOweFeeDetailLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + adminOweFeeDetail: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + feeTypeCd: 'Please select fee type',
  7 + objNameLike: 'Please enter room number',
  8 + startTime: 'Please select start time',
  9 + endTime: 'Please select end time',
  10 + ownerNameLike: 'Please enter owner name'
  11 + },
  12 + list: {
  13 + title: 'Arrears Details'
  14 + },
  15 + table: {
  16 + index: 'No.',
  17 + communityName: 'Community Name',
  18 + objName: 'Room Number',
  19 + ownerName: 'Owner',
  20 + ownerTel: 'Owner Tel',
  21 + builtUpArea: 'Area',
  22 + feeName: 'Fee Item',
  23 + startTime: 'Start Time',
  24 + endTime: 'End Time',
  25 + oweDay: 'Arrears Days',
  26 + oweAmount: 'Arrears Amount'
  27 + },
  28 + summary: {
  29 + subtotal: 'Subtotal',
  30 + total: 'Total',
  31 + owe: 'Arrears',
  32 + yuan: 'Yuan'
  33 + },
  34 + tips: {
  35 + startTime: 'Start time: the billing start time of the created fee',
  36 + oweDayDeposit: 'Arrears days (days): The arrears days of the deposit fee item is the number of days from the start time of the fee to the current day',
  37 + oweDayOther: 'For fee items other than deposits, the arrears days are the number of days from the start time to the end time of the fee',
  38 + oweAmount: 'Arrears amount: the fee payable during the arrears period'
  39 + },
  40 + feeType: {
  41 + property: 'Property Fee',
  42 + parking: 'Parking Fee'
  43 + },
  44 + fetchError: 'Failed to fetch arrears details'
  45 + }
  46 + },
  47 + zh: {
  48 + adminOweFeeDetail: {
  49 + search: {
  50 + title: '查询条件',
  51 + feeTypeCd: '请选择费用大类',
  52 + objNameLike: '请填写房屋编号',
  53 + startTime: '请选择开始时间',
  54 + endTime: '请选择结束时间',
  55 + ownerNameLike: '请填写业主名称'
  56 + },
  57 + list: {
  58 + title: '欠费明细表'
  59 + },
  60 + table: {
  61 + index: '费用编号',
  62 + communityName: '小区名称',
  63 + objName: '房号',
  64 + ownerName: '业主',
  65 + ownerTel: '业主电话',
  66 + builtUpArea: '面积',
  67 + feeName: '费用项',
  68 + startTime: '开始时间',
  69 + endTime: '结束时间',
  70 + oweDay: '欠费时长(天)',
  71 + oweAmount: '欠费金额'
  72 + },
  73 + summary: {
  74 + subtotal: '小计',
  75 + total: '大计',
  76 + owe: '欠费',
  77 + yuan: '元'
  78 + },
  79 + tips: {
  80 + startTime: '费用开始时间:所创建费用的计费起始时间',
  81 + oweDayDeposit: '欠费时长(天):押金费用项欠费时长是费用开始时间到当天的天数',
  82 + oweDayOther: '除押金外的费用项欠费时长是费用的开始时间到费用的结束时间的天数',
  83 + oweAmount: '欠费金额:欠费周期内应缴费用'
  84 + },
  85 + feeType: {
  86 + property: '物业费',
  87 + parking: '停车费'
  88 + },
  89 + fetchError: '获取欠费明细失败'
  90 + }
  91 + }
  92 +}
0 \ No newline at end of file 93 \ No newline at end of file
src/views/fee/adminOweFeeDetailList.vue 0 → 100644
  1 +<template>
  2 + <div class="admin-owe-fee-detail-container animated fadeInRight">
  3 + <el-row :gutter="20" class="flex justify-start">
  4 + <el-col :span="4" class="room-floor-unit-tree">
  5 + <community-floor-tree ref="communityFloorTree" @chooseFloor="handleChooseFloor" />
  6 + </el-col>
  7 + <el-col :span="20" class="margin-top-xs">
  8 + <el-card class="box-card">
  9 + <div slot="header" class="clearfix text-left">
  10 + <span>{{ $t('adminOweFeeDetail.search.title') }}</span>
  11 + </div>
  12 + <el-row :gutter="20">
  13 + <el-col :span="4">
  14 + <el-select v-model="searchForm.feeTypeCd" :placeholder="$t('adminOweFeeDetail.search.feeTypeCd')" clearable
  15 + class="w-full">
  16 + <el-option v-for="item in feeTypeCds" :key="item.statusCd" :label="item.name" :value="item.statusCd" />
  17 + </el-select>
  18 + </el-col>
  19 + <el-col :span="4">
  20 + <el-input v-model="searchForm.objNameLike" :placeholder="$t('adminOweFeeDetail.search.objNameLike')"
  21 + clearable />
  22 + </el-col>
  23 + <el-col :span="4">
  24 + <el-date-picker v-model="searchForm.startTime" type="date"
  25 + :placeholder="$t('adminOweFeeDetail.search.startTime')" class="w-full" />
  26 + </el-col>
  27 + <el-col :span="4">
  28 + <el-date-picker v-model="searchForm.endTime" type="date"
  29 + :placeholder="$t('adminOweFeeDetail.search.endTime')" class="w-full" />
  30 + </el-col>
  31 + <el-col :span="4">
  32 + <el-input v-model="searchForm.ownerNameLike" :placeholder="$t('adminOweFeeDetail.search.ownerNameLike')"
  33 + clearable />
  34 + </el-col>
  35 + <el-col :span="4">
  36 + <el-button type="primary" @click="handleSearch">
  37 + {{ $t('common.search') }}
  38 + </el-button>
  39 + <el-button @click="handleReset" style="margin-left: 10px;">
  40 + {{ $t('common.reset') }}
  41 + </el-button>
  42 + </el-col>
  43 + </el-row>
  44 + </el-card>
  45 +
  46 + <el-card class="box-card margin-top">
  47 + <div slot="header" class="clearfix text-left" >
  48 + <span>{{ $t('adminOweFeeDetail.list.title') }}</span>
  49 + </div>
  50 + <el-table :data="tableData" border style="width: 100%" v-loading="loading">
  51 + <el-table-column prop="index" :label="$t('adminOweFeeDetail.table.index')" width="80" align="center" />
  52 + <el-table-column prop="communityName" :label="$t('adminOweFeeDetail.table.communityName')" align="center" />
  53 + <el-table-column prop="objName" :label="$t('adminOweFeeDetail.table.objName')" align="center" />
  54 + <el-table-column prop="ownerName" :label="$t('adminOweFeeDetail.table.ownerName')" align="center" />
  55 + <el-table-column prop="ownerTel" :label="$t('adminOweFeeDetail.table.ownerTel')" align="center" />
  56 + <el-table-column prop="builtUpArea" :label="$t('adminOweFeeDetail.table.builtUpArea')" align="center" />
  57 + <el-table-column prop="feeName" :label="$t('adminOweFeeDetail.table.feeName')" align="center" />
  58 + <el-table-column prop="startTime" :label="$t('adminOweFeeDetail.table.startTime')" align="center" />
  59 + <el-table-column prop="endTime" :label="$t('adminOweFeeDetail.table.endTime')" align="center" />
  60 + <el-table-column prop="oweDay" :label="$t('adminOweFeeDetail.table.oweDay')" align="center" />
  61 + <el-table-column prop="oweAmount" :label="$t('adminOweFeeDetail.table.oweAmount')" align="center" />
  62 + </el-table>
  63 +
  64 + <el-row class="margin-top text-left">
  65 + <el-col :span="24">
  66 + <div>
  67 + <span class="font-bold">{{ $t('adminOweFeeDetail.summary.subtotal') }}</span>
  68 + <span class="margin-left">
  69 + {{ $t('adminOweFeeDetail.summary.owe') }}: {{ totalPreferentialAmount }}
  70 + {{ $t('adminOweFeeDetail.summary.yuan') }}
  71 + </span>
  72 + </div>
  73 + <div>
  74 + <span class="font-bold">{{ $t('adminOweFeeDetail.summary.total') }}</span>
  75 + <span class="margin-left">
  76 + {{ $t('adminOweFeeDetail.summary.owe') }}: {{ allOweAmount }}
  77 + {{ $t('adminOweFeeDetail.summary.yuan') }}
  78 + </span>
  79 + </div>
  80 + </el-col>
  81 + </el-row>
  82 +
  83 + <el-row class="margin-top text-left">
  84 + <el-col :span="12">
  85 + <div>{{ $t('adminOweFeeDetail.tips.startTime') }}</div>
  86 + <div>{{ $t('adminOweFeeDetail.tips.oweDayDeposit') }}</div>
  87 + <div>{{ $t('adminOweFeeDetail.tips.oweDayOther') }}</div>
  88 + <div>{{ $t('adminOweFeeDetail.tips.oweAmount') }}</div>
  89 + </el-col>
  90 + <el-col :span="12" class="text-right">
  91 + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]"
  92 + :page-size="pagination.size" :total="pagination.total" layout="total, sizes, prev, pager, next, jumper"
  93 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  94 + </el-col>
  95 + </el-row>
  96 + </el-card>
  97 + </el-col>
  98 + </el-row>
  99 + </div>
  100 +</template>
  101 +
  102 +<script>
  103 +import { queryAdminOweFeeDetail } from '@/api/fee/adminOweFeeDetailApi'
  104 +import CommunityFloorTree from '@/components/fee/communityFloorTree'
  105 +
  106 +export default {
  107 + name: 'AdminOweFeeDetailList',
  108 + components: {
  109 + CommunityFloorTree
  110 + },
  111 + data() {
  112 + return {
  113 + loading: false,
  114 + searchForm: {
  115 + floorId: '',
  116 + floorName: '',
  117 + roomNum: '',
  118 + objNameLike: '',
  119 + feeTypeCd: '',
  120 + startTime: '',
  121 + endTime: '',
  122 + communityId: '',
  123 + ownerNameLike: ''
  124 + },
  125 + feeTypeCds: [],
  126 + tableData: [],
  127 + totalPreferentialAmount: 0,
  128 + allOweAmount: 0,
  129 + pagination: {
  130 + current: 1,
  131 + size: 10,
  132 + total: 0
  133 + }
  134 + }
  135 + },
  136 + created() {
  137 + this.initData()
  138 + },
  139 + methods: {
  140 + async initData() {
  141 + try {
  142 + this.loading = true
  143 + await this.getFeeTypeCds()
  144 + await this.getList()
  145 + } catch (error) {
  146 + console.error(error)
  147 + } finally {
  148 + this.loading = false
  149 + }
  150 + },
  151 + async getFeeTypeCds() {
  152 + // 这里应该是获取费用类型的字典数据
  153 + // 假设从字典接口获取数据
  154 + this.feeTypeCds = [
  155 + { statusCd: '1', name: this.$t('adminOweFeeDetail.feeType.property') },
  156 + { statusCd: '2', name: this.$t('adminOweFeeDetail.feeType.parking') }
  157 + ]
  158 + },
  159 + async getList() {
  160 + try {
  161 + this.loading = true
  162 + const params = {
  163 + ...this.searchForm,
  164 + page: this.pagination.current,
  165 + row: this.pagination.size
  166 + }
  167 + const { data, total } = await queryAdminOweFeeDetail(params)
  168 + this.tableData = data.map((item, index) => ({
  169 + ...item,
  170 + index: index + 1
  171 + }))
  172 + this.pagination.total = total
  173 +
  174 + // 计算小计
  175 + let _totalPreferentialAmount = 0.0
  176 + data.forEach(item => {
  177 + _totalPreferentialAmount += parseFloat(item.oweAmount)
  178 + })
  179 + this.totalPreferentialAmount = _totalPreferentialAmount.toFixed(2)
  180 +
  181 + if (data.length > 0) {
  182 + this.allOweAmount = data[0].allOweAmount || 0.0.toFixed(2)
  183 + } else {
  184 + this.allOweAmount = 0.0.toFixed(2)
  185 + }
  186 + } catch (error) {
  187 + console.error(error)
  188 + this.$message.error(this.$t('adminOweFeeDetail.fetchError'))
  189 + } finally {
  190 + this.loading = false
  191 + }
  192 + },
  193 + handleSearch() {
  194 + this.pagination.current = 1
  195 + this.getList()
  196 + },
  197 + handleReset() {
  198 + this.searchForm = {
  199 + floorId: '',
  200 + floorName: '',
  201 + roomNum: '',
  202 + objNameLike: '',
  203 + feeTypeCd: '',
  204 + startTime: '',
  205 + endTime: '',
  206 + communityId: '',
  207 + ownerNameLike: ''
  208 + }
  209 + this.handleSearch()
  210 + },
  211 + handleSizeChange(val) {
  212 + this.pagination.size = val
  213 + this.getList()
  214 + },
  215 + handleCurrentChange(val) {
  216 + this.pagination.current = val
  217 + this.getList()
  218 + },
  219 + handleChooseFloor(floor) {
  220 + this.searchForm.floorId = floor.floorId
  221 + this.searchForm.floorName = floor.floorNum
  222 + this.searchForm.communityId = floor.communityId
  223 + this.searchForm.communityName = floor.communityName
  224 + this.handleSearch()
  225 + }
  226 + }
  227 +}
  228 +</script>
  229 +
  230 +<style lang="scss" scoped>
  231 +.admin-owe-fee-detail-container {
  232 + padding: 20px;
  233 +
  234 + .margin-top {
  235 + margin-top: 20px;
  236 + }
  237 +
  238 + .margin-left {
  239 + margin-left: 10px;
  240 + }
  241 +
  242 + .font-bold {
  243 + font-weight: bold;
  244 + }
  245 +
  246 + .text-right {
  247 + text-align: right;
  248 + }
  249 +
  250 + .room-floor-unit-tree {
  251 + padding-right: 0;
  252 + }
  253 +}
  254 +</style>
0 \ No newline at end of file 255 \ No newline at end of file
src/views/fee/adminPayFeeDetailLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + adminPayFeeDetail: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + startTime: 'Payment Start Time',
  7 + startTimePlaceholder: 'Please select payment start time',
  8 + endTime: 'Payment End Time',
  9 + endTimePlaceholder: 'Please select payment end time',
  10 + paymentMethod: 'Payment Method',
  11 + paymentMethodPlaceholder: 'Please select payment method',
  12 + feeStatus: 'Fee Status',
  13 + feeStatusPlaceholder: 'Please select fee status',
  14 + feeType: 'Fee Type',
  15 + feeTypePlaceholder: 'Please select fee type',
  16 + feeStartTime: 'Fee Start Time',
  17 + feeStartTimePlaceholder: 'Please select fee start time',
  18 + feeEndTime: 'Fee End Time',
  19 + feeEndTimePlaceholder: 'Please select fee end time',
  20 + payerObjName: 'Room/Car Number',
  21 + payerObjNamePlaceholder: 'Room number or license plate number'
  22 + },
  23 + list: {
  24 + title: 'Payment Records',
  25 + tooltip: 'All room payment record details'
  26 + },
  27 + table: {
  28 + orderId: 'Order No',
  29 + payerInfo: 'Room/Owner',
  30 + feeInfo: 'Fee Type>Fee Item',
  31 + feeStatus: 'Fee Status',
  32 + paymentMethod: 'Payment Method',
  33 + feePeriod: 'Payment Period',
  34 + paymentTime: 'Payment Time',
  35 + cashier: 'Cashier',
  36 + payableAmount: 'Payable/Receivable Amount(Yuan)',
  37 + receivedAmount: 'Received Amount(Yuan)',
  38 + withholdAmount: 'Account Deduction(Yuan)',
  39 + discountAmount: 'Discount/Deduction Amount(Yuan)',
  40 + giftAmount: 'Gift Amount(Yuan)',
  41 + lateFee: 'Late Fee(Yuan)',
  42 + area: 'Area(sqm)',
  43 + parkingSpace: 'Parking Space',
  44 + remark: 'Remark'
  45 + },
  46 + summary: {
  47 + subtotal: 'Subtotal',
  48 + total: 'Total',
  49 + receivable: 'Receivable',
  50 + received: 'Received',
  51 + discount: 'Discount',
  52 + deduction: 'Deduction',
  53 + gift: 'Gift',
  54 + lateFee: 'Late Fee',
  55 + yuan: 'Yuan',
  56 + discountDesc: 'Discount amount: discount amount generated under discount rules and no arrears discount rules',
  57 + deductionDesc: 'Deduction amount: deduction amount generated under deduction rules',
  58 + giftDesc: 'Gift amount: payable amount of gift months under gift rules',
  59 + lateFeeDesc: 'Late fee: default late fee generated under default late fee rules, additional late fee to be paid'
  60 + },
  61 + allCommunities: 'All Communities',
  62 + fetchError: 'Failed to fetch payment details',
  63 + fetchCommunityError: 'Failed to fetch community list',
  64 + exportSuccess: 'Export started successfully',
  65 + exportError: 'Failed to export data'
  66 + }
  67 + },
  68 + zh: {
  69 + adminPayFeeDetail: {
  70 + search: {
  71 + title: '查询条件',
  72 + startTime: '缴费开始时间',
  73 + startTimePlaceholder: '请选择缴费开始时间',
  74 + endTime: '缴费结束时间',
  75 + endTimePlaceholder: '请选择缴费结束时间',
  76 + paymentMethod: '支付方式',
  77 + paymentMethodPlaceholder: '请选择支付方式',
  78 + feeStatus: '费用状态',
  79 + feeStatusPlaceholder: '请选择费用状态',
  80 + feeType: '费用类型',
  81 + feeTypePlaceholder: '请选择费用类型',
  82 + feeStartTime: '收费开始时间',
  83 + feeStartTimePlaceholder: '请选择收费开始时间',
  84 + feeEndTime: '收费结束时间',
  85 + feeEndTimePlaceholder: '请选择收费结束时间',
  86 + payerObjName: '房屋/车牌号',
  87 + payerObjNamePlaceholder: '房屋编号或者车牌号'
  88 + },
  89 + list: {
  90 + title: '缴费记录',
  91 + tooltip: '所有房屋缴费记录明细'
  92 + },
  93 + table: {
  94 + orderId: '订单号',
  95 + payerInfo: '房号/业主',
  96 + feeInfo: '费用类型>费用项',
  97 + feeStatus: '费用状态',
  98 + paymentMethod: '支付方式',
  99 + feePeriod: '缴费时间段',
  100 + paymentTime: '缴费时间',
  101 + cashier: '收银员',
  102 + payableAmount: '应缴/应收金额(元)',
  103 + receivedAmount: '实收金额(元)',
  104 + withholdAmount: '账户抵扣(元)',
  105 + discountAmount: '优惠/减免金额(元)',
  106 + giftAmount: '赠送金额(元)',
  107 + lateFee: '滞纳金(元)',
  108 + area: '面积(平方米)',
  109 + parkingSpace: '车位',
  110 + remark: '说明'
  111 + },
  112 + summary: {
  113 + subtotal: '小计',
  114 + total: '大计',
  115 + receivable: '应收',
  116 + received: '实收',
  117 + discount: '优惠',
  118 + deduction: '减免',
  119 + gift: '赠送',
  120 + lateFee: '滞纳金',
  121 + yuan: '元',
  122 + discountDesc: '优惠金额:打折规则和打折无欠费规则下产生的优惠金额',
  123 + deductionDesc: '减免金额:减免规则下产生的减免金额',
  124 + giftDesc: '赠送金额:赠送规则下赠送月份应缴的金额',
  125 + lateFeeDesc: '滞纳金:违约滞纳金规则下产生的违约金额,需额外缴纳的滞纳金'
  126 + },
  127 + allCommunities: '全部小区',
  128 + fetchError: '获取缴费明细失败',
  129 + fetchCommunityError: '获取小区列表失败',
  130 + exportSuccess: '导出成功',
  131 + exportError: '导出失败'
  132 + }
  133 + }
  134 +}
0 \ No newline at end of file 135 \ No newline at end of file
src/views/fee/adminPayFeeDetailList.vue 0 → 100644
  1 +<template>
  2 + <div class="admin-pay-fee-detail-container animated fadeInRight">
  3 + <el-row :gutter="20">
  4 + <el-col :span="4">
  5 + <select-admin-community ref="selectCommunity" @changeCommunity="handleCommunityChange" />
  6 + </el-col>
  7 + <el-col :span="20">
  8 + <el-card>
  9 + <div slot="header" class="clearfix flex justify-between">
  10 + <span>{{ $t('adminPayFeeDetail.search.title') }}</span>
  11 + </div>
  12 + <el-form :model="searchForm" label-width="auto">
  13 + <el-row :gutter="20">
  14 + <el-col :span="6">
  15 + <el-form-item :label="$t('adminPayFeeDetail.search.startTime')">
  16 + <el-date-picker v-model="searchForm.startTime" type="datetime"
  17 + :placeholder="$t('adminPayFeeDetail.search.startTimePlaceholder')" style="width: 100%" />
  18 + </el-form-item>
  19 + </el-col>
  20 + <el-col :span="6">
  21 + <el-form-item :label="$t('adminPayFeeDetail.search.endTime')">
  22 + <el-date-picker v-model="searchForm.endTime" type="datetime"
  23 + :placeholder="$t('adminPayFeeDetail.search.endTimePlaceholder')" style="width: 100%" />
  24 + </el-form-item>
  25 + </el-col>
  26 + <el-col :span="6">
  27 + <el-form-item :label="$t('adminPayFeeDetail.search.paymentMethod')">
  28 + <el-select v-model="searchForm.primeRate"
  29 + :placeholder="$t('adminPayFeeDetail.search.paymentMethodPlaceholder')" style="width: 100%">
  30 + <el-option v-for="item in paymentMethods" :key="item.statusCd" :label="item.name"
  31 + :value="item.statusCd" />
  32 + </el-select>
  33 + </el-form-item>
  34 + </el-col>
  35 + <el-col :span="6">
  36 + <el-form-item :label="$t('adminPayFeeDetail.search.feeStatus')">
  37 + <el-select v-model="searchForm.state" :placeholder="$t('adminPayFeeDetail.search.feeStatusPlaceholder')"
  38 + style="width: 100%">
  39 + <el-option v-for="item in feeStatuses" :key="item.statusCd" :label="item.name"
  40 + :value="item.statusCd" />
  41 + </el-select>
  42 + </el-form-item>
  43 + </el-col>
  44 + </el-row>
  45 + <el-row :gutter="20">
  46 + <el-col :span="6">
  47 + <el-form-item :label="$t('adminPayFeeDetail.search.feeType')">
  48 + <el-select v-model="searchForm.feeTypeCd"
  49 + :placeholder="$t('adminPayFeeDetail.search.feeTypePlaceholder')" style="width: 100%">
  50 + <el-option v-for="item in feeTypes" :key="item.statusCd" :label="item.name" :value="item.statusCd" />
  51 + </el-select>
  52 + </el-form-item>
  53 + </el-col>
  54 + <el-col :span="6">
  55 + <el-form-item :label="$t('adminPayFeeDetail.search.feeStartTime')">
  56 + <el-date-picker v-model="searchForm.feeStartTime" type="datetime"
  57 + :placeholder="$t('adminPayFeeDetail.search.feeStartTimePlaceholder')" style="width: 100%" />
  58 + </el-form-item>
  59 + </el-col>
  60 + <el-col :span="6">
  61 + <el-form-item :label="$t('adminPayFeeDetail.search.feeEndTime')">
  62 + <el-date-picker v-model="searchForm.feeEndTime" type="datetime"
  63 + :placeholder="$t('adminPayFeeDetail.search.feeEndTimePlaceholder')" style="width: 100%" />
  64 + </el-form-item>
  65 + </el-col>
  66 + <el-col :span="6">
  67 + <el-form-item :label="$t('adminPayFeeDetail.search.payerObjName')">
  68 + <el-input v-model="searchForm.payerObjName"
  69 + :placeholder="$t('adminPayFeeDetail.search.payerObjNamePlaceholder')" />
  70 + </el-form-item>
  71 + </el-col>
  72 + </el-row>
  73 + <el-row>
  74 + <el-col :span="24" style="text-align: right">
  75 + <el-button type="primary" @click="handleSearch">{{ $t('common.search') }}</el-button>
  76 + <el-button @click="handleReset">{{ $t('common.reset') }}</el-button>
  77 + </el-col>
  78 + </el-row>
  79 + </el-form>
  80 + </el-card>
  81 +
  82 + <el-card style="margin-top: 20px">
  83 + <div slot="header" class="text-left">
  84 + <span>{{ $t('adminPayFeeDetail.list.title') }}</span>
  85 + <el-tooltip :content="$t('adminPayFeeDetail.list.tooltip')" placement="top">
  86 + <i class="el-icon-info" style="margin-left: 10px; cursor: pointer" />
  87 + </el-tooltip>
  88 + <div style="float: right">
  89 + <el-button type="primary" size="small" @click="handleExport">
  90 + {{ $t('common.export') }}
  91 + </el-button>
  92 + </div>
  93 + </div>
  94 + <el-table :data="tableData" border style="width: 100%">
  95 + <el-table-column prop="oId" :label="$t('adminPayFeeDetail.table.orderId')" align="center" />
  96 + <el-table-column :label="$t('adminPayFeeDetail.table.payerInfo')" align="center" width="150">
  97 + <template slot-scope="scope">
  98 + <div>{{ scope.row.payerObjName }}/{{ scope.row.ownerName }}</div>
  99 + <div>{{ scope.row.communityName }}</div>
  100 + </template>
  101 + </el-table-column>
  102 + <el-table-column :label="$t('adminPayFeeDetail.table.feeInfo')" align="center">
  103 + <template slot-scope="scope">
  104 + {{ scope.row.feeTypeCdName }}>{{ scope.row.feeName }}
  105 + </template>
  106 + </el-table-column>
  107 + <el-table-column prop="stateName" :label="$t('adminPayFeeDetail.table.feeStatus')" align="center" />
  108 + <el-table-column prop="primeRate" :label="$t('adminPayFeeDetail.table.paymentMethod')" align="center" />
  109 + <el-table-column :label="$t('adminPayFeeDetail.table.feePeriod')" align="center" width="150">
  110 + <template slot-scope="scope">
  111 + <div>{{ formatDate(scope.row.startTime) }}</div>
  112 + <div>~</div>
  113 + <div>{{ formatDate(scope.row.endTime) }}</div>
  114 + </template>
  115 + </el-table-column>
  116 + <el-table-column prop="createTime" :label="$t('adminPayFeeDetail.table.paymentTime')" align="center" />
  117 + <el-table-column prop="cashierName" :label="$t('adminPayFeeDetail.table.cashier')" align="center" />
  118 + <el-table-column :label="$t('adminPayFeeDetail.table.payableAmount')" align="center">
  119 + <template slot-scope="scope">
  120 + {{ scope.row.payableAmount }}/{{ scope.row.receivableAmount }}
  121 + </template>
  122 + </el-table-column>
  123 + <el-table-column prop="receivedAmount" :label="$t('adminPayFeeDetail.table.receivedAmount')" align="center" />
  124 + <el-table-column prop="withholdAmount" :label="$t('adminPayFeeDetail.table.withholdAmount')" align="center" />
  125 + <el-table-column :label="$t('adminPayFeeDetail.table.discountAmount')" align="center">
  126 + <template slot-scope="scope">
  127 + {{ scope.row.preferentialAmount }}/{{ scope.row.deductionAmount }}
  128 + </template>
  129 + </el-table-column>
  130 + <el-table-column prop="giftAmount" :label="$t('adminPayFeeDetail.table.giftAmount')" align="center" />
  131 + <el-table-column prop="lateFee" :label="$t('adminPayFeeDetail.table.lateFee')" align="center" />
  132 + <el-table-column prop="builtUpArea" :label="$t('adminPayFeeDetail.table.area')" align="center" />
  133 + <el-table-column prop="psName" :label="$t('adminPayFeeDetail.table.parkingSpace')" align="center" />
  134 + <el-table-column prop="remark" :label="$t('adminPayFeeDetail.table.remark')" align="center" width="200" />
  135 + </el-table>
  136 +
  137 + <div style="margin-top: 20px">
  138 + <el-row>
  139 + <el-col :span="2">
  140 + <div>{{ $t('adminPayFeeDetail.summary.subtotal') }}</div>
  141 + <div>{{ $t('adminPayFeeDetail.summary.total') }}</div>
  142 + </el-col>
  143 + <el-col :span="2">
  144 + <div>
  145 + {{ $t('adminPayFeeDetail.summary.receivable') }}: {{ summary.totalReceivableAmount }}
  146 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  147 + </div>
  148 + <div>
  149 + {{ $t('adminPayFeeDetail.summary.receivable') }}: {{ summary.allReceivableAmount }}
  150 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  151 + </div>
  152 + </el-col>
  153 + <el-col :span="2">
  154 + <div>
  155 + {{ $t('adminPayFeeDetail.summary.received') }}: {{ summary.totalReceivedAmount }}
  156 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  157 + </div>
  158 + <div>
  159 + {{ $t('adminPayFeeDetail.summary.received') }}: {{ summary.allReceivedAmount }}
  160 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  161 + </div>
  162 + </el-col>
  163 + <el-col :span="2">
  164 + <div>
  165 + {{ $t('adminPayFeeDetail.summary.discount') }}: {{ summary.totalPreferentialAmount }}
  166 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  167 + </div>
  168 + <div>
  169 + {{ $t('adminPayFeeDetail.summary.discount') }}: {{ summary.allPreferentialAmount }}
  170 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  171 + </div>
  172 + </el-col>
  173 + <el-col :span="2">
  174 + <div>
  175 + {{ $t('adminPayFeeDetail.summary.deduction') }}: {{ summary.totalDeductionAmount }}
  176 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  177 + </div>
  178 + <div>
  179 + {{ $t('adminPayFeeDetail.summary.deduction') }}: {{ summary.allDeductionAmount }}
  180 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  181 + </div>
  182 + </el-col>
  183 + <el-col :span="2">
  184 + <div>
  185 + {{ $t('adminPayFeeDetail.summary.gift') }}: {{ summary.totalGiftAmount }}
  186 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  187 + </div>
  188 + <div>
  189 + {{ $t('adminPayFeeDetail.summary.gift') }}: {{ summary.allGiftAmount }}
  190 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  191 + </div>
  192 + </el-col>
  193 + <el-col :span="2">
  194 + <div>
  195 + {{ $t('adminPayFeeDetail.summary.lateFee') }}: {{ summary.totalLateFee }}
  196 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  197 + </div>
  198 + <div>
  199 + {{ $t('adminPayFeeDetail.summary.lateFee') }}: {{ summary.allLateFee }}
  200 + {{ $t('adminPayFeeDetail.summary.yuan') }}
  201 + </div>
  202 + </el-col>
  203 + </el-row>
  204 + </div>
  205 +
  206 + <div style="margin-top: 20px" class="text-left">
  207 + <el-row>
  208 + <el-col :span="18">
  209 + <div>{{ $t('adminPayFeeDetail.summary.discountDesc') }}</div>
  210 + <div>{{ $t('adminPayFeeDetail.summary.deductionDesc') }}</div>
  211 + <div>{{ $t('adminPayFeeDetail.summary.giftDesc') }}</div>
  212 + <div>{{ $t('adminPayFeeDetail.summary.lateFeeDesc') }}</div>
  213 + </el-col>
  214 + <el-col :span="6">
  215 + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]"
  216 + :page-size="pagination.size" :total="pagination.total" layout="total, sizes, prev, pager, next, jumper"
  217 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  218 + </el-col>
  219 + </el-row>
  220 + </div>
  221 + </el-card>
  222 + </el-col>
  223 + </el-row>
  224 + </div>
  225 +</template>
  226 +
  227 +<script>
  228 +import { queryAdminPayFeeDetail, exportFeeData } from '@/api/fee/adminPayFeeDetailApi'
  229 +import SelectAdminCommunity from '@/components/community/selectAdminCommunity'
  230 +import { getDict } from '@/api/community/communityApi'
  231 +import {dateFormat} from '@/utils/dateUtil'
  232 +
  233 +export default {
  234 + name: 'AdminPayFeeDetailList',
  235 + components: {
  236 + SelectAdminCommunity
  237 + },
  238 + data() {
  239 + return {
  240 + searchForm: {
  241 + communityId: '',
  242 + floorId: '',
  243 + roomNum: '',
  244 + payerObjName: '',
  245 + configId: '',
  246 + primeRate: '',
  247 + state: '',
  248 + feeTypeCd: '',
  249 + startTime: '',
  250 + endTime: '',
  251 + feeStartTime: '',
  252 + feeEndTime: '',
  253 + page: 1,
  254 + row: 10
  255 + },
  256 + tableData: [],
  257 + paymentMethods: [],
  258 + feeStatuses: [],
  259 + feeTypes: [],
  260 + summary: {
  261 + totalReceivableAmount: '0.00',
  262 + totalReceivedAmount: '0.00',
  263 + allReceivableAmount: '0.00',
  264 + allReceivedAmount: '0.00',
  265 + totalPreferentialAmount: '0.00',
  266 + totalDeductionAmount: '0.00',
  267 + totalLateFee: '0.00',
  268 + totalVacantHousingDiscount: '0.00',
  269 + totalVacantHousingReduction: '0.00',
  270 + totalNoDeduction: '0.00',
  271 + totalCashDeduction: '0.00',
  272 + totalPointDeduction: '0.00',
  273 + totalDiscountCouponDeduction: '0.00',
  274 + allPreferentialAmount: '0.00',
  275 + allDeductionAmount: '0.00',
  276 + allLateFee: '0.00',
  277 + allVacantHousingDiscount: '0.00',
  278 + allVacantHousingReduction: '0.00',
  279 + allGiftAmount: '0.00',
  280 + allNoDeduction: '0.00',
  281 + allCashDeduction: '0.00',
  282 + allPointDeduction: '0.00',
  283 + allDiscountCouponDeduction: '0.00',
  284 + totalGiftAmount: '0.00'
  285 + },
  286 + pagination: {
  287 + current: 1,
  288 + size: 10,
  289 + total: 0
  290 + }
  291 + }
  292 + },
  293 + created() {
  294 + this.loadDictData()
  295 + this.fetchData()
  296 + },
  297 + methods: {
  298 + async loadDictData() {
  299 + this.paymentMethods = await getDict('pay_fee_detail', 'prime_rate')
  300 + this.feeStatuses = await getDict('pay_fee_detail', 'state')
  301 + this.feeTypes = await getDict('pay_fee_config', 'fee_type_cd')
  302 + },
  303 + async fetchData() {
  304 + try {
  305 + const params = { ...this.searchForm }
  306 + const { data, total, sumTotal } = await queryAdminPayFeeDetail(params)
  307 +
  308 + this.tableData = data.map(item => ({
  309 + ...item,
  310 + lateFee: (item.lateFee * -1).toFixed(2)
  311 + }))
  312 +
  313 + this.pagination.total = total
  314 + this.updateSummary(sumTotal)
  315 + } catch (error) {
  316 + //this.$message.error(this.$t('adminPayFeeDetail.fetchError'))
  317 + }
  318 + },
  319 + updateSummary(sumTotal) {
  320 + const defaultZero = '0.00'
  321 +
  322 + this.summary = {
  323 + totalReceivableAmount: sumTotal.totalReceivableAmount || defaultZero,
  324 + totalReceivedAmount: sumTotal.totalReceivedAmount || defaultZero,
  325 + allReceivableAmount: sumTotal.allReceivableAmount || defaultZero,
  326 + allReceivedAmount: sumTotal.allReceivedAmount || defaultZero,
  327 + totalPreferentialAmount: sumTotal.totalPreferentialAmount || defaultZero,
  328 + totalDeductionAmount: sumTotal.totalDeductionAmount || defaultZero,
  329 + totalLateFee: sumTotal.totalLateFee ? (sumTotal.totalLateFee * -1).toFixed(2) : defaultZero,
  330 + allPreferentialAmount: sumTotal.allPreferentialAmount || defaultZero,
  331 + allDeductionAmount: sumTotal.allDeductionAmount || defaultZero,
  332 + allLateFee: sumTotal.allLateFee ? (sumTotal.allLateFee * -1).toFixed(2) : defaultZero,
  333 + allGiftAmount: sumTotal.allGiftAmount || defaultZero,
  334 + totalGiftAmount: sumTotal.totalGiftAmount || defaultZero
  335 + }
  336 + },
  337 + handleSearch() {
  338 + this.searchForm.page = 1
  339 + this.fetchData()
  340 + },
  341 + handleReset() {
  342 + this.searchForm = {
  343 + communityId: this.searchForm.communityId,
  344 + floorId: '',
  345 + roomNum: '',
  346 + payerObjName: '',
  347 + configId: '',
  348 + primeRate: '',
  349 + state: '',
  350 + feeTypeCd: '',
  351 + startTime: '',
  352 + endTime: '',
  353 + feeStartTime: '',
  354 + feeEndTime: '',
  355 + page: 1,
  356 + row: this.searchForm.row
  357 + }
  358 + this.fetchData()
  359 + },
  360 + handleCommunityChange(community) {
  361 + this.searchForm.communityId = community.communityId
  362 + this.fetchData()
  363 + },
  364 + handleSizeChange(size) {
  365 + this.searchForm.row = size
  366 + this.fetchData()
  367 + },
  368 + handleCurrentChange(page) {
  369 + this.searchForm.page = page
  370 + this.fetchData()
  371 + },
  372 + async handleExport() {
  373 + try {
  374 + const params = {
  375 + ...this.searchForm,
  376 + pagePath: 'adminPayFeeDetail'
  377 + }
  378 + await exportFeeData(params)
  379 + this.$message.success(this.$t('adminPayFeeDetail.exportSuccess'))
  380 + // 这里可以添加跳转到下载页面的逻辑
  381 + // this.$router.push('/pages/property/downloadTempFile?tab=下载中心')
  382 + } catch (error) {
  383 + this.$message.error(this.$t('adminPayFeeDetail.exportError'))
  384 + }
  385 + },
  386 + formatDate(date) {
  387 + if (!date) return '-'
  388 + return dateFormat(date)
  389 + }
  390 + }
  391 +}
  392 +</script>
  393 +
  394 +<style lang="scss" scoped>
  395 +.admin-pay-fee-detail-container {
  396 + padding: 20px;
  397 +
  398 + .el-card {
  399 + margin-bottom: 20px;
  400 +
  401 + .el-card__header {
  402 + font-weight: bold;
  403 + background-color: #f5f7fa;
  404 + }
  405 + }
  406 +
  407 + .el-table {
  408 + margin-top: 20px;
  409 +
  410 + .cell {
  411 + white-space: pre-line;
  412 + }
  413 + }
  414 +
  415 + .el-pagination {
  416 + margin-top: 20px;
  417 + text-align: right;
  418 + }
  419 +
  420 + .summary-row {
  421 + margin-top: 20px;
  422 + padding: 10px;
  423 + background-color: #f5f7fa;
  424 + border-radius: 4px;
  425 +
  426 + .summary-item {
  427 + margin-right: 20px;
  428 + }
  429 + }
  430 +}
  431 +</style>
0 \ No newline at end of file 432 \ No newline at end of file
src/views/report/communityFeeSummaryLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + communityFeeSummary: {
  4 + search: {
  5 + title: 'Search Conditions',
  6 + startDate: 'Start Date',
  7 + endDate: 'End Date',
  8 + feeType: 'Fee Type',
  9 + selectFeeType: 'Please select fee type',
  10 + allCommunities: 'All Communities'
  11 + },
  12 + table: {
  13 + title: 'Fee Summary Report',
  14 + tooltip: 'Summarize fee information by month',
  15 + updateDaily: 'Updated daily',
  16 + totalRooms: 'Total Rooms',
  17 + feeRooms: 'Fee Rooms',
  18 + oweRooms: 'Owe Rooms',
  19 + oweFeeFormula: 'History Owe + Current Owe = Total Owe',
  20 + hisOweFee: 'History Owe',
  21 + curOweFee: 'Current Owe',
  22 + totalOweFee: 'Total Owe',
  23 + receivedFeeFormula: 'Owe Recovery + Current Partial + Prepayment = Actual Payment',
  24 + hisReceivedFee: 'Owe Recovery',
  25 + curPartialFee: 'Current Partial',
  26 + preReceivedFee: 'Prepayment',
  27 + totalReceivedFee: 'Actual Payment',
  28 + curReceivableFee: 'Current Receivable',
  29 + curReceivedFee: 'Current Received',
  30 + roomFeeRate: 'Paid Rooms/Fee Rooms = Room Fee Rate',
  31 + feeRate: 'Current Received/Current Receivable = Fee Rate',
  32 + clearRate: 'Owe Recovery/(Owe Recovery + History Owe) = Clear Rate'
  33 + },
  34 + fetchError: 'Failed to fetch fee summary data'
  35 + }
  36 + },
  37 + zh: {
  38 + communityFeeSummary: {
  39 + search: {
  40 + title: '查询条件',
  41 + startDate: '开始时间',
  42 + endDate: '结束时间',
  43 + feeType: '费用大类',
  44 + selectFeeType: '请选择费用大类',
  45 + allCommunities: '全部小区'
  46 + },
  47 + table: {
  48 + title: '费用汇总表',
  49 + tooltip: '以月份为维度,进行费用情况汇总',
  50 + updateDaily: '按天更新',
  51 + totalRooms: '总户数',
  52 + feeRooms: '收费户',
  53 + oweRooms: '欠费户',
  54 + oweFeeFormula: '历史欠费+当期欠费=欠费',
  55 + hisOweFee: '历史欠费',
  56 + curOweFee: '当期欠费',
  57 + totalOweFee: '总欠费',
  58 + receivedFeeFormula: '欠费追回+当期部分+预交=实缴',
  59 + hisReceivedFee: '欠费追回',
  60 + curPartialFee: '当期部分',
  61 + preReceivedFee: '预交',
  62 + totalReceivedFee: '实缴',
  63 + curReceivableFee: '当期应收',
  64 + curReceivedFee: '当期实收',
  65 + roomFeeRate: '已交户/收费户=户收费率',
  66 + feeRate: '当期实收/当期应收=收费率',
  67 + clearRate: '欠费追回/(欠费追回+历史欠费)=清缴率'
  68 + },
  69 + fetchError: '获取费用汇总数据失败'
  70 + }
  71 + }
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
src/views/report/communityFeeSummaryList.vue 0 → 100644
  1 +<template>
  2 + <div class="community-fee-summary-container">
  3 + <el-row class="animated fadeInRight" :gutter="20">
  4 + <el-col :span="4">
  5 + <select-admin-community @changeCommunity="handleCommunityChange" />
  6 + </el-col>
  7 + <el-col :span="20">
  8 + <el-card class="box-card">
  9 + <div slot="header" class="clearfix flex justify-between">
  10 + <span>{{ $t('communityFeeSummary.search.title') }}</span>
  11 + </div>
  12 + <el-row :gutter="20">
  13 + <el-col :span="6">
  14 + <el-date-picker v-model="searchForm.startDate" type="date"
  15 + :placeholder="$t('communityFeeSummary.search.startDate')" class="w-100" />
  16 + </el-col>
  17 + <el-col :span="6">
  18 + <el-date-picker v-model="searchForm.endDate" type="date"
  19 + :placeholder="$t('communityFeeSummary.search.endDate')" class="w-100" />
  20 + </el-col>
  21 + <el-col :span="6">
  22 + <el-select v-model="searchForm.feeTypeCd" :placeholder="$t('communityFeeSummary.search.feeType')"
  23 + class="w-100">
  24 + <el-option :label="$t('communityFeeSummary.search.selectFeeType')" value="" />
  25 + <el-option v-for="item in feeTypeOptions" :key="item.statusCd" :label="item.name"
  26 + :value="item.statusCd" />
  27 + </el-select>
  28 + </el-col>
  29 + <el-col :span="6">
  30 + <el-button type="primary" @click="handleSearch">
  31 + {{ $t('common.search') }}
  32 + </el-button>
  33 + </el-col>
  34 + </el-row>
  35 + </el-card>
  36 +
  37 + <el-card class="box-card mt-20">
  38 + <div slot="header" class="clearfix text-left">
  39 + <span>{{ $t('communityFeeSummary.table.title') }}</span>
  40 + <el-tooltip effect="dark" :content="$t('communityFeeSummary.table.tooltip')" placement="top">
  41 + <i class="el-icon-info" style="cursor: pointer;"></i>
  42 + </el-tooltip>
  43 + <span style="font-size: 10px;">({{ $t('communityFeeSummary.table.updateDaily') }})</span>
  44 + </div>
  45 + <el-table :data="tableData" border style="width: 100%">
  46 + <el-table-column prop="roomCount" :label="$t('communityFeeSummary.table.totalRooms')" align="center" />
  47 + <el-table-column prop="feeRoomCount" :label="$t('communityFeeSummary.table.feeRooms')" align="center" />
  48 + <el-table-column prop="oweRoomCount" :label="$t('communityFeeSummary.table.oweRooms')" align="center" />
  49 + <el-table-column :label="$t('communityFeeSummary.table.oweFeeFormula')" align="center">
  50 + <el-table-column prop="hisOweFee" :label="$t('communityFeeSummary.table.hisOweFee')" align="center" />
  51 + <el-table-column prop="curOweFee" :label="$t('communityFeeSummary.table.curOweFee')" align="center" />
  52 + <el-table-column :label="$t('communityFeeSummary.table.totalOweFee')" align="center">
  53 + <template slot-scope="scope">
  54 + {{ (scope.row.curOweFee + scope.row.hisOweFee).toFixed(2) }}
  55 + </template>
  56 + </el-table-column>
  57 + </el-table-column>
  58 + <el-table-column :label="$t('communityFeeSummary.table.receivedFeeFormula')" align="center">
  59 + <el-table-column prop="hisReceivedFee" :label="$t('communityFeeSummary.table.hisReceivedFee')"
  60 + align="center" />
  61 + <el-table-column :label="$t('communityFeeSummary.table.curPartialFee')" align="center">
  62 + <template slot-scope="scope">
  63 + {{ (scope.row.receivedFee - scope.row.hisReceivedFee - scope.row.preReceivedFee).toFixed(2) }}
  64 + </template>
  65 + </el-table-column>
  66 + <el-table-column prop="preReceivedFee" :label="$t('communityFeeSummary.table.preReceivedFee')"
  67 + align="center" />
  68 + <el-table-column prop="receivedFee" :label="$t('communityFeeSummary.table.totalReceivedFee')"
  69 + align="center" />
  70 + </el-table-column>
  71 + <el-table-column prop="curReceivableFee" :label="$t('communityFeeSummary.table.curReceivableFee')"
  72 + align="center" />
  73 + <el-table-column :label="$t('communityFeeSummary.table.curReceivedFee')" align="center">
  74 + <template slot-scope="scope">
  75 + {{ (scope.row.curReceivableFee - scope.row.curOweFee).toFixed(2) }}
  76 + </template>
  77 + </el-table-column>
  78 + <el-table-column :label="$t('communityFeeSummary.table.roomFeeRate')" align="center">
  79 + <template slot-scope="scope">
  80 + {{ scope.row.feeRoomCount > 0 ?
  81 + (((scope.row.feeRoomCount - scope.row.oweRoomCount) / scope.row.feeRoomCount * 100).toFixed(2) + '%') :
  82 + '0%' }}
  83 + </template>
  84 + </el-table-column>
  85 + <el-table-column :label="$t('communityFeeSummary.table.feeRate')" align="center">
  86 + <template slot-scope="scope">
  87 + {{ scope.row.curReceivableFee > 0 ?
  88 + (((scope.row.curReceivableFee - scope.row.curOweFee) / scope.row.curReceivableFee * 100).toFixed(2) + '%'): '0%' }}
  89 + </template>
  90 + </el-table-column>
  91 + <el-table-column :label="$t('communityFeeSummary.table.clearRate')" align="center">
  92 + <template slot-scope="scope">
  93 + {{ (scope.row.hisReceivedFee + scope.row.hisOweFee) > 0 ?
  94 + ((scope.row.hisReceivedFee / (scope.row.hisReceivedFee + scope.row.hisOweFee) * 100).toFixed(2) + '%') :
  95 + '0%' }}
  96 + </template>
  97 + </el-table-column>
  98 + </el-table>
  99 + </el-card>
  100 + </el-col>
  101 + </el-row>
  102 + </div>
  103 +</template>
  104 +
  105 +<script>
  106 +import { getCommunityFeeSummary } from '@/api/report/communityFeeSummaryApi'
  107 +import SelectAdminCommunity from '@/components/community/selectAdminCommunity'
  108 +
  109 +export default {
  110 + name: 'CommunityFeeSummaryList',
  111 + components: {
  112 + SelectAdminCommunity
  113 + },
  114 + data() {
  115 + return {
  116 + searchForm: {
  117 + startDate: '',
  118 + endDate: '',
  119 + feeTypeCd: '',
  120 + communityId: ''
  121 + },
  122 + feeTypeOptions: [],
  123 + tableData: [],
  124 + loading: false
  125 + }
  126 + },
  127 + created() {
  128 + this.initDate()
  129 + this.loadFeeTypes()
  130 + },
  131 + methods: {
  132 + initDate() {
  133 + const today = new Date()
  134 + const firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
  135 + const lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0)
  136 +
  137 + this.searchForm.startDate = firstDay
  138 + this.searchForm.endDate = lastDay
  139 + },
  140 + loadFeeTypes() {
  141 + this.$store.dispatch('dict/getDict', {
  142 + dictType: 'pay_fee_config',
  143 + dictName: 'fee_type_cd'
  144 + }).then(data => {
  145 + this.feeTypeOptions = data
  146 + })
  147 + },
  148 + handleCommunityChange(community) {
  149 + this.searchForm.communityId = community.communityId
  150 + this.handleSearch()
  151 + },
  152 + async handleSearch() {
  153 + try {
  154 + this.loading = true
  155 + const params = {
  156 + startDate: this.formatDate(this.searchForm.startDate),
  157 + endDate: this.formatDate(this.searchForm.endDate),
  158 + feeTypeCd: this.searchForm.feeTypeCd,
  159 + communityId: this.searchForm.communityId
  160 + }
  161 +
  162 + const { data } = await getCommunityFeeSummary(params)
  163 + this.tableData = data
  164 + } catch (error) {
  165 + this.$message.error(this.$t('communityFeeSummary.fetchError'))
  166 + } finally {
  167 + this.loading = false
  168 + }
  169 + },
  170 + formatDate(date) {
  171 + if (!date) return ''
  172 + const d = new Date(date)
  173 + let month = '' + (d.getMonth() + 1)
  174 + let day = '' + d.getDate()
  175 + const year = d.getFullYear()
  176 +
  177 + if (month.length < 2) month = '0' + month
  178 + if (day.length < 2) day = '0' + day
  179 +
  180 + return [year, month, day].join('-')
  181 + }
  182 + }
  183 +}
  184 +</script>
  185 +
  186 +<style lang="scss" scoped>
  187 +.community-fee-summary-container {
  188 + padding: 20px;
  189 +
  190 + .box-card {
  191 + margin-bottom: 20px;
  192 + }
  193 +
  194 + .mt-20 {
  195 + margin-top: 20px;
  196 + }
  197 +
  198 + .w-100 {
  199 + width: 100%;
  200 + }
  201 +}
  202 +</style>
0 \ No newline at end of file 203 \ No newline at end of file
src/views/report/operationalAnalysisLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + operationalAnalysis: {
  4 + allCommunities: 'All Communities',
  5 + fetchError: 'Failed to fetch operational analysis data',
  6 + fetchCommunityError: 'Failed to fetch community list',
  7 + chart: {
  8 + payFee: 'Payment Orders',
  9 + repair: 'Repair Orders',
  10 + inspection: 'Inspections',
  11 + maintainance: 'Maintenance',
  12 + itemIn: 'Purchase Orders',
  13 + itemOut: 'Withdrawal Orders',
  14 + carIn: 'Vehicle Entries',
  15 + personIn: 'Door Openings',
  16 + contract: 'Contracts'
  17 + }
  18 + }
  19 + },
  20 + zh: {
  21 + operationalAnalysis: {
  22 + allCommunities: '全部小区',
  23 + fetchError: '获取运营分析数据失败',
  24 + fetchCommunityError: '获取小区列表失败',
  25 + chart: {
  26 + payFee: '缴费订单数',
  27 + repair: '报修订单数',
  28 + inspection: '巡检数',
  29 + maintainance: '保养数',
  30 + itemIn: '采购订单数',
  31 + itemOut: '领用订单数',
  32 + carIn: '车辆进场数',
  33 + personIn: '开门记录',
  34 + contract: '签订合同数'
  35 + }
  36 + }
  37 + }
  38 +}
0 \ No newline at end of file 39 \ No newline at end of file
src/views/report/operationalAnalysisList.vue 0 → 100644
  1 +<template>
  2 + <div class="operational-analysis-container">
  3 + <el-row class="animated fadeInRight ecommerce">
  4 + <el-col :span="4">
  5 + <select-admin-community @changeCommunity="handleCommunityChange" />
  6 + </el-col>
  7 + <el-col :span="20">
  8 + <el-row>
  9 + <el-col v-for="(chart, index) in charts" :key="index" :span="8" class="margin-bottom">
  10 + <el-card class="box-card">
  11 + <div :id="chart.id" style="width: 100%; height: 300px;"></div>
  12 + </el-card>
  13 + </el-col>
  14 + </el-row>
  15 + </el-col>
  16 + </el-row>
  17 + </div>
  18 +</template>
  19 +
  20 +<script>
  21 +import SelectAdminCommunity from '@/components/community/selectAdminCommunity'
  22 +import { getCommunityOperationalAnalysis } from '@/api/report/operationalAnalysisApi'
  23 +import * as echarts from 'echarts'
  24 +
  25 +export default {
  26 + name: 'OperationalAnalysisList',
  27 + components: {
  28 + SelectAdminCommunity
  29 + },
  30 + data() {
  31 + return {
  32 + charts: [
  33 + { id: 'communityPayFeeDetail', title: this.$t('operationalAnalysis.chart.payFee') },
  34 + { id: 'communityRepair', title: this.$t('operationalAnalysis.chart.repair') },
  35 + { id: 'communityInspection', title: this.$t('operationalAnalysis.chart.inspection') },
  36 + { id: 'communityMaintainance', title: this.$t('operationalAnalysis.chart.maintainance') },
  37 + { id: 'communityItemIn', title: this.$t('operationalAnalysis.chart.itemIn') },
  38 + { id: 'communityItemOut', title: this.$t('operationalAnalysis.chart.itemOut') },
  39 + { id: 'communityCarIn', title: this.$t('operationalAnalysis.chart.carIn') },
  40 + { id: 'communityPersonIn', title: this.$t('operationalAnalysis.chart.personIn') },
  41 + { id: 'communityContract', title: this.$t('operationalAnalysis.chart.contract') }
  42 + ],
  43 + communityId: ''
  44 + }
  45 + },
  46 + methods: {
  47 + handleCommunityChange(community) {
  48 + this.communityId = community.communityId
  49 + this.loadOperationalAnalysisData()
  50 + },
  51 + async loadOperationalAnalysisData() {
  52 + try {
  53 + const params = {
  54 + communityId: this.communityId
  55 + }
  56 + const res = await getCommunityOperationalAnalysis(params)
  57 + this.initCharts(res.data)
  58 + } catch (error) {
  59 + this.$message.error(this.$t('operationalAnalysis.fetchError'))
  60 + }
  61 + },
  62 + initCharts(data) {
  63 + this.initAnalysisChart(data.feeDetailData, 'communityPayFeeDetail', this.$t('operationalAnalysis.chart.payFee'), this.$t('operationalAnalysis.chart.payFee'))
  64 + this.initAnalysisChart(data.repairData, 'communityRepair', this.$t('operationalAnalysis.chart.repair'), this.$t('operationalAnalysis.chart.repair'))
  65 + this.initAnalysisChart(data.inspectionData, 'communityInspection', this.$t('operationalAnalysis.chart.inspection'), this.$t('operationalAnalysis.chart.inspection'))
  66 + this.initAnalysisChart(data.maintainanceData, 'communityMaintainance', this.$t('operationalAnalysis.chart.maintainance'), this.$t('operationalAnalysis.chart.maintainance'))
  67 + this.initAnalysisChart(data.itemInData, 'communityItemIn', this.$t('operationalAnalysis.chart.itemIn'), this.$t('operationalAnalysis.chart.itemIn'))
  68 + this.initAnalysisChart(data.itemOutData, 'communityItemOut', this.$t('operationalAnalysis.chart.itemOut'), this.$t('operationalAnalysis.chart.itemOut'))
  69 + this.initAnalysisChart(data.carInData, 'communityCarIn', this.$t('operationalAnalysis.chart.carIn'), this.$t('operationalAnalysis.chart.carIn'))
  70 + this.initAnalysisChart(data.personInData, 'communityPersonIn', this.$t('operationalAnalysis.chart.personIn'), this.$t('operationalAnalysis.chart.personIn'))
  71 + this.initAnalysisChart(data.contractData, 'communityContract', this.$t('operationalAnalysis.chart.contract'), this.$t('operationalAnalysis.chart.contract'))
  72 + },
  73 + initAnalysisChart(data, elementId, title, lineName) {
  74 + const dom = document.getElementById(elementId)
  75 + if (!dom) return
  76 +
  77 + const myChart = echarts.init(dom)
  78 + const createTime = []
  79 + const countValues = []
  80 +
  81 + data.forEach(item => {
  82 + createTime.push(item.createTime)
  83 + countValues.push(item.countValue)
  84 + })
  85 +
  86 + const option = {
  87 + title: {
  88 + text: title
  89 + },
  90 + tooltip: {
  91 + trigger: 'axis'
  92 + },
  93 + legend: {
  94 + data: createTime
  95 + },
  96 + grid: {
  97 + left: '3%',
  98 + right: '4%',
  99 + bottom: '3%',
  100 + containLabel: true
  101 + },
  102 + toolbox: {
  103 + feature: {
  104 + saveAsImage: {}
  105 + }
  106 + },
  107 + xAxis: {
  108 + type: 'category',
  109 + boundaryGap: false,
  110 + data: createTime
  111 + },
  112 + yAxis: {
  113 + type: 'value'
  114 + },
  115 + series: [{
  116 + name: lineName,
  117 + type: 'line',
  118 + stack: 'Total',
  119 + data: countValues
  120 + }]
  121 + }
  122 +
  123 + myChart.setOption(option)
  124 + window.addEventListener('resize', () => {
  125 + myChart.resize()
  126 + })
  127 + }
  128 + }
  129 +}
  130 +</script>
  131 +
  132 +<style lang="scss" scoped>
  133 +.operational-analysis-container {
  134 + padding: 20px;
  135 +
  136 + .margin-bottom {
  137 + margin-bottom: 20px;
  138 + }
  139 +
  140 + .box-card {
  141 + margin: 0 10px;
  142 + }
  143 +}
  144 +</style>
0 \ No newline at end of file 145 \ No newline at end of file