Commit eafabeed42c8a0fad9c27ae92e5b27e426633f05

Authored by wuxw
1 parent e18d1bbb

开发完成业主车辆

src/api/car/carAddParkingSpaceApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询车辆信息
  5 +export function getCarInfo(params) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/owner.queryOwnerCars',
  9 + method: 'get',
  10 + params: {
  11 + ...params,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + if (res.code === 0) {
  17 + resolve(res)
  18 + } else {
  19 + reject(new Error(res.msg || '获取车辆信息失败'))
  20 + }
  21 + }).catch(error => {
  22 + reject(error)
  23 + })
  24 + })
  25 +}
  26 +
  27 +// 添加车辆停车位
  28 +export function addCarParkingSpace(data) {
  29 + return new Promise((resolve, reject) => {
  30 + request({
  31 + url: '/owner.carAddParkingSpace',
  32 + method: 'post',
  33 + data: {
  34 + ...data,
  35 + communityId: getCommunityId()
  36 + }
  37 + }).then(response => {
  38 + const res = response.data
  39 + if (res.code === 0) {
  40 + resolve(res)
  41 + } else {
  42 + reject(new Error(res.msg || '添加停车位失败'))
  43 + }
  44 + }).catch(error => {
  45 + reject(error)
  46 + })
  47 + })
  48 +}
  49 +
  50 +// 查询停车位列表
  51 +export function queryParkingSpaces(params) {
  52 + return new Promise((resolve, reject) => {
  53 + request({
  54 + url: '/parkingSpace.queryParkingSpaces',
  55 + method: 'get',
  56 + params: {
  57 + ...params,
  58 + communityId: getCommunityId()
  59 + }
  60 + }).then(response => {
  61 + const res = response.data
  62 + if (res.code === 0) {
  63 + resolve(res)
  64 + } else {
  65 + reject(new Error(res.msg || '查询停车位失败'))
  66 + }
  67 + }).catch(error => {
  68 + reject(error)
  69 + })
  70 + })
  71 +}
  72 +
  73 +// 查询停车场列表
  74 +export function listParkingAreas() {
  75 + return new Promise((resolve, reject) => {
  76 + request({
  77 + url: '/parkingArea.listParkingAreas',
  78 + method: 'get',
  79 + params: {
  80 + page: 1,
  81 + row: 50,
  82 + communityId: getCommunityId()
  83 + }
  84 + }).then(response => {
  85 + const res = response.data
  86 + if (res.code === 0) {
  87 + resolve(res)
  88 + } else {
  89 + reject(new Error(res.msg || '查询停车场失败'))
  90 + }
  91 + }).catch(error => {
  92 + reject(error)
  93 + })
  94 + })
  95 +}
0 96 \ No newline at end of file
... ...
src/api/car/hireParkingSpaceApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 保存车辆信息
  5 +export function saveOwnerCar(data) {
  6 + return new Promise((resolve, reject) => {
  7 + request({
  8 + url: '/owner.saveOwnerCar',
  9 + method: 'post',
  10 + data: {
  11 + ...data,
  12 + communityId: getCommunityId()
  13 + }
  14 + }).then(response => {
  15 + const res = response.data
  16 + if (res.code === 0) {
  17 + resolve(res)
  18 + } else {
  19 + reject(new Error(res.msg || '保存车辆信息失败'))
  20 + }
  21 + }).catch(error => {
  22 + reject(error)
  23 + })
  24 + })
  25 +}
  26 +
  27 +// 查询业主列表
  28 +export function queryOwners(params) {
  29 + return new Promise((resolve, reject) => {
  30 + request({
  31 + url: '/owner.queryOwners',
  32 + method: 'get',
  33 + params: {
  34 + ...params,
  35 + communityId: getCommunityId()
  36 + }
  37 + }).then(response => {
  38 + const res = response.data
  39 + if (res.code === 0) {
  40 + resolve({
  41 + data: res.data,
  42 + total: res.records
  43 + })
  44 + } else {
  45 + reject(new Error(res.msg || '查询业主列表失败'))
  46 + }
  47 + }).catch(error => {
  48 + reject(error)
  49 + })
  50 + })
  51 +}
  52 +
  53 +// 查询停车位列表
  54 +export function queryParkingSpaces(params) {
  55 + return new Promise((resolve, reject) => {
  56 + request({
  57 + url: '/parkingSpace.queryParkingSpaces',
  58 + method: 'get',
  59 + params: {
  60 + ...params,
  61 + communityId: getCommunityId()
  62 + }
  63 + }).then(response => {
  64 + const res = response.data
  65 + resolve(res)
  66 + }).catch(error => {
  67 + reject(error)
  68 + })
  69 + })
  70 +}
  71 +
  72 +// 获取停车场区域列表
  73 +export function listParkingAreas(params) {
  74 + return new Promise((resolve, reject) => {
  75 + request({
  76 + url: '/parkingArea.listParkingAreas',
  77 + method: 'get',
  78 + params: {
  79 + ...params,
  80 + communityId: getCommunityId()
  81 + }
  82 + }).then(response => {
  83 + const res = response.data
  84 + resolve(res)
  85 + }).catch(error => {
  86 + reject(error)
  87 + })
  88 + })
  89 +}
  90 +
  91 +// 获取车辆属性规格
  92 +export function getCarAttrSpec() {
  93 + return new Promise((resolve, reject) => {
  94 + request({
  95 + url: '/attr.getAttrSpec',
  96 + method: 'get',
  97 + params: {
  98 + specCd: 'owner_car_attr',
  99 + communityId: getCommunityId()
  100 + }
  101 + }).then(response => {
  102 + const res = response.data
  103 + if (res.code === 0) {
  104 + resolve(res.data)
  105 + } else {
  106 + reject(new Error(res.msg || '获取车辆属性规格失败'))
  107 + }
  108 + }).catch(error => {
  109 + reject(error)
  110 + })
  111 + })
  112 +}
  113 +
  114 +// 获取字典数据
  115 +export function getDict(dictType, dictCd) {
  116 + return new Promise((resolve, reject) => {
  117 + request({
  118 + url: '/dict.getDict',
  119 + method: 'get',
  120 + params: {
  121 + dictType,
  122 + dictCd,
  123 + communityId: getCommunityId()
  124 + }
  125 + }).then(response => {
  126 + const res = response.data
  127 + if (res.code === 0) {
  128 + resolve(res.data)
  129 + } else {
  130 + reject(new Error(res.msg || '获取字典数据失败'))
  131 + }
  132 + }).catch(error => {
  133 + reject(error)
  134 + })
  135 + })
  136 +}
0 137 \ No newline at end of file
... ...
src/api/car/listOwnerCarApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询业主车辆
  5 +export function queryOwnerCars(params) {
  6 + return new Promise((resolve, reject) => {
  7 + const communityId = getCommunityId()
  8 + request({
  9 + url: '/owner.queryOwnerCars',
  10 + method: 'get',
  11 + params: {
  12 + ...params,
  13 + communityId
  14 + }
  15 + }).then(response => {
  16 + const res = response.data
  17 + if (res.code === 0) {
  18 + resolve(res)
  19 + } else {
  20 + reject(new Error(res.msg || '查询业主车辆失败'))
  21 + }
  22 + }).catch(error => {
  23 + reject(error)
  24 + })
  25 + })
  26 +}
  27 +
  28 +// 删除业主车辆
  29 +export function deleteOwnerCars(data) {
  30 + return new Promise((resolve, reject) => {
  31 + const communityId = getCommunityId()
  32 + request({
  33 + url: '/owner.deleteOwnerCars',
  34 + method: 'post',
  35 + data: {
  36 + ...data,
  37 + communityId
  38 + }
  39 + }).then(response => {
  40 + const res = response.data
  41 + if (res.code === 0) {
  42 + resolve(res)
  43 + } else {
  44 + reject(new Error(res.msg || '删除业主车辆失败'))
  45 + }
  46 + }).catch(error => {
  47 + reject(error)
  48 + })
  49 + })
  50 +}
  51 +
  52 +// 释放车位
  53 +export function deleteCarParkingSpace(data) {
  54 + return new Promise((resolve, reject) => {
  55 + const communityId = getCommunityId()
  56 + request({
  57 + url: '/owner.deleteCarParkingSpace',
  58 + method: 'post',
  59 + data: {
  60 + ...data,
  61 + communityId
  62 + }
  63 + }).then(response => {
  64 + const res = response.data
  65 + if (res.code === 0) {
  66 + resolve(res)
  67 + } else {
  68 + reject(new Error(res.msg || '释放车位失败'))
  69 + }
  70 + }).catch(error => {
  71 + reject(error)
  72 + })
  73 + })
  74 +}
  75 +
  76 +// 导入车辆数据
  77 +export function importData(data) {
  78 + return new Promise((resolve, reject) => {
  79 + const communityId = getCommunityId()
  80 + const formData = new FormData()
  81 + formData.append('uploadFile', data.file)
  82 + formData.append('communityId', communityId)
  83 + formData.append('importAdapt', data.importAdapt || 'importOwnerCar')
  84 +
  85 + request({
  86 + url: '/callComponent/upload/assetImport/importData',
  87 + method: 'post',
  88 + data: formData,
  89 + headers: {
  90 + 'Content-Type': 'multipart/form-data'
  91 + }
  92 + }).then(response => {
  93 + const res = response.data
  94 + if (res.code === 0) {
  95 + resolve(res)
  96 + } else {
  97 + reject(new Error(res.msg || '导入失败'))
  98 + }
  99 + }).catch(error => {
  100 + reject(error)
  101 + })
  102 + })
  103 +}
  104 +
  105 +// 修改车辆信息
  106 +export function editOwnerCar(data) {
  107 + return new Promise((resolve, reject) => {
  108 + const communityId = getCommunityId()
  109 + request({
  110 + url: '/owner.editOwnerCar',
  111 + method: 'post',
  112 + data: {
  113 + ...data,
  114 + communityId
  115 + }
  116 + }).then(response => {
  117 + const res = response.data
  118 + if (res.code === 0) {
  119 + resolve(res)
  120 + } else {
  121 + reject(new Error(res.msg || '修改车辆信息失败'))
  122 + }
  123 + }).catch(error => {
  124 + reject(error)
  125 + })
  126 + })
  127 +}
  128 +
  129 +// 导出车辆数据
  130 +export function exportData(params) {
  131 + return new Promise((resolve, reject) => {
  132 + const communityId = getCommunityId()
  133 + request({
  134 + url: '/export.exportData',
  135 + method: 'get',
  136 + params: {
  137 + ...params,
  138 + communityId,
  139 + pagePath: 'exportOwnerCar'
  140 + }
  141 + }).then(response => {
  142 + const res = response.data
  143 + if (res.code === 0) {
  144 + resolve(res)
  145 + } else {
  146 + reject(new Error(res.msg || '导出失败'))
  147 + }
  148 + }).catch(error => {
  149 + reject(error)
  150 + })
  151 + })
  152 +}
0 153 \ No newline at end of file
... ...
src/api/car/parkingBoxManageApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +import { getCommunityId } from '@/api/community/communityApi'
  3 +
  4 +// 查询岗亭列表
  5 +export function getParkingBoxList(params) {
  6 + return new Promise((resolve, reject) => {
  7 + const communityId = getCommunityId()
  8 + request({
  9 + url: '/iot.getOpenApi',
  10 + method: 'get',
  11 + params: {
  12 + ...params,
  13 + communityId,
  14 + iotApiCode: 'listParkingBoxBmoImpl'
  15 + }
  16 + }).then(response => {
  17 + const res = response.data
  18 + if (res.code === 0) {
  19 + resolve(res)
  20 + } else {
  21 + reject(new Error(res.msg || '获取岗亭列表失败'))
  22 + }
  23 + }).catch(error => {
  24 + reject(error)
  25 + })
  26 + })
  27 +}
  28 +
  29 +// 添加岗亭
  30 +export function addParkingBox(data) {
  31 + return new Promise((resolve, reject) => {
  32 + const communityId = getCommunityId()
  33 + request({
  34 + url: '/parkingBox.saveParkingBox',
  35 + method: 'post',
  36 + data: {
  37 + ...data,
  38 + communityId
  39 + }
  40 + }).then(response => {
  41 + const res = response.data
  42 + if (res.code === 0) {
  43 + resolve(res)
  44 + } else {
  45 + reject(new Error(res.msg || '添加岗亭失败'))
  46 + }
  47 + }).catch(error => {
  48 + reject(error)
  49 + })
  50 + })
  51 +}
  52 +
  53 +// 更新岗亭
  54 +export function updateParkingBox(data) {
  55 + return new Promise((resolve, reject) => {
  56 + const communityId = getCommunityId()
  57 + request({
  58 + url: '/parkingBox.updateParkingBox',
  59 + method: 'post',
  60 + data: {
  61 + ...data,
  62 + communityId
  63 + }
  64 + }).then(response => {
  65 + const res = response.data
  66 + if (res.code === 0) {
  67 + resolve(res)
  68 + } else {
  69 + reject(new Error(res.msg || '更新岗亭失败'))
  70 + }
  71 + }).catch(error => {
  72 + reject(error)
  73 + })
  74 + })
  75 +}
  76 +
  77 +// 删除岗亭
  78 +export function deleteParkingBox(data) {
  79 + return new Promise((resolve, reject) => {
  80 + const communityId = getCommunityId()
  81 + request({
  82 + url: '/parkingBox.deleteParkingBox',
  83 + method: 'post',
  84 + data: {
  85 + ...data,
  86 + communityId
  87 + }
  88 + }).then(response => {
  89 + const res = response.data
  90 + if (res.code === 0) {
  91 + resolve(res)
  92 + } else {
  93 + reject(new Error(res.msg || '删除岗亭失败'))
  94 + }
  95 + }).catch(error => {
  96 + reject(error)
  97 + })
  98 + })
  99 +}
  100 +
  101 +// 获取停车场列表
  102 +export function getParkingAreaList() {
  103 + return new Promise((resolve, reject) => {
  104 + const communityId = getCommunityId()
  105 + request({
  106 + url: '/parkingArea.listParkingAreas',
  107 + method: 'get',
  108 + params: {
  109 + page: 1,
  110 + row: 50,
  111 + communityId
  112 + }
  113 + }).then(response => {
  114 + const res = response.data
  115 + if (res.code === 0) {
  116 + resolve(res.data.parkingAreas || [])
  117 + } else {
  118 + reject(new Error(res.msg || '获取停车场列表失败'))
  119 + }
  120 + }).catch(error => {
  121 + reject(error)
  122 + })
  123 + })
  124 +}
0 125 \ No newline at end of file
... ...
src/api/fee/buyCarMonthCardApi.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +/**
  4 + * 购买月卡订单
  5 + * @param {Object} data 订单数据
  6 + * @returns {Promise}
  7 + */
  8 +export function buyCarMonthOrder(data) {
  9 + return new Promise((resolve, reject) => {
  10 + request({
  11 + url: '/carMonth.buyCarMonthOrder',
  12 + method: 'post',
  13 + data
  14 + }).then(response => {
  15 + const res = response.data
  16 + if (res.code === 0) {
  17 + resolve(res)
  18 + } else {
  19 + reject(new Error(res.msg || '购买月卡失败'))
  20 + }
  21 + }).catch(error => {
  22 + reject(error)
  23 + })
  24 + })
  25 +}
  26 +
  27 +/**
  28 + * 查询车主车辆
  29 + * @param {Object} params 查询参数
  30 + * @returns {Promise}
  31 + */
  32 +export function queryOwnerCars(params) {
  33 + return new Promise((resolve, reject) => {
  34 + request({
  35 + url: '/owner.queryOwnerCars',
  36 + method: 'get',
  37 + params
  38 + }).then(response => {
  39 + const res = response.data
  40 + if (res.code === 0) {
  41 + resolve(res)
  42 + } else {
  43 + reject(new Error(res.msg || '查询车主车辆失败'))
  44 + }
  45 + }).catch(error => {
  46 + reject(error)
  47 + })
  48 + })
  49 +}
  50 +
  51 +/**
  52 + * 获取月卡列表
  53 + * @param {Object} params 查询参数
  54 + * @returns {Promise}
  55 + */
  56 +export function getOpenApi(params) {
  57 + return new Promise((resolve, reject) => {
  58 + request({
  59 + url: '/iot.getOpenApi',
  60 + method: 'get',
  61 + params
  62 + }).then(response => {
  63 + const res = response.data
  64 + if (res.code === 0) {
  65 + resolve(res)
  66 + } else {
  67 + reject(new Error(res.msg || '获取月卡列表失败'))
  68 + }
  69 + }).catch(error => {
  70 + reject(error)
  71 + })
  72 + })
  73 +}
0 74 \ No newline at end of file
... ...
src/components/car/SearchParkingSpace.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('searchParkingSpace.title')" :visible.sync="visible" width="80%" @close="handleClose">
  3 + <el-card>
  4 + <el-row v-if="showSearchCondition" :gutter="20">
  5 + <el-col :span="8">
  6 + <el-select v-model="searchParkingSpaceInfo.areaNum" class="w-full"
  7 + :placeholder="$t('searchParkingSpace.parkingAreaPlaceholder')">
  8 + <el-option v-for="item in parkingAreas" :key="item.num" :label="item.num" :value="item.num" />
  9 + </el-select>
  10 + </el-col>
  11 + <el-col :span="8">
  12 + <el-input v-model="searchParkingSpaceInfo.num" :placeholder="$t('searchParkingSpace.numPlaceholder')" />
  13 + </el-col>
  14 + <el-col :span="8">
  15 + <el-button type="primary" @click="searchParkingSpaces">
  16 + {{ $t('common.search') }}
  17 + </el-button>
  18 + <el-button @click="resetParkingSpaces">
  19 + {{ $t('common.reset') }}
  20 + </el-button>
  21 + </el-col>
  22 + </el-row>
  23 +
  24 + <el-table :data="searchParkingSpaceInfo.parkingSpaces" class="mt-20">
  25 + <el-table-column prop="areaNum" :label="$t('searchParkingSpace.areaNum')" align="center" />
  26 + <el-table-column prop="num" :label="$t('searchParkingSpace.num')" align="center" />
  27 + <el-table-column :label="$t('searchParkingSpace.type')" align="center">
  28 + <template slot-scope="scope">
  29 + {{ scope.row.typeCd === '1001' ? $t('searchParkingSpace.ground') : $t('searchParkingSpace.underground') }}
  30 + </template>
  31 + </el-table-column>
  32 + <el-table-column prop="parkingTypeName" :label="$t('searchParkingSpace.parkingType')" align="center" />
  33 + <el-table-column prop="stateName" :label="$t('searchParkingSpace.state')" align="center" />
  34 + <el-table-column prop="area" :label="$t('searchParkingSpace.area')" align="center" />
  35 + <el-table-column :label="$t('common.operation')" align="center" width="120">
  36 + <template slot-scope="scope">
  37 + <el-button type="primary" size="small" @click="chooseParkingSpace(scope.row)">
  38 + {{ $t('common.select') }}
  39 + </el-button>
  40 + </template>
  41 + </el-table-column>
  42 + </el-table>
  43 +
  44 + <el-pagination class="mt-20" :current-page="pagination.currentPage" :page-sizes="[10, 20, 30, 50]"
  45 + :page-size="pagination.pageSize" :total="pagination.total" layout="total, sizes, prev, pager, next, jumper"
  46 + @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  47 + </el-card>
  48 + </el-dialog>
  49 +</template>
  50 +
  51 +<script>
  52 +import { queryParkingSpaces, listParkingAreas } from '@/api/car/hireParkingSpaceApi'
  53 +import { getCommunityId } from '@/api/community/communityApi'
  54 +
  55 +export default {
  56 + name: 'SearchParkingSpace',
  57 + props: {
  58 + parkingSpaceFlag: {
  59 + type: String,
  60 + default: 'F'
  61 + },
  62 + showSearchCondition: {
  63 + type: Boolean,
  64 + default: true
  65 + }
  66 + },
  67 + data() {
  68 + return {
  69 + visible: false,
  70 + searchParkingSpaceInfo: {
  71 + parkingSpaces: [],
  72 + areaNum: '',
  73 + num: '',
  74 + carNum: ''
  75 + },
  76 + parkingAreas: [],
  77 + pagination: {
  78 + currentPage: 1,
  79 + pageSize: 10,
  80 + total: 0
  81 + }
  82 + }
  83 + },
  84 + async created() {
  85 + await this.listParkingAreas()
  86 + },
  87 + methods: {
  88 + open() {
  89 + this.visible = true
  90 + this.resetParkingSpaces()
  91 + this.loadAllParkingSpaceInfo()
  92 + },
  93 +
  94 + handleClose() {
  95 + this.visible = false
  96 + },
  97 +
  98 + async listParkingAreas() {
  99 + try {
  100 + const params = {
  101 + page: 1,
  102 + row: 50,
  103 + communityId: getCommunityId()
  104 + }
  105 +
  106 + const { data } = await listParkingAreas(params)
  107 + this.parkingAreas = data
  108 + } catch (error) {
  109 + console.error('加载停车场区域失败:', error)
  110 + }
  111 + },
  112 +
  113 + async loadAllParkingSpaceInfo() {
  114 + try {
  115 + const params = {
  116 + page: this.pagination.currentPage,
  117 + row: this.pagination.pageSize,
  118 + communityId: getCommunityId(),
  119 + num: this.searchParkingSpaceInfo.num,
  120 + areaNum: this.searchParkingSpaceInfo.areaNum,
  121 + carNum: this.searchParkingSpaceInfo.carNum,
  122 + state: this.parkingSpaceFlag
  123 + }
  124 +
  125 + const { parkingSpaces, total } = await queryParkingSpaces(params)
  126 + this.searchParkingSpaceInfo.parkingSpaces = parkingSpaces
  127 + this.pagination.total = total
  128 + } catch (error) {
  129 + console.error('加载停车位信息失败:', error)
  130 + this.$message.error(this.$t('searchParkingSpace.loadError'))
  131 + }
  132 + },
  133 +
  134 + chooseParkingSpace(parkingSpace) {
  135 + this.$emit('choose-parking-space', parkingSpace)
  136 + this.visible = false
  137 + },
  138 +
  139 + searchParkingSpaces() {
  140 + this.pagination.currentPage = 1
  141 + this.loadAllParkingSpaceInfo()
  142 + },
  143 +
  144 + resetParkingSpaces() {
  145 + this.searchParkingSpaceInfo.carNum = ''
  146 + this.searchParkingSpaceInfo.num = ''
  147 + this.searchParkingSpaceInfo.areaNum = ''
  148 + this.pagination.currentPage = 1
  149 + this.loadAllParkingSpaceInfo()
  150 + },
  151 +
  152 + handleSizeChange(size) {
  153 + this.pagination.pageSize = size
  154 + this.loadAllParkingSpaceInfo()
  155 + },
  156 +
  157 + handleCurrentChange(page) {
  158 + this.pagination.currentPage = page
  159 + this.loadAllParkingSpaceInfo()
  160 + }
  161 + }
  162 +}
  163 +</script>
  164 +
  165 +<style scoped>
  166 +.w-full {
  167 + width: 100%;
  168 +}
  169 +
  170 +.mt-20 {
  171 + margin-top: 20px;
  172 +}
  173 +</style>
0 174 \ No newline at end of file
... ...
src/components/car/addParkingBox.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('addParkingBox.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form ref="form" :model="formData" label-width="120px">
  9 + <el-form-item
  10 + :label="$t('addParkingBox.boothName')"
  11 + prop="boxName"
  12 + :rules="[
  13 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.boothName'), trigger: 'blur' },
  14 + { max: 64, message: $t('addParkingBox.maxLength'), trigger: 'blur' }
  15 + ]"
  16 + >
  17 + <el-input
  18 + v-model="formData.boxName"
  19 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.boothName')"
  20 + />
  21 + </el-form-item>
  22 +
  23 + <el-form-item
  24 + :label="$t('addParkingBox.parkingLot')"
  25 + prop="paId"
  26 + :rules="[
  27 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.parkingLot'), trigger: 'change' }
  28 + ]"
  29 + >
  30 + <el-select
  31 + v-model="formData.paId"
  32 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.parkingLot')"
  33 + style="width:100%"
  34 + >
  35 + <el-option
  36 + v-for="item in parkingAreas"
  37 + :key="item.paId"
  38 + :label="item.num"
  39 + :value="item.paId"
  40 + />
  41 + </el-select>
  42 + </el-form-item>
  43 +
  44 + <el-form-item
  45 + :label="$t('addParkingBox.tempCarIn')"
  46 + prop="tempCarIn"
  47 + :rules="[
  48 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.tempCarIn'), trigger: 'change' }
  49 + ]"
  50 + >
  51 + <el-select
  52 + v-model="formData.tempCarIn"
  53 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.tempCarIn')"
  54 + style="width:100%"
  55 + >
  56 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  57 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  58 + </el-select>
  59 + </el-form-item>
  60 +
  61 + <el-form-item
  62 + :label="$t('addParkingBox.tempAuth')"
  63 + prop="tempAuth"
  64 + :rules="[
  65 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.tempAuth'), trigger: 'change' }
  66 + ]"
  67 + >
  68 + <el-select
  69 + v-model="formData.tempAuth"
  70 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.tempAuth')"
  71 + style="width:100%"
  72 + >
  73 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  74 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  75 + </el-select>
  76 + </el-form-item>
  77 +
  78 + <el-form-item
  79 + :label="$t('addParkingBox.fee')"
  80 + prop="fee"
  81 + :rules="[
  82 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.fee'), trigger: 'change' }
  83 + ]"
  84 + >
  85 + <el-select
  86 + v-model="formData.fee"
  87 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.fee')"
  88 + style="width:100%"
  89 + >
  90 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  91 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  92 + </el-select>
  93 + </el-form-item>
  94 +
  95 + <el-form-item
  96 + :label="$t('addParkingBox.blueCarIn')"
  97 + prop="blueCarIn"
  98 + :rules="[
  99 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.blueCarIn'), trigger: 'change' }
  100 + ]"
  101 + >
  102 + <el-select
  103 + v-model="formData.blueCarIn"
  104 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.blueCarIn')"
  105 + style="width:100%"
  106 + >
  107 + <el-option value="Y" :label="$t('addParkingBox.canEnter')" />
  108 + <el-option value="N" :label="$t('addParkingBox.cannotEnter')" />
  109 + </el-select>
  110 + </el-form-item>
  111 +
  112 + <el-form-item
  113 + :label="$t('addParkingBox.yelowCarIn')"
  114 + prop="yelowCarIn"
  115 + :rules="[
  116 + { required: true, message: $t('addParkingBox.required') + $t('addParkingBox.yelowCarIn'), trigger: 'change' }
  117 + ]"
  118 + >
  119 + <el-select
  120 + v-model="formData.yelowCarIn"
  121 + :placeholder="$t('addParkingBox.required') + $t('addParkingBox.yelowCarIn')"
  122 + style="width:100%"
  123 + >
  124 + <el-option value="Y" :label="$t('addParkingBox.canExit')" />
  125 + <el-option value="N" :label="$t('addParkingBox.cannotExit')" />
  126 + </el-select>
  127 + </el-form-item>
  128 +
  129 + <el-form-item :label="$t('addParkingBox.remark')">
  130 + <el-input
  131 + v-model="formData.remark"
  132 + type="textarea"
  133 + :placeholder="$t('addParkingBox.optional') + $t('addParkingBox.remark')"
  134 + />
  135 + </el-form-item>
  136 + </el-form>
  137 +
  138 + <div slot="footer" class="dialog-footer">
  139 + <el-button @click="visible = false">
  140 + {{ $t('addParkingBox.cancel') }}
  141 + </el-button>
  142 + <el-button type="primary" @click="handleSubmit">
  143 + {{ $t('addParkingBox.save') }}
  144 + </el-button>
  145 + </div>
  146 + </el-dialog>
  147 +</template>
  148 +
  149 +<script>
  150 +import { addParkingBox, getParkingAreaList } from '@/api/car/parkingBoxManageApi'
  151 +
  152 +export default {
  153 + name: 'AddParkingBox',
  154 + data() {
  155 + return {
  156 + visible: false,
  157 + formData: {
  158 + boxName: '',
  159 + paId: '',
  160 + tempCarIn: '',
  161 + tempAuth: '',
  162 + fee: '',
  163 + blueCarIn: '',
  164 + yelowCarIn: '',
  165 + remark: ''
  166 + },
  167 + parkingAreas: []
  168 + }
  169 + },
  170 + methods: {
  171 + open() {
  172 + this.visible = true
  173 + this.loadParkingAreas()
  174 + },
  175 +
  176 + async loadParkingAreas() {
  177 + try {
  178 + this.parkingAreas = await getParkingAreaList()
  179 + } catch (error) {
  180 + console.error('获取停车场列表失败:', error)
  181 + this.$message.error(this.$t('parkingBoxManage.fetchParkingAreaError'))
  182 + }
  183 + },
  184 +
  185 + handleClose() {
  186 + this.$refs.form.resetFields()
  187 + this.formData = {
  188 + boxName: '',
  189 + paId: '',
  190 + tempCarIn: '',
  191 + tempAuth: '',
  192 + fee: '',
  193 + blueCarIn: '',
  194 + yelowCarIn: '',
  195 + remark: ''
  196 + }
  197 + },
  198 +
  199 + handleSubmit() {
  200 + this.$refs.form.validate(async valid => {
  201 + if (valid) {
  202 + try {
  203 + await addParkingBox(this.formData)
  204 + this.$message.success(this.$t('parkingBoxManage.addSuccess'))
  205 + this.visible = false
  206 + this.$emit('success')
  207 + } catch (error) {
  208 + console.error('添加岗亭失败:', error)
  209 + this.$message.error(this.$t('parkingBoxManage.addError'))
  210 + }
  211 + }
  212 + })
  213 + }
  214 + }
  215 +}
  216 +</script>
0 217 \ No newline at end of file
... ...
src/components/car/deleteOwnerCar.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('listOwnerCar.confirmDelete')"
  4 + :visible.sync="visible"
  5 + width="500px">
  6 + <p>{{ $t('listOwnerCar.deleteCarConfirm') }}</p>
  7 +
  8 + <div slot="footer" class="dialog-footer">
  9 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  10 + <el-button type="danger" @click="confirmDelete" :loading="loading">
  11 + {{ $t('common.confirm') }}
  12 + </el-button>
  13 + </div>
  14 + </el-dialog>
  15 +</template>
  16 +
  17 +<script>
  18 +import { deleteOwnerCars } from '@/api/car/listOwnerCarApi'
  19 +
  20 +export default {
  21 + name: 'DeleteOwnerCar',
  22 + data() {
  23 + return {
  24 + visible: false,
  25 + loading: false,
  26 + carInfo: null
  27 + }
  28 + },
  29 + methods: {
  30 + open(carInfo) {
  31 + this.carInfo = carInfo
  32 + this.visible = true
  33 + },
  34 +
  35 + async confirmDelete() {
  36 + if (!this.carInfo || !this.carInfo.carId) return
  37 +
  38 + this.loading = true
  39 + try {
  40 + await deleteOwnerCars({ carId: this.carInfo.carId })
  41 + this.$message.success(this.$t('listOwnerCar.deleteSuccess'))
  42 + this.$emit('success')
  43 + this.visible = false
  44 + } catch (error) {
  45 + console.error('删除车辆失败:', error)
  46 + this.$message.error(error.message || this.$t('listOwnerCar.deleteError'))
  47 + } finally {
  48 + this.loading = false
  49 + }
  50 + }
  51 + }
  52 +}
  53 +</script>
0 54 \ No newline at end of file
... ...
src/components/car/deleteParkingBox.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('deleteParkingBox.title')"
  4 + :visible.sync="visible"
  5 + width="30%"
  6 + center
  7 + >
  8 + <p>{{ $t('deleteParkingBox.confirmDelete') }}</p>
  9 +
  10 + <div slot="footer" class="dialog-footer">
  11 + <el-button @click="visible = false">
  12 + {{ $t('deleteParkingBox.cancel') }}
  13 + </el-button>
  14 + <el-button type="danger" @click="handleConfirm">
  15 + {{ $t('deleteParkingBox.confirm') }}
  16 + </el-button>
  17 + </div>
  18 + </el-dialog>
  19 +</template>
  20 +
  21 +<script>
  22 +import { deleteParkingBox } from '@/api/car/parkingBoxManageApi'
  23 +
  24 +export default {
  25 + name: 'DeleteParkingBox',
  26 + data() {
  27 + return {
  28 + visible: false,
  29 + boxId: ''
  30 + }
  31 + },
  32 + methods: {
  33 + open(row) {
  34 + this.boxId = row.boxId
  35 + this.visible = true
  36 + },
  37 +
  38 + async handleConfirm() {
  39 + try {
  40 + await deleteParkingBox({ boxId: this.boxId })
  41 + this.$message.success(this.$t('parkingBoxManage.deleteSuccess'))
  42 + this.visible = false
  43 + this.$emit('success')
  44 + } catch (error) {
  45 + console.error('删除岗亭失败:', error)
  46 + this.$message.error(this.$t('parkingBoxManage.deleteError'))
  47 + }
  48 + }
  49 + }
  50 +}
  51 +</script>
0 52 \ No newline at end of file
... ...
src/components/car/editCar.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('listOwnerCar.editCar')"
  4 + :visible.sync="visible"
  5 + width="600px"
  6 + @close="resetForm">
  7 + <el-form :model="form" label-width="120px">
  8 + <el-form-item :label="$t('listOwnerCar.carNum')" required>
  9 + <el-input
  10 + v-model="form.carNum"
  11 + :placeholder="$t('listOwnerCar.inputCarNumRequired')" />
  12 + </el-form-item>
  13 +
  14 + <el-form-item :label="$t('listOwnerCar.carBrand')">
  15 + <el-input
  16 + v-model="form.carBrand"
  17 + :placeholder="$t('listOwnerCar.inputCarBrand')" />
  18 + </el-form-item>
  19 +
  20 + <el-form-item :label="$t('listOwnerCar.carType')" required>
  21 + <el-select
  22 + v-model="form.carType"
  23 + :placeholder="$t('listOwnerCar.selectType')"
  24 + style="width: 100%">
  25 + <el-option
  26 + v-for="item in carTypes"
  27 + :key="item.statusCd"
  28 + :label="item.name"
  29 + :value="item.statusCd" />
  30 + </el-select>
  31 + </el-form-item>
  32 +
  33 + <el-form-item :label="$t('listOwnerCar.color')">
  34 + <el-input
  35 + v-model="form.carColor"
  36 + :placeholder="$t('listOwnerCar.inputCarColor')" />
  37 + </el-form-item>
  38 +
  39 + <el-form-item :label="$t('listOwnerCar.licensePlateType')" required>
  40 + <el-select
  41 + v-model="form.leaseType"
  42 + :placeholder="$t('listOwnerCar.selectLicensePlateType')"
  43 + style="width: 100%">
  44 + <el-option value="H" :label="$t('listOwnerCar.monthlyRentCar')"></el-option>
  45 + <el-option value="S" :label="$t('listOwnerCar.soldCar')"></el-option>
  46 + <el-option value="I" :label="$t('listOwnerCar.internalCar')"></el-option>
  47 + <el-option value="NM" :label="$t('listOwnerCar.freeCar')"></el-option>
  48 + </el-select>
  49 + </el-form-item>
  50 +
  51 + <template v-if="form.leaseType === 'H' || form.leaseType === 'S'">
  52 + <el-form-item :label="$t('listOwnerCar.startRentTime')" required>
  53 + <el-date-picker
  54 + v-model="form.startTime"
  55 + type="datetime"
  56 + :placeholder="$t('listOwnerCar.inputStartRentTime')"
  57 + style="width: 100%"
  58 + value-format="yyyy-MM-dd HH:mm:ss" />
  59 + </el-form-item>
  60 +
  61 + <el-form-item :label="$t('listOwnerCar.endRentTime')" required>
  62 + <el-date-picker
  63 + v-model="form.endTime"
  64 + type="datetime"
  65 + :placeholder="$t('listOwnerCar.inputEndRentTime')"
  66 + style="width: 100%"
  67 + value-format="yyyy-MM-dd HH:mm:ss"
  68 + :disabledDate="disabledEndDate" />
  69 + </el-form-item>
  70 + </template>
  71 +
  72 + <el-form-item :label="$t('listOwnerCar.remark')">
  73 + <el-input
  74 + v-model="form.remark"
  75 + type="textarea"
  76 + :rows="3"
  77 + :placeholder="$t('listOwnerCar.inputRemark')" />
  78 + </el-form-item>
  79 + </el-form>
  80 +
  81 + <div slot="footer" class="dialog-footer">
  82 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  83 + <el-button type="primary" @click="submitForm" :loading="loading">
  84 + {{ $t('common.save') }}
  85 + </el-button>
  86 + </div>
  87 + </el-dialog>
  88 +</template>
  89 +
  90 +<script>
  91 +import { editOwnerCar } from '@/api/car/listOwnerCarApi'
  92 +import { getDict } from '@/api/community/communityApi'
  93 +
  94 +export default {
  95 + name: 'EditCar',
  96 + data() {
  97 + return {
  98 + visible: false,
  99 + loading: false,
  100 + carTypes: [],
  101 + form: {
  102 + carId: '',
  103 + memberId: '',
  104 + carNum: '',
  105 + carBrand: '',
  106 + carType: '',
  107 + carColor: '',
  108 + remark: '',
  109 + startTime: '',
  110 + endTime: '',
  111 + leaseType: ''
  112 + }
  113 + }
  114 + },
  115 + mounted() {
  116 + this.getCarTypes()
  117 + },
  118 + methods: {
  119 + open(carInfo) {
  120 + this.form = { ...carInfo }
  121 + this.visible = true
  122 + },
  123 +
  124 + async getCarTypes() {
  125 + try {
  126 + this.carTypes = await getDict('owner_car', 'car_type')
  127 + } catch (error) {
  128 + console.error('获取车辆类型失败:', error)
  129 + }
  130 + },
  131 +
  132 + disabledEndDate(endTime) {
  133 + if (!this.form.startTime) return false
  134 + const startTime = new Date(this.form.startTime)
  135 + return endTime.getTime() <= startTime.getTime()
  136 + },
  137 +
  138 + async submitForm() {
  139 + if (!this.validateForm()) return
  140 +
  141 + this.loading = true
  142 + try {
  143 + await editOwnerCar(this.form)
  144 + this.$message.success(this.$t('listOwnerCar.updateSuccess'))
  145 + this.$emit('success')
  146 + this.visible = false
  147 + } catch (error) {
  148 + console.error('更新车辆信息失败:', error)
  149 + this.$message.error(error.message || this.$t('listOwnerCar.updateError'))
  150 + } finally {
  151 + this.loading = false
  152 + }
  153 + },
  154 +
  155 + validateForm() {
  156 + if (!this.form.carNum) {
  157 + this.$message.warning(this.$t('listOwnerCar.inputCarNumRequired'))
  158 + return false
  159 + }
  160 +
  161 + if (!this.form.carType) {
  162 + this.$message.warning(this.$t('listOwnerCar.selectTypeRequired'))
  163 + return false
  164 + }
  165 +
  166 + if (!this.form.leaseType) {
  167 + this.$message.warning(this.$t('listOwnerCar.selectLicensePlateTypeRequired'))
  168 + return false
  169 + }
  170 +
  171 + if ((this.form.leaseType === 'H' || this.form.leaseType === 'S') &&
  172 + (!this.form.startTime || !this.form.endTime)) {
  173 + this.$message.warning(this.$t('listOwnerCar.rentTimeRequired'))
  174 + return false
  175 + }
  176 +
  177 + if (this.form.startTime && this.form.endTime) {
  178 + const start = new Date(this.form.startTime)
  179 + const end = new Date(this.form.endTime)
  180 + if (start >= end) {
  181 + this.$message.warning(this.$t('listOwnerCar.endTimeAfterStart'))
  182 + return false
  183 + }
  184 + }
  185 +
  186 + return true
  187 + },
  188 +
  189 + resetForm() {
  190 + this.form = {
  191 + carId: '',
  192 + memberId: '',
  193 + carNum: '',
  194 + carBrand: '',
  195 + carType: '',
  196 + carColor: '',
  197 + remark: '',
  198 + startTime: '',
  199 + endTime: '',
  200 + leaseType: ''
  201 + }
  202 + }
  203 + }
  204 +}
  205 +</script>
0 206 \ No newline at end of file
... ...
src/components/car/editParkingBox.vue 0 → 100644
  1 +<template>
  2 + <el-dialog
  3 + :title="$t('editParkingBox.title')"
  4 + :visible.sync="visible"
  5 + width="50%"
  6 + @close="handleClose"
  7 + >
  8 + <el-form ref="form" :model="formData" label-width="120px">
  9 + <el-form-item
  10 + :label="$t('editParkingBox.boothName')"
  11 + prop="boxName"
  12 + :rules="[
  13 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.boothName'), trigger: 'blur' },
  14 + { max: 64, message: $t('editParkingBox.maxLength'), trigger: 'blur' }
  15 + ]"
  16 + >
  17 + <el-input
  18 + v-model="formData.boxName"
  19 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.boothName')"
  20 + />
  21 + </el-form-item>
  22 +
  23 + <el-form-item
  24 + :label="$t('editParkingBox.parkingLot')"
  25 + prop="paId"
  26 + :rules="[
  27 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.parkingLot'), trigger: 'change' }
  28 + ]"
  29 + >
  30 + <el-select
  31 + v-model="formData.paId"
  32 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.parkingLot')"
  33 + style="width:100%"
  34 + >
  35 + <el-option
  36 + v-for="item in parkingAreas"
  37 + :key="item.paId"
  38 + :label="item.num"
  39 + :value="item.paId"
  40 + />
  41 + </el-select>
  42 + </el-form-item>
  43 +
  44 + <el-form-item
  45 + :label="$t('editParkingBox.tempCarIn')"
  46 + prop="tempCarIn"
  47 + :rules="[
  48 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.tempCarIn'), trigger: 'change' }
  49 + ]"
  50 + >
  51 + <el-select
  52 + v-model="formData.tempCarIn"
  53 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.tempCarIn')"
  54 + style="width:100%"
  55 + >
  56 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  57 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  58 + </el-select>
  59 + </el-form-item>
  60 +
  61 + <el-form-item
  62 + :label="$t('editParkingBox.tempAuth')"
  63 + prop="tempAuth"
  64 + :rules="[
  65 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.tempAuth'), trigger: 'change' }
  66 + ]"
  67 + >
  68 + <el-select
  69 + v-model="formData.tempAuth"
  70 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.tempAuth')"
  71 + style="width:100%"
  72 + >
  73 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  74 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  75 + </el-select>
  76 + </el-form-item>
  77 +
  78 + <el-form-item
  79 + :label="$t('editParkingBox.fee')"
  80 + prop="fee"
  81 + :rules="[
  82 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.fee'), trigger: 'change' }
  83 + ]"
  84 + >
  85 + <el-select
  86 + v-model="formData.fee"
  87 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.fee')"
  88 + style="width:100%"
  89 + >
  90 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  91 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  92 + </el-select>
  93 + </el-form-item>
  94 +
  95 + <el-form-item
  96 + :label="$t('editParkingBox.blueCarIn')"
  97 + prop="blueCarIn"
  98 + :rules="[
  99 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.blueCarIn'), trigger: 'change' }
  100 + ]"
  101 + >
  102 + <el-select
  103 + v-model="formData.blueCarIn"
  104 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.blueCarIn')"
  105 + style="width:100%"
  106 + >
  107 + <el-option value="Y" :label="$t('editParkingBox.canEnter')" />
  108 + <el-option value="N" :label="$t('editParkingBox.cannotEnter')" />
  109 + </el-select>
  110 + </el-form-item>
  111 +
  112 + <el-form-item
  113 + :label="$t('editParkingBox.yelowCarIn')"
  114 + prop="yelowCarIn"
  115 + :rules="[
  116 + { required: true, message: $t('editParkingBox.required') + $t('editParkingBox.yelowCarIn'), trigger: 'change' }
  117 + ]"
  118 + >
  119 + <el-select
  120 + v-model="formData.yelowCarIn"
  121 + :placeholder="$t('editParkingBox.required') + $t('editParkingBox.yelowCarIn')"
  122 + style="width:100%"
  123 + >
  124 + <el-option value="Y" :label="$t('editParkingBox.canExit')" />
  125 + <el-option value="N" :label="$t('editParkingBox.cannotExit')" />
  126 + </el-select>
  127 + </el-form-item>
  128 +
  129 + <el-form-item :label="$t('editParkingBox.remark')">
  130 + <el-input
  131 + v-model="formData.remark"
  132 + type="textarea"
  133 + :placeholder="$t('editParkingBox.optional') + $t('editParkingBox.remark')"
  134 + />
  135 + </el-form-item>
  136 + </el-form>
  137 +
  138 + <div slot="footer" class="dialog-footer">
  139 + <el-button @click="visible = false">
  140 + {{ $t('editParkingBox.cancel') }}
  141 + </el-button>
  142 + <el-button type="primary" @click="handleSubmit">
  143 + {{ $t('editParkingBox.save') }}
  144 + </el-button>
  145 + </div>
  146 + </el-dialog>
  147 +</template>
  148 +
  149 +<script>
  150 +import { updateParkingBox, getParkingAreaList } from '@/api/car/parkingBoxManageApi'
  151 +
  152 +export default {
  153 + name: 'EditParkingBox',
  154 + data() {
  155 + return {
  156 + visible: false,
  157 + formData: {
  158 + boxId: '',
  159 + boxName: '',
  160 + paId: '',
  161 + tempCarIn: '',
  162 + tempAuth: '',
  163 + fee: '',
  164 + blueCarIn: '',
  165 + yelowCarIn: '',
  166 + remark: ''
  167 + },
  168 + parkingAreas: []
  169 + }
  170 + },
  171 + methods: {
  172 + open(row) {
  173 + this.formData = { ...row }
  174 + this.visible = true
  175 + this.loadParkingAreas()
  176 + },
  177 +
  178 + async loadParkingAreas() {
  179 + try {
  180 + this.parkingAreas = await getParkingAreaList()
  181 + } catch (error) {
  182 + console.error('获取停车场列表失败:', error)
  183 + this.$message.error(this.$t('parkingBoxManage.fetchParkingAreaError'))
  184 + }
  185 + },
  186 +
  187 + handleClose() {
  188 + this.$refs.form.resetFields()
  189 + },
  190 +
  191 + handleSubmit() {
  192 + this.$refs.form.validate(async valid => {
  193 + if (valid) {
  194 + try {
  195 + await updateParkingBox(this.formData)
  196 + this.$message.success(this.$t('parkingBoxManage.updateSuccess'))
  197 + this.visible = false
  198 + this.$emit('success')
  199 + } catch (error) {
  200 + console.error('更新岗亭失败:', error)
  201 + this.$message.error(this.$t('parkingBoxManage.updateError'))
  202 + }
  203 + }
  204 + })
  205 + }
  206 + }
  207 +}
  208 +</script>
0 209 \ No newline at end of file
... ...
src/components/car/importOwnerCar.vue 0 → 100644
  1 +<template>
  2 + <el-dialog :title="$t('listOwnerCar.importCarTitle')" :visible.sync="visible" width="600px" @close="resetForm">
  3 + <el-form :model="form" label-width="120px">
  4 + <el-form-item :label="$t('listOwnerCar.selectFile')">
  5 + <el-upload class="upload-demo" action="" :on-change="handleFileChange" :auto-upload="false"
  6 + :show-file-list="false">
  7 + <el-button size="small" type="primary">{{ $t('listOwnerCar.selectFile') }}</el-button>
  8 + <div slot="tip" class="el-upload__tip">
  9 + {{ fileName || $t('listOwnerCar.fileRequired') }}
  10 + </div>
  11 + </el-upload>
  12 + </el-form-item>
  13 +
  14 + <el-form-item :label="$t('listOwnerCar.downloadTemplate')">
  15 + <div>
  16 + {{ $t('listOwnerCar.downloadFirst') }}
  17 + <el-link type="primary" href="/import/importCar.xlsx" target="_blank">
  18 + {{ $t('listOwnerCar.importTemplate') }}
  19 + </el-link>
  20 + <span>{{ $t('listOwnerCar.prepareData') }}</span>
  21 + </div>
  22 + </el-form-item>
  23 + </el-form>
  24 +
  25 + <div slot="footer" class="dialog-footer">
  26 + <el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
  27 + <el-button type="primary" @click="importData" :loading="loading">
  28 + {{ $t('listOwnerCar.import') }}
  29 + </el-button>
  30 + </div>
  31 + </el-dialog>
  32 +</template>
  33 +
  34 +<script>
  35 +import { importData } from '@/api/car/listOwnerCarApi'
  36 +
  37 +export default {
  38 + name: 'ImportOwnerCar',
  39 + data() {
  40 + return {
  41 + visible: false,
  42 + loading: false,
  43 + file: null,
  44 + fileName: ''
  45 + }
  46 + },
  47 + methods: {
  48 + open() {
  49 + this.visible = true
  50 + },
  51 +
  52 + handleFileChange(file) {
  53 + this.file = file.raw
  54 + this.fileName = file.name
  55 + },
  56 +
  57 + async importData() {
  58 + if (!this.file) {
  59 + this.$message.warning(this.$t('listOwnerCar.fileRequired'))
  60 + return
  61 + }
  62 +
  63 + if (!this.checkFileType(this.fileName)) {
  64 + this.$message.warning(this.$t('listOwnerCar.invalidExcel'))
  65 + return
  66 + }
  67 +
  68 + if (!this.checkFileSize(this.file.size)) {
  69 + this.$message.warning(this.$t('listOwnerCar.fileSizeExceed'))
  70 + return
  71 + }
  72 +
  73 + this.loading = true
  74 + try {
  75 + const response = await importData({
  76 + file: this.file,
  77 + importAdapt: 'importOwnerCar'
  78 + })
  79 +
  80 + this.$message.success(this.$t('listOwnerCar.importSuccess'))
  81 + this.$emit('success')
  82 + this.visible = false
  83 + this.$router.push(`/pages/property/assetImportLogDetail?logId=${response.data.logId}&logType=importOwnerCar`)
  84 + } catch (error) {
  85 + console.error('导入失败:', error)
  86 + this.$message.error(error.message || this.$t('listOwnerCar.importError'))
  87 + } finally {
  88 + this.loading = false
  89 + }
  90 + },
  91 +
  92 + checkFileType(fileName) {
  93 + const ext = fileName.split('.').pop().toLowerCase()
  94 + return ['xlsx', 'xls'].includes(ext)
  95 + },
  96 +
  97 + checkFileSize(size) {
  98 + const maxSize = 2 * 1024 * 1024 // 2MB
  99 + return size <= maxSize
  100 + },
  101 +
  102 + resetForm() {
  103 + this.file = null
  104 + this.fileName = ''
  105 + }
  106 + }
  107 +}
  108 +</script>
0 109 \ No newline at end of file
... ...
src/i18n/commonLang.js
... ... @@ -43,6 +43,7 @@ export const messages = {
43 43 remark: 'Remark',
44 44 hour:'hour',
45 45 more:'More',
  46 + tip:'Tip',
46 47 }
47 48 },
48 49 zh: {
... ... @@ -89,6 +90,8 @@ export const messages = {
89 90 remark: '备注',
90 91 hour:'时',
91 92 more:'更多',
  93 + tip:'提示',
  94 +
92 95 }
93 96 }
94 97 }
95 98 \ No newline at end of file
... ...
src/i18n/index.js
... ... @@ -146,6 +146,11 @@ import { messages as invoiceApplyDetailMessages } from &#39;../views/fee/invoiceAppl
146 146 import { messages as feeConfigDetailMessages } from '../views/fee/feeConfigDetailLang'
147 147 import { messages as parkingAreaManageMessages } from '../views/car/parkingAreaManageLang'
148 148 import { messages as listParkingSpaceMessages } from '../views/car/listParkingSpaceLang'
  149 +import { messages as parkingBoxManageMessages } from '../views/car/parkingBoxManageLang'
  150 +import { messages as listOwnerCarMessages } from '../views/car/listOwnerCarLang'
  151 +import { messages as hireParkingSpaceMessages } from '../views/car/hireParkingSpaceLang'
  152 +import { messages as carAddParkingSpaceMessages } from '../views/car/carAddParkingSpaceLang'
  153 +import { messages as buyCarMonthCardMessages } from '../views/fee/buyCarMonthCardLang'
149 154  
150 155 Vue.use(VueI18n)
151 156  
... ... @@ -296,6 +301,11 @@ const messages = {
296 301 ...feeConfigDetailMessages.en,
297 302 ...parkingAreaManageMessages.en,
298 303 ...listParkingSpaceMessages.en,
  304 + ...parkingBoxManageMessages.en,
  305 + ...listOwnerCarMessages.en,
  306 + ...hireParkingSpaceMessages.en,
  307 + ...carAddParkingSpaceMessages.en,
  308 + ...buyCarMonthCardMessages.en,
299 309 },
300 310 zh: {
301 311 ...loginMessages.zh,
... ... @@ -442,6 +452,11 @@ const messages = {
442 452 ...feeConfigDetailMessages.zh,
443 453 ...parkingAreaManageMessages.zh,
444 454 ...listParkingSpaceMessages.zh,
  455 + ...parkingBoxManageMessages.zh,
  456 + ...listOwnerCarMessages.zh,
  457 + ...hireParkingSpaceMessages.zh,
  458 + ...carAddParkingSpaceMessages.zh,
  459 + ...buyCarMonthCardMessages.zh,
445 460 }
446 461 }
447 462  
... ...
src/router/index.js
... ... @@ -717,15 +717,40 @@ const routes = [
717 717 component: () => import('@/views/fee/feeConfigDetailList.vue')
718 718 },
719 719 {
720   - path:'/pages/property/parkingAreaManage',
721   - name:'/pages/property/parkingAreaManage',
  720 + path: '/pages/property/parkingAreaManage',
  721 + name: '/pages/property/parkingAreaManage',
722 722 component: () => import('@/views/car/parkingAreaManageList.vue')
723   - },
724   - {
725   - path:'/pages/property/listParkingSpace',
726   - name:'/pages/property/listParkingSpace',
727   - component: () => import('@/views/car/listParkingSpaceList.vue')
728   - },
  723 + },
  724 + {
  725 + path: '/pages/property/listParkingSpace',
  726 + name: '/pages/property/listParkingSpace',
  727 + component: () => import('@/views/car/listParkingSpaceList.vue')
  728 + },
  729 + {
  730 + path: '/pages/property/parkingBoxManage',
  731 + name: '/pages/property/parkingBoxManage',
  732 + component: () => import('@/views/car/parkingBoxManageList.vue')
  733 + },
  734 + {
  735 + path: '/pages/property/listOwnerCar',
  736 + name: '/pages/property/listOwnerCar',
  737 + component: () => import('@/views/car/listOwnerCarList.vue')
  738 + },
  739 + {
  740 + path: '/views/car/hireParkingSpace',
  741 + name: '/views/car/hireParkingSpace',
  742 + component: () => import('@/views/car/hireParkingSpaceList.vue')
  743 + },
  744 + {
  745 + path: '/views/car/carAddParkingSpace',
  746 + name: '/views/car/carAddParkingSpace',
  747 + component: () => import('@/views/car/carAddParkingSpaceList.vue')
  748 + },
  749 + {
  750 + path: '/views/fee/buyCarMonthCard',
  751 + name: '/views/fee/buyCarMonthCard',
  752 + component: () => import('@/views/fee/buyCarMonthCardList.vue')
  753 + },
729 754 // 其他子路由可以在这里添加
730 755 ]
731 756 },
... ...
src/views/car/carAddParkingSpaceLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + carAddParkingSpace: {
  4 + vehicleInfo: 'Vehicle Information',
  5 + licensePlate: 'License Plate',
  6 + requiredLicensePlate: 'Required, please fill in the license plate',
  7 + startTime: 'Start Time',
  8 + requiredStartTime: 'Required, please fill in the start time',
  9 + endTime: 'End Time',
  10 + requiredEndTime: 'Required, please fill in the end time',
  11 + remark: 'Remark',
  12 + remarkPlaceholder: 'Please fill in remarks',
  13 + submit: 'Submit',
  14 + parkingSpaceInfo: 'Parking Space Information',
  15 + parkingSpaceId: 'Parking Space ID',
  16 + number: 'Number',
  17 + parkingLot: 'Parking Lot',
  18 + parkingSpaceStatus: 'Parking Space Status',
  19 + area: 'Area',
  20 + squareMeters: 'square meters',
  21 + remarks: 'Remarks',
  22 + selectParkingSpace: 'Select Parking Space',
  23 + search: 'Search',
  24 + reset: 'Reset',
  25 + parkingLotCode: 'Parking Lot Code',
  26 + parkingSpaceCode: 'Parking Space Code',
  27 + parkingLotType: 'Parking Lot Type',
  28 + parkingSpaceType: 'Parking Space Type',
  29 + operation: 'Operation',
  30 + select: 'Select',
  31 + aboveGround: 'Above Ground',
  32 + underground: 'Underground',
  33 + free: 'Free',
  34 + sold: 'Sold',
  35 + rented: 'Rented',
  36 + unknown: 'Unknown'
  37 + }
  38 + },
  39 + zh: {
  40 + carAddParkingSpace: {
  41 + vehicleInfo: '车辆信息',
  42 + licensePlate: '车牌号',
  43 + requiredLicensePlate: '必填,请填写车牌号',
  44 + startTime: '起租时间',
  45 + requiredStartTime: '必填,请填写起租时间',
  46 + endTime: '结租时间',
  47 + requiredEndTime: '必填,请填写结租时间',
  48 + remark: '备注',
  49 + remarkPlaceholder: '请填写备注信息',
  50 + submit: '提交',
  51 + parkingSpaceInfo: '停车位信息',
  52 + parkingSpaceId: '车位ID',
  53 + number: '编号',
  54 + parkingLot: '停车场',
  55 + parkingSpaceStatus: '车位状态',
  56 + area: '面积',
  57 + squareMeters: '平方米',
  58 + remarks: '备注',
  59 + selectParkingSpace: '选择车位',
  60 + search: '查询',
  61 + reset: '重置',
  62 + parkingLotCode: '停车场编码',
  63 + parkingSpaceCode: '停车位编码',
  64 + parkingLotType: '停车场类型',
  65 + parkingSpaceType: '车位类型',
  66 + operation: '操作',
  67 + select: '选择',
  68 + aboveGround: '地上停车位',
  69 + underground: '地下停车位',
  70 + free: '空闲',
  71 + sold: '已售卖',
  72 + rented: '已出租',
  73 + unknown: '未知'
  74 + }
  75 + }
  76 +}
0 77 \ No newline at end of file
... ...
src/views/car/carAddParkingSpaceList.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="24">
  5 + <el-card>
  6 + <div slot="header">
  7 + <span>{{ $t('carAddParkingSpace.vehicleInfo') }}</span>
  8 + </div>
  9 + <div>
  10 + <el-form label-width="120px">
  11 + <el-row :gutter="20">
  12 + <el-col :span="12">
  13 + <el-form-item :label="$t('hireParkingSpace.parkingSpace')" prop="psName" required>
  14 + <el-input v-model="carAddParkingSpaceInfo.psName" disabled style="width: 80%;"
  15 + :placeholder="$t('hireParkingSpace.parkingSpacePlaceholder')" />
  16 + <el-button type="primary" size="small" class="margin-left" @click="openSearchParkingSpaceModel">
  17 + {{ $t('common.select') }}
  18 + </el-button>
  19 + </el-form-item>
  20 + </el-col>
  21 + <el-col :span="12">
  22 + <el-form-item :label="$t('carAddParkingSpace.licensePlate')">
  23 + <el-input v-model.trim="carAddParkingSpaceInfo.carNum"
  24 + :placeholder="$t('carAddParkingSpace.requiredLicensePlate')" disabled />
  25 + </el-form-item>
  26 + </el-col>
  27 + </el-row>
  28 +
  29 + <el-row :gutter="20">
  30 + <el-col :span="12">
  31 + <el-form-item :label="$t('carAddParkingSpace.startTime')">
  32 + <el-date-picker v-model="carAddParkingSpaceInfo.startTime" style="width: 100%;" type="date"
  33 + :placeholder="$t('carAddParkingSpace.requiredStartTime')" value-format="yyyy-MM-dd" />
  34 + </el-form-item>
  35 + </el-col>
  36 + <el-col :span="12">
  37 + <el-form-item :label="$t('carAddParkingSpace.endTime')">
  38 + <el-date-picker v-model="carAddParkingSpaceInfo.endTime" style="width: 100%;" type="date"
  39 + :placeholder="$t('carAddParkingSpace.requiredEndTime')" value-format="yyyy-MM-dd" />
  40 + </el-form-item>
  41 + </el-col>
  42 + </el-row>
  43 +
  44 + <el-row :gutter="20">
  45 + <el-col :span="24">
  46 + <el-form-item :label="$t('carAddParkingSpace.remark')">
  47 + <el-input v-model.trim="carAddParkingSpaceInfo.remark" type="textarea"
  48 + :placeholder="$t('carAddParkingSpace.remarkPlaceholder')" rows="3" />
  49 + </el-form-item>
  50 + </el-col>
  51 + </el-row>
  52 + </el-form>
  53 + </div>
  54 + </el-card>
  55 + </el-col>
  56 + </el-row>
  57 +
  58 + <el-row :gutter="20">
  59 + <el-col :span="24" style="text-align: right; margin-top: 20px;">
  60 + <el-button type="primary" @click="submitForm">
  61 + {{ $t('carAddParkingSpace.submit') }}
  62 + </el-button>
  63 + </el-col>
  64 + </el-row>
  65 + <search-parking-space ref="searchParkingSpace" @choose-parking-space="handleChooseParkingSpace" />
  66 + </div>
  67 +</template>
  68 +
  69 +<script>
  70 +import { getCarInfo, addCarParkingSpace } from '@/api/car/carAddParkingSpaceApi'
  71 +import SearchParkingSpace from '@/components/car/SearchParkingSpace'
  72 +
  73 +export default {
  74 + name: 'CarAddParkingSpaceList',
  75 + components: {
  76 + SearchParkingSpace
  77 + },
  78 + data() {
  79 + return {
  80 + callBackComponent: 'carAddParkingSpace',
  81 + callBackFunction: 'notify',
  82 + carAddParkingSpaceInfo: {
  83 + carNum: '',
  84 + remark: '',
  85 + startTime: '',
  86 + endTime: '',
  87 + carId: '',
  88 + psId: '',
  89 + psName: '',
  90 + memberId: ''
  91 + }
  92 + }
  93 + },
  94 + created() {
  95 + this.carAddParkingSpaceInfo.carId = this.$route.query.carId
  96 + this.loadCarInfo()
  97 + },
  98 + methods: {
  99 + async loadCarInfo() {
  100 + try {
  101 + const params = {
  102 + page: 1,
  103 + row: 1,
  104 + carId: this.carAddParkingSpaceInfo.carId
  105 + }
  106 +
  107 + const res = await getCarInfo(params)
  108 + if (res.code === 0 && res.data.length > 0) {
  109 + const carData = res.data[0]
  110 + this.carAddParkingSpaceInfo = {
  111 + ...this.carAddParkingSpaceInfo,
  112 + carNum: carData.carNum || '',
  113 + psId: carData.psId || ''
  114 + }
  115 + this.$refs.viewSelectParkingSpace.handleShowParkingSpace(carData)
  116 + }
  117 + } catch (error) {
  118 + console.error('Failed to load car info:', error)
  119 + }
  120 + },
  121 + handleParkingSpaceSelected(info) {
  122 + this.carAddParkingSpaceInfo.psId = info.psId
  123 + },
  124 + validateForm() {
  125 + if (!this.carAddParkingSpaceInfo.carNum) {
  126 + this.$message.error(this.$t('carAddParkingSpace.requiredLicensePlate'))
  127 + return false
  128 + }
  129 + if (!this.carAddParkingSpaceInfo.startTime) {
  130 + this.$message.error(this.$t('carAddParkingSpace.requiredStartTime'))
  131 + return false
  132 + }
  133 + if (!this.carAddParkingSpaceInfo.endTime) {
  134 + this.$message.error(this.$t('carAddParkingSpace.requiredEndTime'))
  135 + return false
  136 + }
  137 + if (!this.carAddParkingSpaceInfo.psId) {
  138 + this.$message.error(this.$t('carAddParkingSpace.parkingSpaceRequired'))
  139 + return false
  140 + }
  141 +
  142 + const start = new Date(this.carAddParkingSpaceInfo.startTime)
  143 + const end = new Date(this.carAddParkingSpaceInfo.endTime)
  144 + if (start >= end) {
  145 + this.$message.error(this.$t('carAddParkingSpace.endTimeAfterStart'))
  146 + return false
  147 + }
  148 +
  149 + return true
  150 + },
  151 + async submitForm() {
  152 + if (!this.validateForm()) return
  153 +
  154 + try {
  155 + const data = {
  156 + ...this.carAddParkingSpaceInfo,
  157 + memberId: this.carId
  158 + }
  159 +
  160 + const res = await addCarParkingSpace(data)
  161 + if (res.code === 0) {
  162 + this.$message.success(this.$t('carAddParkingSpace.submitSuccess'))
  163 + this.$router.go(-1)
  164 + } else {
  165 + this.$message.error(res.msg || this.$t('carAddParkingSpace.submitFailed'))
  166 + }
  167 + } catch (error) {
  168 + console.error('Submit failed:', error)
  169 + this.$message.error(this.$t('carAddParkingSpace.submitFailed'))
  170 + }
  171 + },
  172 + openSearchParkingSpaceModel() {
  173 + this.$refs.searchParkingSpace.open()
  174 + },
  175 + handleChooseParkingSpace(parkingSpace) {
  176 + this.carAddParkingSpaceInfo.psName = `${parkingSpace.areaNum}-${parkingSpace.num}`
  177 + this.carAddParkingSpaceInfo.psId = parkingSpace.psId
  178 + }
  179 + }
  180 +}
  181 +</script>
  182 +
  183 +<style scoped>
  184 +.app-container {
  185 + padding: 20px;
  186 +}
  187 +
  188 +.el-row {
  189 + margin-bottom: 20px;
  190 +}
  191 +
  192 +.margin-left {
  193 + margin-left: 10px;
  194 +}
  195 +</style>
0 196 \ No newline at end of file
... ...
src/views/car/hireParkingSpaceLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + hireParkingSpace: {
  4 + title: 'Add Vehicle',
  5 + carNum: 'License Plate',
  6 + carNumPlaceholder: 'Required, please enter license plate',
  7 + carBrand: 'Car Brand',
  8 + carBrandPlaceholder: 'Optional, please enter car brand',
  9 + carType: 'Vehicle Type',
  10 + carTypePlaceholder: 'Required, please select vehicle type',
  11 + carColor: 'Color',
  12 + carColorPlaceholder: 'Optional, please enter color',
  13 + licenseType: 'License Type',
  14 + licenseTypePlaceholder: 'Required, please select license type',
  15 + monthlyRent: 'Monthly Rent',
  16 + saleCar: 'For Sale',
  17 + internalCar: 'Internal Vehicle',
  18 + freeCar: 'Free Vehicle',
  19 + startTime: 'Start Time',
  20 + startTimePlaceholder: 'Required, please select start time',
  21 + endTime: 'End Time',
  22 + endTimePlaceholder: 'Required, please select end time',
  23 + owner: 'Owner',
  24 + ownerPlaceholder: 'Required, please select owner',
  25 + parkingSpace: 'Parking Space',
  26 + parkingSpacePlaceholder: 'Required, please select parking space',
  27 + remark: 'Remark',
  28 + remarkPlaceholder: 'Optional, please enter remark',
  29 + validate: {
  30 + carNum: 'License plate is required',
  31 + carType: 'Vehicle type is required',
  32 + licenseType: 'License type is required',
  33 + owner: 'Owner is required',
  34 + parkingSpace: 'Parking space is required',
  35 + dateRange: 'Date range is required'
  36 + },
  37 + saveSuccess: 'Vehicle information saved successfully!',
  38 + saveError: 'Failed to save vehicle information'
  39 + },
  40 + searchOwner: {
  41 + title: 'Select Owner',
  42 + roomPlaceholder: 'Enter room number (Building-Unit-Room)',
  43 + namePlaceholder: 'Enter owner name',
  44 + memberId: 'Owner ID',
  45 + name: 'Name',
  46 + personType: 'Person Type',
  47 + personRole: 'Person Role',
  48 + idCard: 'ID Card',
  49 + contact: 'Contact',
  50 + loadError: 'Failed to load owner information'
  51 + },
  52 + searchParkingSpace: {
  53 + title: 'Select Parking Space',
  54 + parkingAreaPlaceholder: 'Select parking area',
  55 + numPlaceholder: 'Enter parking space number',
  56 + areaNum: 'Parking Area',
  57 + num: 'Parking Space',
  58 + type: 'Type',
  59 + ground: 'Ground',
  60 + underground: 'Underground',
  61 + parkingType: 'Parking Type',
  62 + state: 'State',
  63 + area: 'Area',
  64 + loadError: 'Failed to load parking space information'
  65 + }
  66 + },
  67 + zh: {
  68 + hireParkingSpace: {
  69 + title: '添加车辆',
  70 + carNum: '车牌号',
  71 + carNumPlaceholder: '必填,请填写车牌号',
  72 + carBrand: '汽车品牌',
  73 + carBrandPlaceholder: '选填,请填写汽车品牌',
  74 + carType: '车类型',
  75 + carTypePlaceholder: '必填,请选择车辆类型',
  76 + carColor: '颜色',
  77 + carColorPlaceholder: '选填,请填写颜色',
  78 + licenseType: '车牌类型',
  79 + licenseTypePlaceholder: '必填,请选择车牌类型',
  80 + monthlyRent: '月租车',
  81 + saleCar: '出售车辆',
  82 + internalCar: '内部车',
  83 + freeCar: '免费车',
  84 + startTime: '开始时间',
  85 + startTimePlaceholder: '必填,请填写开始时间',
  86 + endTime: '结束时间',
  87 + endTimePlaceholder: '必填,请填写结束时间',
  88 + owner: '业主',
  89 + ownerPlaceholder: '必填,请选择业主',
  90 + parkingSpace: '车位',
  91 + parkingSpacePlaceholder: '必填,请选择车位',
  92 + remark: '备注',
  93 + remarkPlaceholder: '选填,请填写备注',
  94 + validate: {
  95 + carNum: '车牌号不能为空',
  96 + carType: '车类型不能为空',
  97 + licenseType: '车牌类型不能为空',
  98 + owner: '业主不能为空',
  99 + parkingSpace: '车位不能为空',
  100 + dateRange: '请选择日期范围'
  101 + },
  102 + saveSuccess: '车辆信息保存成功!',
  103 + saveError: '车辆信息保存失败'
  104 + },
  105 + searchOwner: {
  106 + title: '选择业主',
  107 + roomPlaceholder: '输入房屋编号楼栋-单元-房屋',
  108 + namePlaceholder: '输入业主名称',
  109 + memberId: '业主编号',
  110 + name: '名称',
  111 + personType: '人员类型',
  112 + personRole: '人员角色',
  113 + idCard: '证件号',
  114 + contact: '联系方式',
  115 + loadError: '加载业主信息失败'
  116 + },
  117 + searchParkingSpace: {
  118 + title: '选择停车位',
  119 + parkingAreaPlaceholder: '请选择停车场',
  120 + numPlaceholder: '请输入停车位编号',
  121 + areaNum: '停车场编码',
  122 + num: '停车位编码',
  123 + type: '停车场类型',
  124 + ground: '地上停车位',
  125 + underground: '地下停车位',
  126 + parkingType: '车位类型',
  127 + state: '车位状态',
  128 + area: '面积',
  129 + loadError: '加载停车位信息失败'
  130 + }
  131 + }
  132 +}
0 133 \ No newline at end of file
... ...
src/views/car/hireParkingSpaceList.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-card class="box-card margin">
  4 + <div slot="header" class="flex justify-between">
  5 + <span>{{ $t('hireParkingSpace.title') }}</span>
  6 + <div class="">
  7 + <el-button type="primary" size="small" icon="el-icon-close" @click="goBack">
  8 + {{ $t('common.back') }}
  9 + </el-button>
  10 + </div>
  11 + </div>
  12 +
  13 + <el-form ref="form" :model="hireParkingSpaceInfo" label-width="120px" label-position="right">
  14 + <el-row :gutter="20">
  15 + <el-col :span="12">
  16 + <el-form-item :label="$t('hireParkingSpace.carNum')" prop="carNum" required>
  17 + <el-input v-model="hireParkingSpaceInfo.carNum" :placeholder="$t('hireParkingSpace.carNumPlaceholder')" />
  18 + </el-form-item>
  19 + </el-col>
  20 + <el-col :span="12">
  21 + <el-form-item :label="$t('hireParkingSpace.carBrand')">
  22 + <el-input v-model="hireParkingSpaceInfo.carBrand"
  23 + :placeholder="$t('hireParkingSpace.carBrandPlaceholder')" />
  24 + </el-form-item>
  25 + </el-col>
  26 + </el-row>
  27 +
  28 + <el-row :gutter="20">
  29 + <el-col :span="12">
  30 + <el-form-item :label="$t('hireParkingSpace.carType')" prop="carType" required>
  31 + <el-select v-model="hireParkingSpaceInfo.carType" class="w-full"
  32 + :placeholder="$t('hireParkingSpace.carTypePlaceholder')">
  33 + <el-option v-for="carType in hireParkingSpaceInfo.carTypes" :key="carType.statusCd" :label="carType.name"
  34 + :value="carType.statusCd" />
  35 + </el-select>
  36 + </el-form-item>
  37 + </el-col>
  38 + <el-col :span="12">
  39 + <el-form-item :label="$t('hireParkingSpace.carColor')">
  40 + <el-input v-model="hireParkingSpaceInfo.carColor"
  41 + :placeholder="$t('hireParkingSpace.carColorPlaceholder')" />
  42 + </el-form-item>
  43 + </el-col>
  44 + </el-row>
  45 +
  46 + <el-row :gutter="20">
  47 + <el-col :span="12">
  48 + <el-form-item :label="$t('hireParkingSpace.licenseType')" prop="leaseType" required>
  49 + <el-select v-model="hireParkingSpaceInfo.leaseType" class="w-full"
  50 + :placeholder="$t('hireParkingSpace.licenseTypePlaceholder')" @change="changeLeaseType">
  51 + <el-option :label="$t('hireParkingSpace.monthlyRent')" value="H" />
  52 + <el-option :label="$t('hireParkingSpace.saleCar')" value="S" />
  53 + <el-option :label="$t('hireParkingSpace.internalCar')" value="I" />
  54 + <el-option :label="$t('hireParkingSpace.freeCar')" value="NM" />
  55 + </el-select>
  56 + </el-form-item>
  57 + </el-col>
  58 + </el-row>
  59 +
  60 + <el-row v-if="showDateRange" :gutter="20">
  61 + <el-col :span="12">
  62 + <el-form-item :label="$t('hireParkingSpace.startTime')" prop="startTime" required>
  63 + <el-date-picker v-model="hireParkingSpaceInfo.startTime" type="date" value-format="yyyy-MM-dd" class="w-full"
  64 + :placeholder="$t('hireParkingSpace.startTimePlaceholder')" />
  65 + </el-form-item>
  66 + </el-col>
  67 + <el-col :span="12">
  68 + <el-form-item :label="$t('hireParkingSpace.endTime')" prop="endTime" required>
  69 + <el-date-picker v-model="hireParkingSpaceInfo.endTime" type="date" value-format="yyyy-MM-dd" class="w-full"
  70 + :placeholder="$t('hireParkingSpace.endTimePlaceholder')" />
  71 + </el-form-item>
  72 + </el-col>
  73 + </el-row>
  74 +
  75 + <el-row :gutter="20">
  76 + <el-col :span="8">
  77 + <el-form-item :label="$t('hireParkingSpace.owner')" prop="ownerName" required>
  78 + <el-input v-model="hireParkingSpaceInfo.ownerName" disabled
  79 + :placeholder="$t('hireParkingSpace.ownerPlaceholder')" />
  80 + </el-form-item>
  81 + </el-col>
  82 + <el-col :span="4">
  83 + <div class="">
  84 + <el-button type="primary" size="small" @click="openChooseOwner">
  85 + {{ $t('common.select') }}
  86 + </el-button>
  87 + </div>
  88 + </el-col>
  89 + <el-col :span="8">
  90 + <el-form-item :label="$t('hireParkingSpace.parkingSpace')" prop="psName" required>
  91 + <el-input v-model="hireParkingSpaceInfo.psName" disabled
  92 + :placeholder="$t('hireParkingSpace.parkingSpacePlaceholder')" />
  93 + </el-form-item>
  94 + </el-col>
  95 + <el-col :span="4">
  96 + <el-button type="primary" size="small" @click="openSearchParkingSpaceModel">
  97 + {{ $t('common.select') }}
  98 + </el-button>
  99 + </el-col>
  100 + </el-row>
  101 +
  102 + <el-row v-for="(item, index) in hireParkingSpaceInfo.attrs" :key="index">
  103 + <el-col :span="24">
  104 + <el-form-item :label="item.specName">
  105 + <el-input v-if="item.specType === '2233'" v-model="item.value" :placeholder="item.specHoldplace" />
  106 + <el-select v-else-if="item.specType === '3344'" v-model="item.value" class="w-full"
  107 + :placeholder="item.specHoldplace">
  108 + <el-option v-for="(val, idx) in item.values" :key="idx" :label="val.valueName" :value="val.value" />
  109 + </el-select>
  110 + </el-form-item>
  111 + </el-col>
  112 + </el-row>
  113 +
  114 + <el-row>
  115 + <el-col :span="24">
  116 + <el-form-item :label="$t('hireParkingSpace.remark')">
  117 + <el-input v-model="hireParkingSpaceInfo.remark" type="textarea" :rows="3"
  118 + :placeholder="$t('hireParkingSpace.remarkPlaceholder')" />
  119 + </el-form-item>
  120 + </el-col>
  121 + </el-row>
  122 + </el-form>
  123 + </el-card>
  124 +
  125 + <div class="mt-20 text-right">
  126 + <el-button @click="goBack">{{ $t('common.back') }}</el-button>
  127 + <el-button type="primary" @click="saveAddCarInfo">
  128 + {{ $t('common.submit') }}
  129 + </el-button>
  130 + </div>
  131 +
  132 + <search-owner ref="searchOwner" @chooseOwner="handleChooseOwner" />
  133 + <search-parking-space ref="searchParkingSpace" @choose-parking-space="handleChooseParkingSpace" />
  134 + </div>
  135 +</template>
  136 +
  137 +<script>
  138 +import { saveOwnerCar } from '@/api/car/hireParkingSpaceApi'
  139 +import { getCommunityId, getDict } from '@/api/community/communityApi'
  140 +import SearchOwner from '@/components/owner/SearchOwner'
  141 +import SearchParkingSpace from '@/components/car/SearchParkingSpace'
  142 +import { getSpecAttrList } from '@/api/dev/attrSpecApi'
  143 +
  144 +export default {
  145 + name: 'HireParkingSpaceList',
  146 + components: {
  147 + SearchOwner,
  148 + SearchParkingSpace
  149 + },
  150 + data() {
  151 + return {
  152 + hireParkingSpaceInfo: {
  153 + carNum: '',
  154 + carBrand: '',
  155 + carType: '',
  156 + carColor: '',
  157 + remark: '',
  158 + startTime: '',
  159 + endTime: '',
  160 + leaseType: 'H',
  161 + ownerId: '',
  162 + ownerName: '',
  163 + psId: '',
  164 + psName: '',
  165 + attrs: [],
  166 + carTypes: []
  167 + }
  168 + }
  169 + },
  170 + computed: {
  171 + showDateRange() {
  172 + return ['H', 'S'].includes(this.hireParkingSpaceInfo.leaseType)
  173 + }
  174 + },
  175 + async created() {
  176 + await this.initData()
  177 + },
  178 + methods: {
  179 + async initData() {
  180 + this.communityId = getCommunityId()
  181 +
  182 + // 加载车辆类型字典
  183 + try {
  184 + const carTypes = await getDict('owner_car', 'car_type')
  185 + this.hireParkingSpaceInfo.carTypes = carTypes
  186 + } catch (error) {
  187 + console.error('加载车辆类型失败:', error)
  188 + }
  189 +
  190 + // 加载车辆属性规格
  191 + try {
  192 + await this.loadCarAttrSpec()
  193 + } catch (error) {
  194 + console.error('加载车辆属性失败:', error)
  195 + }
  196 + },
  197 +
  198 + async loadCarAttrSpec() {
  199 + try {
  200 + const attrs = await getSpecAttrList('owner_car_attr')
  201 + this.hireParkingSpaceInfo.attrs = attrs.map(item => {
  202 + if (item.specShow === 'Y') {
  203 + return {
  204 + ...item,
  205 + value: '',
  206 + values: item.specType === '3344' ? [] : undefined
  207 + }
  208 + }
  209 + return null
  210 + }).filter(Boolean)
  211 + } catch (error) {
  212 + console.error('加载车辆属性规格失败:', error)
  213 + }
  214 + },
  215 +
  216 + changeLeaseType() {
  217 + this.hireParkingSpaceInfo.startTime = ''
  218 + this.hireParkingSpaceInfo.endTime = ''
  219 + },
  220 +
  221 + openChooseOwner() {
  222 + this.$refs.searchOwner.open()
  223 + },
  224 +
  225 + handleChooseOwner(owner) {
  226 + this.hireParkingSpaceInfo.ownerName = owner.name
  227 + this.hireParkingSpaceInfo.ownerId = owner.memberId
  228 + },
  229 +
  230 + openSearchParkingSpaceModel() {
  231 + this.$refs.searchParkingSpace.open()
  232 + },
  233 +
  234 + handleChooseParkingSpace(parkingSpace) {
  235 + this.hireParkingSpaceInfo.psName = `${parkingSpace.areaNum}-${parkingSpace.num}`
  236 + this.hireParkingSpaceInfo.psId = parkingSpace.psId
  237 + },
  238 +
  239 + validateForm() {
  240 + const requiredFields = [
  241 + { field: 'carNum', message: this.$t('hireParkingSpace.validate.carNum') },
  242 + { field: 'carType', message: this.$t('hireParkingSpace.validate.carType') },
  243 + { field: 'leaseType', message: this.$t('hireParkingSpace.validate.licenseType') },
  244 + { field: 'ownerId', message: this.$t('hireParkingSpace.validate.owner') },
  245 + { field: 'psId', message: this.$t('hireParkingSpace.validate.parkingSpace') }
  246 + ]
  247 +
  248 + for (const { field, message } of requiredFields) {
  249 + if (!this.hireParkingSpaceInfo[field]) {
  250 + this.$message.error(message)
  251 + return false
  252 + }
  253 + }
  254 +
  255 + // 验证日期范围
  256 + if (this.showDateRange) {
  257 + if (!this.hireParkingSpaceInfo.startTime || !this.hireParkingSpaceInfo.endTime) {
  258 + this.$message.error(this.$t('hireParkingSpace.validate.dateRange'))
  259 + return false
  260 + }
  261 + }
  262 +
  263 + // 验证自定义属性
  264 + for (const attr of this.hireParkingSpaceInfo.attrs) {
  265 + if (attr.required === 'Y' && !attr.value) {
  266 + this.$message.error(attr.specHoldplace)
  267 + return false
  268 + }
  269 + }
  270 +
  271 + return true
  272 + },
  273 +
  274 + async saveAddCarInfo() {
  275 + if (!this.validateForm()) return
  276 +
  277 + try {
  278 + const params = {
  279 + ...this.hireParkingSpaceInfo,
  280 + communityId: this.communityId
  281 + }
  282 +
  283 + await saveOwnerCar(params)
  284 + this.$message.success(this.$t('hireParkingSpace.saveSuccess'))
  285 + this.goBack()
  286 + } catch (error) {
  287 + console.error('保存失败:', error)
  288 + this.$message.error(this.$t('hireParkingSpace.saveError'))
  289 + }
  290 + },
  291 +
  292 + goBack() {
  293 + this.$router.go(-1)
  294 + }
  295 + }
  296 +}
  297 +</script>
  298 +
  299 +<style scoped>
  300 +.card-header-actions {
  301 + position: absolute;
  302 + right: 20px;
  303 + top: 15px;
  304 +}
  305 +
  306 +.w-full {
  307 + width: 100%;
  308 +}
  309 +
  310 +.mt-10 {
  311 + margin-top: 10px;
  312 +}
  313 +
  314 +.mt-20 {
  315 + margin-top: 20px;
  316 +}
  317 +</style>
0 318 \ No newline at end of file
... ...
src/views/car/listOwnerCarLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + listOwnerCar: {
  4 + searchCondition: 'Search Condition',
  5 + inputCarNum: 'Please enter the license plate number',
  6 + inputParkingSpaceNum: 'Please fill in the parking space number',
  7 + selectParkingSpaceStatus: 'Required, please select the parking space status',
  8 + inputOwnerName: 'Please fill in the owner name',
  9 + inputContact: 'Please enter contact information',
  10 + inputMemberCarNum: 'Please enter member license plate number',
  11 + query: 'Query',
  12 + reset: 'Reset',
  13 + carInfo: 'Car Information',
  14 + importCar: 'Import Cars',
  15 + export: 'Export',
  16 + add: 'Add',
  17 + carNum: 'License Plate Number',
  18 + memberCar: 'Member Car',
  19 + roomNum: 'Room Number',
  20 + carPlateType: 'License Plate Type',
  21 + carType: 'Car Type',
  22 + color: 'Color',
  23 + owner: 'Owner',
  24 + parkingSpace: 'Parking Space',
  25 + validity: 'Validity',
  26 + status: 'Status',
  27 + remark: 'Remark',
  28 + operation: 'Operation',
  29 + temporaryCar: 'Temporary Car',
  30 + parkingSpaceReleased: 'Parking space released',
  31 + normal: 'Normal',
  32 + expired: 'Expired',
  33 + noParkingSpace: 'No Parking Space',
  34 + release: 'Release',
  35 + renew: 'Renew',
  36 + buyMonthlyCard: 'Buy Monthly Card',
  37 + modify: 'Modify',
  38 + delete: 'Delete',
  39 + confirmDelete: 'Are you sure to delete?',
  40 + cancel: 'Cancel',
  41 + confirm: 'Confirm',
  42 + importCarTitle: 'Car Import',
  43 + selectFile: 'Select File',
  44 + downloadTemplate: 'Download Template',
  45 + importTemplate: 'Import Template',
  46 + prepareData: 'Prepare data and upload',
  47 + import: 'Import',
  48 + editCar: 'Edit Car',
  49 + carBrand: 'Car Brand',
  50 + inputCarBrand: 'Optional, please fill in the car brand, e.g. BMW X6',
  51 + inputCarNumRequired: 'Required, please fill in the license plate number',
  52 + selectType: 'Required, please select type',
  53 + inputCarColor: 'Optional, please fill in the car color, e.g. white',
  54 + licensePlateType: 'License Plate Type',
  55 + selectLicensePlateType: 'Required, please select license plate type',
  56 + monthlyRentCar: 'Monthly Rent Car',
  57 + soldCar: 'Sold Car',
  58 + internalCar: 'Internal Car',
  59 + freeCar: 'Free Car',
  60 + startRentTime: 'Start Rent Time',
  61 + endRentTime: 'End Rent Time',
  62 + inputStartRentTime: 'Required, please fill in the start rent time',
  63 + inputEndRentTime: 'Required, please fill in the end rent time',
  64 + inputRemark: 'Please fill in remarks',
  65 + save: 'Save',
  66 + deleteCarConfirm: 'Confirm whether to delete',
  67 + fileRequired: 'Required, please select data file',
  68 + invalidExcel: 'Invalid Excel format',
  69 + fileSizeExceed: 'Excel file size cannot exceed 2M',
  70 + importSuccess: 'Import successful',
  71 + importError: 'Import failed',
  72 + updateSuccess: 'Update successful',
  73 + updateError: 'Update failed',
  74 + deleteSuccess: 'Delete successful',
  75 + deleteError: 'Delete failed',
  76 + releaseSuccess: 'Release successful',
  77 + releaseError: 'Release failed',
  78 + exportSuccess: 'Export successful',
  79 + exportError: 'Export failed',
  80 + fetchError: 'Failed to get data',
  81 + allCars: 'All Cars',
  82 + expiredCars: 'Expired Cars',
  83 + downloadFirst: 'Please download first ',
  84 + selectTypeRequired: 'Please select car type',
  85 + selectLicensePlateTypeRequired: 'Please select license plate type',
  86 + rentTimeRequired: 'Please fill in rent time',
  87 + endTimeAfterStart: 'End time must be after start time',
  88 + iotSyncDetail: 'IoT sync details',
  89 + confirmRelease: 'Are you sure to release the car?',
  90 + }
  91 + },
  92 + zh: {
  93 + listOwnerCar: {
  94 + searchCondition: '查询条件',
  95 + inputCarNum: '请输入车牌号',
  96 + inputParkingSpaceNum: '请填写车位编号',
  97 + selectParkingSpaceStatus: '必填,请选择车位状态',
  98 + inputOwnerName: '请填写业主名称',
  99 + inputContact: '请输入联系方式',
  100 + inputMemberCarNum: '请输入成员车牌号',
  101 + query: '查询',
  102 + reset: '重置',
  103 + carInfo: '车辆信息',
  104 + importCar: '车辆导入',
  105 + export: '导出',
  106 + add: '添加',
  107 + carNum: '车牌号',
  108 + memberCar: '成员车辆',
  109 + roomNum: '房屋号',
  110 + carPlateType: '车牌类型',
  111 + carType: '车辆类型',
  112 + color: '颜色',
  113 + owner: '业主',
  114 + parkingSpace: '车位',
  115 + validity: '有效期',
  116 + status: '状态',
  117 + remark: '备注',
  118 + operation: '操作',
  119 + temporaryCar: '临时车',
  120 + parkingSpaceReleased: '车位已释放',
  121 + normal: '正常',
  122 + expired: '到期',
  123 + noParkingSpace: '无车位',
  124 + release: '释放',
  125 + renew: '续租',
  126 + buyMonthlyCard: '买月卡',
  127 + modify: '修改',
  128 + delete: '删除',
  129 + confirmDelete: '确认删除?',
  130 + cancel: '取消',
  131 + confirm: '确认',
  132 + importCarTitle: '车辆导入',
  133 + selectFile: '选择文件',
  134 + downloadTemplate: '下载模板',
  135 + importTemplate: '导入模板',
  136 + prepareData: '准备数据后,上传导入',
  137 + import: '导入',
  138 + editCar: '修改车辆',
  139 + carBrand: '车品牌',
  140 + inputCarBrand: '可选,请填写车品牌,如宝马X6',
  141 + inputCarNumRequired: '必填,请填写车牌号',
  142 + selectType: '必填,请选择类型',
  143 + inputCarColor: '可选,请填写车颜色,如白色',
  144 + licensePlateType: '车牌类型',
  145 + selectLicensePlateType: '必填,请选择车牌类型',
  146 + monthlyRentCar: '月租车',
  147 + soldCar: '出售车辆',
  148 + internalCar: '内部车',
  149 + freeCar: '免费车',
  150 + startRentTime: '起租时间',
  151 + endRentTime: '结租时间',
  152 + inputStartRentTime: '必填,请填写起租时间',
  153 + inputEndRentTime: '必填,请填写结租时间',
  154 + inputRemark: '请填写备注信息',
  155 + save: '保存',
  156 + deleteCarConfirm: '确认是否删除',
  157 + fileRequired: '必填,请选择数据文件',
  158 + invalidExcel: '不是有效的Excel格式',
  159 + fileSizeExceed: 'Excel文件大小不能超过2M',
  160 + importSuccess: '导入成功',
  161 + importError: '导入失败',
  162 + updateSuccess: '修改成功',
  163 + updateError: '修改失败',
  164 + deleteSuccess: '删除成功',
  165 + deleteError: '删除失败',
  166 + releaseSuccess: '释放成功',
  167 + releaseError: '释放失败',
  168 + exportSuccess: '导出成功',
  169 + exportError: '导出失败',
  170 + fetchError: '获取数据失败',
  171 + allCars: '全部车辆',
  172 + expiredCars: '到期车辆',
  173 + downloadFirst: '请先下载 ',
  174 + selectTypeRequired: '请选择车辆类型',
  175 + selectLicensePlateTypeRequired: '请选择车牌类型',
  176 + rentTimeRequired: '请填写起租时间和结租时间',
  177 + endTimeAfterStart: '结租时间必须大于起租时间',
  178 + iotSyncDetail: '同步物联网详情',
  179 + confirmRelease: '确认释放车辆?',
  180 + }
  181 + }
  182 +}
0 183 \ No newline at end of file
... ...
src/views/car/listOwnerCarList.vue 0 → 100644
  1 +<template>
  2 + <div class="list-owner-car-container">
  3 + <el-row :gutter="20">
  4 + <el-col :span="3">
  5 + <el-card class="tree-card">
  6 + <ul class="lease-type-list">
  7 + <li v-for="(item, index) in leaseTypes" :key="index" @click="switchLeaseType(item)"
  8 + :class="{ 'selected': conditions.leaseType === item.statusCd }">
  9 + {{ item.name }}
  10 + </li>
  11 + </ul>
  12 + </el-card>
  13 + </el-col>
  14 + <el-col :span="21">
  15 + <el-card class="search-card">
  16 + <div slot="header" class="flex justify-between">
  17 + <span>{{ $t('listOwnerCar.searchCondition') }}</span>
  18 + </div>
  19 + <el-row :gutter="20">
  20 + <el-col :span="6">
  21 + <el-input v-model.trim="conditions.carNumLike" :placeholder="$t('listOwnerCar.inputCarNum')" clearable />
  22 + </el-col>
  23 + <el-col :span="6">
  24 + <el-input v-model.trim="conditions.num" :placeholder="$t('listOwnerCar.inputParkingSpaceNum')" clearable />
  25 + </el-col>
  26 + <el-col :span="6">
  27 + <el-select v-model="conditions.valid" :placeholder="$t('listOwnerCar.selectParkingSpaceStatus')" clearable>
  28 + <el-option value="1" :label="$t('listOwnerCar.normal')"></el-option>
  29 + <el-option value="3" :label="$t('listOwnerCar.expired')"></el-option>
  30 + <el-option value="2" :label="$t('listOwnerCar.noParkingSpace')"></el-option>
  31 + </el-select>
  32 + </el-col>
  33 + <el-col :span="6">
  34 + <el-input v-model.trim="conditions.ownerName" :placeholder="$t('listOwnerCar.inputOwnerName')" clearable />
  35 + </el-col>
  36 + </el-row>
  37 + <el-row :gutter="20" class="mt-10">
  38 + <el-col :span="6">
  39 + <el-input v-model.trim="conditions.link" :placeholder="$t('listOwnerCar.inputContact')" clearable />
  40 + </el-col>
  41 + <el-col :span="6">
  42 + <el-input v-model.trim="conditions.memberCarNumLike" :placeholder="$t('listOwnerCar.inputMemberCarNum')"
  43 + clearable />
  44 + </el-col>
  45 + <el-col :span="12" class="text-right">
  46 + <el-button type="primary" @click="queryMethod" icon="el-icon-search">
  47 + {{ $t('listOwnerCar.query') }}
  48 + </el-button>
  49 + <el-button @click="resetMethod" icon="el-icon-refresh">
  50 + {{ $t('listOwnerCar.reset') }}
  51 + </el-button>
  52 + </el-col>
  53 + </el-row>
  54 + </el-card>
  55 +
  56 + <el-card class="mt-20">
  57 + <div slot="header" class="flex justify-between">
  58 + <span>{{ $t('listOwnerCar.carInfo') }}</span>
  59 + <div class="header-buttons">
  60 + <el-button v-if="hasPrivilege('502023032813991677')" type="primary" size="small" @click="addOwnerCar">
  61 + {{ $t('listOwnerCar.add') }}
  62 + </el-button>
  63 + <el-button size="small" @click="openOwnerCarImport">
  64 + <i class="el-icon-upload2"></i> {{ $t('listOwnerCar.importCar') }}
  65 + </el-button>
  66 + <el-button size="small" @click="exportExcel">
  67 + <i class="el-icon-download"></i> {{ $t('listOwnerCar.export') }}
  68 + </el-button>
  69 + </div>
  70 + </div>
  71 +
  72 + <el-table :data="ownerCars" border v-loading="loading">
  73 + <el-table-column prop="carNum" :label="$t('listOwnerCar.carNum')" align="center">
  74 + <template slot-scope="scope">
  75 + <router-link :to="`/pages/car/carDetail?memberId=${scope.row.memberId}`" target="_blank">
  76 + {{ scope.row.carNum }}
  77 + </router-link>
  78 + </template>
  79 + </el-table-column>
  80 + <el-table-column prop="memberCarCount" :label="$t('listOwnerCar.memberCar')" align="center">
  81 + <template slot-scope="scope">
  82 + <a href="javascript:void(0)" @click="toCarMember(scope.row)">
  83 + {{ scope.row.memberCarCount || 0 }}
  84 + </a>
  85 + </template>
  86 + </el-table-column>
  87 + <el-table-column prop="roomName" :label="$t('listOwnerCar.roomNum')" align="center" width="150" />
  88 + <el-table-column :label="$t('listOwnerCar.carPlateType')" align="center">
  89 + <template slot-scope="scope">
  90 + <span v-if="scope.row.leaseType === 'T'">
  91 + {{ $t('listOwnerCar.temporaryCar') }}
  92 + </span>
  93 + <span v-else>{{ scope.row.leaseTypeName }}</span>
  94 + </template>
  95 + </el-table-column>
  96 + <el-table-column prop="carTypeName" :label="$t('listOwnerCar.carType')" align="center" />
  97 + <el-table-column prop="carColor" :label="$t('listOwnerCar.color')" align="center" />
  98 + <el-table-column :label="$t('listOwnerCar.owner')" align="center">
  99 + <template slot-scope="scope">
  100 + <router-link :to="`/pages/owner/ownerDetail?ownerId=${scope.row.ownerId}`" target="_blank">
  101 + {{ scope.row.ownerName }}({{ scope.row.link }})
  102 + </router-link>
  103 + </template>
  104 + </el-table-column>
  105 + <el-table-column :label="$t('listOwnerCar.parkingSpace')" align="center">
  106 + <template slot-scope="scope">
  107 + <span v-if="scope.row.areaNum && scope.row.state === '1001'">
  108 + {{ scope.row.areaNum }}-{{ scope.row.num }}
  109 + </span>
  110 + <span v-else>{{ $t('listOwnerCar.parkingSpaceReleased') }}</span>
  111 + </template>
  112 + </el-table-column>
  113 + <el-table-column :label="$t('listOwnerCar.validity')" align="center">
  114 + <template slot-scope="scope">
  115 + <div v-if="scope.row.leaseType === 'H' || scope.row.leaseType === 'S'">
  116 + {{ scope.row.startTime }}<br>~{{ scope.row.endTime }}
  117 + </div>
  118 + <div v-else>--</div>
  119 + </template>
  120 + </el-table-column>
  121 + <el-table-column :label="$t('listOwnerCar.status')" align="center">
  122 + <template slot-scope="scope">
  123 + {{ getCarState(scope.row) }}
  124 + <span v-if="scope.row.iotStateName">
  125 + (<a href="javascript:void(0)" @click="viewIotStateRemark(scope.row)">{{ scope.row.iotStateName }}</a>)
  126 + </span>
  127 + </template>
  128 + </el-table-column>
  129 + <el-table-column prop="remark" :label="$t('listOwnerCar.remark')" align="center" />
  130 + <el-table-column :label="$t('listOwnerCar.operation')" align="center" width="280">
  131 + <template slot-scope="scope">
  132 + <el-button v-if="scope.row.state !== '3003' && hasPrivilege('502023032855861676')" size="mini"
  133 + @click="deleteCarParkingSpace(scope.row)">
  134 + {{ $t('listOwnerCar.release') }}
  135 + </el-button>
  136 + <el-button v-else-if="hasPrivilege('502023032809021678')" size="mini"
  137 + @click="addCarParkingSpace(scope.row)">
  138 + {{ $t('listOwnerCar.renew') }}
  139 + </el-button>
  140 + <el-button v-if="scope.row.leaseType === 'H'" size="mini" @click="toBuyCarMonthCard(scope.row)">
  141 + {{ $t('listOwnerCar.buyMonthlyCard') }}
  142 + </el-button>
  143 + <el-button v-if="scope.row.carTypeCd !== '1003' && hasPrivilege('502023032804261679')" size="mini"
  144 + @click="openEditOwnerCar(scope.row)">
  145 + {{ $t('listOwnerCar.modify') }}
  146 + </el-button>
  147 + <el-button v-if="hasPrivilege('502023032822121680')" size="mini" type="danger"
  148 + @click="openDelOwnerCarModel(scope.row)">
  149 + {{ $t('listOwnerCar.delete') }}
  150 + </el-button>
  151 + </template>
  152 + </el-table-column>
  153 + </el-table>
  154 +
  155 + <el-pagination class="mt-20" :current-page="currentPage" :page-sizes="[10, 20, 30, 50]" :page-size="pageSize"
  156 + layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
  157 + @current-change="handleCurrentChange" />
  158 + </el-card>
  159 + </el-col>
  160 + </el-row>
  161 +
  162 + <import-owner-car ref="importOwnerCar" @success="listOwnerCarData" />
  163 + <edit-car ref="editCar" @success="listOwnerCarData" />
  164 + <delete-owner-car ref="deleteOwnerCar" @success="listOwnerCarData" />
  165 + </div>
  166 +</template>
  167 +
  168 +<script>
  169 +import { queryOwnerCars, exportData, deleteCarParkingSpace } from '@/api/car/listOwnerCarApi'
  170 +import ImportOwnerCar from '@/components/car/importOwnerCar'
  171 +import EditCar from '@/components/car/editCar'
  172 +import DeleteOwnerCar from '@/components/car/deleteOwnerCar'
  173 +import { getDict } from '@/api/community/communityApi'
  174 +import { getCommunityId } from '@/api/community/communityApi'
  175 +
  176 +
  177 +export default {
  178 + name: 'ListOwnerCar',
  179 + components: { ImportOwnerCar, EditCar, DeleteOwnerCar },
  180 + data() {
  181 + return {
  182 + loading: false,
  183 + ownerCars: [],
  184 + leaseTypes: [],
  185 + conditions: {
  186 + carNumLike: '',
  187 + num: '',
  188 + valid: '',
  189 + leaseType: 'H',
  190 + ownerName: '',
  191 + link: '',
  192 + memberCarNumLike: ''
  193 + },
  194 + currentPage: 1,
  195 + pageSize: 10,
  196 + total: 0,
  197 + communityId: ''
  198 + }
  199 + },
  200 + created() {
  201 + this.communityId = getCommunityId()
  202 + this.initData()
  203 + this.listOwnerCarData()
  204 + },
  205 + methods: {
  206 + async initData() {
  207 + try {
  208 + const leaseTypes = await getDict('owner_car', 'lease_type')
  209 + this.leaseTypes = [{ statusCd: '', name: this.$t('listOwnerCar.allCars') }, ...leaseTypes,
  210 + { statusCd: 'expireCar', name: this.$t('listOwnerCar.expiredCars') }]
  211 + } catch (error) {
  212 + console.error('获取租赁类型失败:', error)
  213 + }
  214 + },
  215 +
  216 + async listOwnerCarData() {
  217 + this.loading = true
  218 + try {
  219 + const params = {
  220 + ...this.conditions,
  221 + page: this.currentPage,
  222 + row: this.pageSize,
  223 + communityId: this.communityId
  224 + }
  225 +
  226 + if (this.conditions.leaseType === 'expireCar') {
  227 + params.leaseType = ''
  228 + params.valid = 3
  229 + }
  230 +
  231 + const response = await queryOwnerCars(params)
  232 + this.ownerCars = response.data
  233 + this.total = response.total
  234 + } catch (error) {
  235 + console.error('获取车辆列表失败:', error)
  236 + this.$message.error(this.$t('listOwnerCar.fetchError'))
  237 + } finally {
  238 + this.loading = false
  239 + }
  240 + },
  241 +
  242 + switchLeaseType(item) {
  243 + this.conditions.leaseType = item.statusCd
  244 + this.currentPage = 1
  245 + this.listOwnerCarData()
  246 + },
  247 +
  248 + queryMethod() {
  249 + this.currentPage = 1
  250 + this.listOwnerCarData()
  251 + },
  252 +
  253 + resetMethod() {
  254 + this.conditions = {
  255 + carNumLike: '',
  256 + num: '',
  257 + valid: '',
  258 + leaseType: 'H',
  259 + ownerName: '',
  260 + link: '',
  261 + memberCarNumLike: ''
  262 + }
  263 + this.currentPage = 1
  264 + this.listOwnerCarData()
  265 + },
  266 +
  267 + handleSizeChange(val) {
  268 + this.pageSize = val
  269 + this.listOwnerCarData()
  270 + },
  271 +
  272 + handleCurrentChange(val) {
  273 + this.currentPage = val
  274 + this.listOwnerCarData()
  275 + },
  276 +
  277 + addOwnerCar() {
  278 + this.$router.push('/views/car/hireParkingSpace')
  279 + },
  280 +
  281 + openEditOwnerCar(car) {
  282 + this.$refs.editCar.open(car)
  283 + },
  284 +
  285 + openDelOwnerCarModel(car) {
  286 + this.$refs.deleteOwnerCar.open(car)
  287 + },
  288 +
  289 + deleteCarParkingSpace(car) {
  290 + this.$confirm(this.$t('listOwnerCar.confirmRelease'), this.$t('common.tip'), {
  291 + confirmButtonText: this.$t('common.confirm'),
  292 + cancelButtonText: this.$t('common.cancel'),
  293 + type: 'warning'
  294 + }).then(async () => {
  295 + try {
  296 + await deleteCarParkingSpace(car)
  297 + this.$message.success(this.$t('listOwnerCar.releaseSuccess'))
  298 + this.listOwnerCarData()
  299 + } catch (error) {
  300 + console.error('释放车位失败:', error)
  301 + this.$message.error(error.message || this.$t('listOwnerCar.releaseError'))
  302 + }
  303 + })
  304 + },
  305 +
  306 + addCarParkingSpace(car) {
  307 + this.$router.push(`/views/car/carAddParkingSpace?carId=${car.carId}`)
  308 + },
  309 +
  310 + toBuyCarMonthCard(car) {
  311 + this.$router.push(`/views/fee/buyCarMonthCard?carNum=${car.carNum}`)
  312 + },
  313 +
  314 + toCarMember(car) {
  315 + this.$router.push(`/pages/property/listOwnerCarMember?carId=${car.carId}&carNum=${car.carNum}`)
  316 + },
  317 +
  318 + getCarState(car) {
  319 + if (car.state === '3003') {
  320 + return this.$t('listOwnerCar.expired')
  321 + }
  322 + const endTime = new Date(car.endTime)
  323 + if (endTime > new Date()) {
  324 + return this.$t('listOwnerCar.normal')
  325 + }
  326 + return this.$t('listOwnerCar.expired')
  327 + },
  328 +
  329 + openOwnerCarImport() {
  330 + this.$refs.importOwnerCar.open()
  331 + },
  332 +
  333 + async exportExcel() {
  334 + try {
  335 + const params = {
  336 + ...this.conditions,
  337 + communityId: this.communityId,
  338 + pagePath: 'exportOwnerCar'
  339 + }
  340 + await exportData(params)
  341 + this.$message.success(this.$t('listOwnerCar.exportSuccess'))
  342 + this.$router.push('/pages/property/downloadTempFile?tab=下载中心')
  343 + } catch (error) {
  344 + console.error('导出失败:', error)
  345 + this.$message.error(error.message || this.$t('listOwnerCar.exportError'))
  346 + }
  347 + },
  348 +
  349 + viewIotStateRemark(car) {
  350 + this.$alert(car.iotRemark, `${car.carNum} ${this.$t('listOwnerCar.iotSyncDetail')}`, {
  351 + confirmButtonText: this.$t('common.confirm')
  352 + })
  353 + },
  354 +
  355 + }
  356 +}
  357 +</script>
  358 +
  359 +<style lang="scss" scoped>
  360 +.list-owner-car-container {
  361 + padding: 20px;
  362 +
  363 + .tree-card {
  364 + height: 100%;
  365 +
  366 + .lease-type-list {
  367 + list-style: none;
  368 + padding: 0;
  369 + margin: 0;
  370 +
  371 + li {
  372 + padding: 10px 15px;
  373 + text-align: center;
  374 + cursor: pointer;
  375 + border-bottom: 1px solid #eee;
  376 + transition: all 0.3s;
  377 +
  378 + &:hover {
  379 + background-color: #f5f7fa;
  380 + }
  381 +
  382 + &.selected {
  383 + background-color: #409EFF;
  384 + color: white;
  385 + }
  386 + }
  387 + }
  388 + }
  389 +
  390 + .search-card {
  391 + margin-bottom: 20px;
  392 + }
  393 +
  394 + .header-buttons {
  395 + float: right;
  396 + }
  397 +
  398 + .mt-10 {
  399 + margin-top: 10px;
  400 + }
  401 +
  402 + .mt-20 {
  403 + margin-top: 20px;
  404 + }
  405 +
  406 + .text-right {
  407 + text-align: right;
  408 + }
  409 +
  410 + .el-table .expired-row {
  411 + background-color: #fef0f0;
  412 + }
  413 +}
  414 +</style>
0 415 \ No newline at end of file
... ...
src/views/car/parkingBoxManageLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + parkingBoxManage: {
  4 + searchTitle: 'Search Conditions',
  5 + boxIdPlaceholder: 'Please enter booth number',
  6 + boxNamePlaceholder: 'Please enter booth name',
  7 + tempCarInPlaceholder: 'Please select temporary car entry',
  8 + yes: 'Yes',
  9 + no: 'No',
  10 + searchBtn: 'Search',
  11 + resetBtn: 'Reset',
  12 + listTitle: 'Booth Information',
  13 + boxId: 'Booth Number',
  14 + boxName: 'Booth Name',
  15 + parkingLot: 'Parking Lot',
  16 + tempCarIn: 'Temporary Car Entry',
  17 + tempAuth: 'Temporary Car Audit',
  18 + fee: 'Chargeable',
  19 + blueCarIn: 'Already Present',
  20 + yelowCarIn: 'Not Present',
  21 + remark: 'Remark',
  22 + operation: 'Operation',
  23 + console: 'Console',
  24 + dataSource: 'Data comes from IoT system',
  25 + pagination: 'Pagination'
  26 + },
  27 + addParkingBox: {
  28 + title: 'Add Booth',
  29 + boothName: 'Booth Name',
  30 + parkingLot: 'Parking Lot',
  31 + tempCarIn: 'Temporary Car Entry',
  32 + tempAuth: 'Temporary Car Audit',
  33 + fee: 'Chargeable',
  34 + blueCarIn: 'Already Present',
  35 + yelowCarIn: 'Not Present',
  36 + remark: 'Remark',
  37 + required: 'Required',
  38 + optional: 'Optional',
  39 + save: 'Save',
  40 + cancel: 'Cancel'
  41 + },
  42 + editParkingBox: {
  43 + title: 'Edit Booth',
  44 + boothName: 'Booth Name',
  45 + parkingLot: 'Parking Lot',
  46 + tempCarIn: 'Temporary Car Entry',
  47 + tempAuth: 'Temporary Car Audit',
  48 + fee: 'Chargeable',
  49 + blueCarIn: 'Already Present',
  50 + yelowCarIn: 'Not Present',
  51 + remark: 'Remark',
  52 + save: 'Save',
  53 + cancel: 'Cancel'
  54 + },
  55 + deleteParkingBox: {
  56 + title: 'Confirm Operation',
  57 + confirmDelete: 'Confirm delete booth?',
  58 + cancel: 'Cancel',
  59 + confirm: 'Confirm Delete'
  60 + }
  61 + },
  62 + zh: {
  63 + parkingBoxManage: {
  64 + searchTitle: '查询条件',
  65 + boxIdPlaceholder: '请输入岗亭编号',
  66 + boxNamePlaceholder: '请输入岗亭名称',
  67 + tempCarInPlaceholder: '请选择临时车进场',
  68 + yes: '是',
  69 + no: '否',
  70 + searchBtn: '查询',
  71 + resetBtn: '重置',
  72 + listTitle: '岗亭信息',
  73 + boxId: '岗亭编号',
  74 + boxName: '岗亭名称',
  75 + parkingLot: '停车场',
  76 + tempCarIn: '临时车进场',
  77 + tempAuth: '临时车审核',
  78 + fee: '是否收费',
  79 + blueCarIn: '已在场',
  80 + yelowCarIn: '未在场',
  81 + remark: '备注',
  82 + operation: '操作',
  83 + console: '控制台',
  84 + dataSource: '数据来源于物联网系统,请到物联网系统管理数据',
  85 + pagination: '分页'
  86 + },
  87 + addParkingBox: {
  88 + title: '添加岗亭',
  89 + boothName: '岗亭名称',
  90 + parkingLot: '停车场',
  91 + tempCarIn: '临时车进场',
  92 + tempAuth: '临时车审核',
  93 + fee: '是否收费',
  94 + blueCarIn: '已在场',
  95 + yelowCarIn: '未在场',
  96 + remark: '备注',
  97 + required: '必填',
  98 + optional: '选填',
  99 + save: '保存',
  100 + cancel: '取消'
  101 + },
  102 + editParkingBox: {
  103 + title: '修改岗亭',
  104 + boothName: '岗亭名称',
  105 + parkingLot: '停车场',
  106 + tempCarIn: '临时车进场',
  107 + tempAuth: '临时车审核',
  108 + fee: '是否收费',
  109 + blueCarIn: '已在场',
  110 + yelowCarIn: '未在场',
  111 + remark: '备注',
  112 + save: '保存',
  113 + cancel: '取消'
  114 + },
  115 + deleteParkingBox: {
  116 + title: '请确认您的操作',
  117 + confirmDelete: '确定删除岗亭?',
  118 + cancel: '点错了',
  119 + confirm: '确认删除'
  120 + }
  121 + }
  122 +}
0 123 \ No newline at end of file
... ...
src/views/car/parkingBoxManageList.vue 0 → 100644
  1 +<template>
  2 + <div class="parking-box-manage-container">
  3 + <el-card class="search-card">
  4 + <div slot="header" class="flex justify-between">
  5 + <span>{{ $t('parkingBoxManage.searchTitle') }}</span>
  6 + </div>
  7 + <el-row :gutter="20">
  8 + <el-col :span="6">
  9 + <el-input v-model.trim="searchForm.boxId" :placeholder="$t('parkingBoxManage.boxIdPlaceholder')" clearable />
  10 + </el-col>
  11 + <el-col :span="6">
  12 + <el-input v-model.trim="searchForm.boxName" :placeholder="$t('parkingBoxManage.boxNamePlaceholder')"
  13 + clearable />
  14 + </el-col>
  15 + <el-col :span="6">
  16 + <el-select v-model="searchForm.tempCarIn" :placeholder="$t('parkingBoxManage.tempCarInPlaceholder')" clearable
  17 + style="width:100%">
  18 + <el-option value="" :label="$t('parkingBoxManage.tempCarInPlaceholder')" />
  19 + <el-option value="Y" :label="$t('parkingBoxManage.yes')" />
  20 + <el-option value="N" :label="$t('parkingBoxManage.no')" />
  21 + </el-select>
  22 + </el-col>
  23 + <el-col :span="6">
  24 + <el-button type="primary" @click="handleSearch">
  25 + {{ $t('parkingBoxManage.searchBtn') }}
  26 + </el-button>
  27 + <el-button @click="handleReset">
  28 + {{ $t('parkingBoxManage.resetBtn') }}
  29 + </el-button>
  30 + </el-col>
  31 + </el-row>
  32 + </el-card>
  33 +
  34 + <el-card class="list-card">
  35 + <div slot="header" class="flex justify-between">
  36 + <span>{{ $t('parkingBoxManage.listTitle') }}</span>
  37 + </div>
  38 +
  39 + <el-table v-loading="loading" :data="tableData" border style="width: 100%">
  40 + <el-table-column prop="boxId" :label="$t('parkingBoxManage.boxId')" align="center" />
  41 + <el-table-column prop="boxName" :label="$t('parkingBoxManage.boxName')" align="center" />
  42 + <el-table-column prop="paNum" :label="$t('parkingBoxManage.parkingLot')" align="center" />
  43 + <el-table-column prop="tempCarIn" :label="$t('parkingBoxManage.tempCarIn')" align="center">
  44 + <template slot-scope="scope">
  45 + {{ scope.row.tempCarIn === 'Y' ? $t('parkingBoxManage.yes') : $t('parkingBoxManage.no') }}
  46 + </template>
  47 + </el-table-column>
  48 + <el-table-column prop="tempAuth" :label="$t('parkingBoxManage.tempAuth')" align="center">
  49 + <template slot-scope="scope">
  50 + {{ scope.row.tempAuth === 'Y' ? $t('parkingBoxManage.yes') : $t('parkingBoxManage.no') }}
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column prop="fee" :label="$t('parkingBoxManage.fee')" align="center">
  54 + <template slot-scope="scope">
  55 + {{ scope.row.fee === 'Y' ? $t('parkingBoxManage.yes') : $t('parkingBoxManage.no') }}
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column prop="blueCarIn" :label="$t('parkingBoxManage.blueCarIn')" align="center">
  59 + <template slot-scope="scope">
  60 + {{ scope.row.blueCarIn === 'Y' ? $t('parkingBoxManage.yes') : $t('parkingBoxManage.no') }}
  61 + </template>
  62 + </el-table-column>
  63 + <el-table-column prop="yelowCarIn" :label="$t('parkingBoxManage.yelowCarIn')" align="center">
  64 + <template slot-scope="scope">
  65 + {{ scope.row.yelowCarIn === 'Y' ? $t('parkingBoxManage.yes') : $t('parkingBoxManage.no') }}
  66 + </template>
  67 + </el-table-column>
  68 + <el-table-column prop="remark" :label="$t('parkingBoxManage.remark')" align="center" />
  69 + <el-table-column :label="$t('parkingBoxManage.operation')" align="center" width="120">
  70 + <template slot-scope="scope">
  71 + <el-button v-if="hasPrivilege('502022090904120022')" type="text" size="small"
  72 + @click="handleOpenConsole(scope.row)">
  73 + {{ $t('parkingBoxManage.console') }}
  74 + </el-button>
  75 + </template>
  76 + </el-table-column>
  77 + </el-table>
  78 +
  79 + <el-row class="pagination-row">
  80 + <el-col :span="12" class="data-source">
  81 + {{ $t('parkingBoxManage.dataSource') }}
  82 + </el-col>
  83 + <el-col :span="12">
  84 + <el-pagination :current-page="pagination.current" :page-sizes="[10, 20, 30, 50]" :page-size="pagination.size"
  85 + layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handleSizeChange"
  86 + @current-change="handleCurrentChange" />
  87 + </el-col>
  88 + </el-row>
  89 + </el-card>
  90 +
  91 + </div>
  92 +</template>
  93 +
  94 +<script>
  95 +import { getParkingBoxList } from '@/api/car/parkingBoxManageApi'
  96 +
  97 +export default {
  98 + name: 'ParkingBoxManageList',
  99 + components: {
  100 + },
  101 + data() {
  102 + return {
  103 + loading: false,
  104 + searchForm: {
  105 + boxId: '',
  106 + boxName: '',
  107 + tempCarIn: ''
  108 + },
  109 + tableData: [],
  110 + pagination: {
  111 + current: 1,
  112 + size: 10,
  113 + total: 0
  114 + }
  115 + }
  116 + },
  117 + created() {
  118 + this.fetchData()
  119 + },
  120 + methods: {
  121 +
  122 + async fetchData() {
  123 + this.loading = true
  124 + try {
  125 + const params = {
  126 + page: this.pagination.current,
  127 + row: this.pagination.size,
  128 + ...this.searchForm
  129 + }
  130 +
  131 + const res = await getParkingBoxList(params)
  132 + this.tableData = res.data
  133 + this.pagination.total = res.total || 0
  134 + this.pagination.current = res.page || 1
  135 + } catch (error) {
  136 + console.error('获取岗亭列表失败:', error)
  137 + this.$message.error(this.$t('parkingBoxManage.fetchError'))
  138 + } finally {
  139 + this.loading = false
  140 + }
  141 + },
  142 +
  143 + handleSearch() {
  144 + this.pagination.current = 1
  145 + this.fetchData()
  146 + },
  147 +
  148 + handleReset() {
  149 + this.searchForm = {
  150 + boxId: '',
  151 + boxName: '',
  152 + tempCarIn: ''
  153 + }
  154 + this.handleSearch()
  155 + },
  156 +
  157 + handleSizeChange(size) {
  158 + this.pagination.size = size
  159 + this.fetchData()
  160 + },
  161 +
  162 + handleCurrentChange(current) {
  163 + this.pagination.current = current
  164 + this.fetchData()
  165 + },
  166 +
  167 + handleSuccess() {
  168 + this.fetchData()
  169 + },
  170 +
  171 + handleOpenAdd() {
  172 + this.$refs.addDialog.open()
  173 + },
  174 +
  175 + handleOpenEdit(row) {
  176 + this.$refs.editDialog.open(row)
  177 + },
  178 +
  179 + handleOpenDelete(row) {
  180 + this.$refs.deleteDialog.open(row)
  181 + },
  182 +
  183 + handleOpenConsole(row) {
  184 + // 跳转到控制台页面
  185 + this.$router.push({
  186 + path: '/car/parkingAreaControl',
  187 + query: {
  188 + boxId: row.boxId,
  189 + paId: row.paId
  190 + }
  191 + })
  192 + }
  193 + }
  194 +}
  195 +</script>
  196 +
  197 +<style lang="scss" scoped>
  198 +.parking-box-manage-container {
  199 + padding: 20px;
  200 +
  201 + .search-card {
  202 + margin-bottom: 20px;
  203 + }
  204 +
  205 + .list-card {
  206 + margin-bottom: 20px;
  207 + }
  208 +
  209 + .pagination-row {
  210 + margin-top: 20px;
  211 + display: flex;
  212 + align-items: center;
  213 +
  214 + .data-source {
  215 + display: flex;
  216 + align-items: center;
  217 + color: #999;
  218 + font-size: 14px;
  219 + }
  220 + }
  221 +}
  222 +</style>
0 223 \ No newline at end of file
... ...
src/views/fee/buyCarMonthCardLang.js 0 → 100644
  1 +export const messages = {
  2 + en: {
  3 + buyCarMonthCard: {
  4 + title: 'Buy Monthly Card',
  5 + carNum: 'License Plate',
  6 + card: 'Monthly Card',
  7 + paymentMethod: 'Payment Method',
  8 + receivableAmount: 'Receivable Amount',
  9 + receivedAmount: 'Received Amount',
  10 + remark: 'Remark',
  11 + tip: 'Monthly cards need to be added in the IoT system and must be added to the parking lot where the vehicle is located',
  12 + placeholders: {
  13 + carNum: 'Required, please enter license plate number',
  14 + card: 'Required, please select monthly card',
  15 + paymentMethod: 'Required, please select payment method',
  16 + receivableAmount: 'Required, receivable amount',
  17 + receivedAmount: 'Required, received amount',
  18 + remark: 'Optional, please enter remark'
  19 + },
  20 + rules: {
  21 + carNumRequired: 'License plate number is required',
  22 + cardRequired: 'Monthly card is required',
  23 + paymentMethodRequired: 'Payment method is required',
  24 + receivedAmountRequired: 'Received amount is required'
  25 + },
  26 + warnings: {
  27 + carNotExist: 'Monthly rental car does not exist, please add it first',
  28 + carNoParkingSpace: 'Monthly rental car is not bound to a parking space'
  29 + },
  30 + errors: {
  31 + getDictFailed: 'Failed to get payment methods',
  32 + queryCarFailed: 'Failed to query vehicle information',
  33 + getCardsFailed: 'Failed to get monthly card list',
  34 + saveFailed: 'Failed to save monthly card order'
  35 + },
  36 + success: {
  37 + saveSuccess: 'Monthly card purchased successfully'
  38 + }
  39 + }
  40 + },
  41 + zh: {
  42 + buyCarMonthCard: {
  43 + title: '购买月卡',
  44 + carNum: '车牌号',
  45 + card: '月卡',
  46 + paymentMethod: '支付方式',
  47 + receivableAmount: '应收金额',
  48 + receivedAmount: '实收金额',
  49 + remark: '备注',
  50 + tip: '月卡请到物联网系统添加,需要添加到车辆所在的停车场',
  51 + placeholders: {
  52 + carNum: '必填,请填写车牌号',
  53 + card: '必填,请选择月卡',
  54 + paymentMethod: '必填,请选择支付方式',
  55 + receivableAmount: '必填,应收金额',
  56 + receivedAmount: '必填,实收金额',
  57 + remark: '选填,请填写备注'
  58 + },
  59 + rules: {
  60 + carNumRequired: '车牌号不能为空',
  61 + cardRequired: '月卡不能为空',
  62 + paymentMethodRequired: '支付方式不能为空',
  63 + receivedAmountRequired: '实收金额不能为空'
  64 + },
  65 + warnings: {
  66 + carNotExist: '月租车不存在,请先添加为月租车',
  67 + carNoParkingSpace: '月租车没有绑定车位'
  68 + },
  69 + errors: {
  70 + getDictFailed: '获取支付方式失败',
  71 + queryCarFailed: '查询车辆信息失败',
  72 + getCardsFailed: '获取月卡列表失败',
  73 + saveFailed: '保存月卡订单失败'
  74 + },
  75 + success: {
  76 + saveSuccess: '月卡购买成功'
  77 + }
  78 + }
  79 + }
  80 +}
0 81 \ No newline at end of file
... ...
src/views/fee/buyCarMonthCardList.vue 0 → 100644
  1 +<template>
  2 + <el-card class="buy-car-month-card-container">
  3 + <div slot="header">
  4 + <h5>{{ $t('buyCarMonthCard.title') }}</h5>
  5 + </div>
  6 +
  7 + <el-form ref="form" :model="buyCarMonthCardInfo" label-width="120px">
  8 + <el-form-item :label="$t('buyCarMonthCard.carNum')" prop="carNum"
  9 + :rules="[{ required: true, message: $t('buyCarMonthCard.rules.carNumRequired'), trigger: 'blur' }]">
  10 + <el-input v-model="buyCarMonthCardInfo.carNum" :placeholder="$t('buyCarMonthCard.placeholders.carNum')"
  11 + @blur="_queryOwnerCar" clearable>
  12 + </el-input>
  13 + </el-form-item>
  14 +
  15 + <el-form-item :label="$t('buyCarMonthCard.card')" prop="cardId"
  16 + :rules="[{ required: true, message: $t('buyCarMonthCard.rules.cardRequired'), trigger: 'change' }]">
  17 + <el-select v-model="buyCarMonthCardInfo.cardId" :placeholder="$t('buyCarMonthCard.placeholders.card')"
  18 + @change="_changeCard" style="width:100%">
  19 + <el-option v-for="(item, index) in buyCarMonthCardInfo.cards" :key="index"
  20 + :label="`${item.cardName}(${item.cardMonth}${$t('common.month')})`" :value="item.cardId">
  21 + </el-option>
  22 + </el-select>
  23 + <div class="tip">{{ $t('buyCarMonthCard.tip') }}</div>
  24 + </el-form-item>
  25 +
  26 + <el-form-item :label="$t('buyCarMonthCard.paymentMethod')" prop="primeRate"
  27 + :rules="[{ required: true, message: $t('buyCarMonthCard.rules.paymentMethodRequired'), trigger: 'change' }]">
  28 + <el-select v-model="buyCarMonthCardInfo.primeRate" :placeholder="$t('buyCarMonthCard.placeholders.paymentMethod')"
  29 + style="width:100%">
  30 + <el-option v-for="(item, index) in filteredPrimeRates" :key="index" :label="item.name" :value="item.statusCd">
  31 + </el-option>
  32 + </el-select>
  33 + </el-form-item>
  34 +
  35 + <el-form-item :label="$t('buyCarMonthCard.receivableAmount')">
  36 + <el-input v-model="buyCarMonthCardInfo.receivableAmount"
  37 + :placeholder="$t('buyCarMonthCard.placeholders.receivableAmount')" disabled>
  38 + </el-input>
  39 + </el-form-item>
  40 +
  41 + <el-form-item :label="$t('buyCarMonthCard.receivedAmount')" prop="receivedAmount"
  42 + :rules="[{ required: true, message: $t('buyCarMonthCard.rules.receivedAmountRequired'), trigger: 'blur' }]">
  43 + <el-input v-model="buyCarMonthCardInfo.receivedAmount" type="number"
  44 + :placeholder="$t('buyCarMonthCard.placeholders.receivedAmount')">
  45 + </el-input>
  46 + </el-form-item>
  47 +
  48 + <el-form-item :label="$t('buyCarMonthCard.remark')">
  49 + <el-input v-model="buyCarMonthCardInfo.remark" type="textarea"
  50 + :placeholder="$t('buyCarMonthCard.placeholders.remark')">
  51 + </el-input>
  52 + </el-form-item>
  53 +
  54 + <el-form-item>
  55 + <el-button type="primary" @click="saveCarMonthOrderInfo">
  56 + <i class="el-icon-check"></i>{{ $t('common.submit') }}
  57 + </el-button>
  58 + <el-button type="warning" @click="goBack" style="margin-left:20px;">
  59 + {{ $t('common.back') }}
  60 + </el-button>
  61 + </el-form-item>
  62 + </el-form>
  63 + </el-card>
  64 +</template>
  65 +
  66 +<script>
  67 +import { buyCarMonthOrder, queryOwnerCars, getOpenApi } from '@/api/fee/buyCarMonthCardApi'
  68 +import { getDict } from '@/api/community/communityApi'
  69 +import { getCommunityId } from '@/api/community/communityApi'
  70 +
  71 +export default {
  72 + name: 'BuyCarMonthCardList',
  73 + data() {
  74 + return {
  75 + buyCarMonthCardInfo: {
  76 + orderId: '',
  77 + cardId: '',
  78 + carNum: '',
  79 + carId: '',
  80 + primeRate: '',
  81 + receivableAmount: '',
  82 + receivedAmount: '',
  83 + remark: '',
  84 + cards: [],
  85 + primeRates: [],
  86 + areaNum: ''
  87 + },
  88 + communityId: ''
  89 + }
  90 + },
  91 + computed: {
  92 + filteredPrimeRates() {
  93 + return this.buyCarMonthCardInfo.primeRates.filter(
  94 + item => item.statusCd !== '5' && item.statusCd !== '6' && item.statusCd !== '8'
  95 + )
  96 + }
  97 + },
  98 + mounted() {
  99 + this.communityId = getCommunityId()
  100 +
  101 + // 从路由参数获取车牌号
  102 + if (this.$route.query.carNum) {
  103 + this.buyCarMonthCardInfo.carNum = this.$route.query.carNum
  104 + this._queryOwnerCar()
  105 + }
  106 +
  107 + // 获取支付方式字典
  108 + this.getPrimeRateDict()
  109 + },
  110 + methods: {
  111 + async getPrimeRateDict() {
  112 + try {
  113 + const data = await getDict('pay_fee_detail', 'prime_rate')
  114 + this.buyCarMonthCardInfo.primeRates = data
  115 + } catch (error) {
  116 + console.error('获取支付方式字典失败:', error)
  117 + this.$message.error(this.$t('buyCarMonthCard.errors.getDictFailed'))
  118 + }
  119 + },
  120 +
  121 + async _queryOwnerCar() {
  122 + if (!this.buyCarMonthCardInfo.carNum) return
  123 +
  124 + try {
  125 + const params = {
  126 + page: 1,
  127 + row: 1,
  128 + communityId: this.communityId,
  129 + carNum: this.buyCarMonthCardInfo.carNum,
  130 + carTypeCd: '1001'
  131 + }
  132 +
  133 + const res = await queryOwnerCars(params)
  134 + if (res.total < 1) {
  135 + this.$message.warning(this.$t('buyCarMonthCard.warnings.carNotExist'))
  136 + return
  137 + }
  138 +
  139 + const carData = res.data[0]
  140 + if (!carData.paId) {
  141 + this.$message.warning(this.$t('buyCarMonthCard.warnings.carNoParkingSpace'))
  142 + return
  143 + }
  144 +
  145 + this.buyCarMonthCardInfo.carId = carData.carId
  146 + this.buyCarMonthCardInfo.areaNum = carData.areaNum
  147 + this._listBuyMonthCards()
  148 + } catch (error) {
  149 + console.error('查询车主车辆失败:', error)
  150 + this.$message.error(this.$t('buyCarMonthCard.errors.queryCarFailed'))
  151 + }
  152 + },
  153 +
  154 + async _listBuyMonthCards() {
  155 + if (!this.buyCarMonthCardInfo.areaNum) return
  156 +
  157 + try {
  158 + const params = {
  159 + page: 1,
  160 + row: 100,
  161 + communityId: this.communityId,
  162 + paNum: this.buyCarMonthCardInfo.areaNum,
  163 + iotApiCode: 'queryCarMonthCardBmoImpl'
  164 + }
  165 +
  166 + const res = await getOpenApi(params)
  167 + this.buyCarMonthCardInfo.cards = res.data.data || []
  168 + } catch (error) {
  169 + console.error('获取月卡列表失败:', error)
  170 + this.$message.error(this.$t('buyCarMonthCard.errors.getCardsFailed'))
  171 + }
  172 + },
  173 +
  174 + _changeCard() {
  175 + const cardId = this.buyCarMonthCardInfo.cardId
  176 + const selectedCard = this.buyCarMonthCardInfo.cards.find(item => item.cardId === cardId)
  177 +
  178 + if (selectedCard) {
  179 + this.buyCarMonthCardInfo.receivableAmount = selectedCard.cardPrice
  180 + this.buyCarMonthCardInfo.receivedAmount = selectedCard.cardPrice
  181 + }
  182 + },
  183 +
  184 + async saveCarMonthOrderInfo() {
  185 + try {
  186 + // 表单验证
  187 + await this.$refs.form.validate()
  188 +
  189 + const params = {
  190 + ...this.buyCarMonthCardInfo,
  191 + communityId: this.communityId
  192 + }
  193 +
  194 + const res = await buyCarMonthOrder(params)
  195 + if (res.code === 0) {
  196 + this.$message.success(this.$t('buyCarMonthCard.success.saveSuccess'))
  197 + this.goBack()
  198 + } else {
  199 + this.$message.error(res.msg || this.$t('buyCarMonthCard.errors.saveFailed'))
  200 + }
  201 + } catch (error) {
  202 + if (error instanceof Error) {
  203 + console.error('保存月卡订单失败:', error)
  204 + this.$message.error(error.message)
  205 + }
  206 + // 验证失败不显示错误
  207 + }
  208 + },
  209 +
  210 + clearBuyCarMonthCardInfo() {
  211 + this.$refs.form.resetFields()
  212 + this.buyCarMonthCardInfo.cards = []
  213 + this.buyCarMonthCardInfo.areaNum = ''
  214 + this.getPrimeRateDict()
  215 + },
  216 +
  217 + goBack() {
  218 + this.$router.go(-1)
  219 + }
  220 + }
  221 +}
  222 +</script>
  223 +
  224 +<style scoped>
  225 +.buy-car-month-card-container {
  226 + margin: 20px;
  227 +}
  228 +
  229 +.tip {
  230 + font-size: 12px;
  231 + color: #909399;
  232 + margin-top: 5px;
  233 +}
  234 +</style>
0 235 \ No newline at end of file
... ...