594 lines
21 KiB
Vue
594 lines
21 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<!-- 统计数据展示区域 -->
|
||
<!-- <el-row :gutter="20" class="statistics-row">
|
||
<el-col :span="4">
|
||
<div class="stat-card">
|
||
<div class="stat-label">管理面积</div>
|
||
<div class="stat-value">{{ statistics.managementArea }} m²</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="4">
|
||
<div class="stat-card">
|
||
<div class="stat-label">可招商面积</div>
|
||
<div class="stat-value">{{ statistics.availableArea }} m²</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="4">
|
||
<div class="stat-card">
|
||
<div class="stat-label">可招商占比</div>
|
||
<div class="stat-value">{{ statistics.availableRatio }}%</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="4">
|
||
<div class="stat-card">
|
||
<div class="stat-label">总房源数量</div>
|
||
<div class="stat-value">{{ statistics.totalRooms }} 间</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="4">
|
||
<div class="stat-card">
|
||
<div class="stat-label">可招商房源数量</div>
|
||
<div class="stat-value">{{ statistics.availableRooms }} 间</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row> ..>
|
||
|
||
<!-- 搜索区域 -->
|
||
<el-card class="box-card">
|
||
<div slot="header" class="clearfix">
|
||
<span>项目列表</span>
|
||
<el-button
|
||
style="float: right;"
|
||
type="primary"
|
||
icon="el-icon-plus"
|
||
size="mini"
|
||
@click="handleAdd">新增项目</el-button>
|
||
</div>
|
||
|
||
<!-- 搜索表单 -->
|
||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||
<el-form-item label="项目名称">
|
||
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable size="small" />
|
||
</el-form-item>
|
||
<el-form-item label="项目类型">
|
||
<el-select v-model="queryParams.projectType" placeholder="请选择项目类型" clearable size="small">
|
||
<el-option label="写字楼" value="写字楼" />
|
||
<el-option label="产业园区" value="产业园区" />
|
||
<el-option label="商场" value="商场" />
|
||
<el-option label="联合办公" value="联合办公" />
|
||
<el-option label="公寓" value="公寓" />
|
||
<el-option label="小区" value="小区" />
|
||
<el-option label="社区养老" value="社区养老" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<!-- <el-form-item label="所属地区">
|
||
<region-selector v-model="queryParams.region" :value="queryParams.region" placeholder="请选择所属地区" size="small" @change="handleRegionChange" />
|
||
</el-form-item> -->
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 表格区域 -->
|
||
<el-table v-loading="loading" :data="projectList" border style="width: 100%">
|
||
<el-table-column prop="projectName" label="项目名称" />
|
||
<el-table-column prop="projectType" label="项目类型" />
|
||
<el-table-column prop="region" label="所属地区" />
|
||
<el-table-column prop="manageArea" label="管理面积(㎡)" />
|
||
<el-table-column prop="availableArea" label="可招商面积(㎡)" />
|
||
<el-table-column prop="totalRooms" label="总房源数量" />
|
||
<el-table-column prop="availableRooms" label="可招商房源数量" />
|
||
<el-table-column label="操作" width="200">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" @click="handleDetail(scope.row)">详情</el-button>
|
||
<el-button size="mini" type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
||
<el-button size="mini" type="text" style="color: #F56C6C" @click="handleDelete(scope.row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页区域 -->
|
||
<el-pagination
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
:current-page="queryParams.pageNum"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:page-size="queryParams.pageSize"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
:total="total"
|
||
class="pagination">
|
||
</el-pagination>
|
||
</el-card>
|
||
|
||
<!-- 新增/编辑对话框 -->
|
||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="50%" @open="onDialogOpen" @close="onDialogClose">
|
||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="项目类型" prop="projectType">
|
||
<el-select v-model="form.projectType" placeholder="请选择项目类型" :disabled="form.id !== undefined">
|
||
<el-option label="写字楼" value="写字楼" />
|
||
<el-option label="产业园区" value="产业园区" />
|
||
<el-option label="商场" value="商场" />
|
||
<el-option label="联合办公" value="联合办公" />
|
||
<el-option label="公寓" value="公寓" />
|
||
<el-option label="小区" value="小区" />
|
||
<el-option label="社区养老" value="社区养老" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="项目名称" prop="projectName">
|
||
<el-input v-model="form.projectName" placeholder="请输入项目名称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="项目简称" prop="projectShortName">
|
||
<el-input v-model="form.projectShortName" placeholder="请输入项目简称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="所属地区" prop="region">
|
||
<region-selector v-model="form.region" placeholder="请选择所属地区" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-form-item label="详细地址" prop="address">
|
||
<el-input v-model="form.address" type="textarea" placeholder="请输入详细地址" />
|
||
</el-form-item>
|
||
|
||
<el-form-item label="项目标签" prop="projectTags">
|
||
<el-select v-model="form.projectTags" multiple placeholder="请选择项目标签">
|
||
<el-option
|
||
v-for="tag in tagOptions"
|
||
:key="tag.id"
|
||
:label="tag.tagName"
|
||
:value="tag.id">
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="项目简介" prop="projectDesc">
|
||
<el-input v-model="form.projectDesc" type="textarea" :rows="4" placeholder="请输入项目简介" />
|
||
</el-form-item>
|
||
|
||
<el-form-item label="项目图片" prop="projectImage">
|
||
<el-upload
|
||
class="avatar-uploader"
|
||
action="/api/upload"
|
||
:show-file-list="false"
|
||
:on-success="handleImageSuccess"
|
||
:before-upload="beforeImageUpload">
|
||
<img v-if="form.projectImage" :src="form.projectImage" class="avatar">
|
||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||
</el-upload>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 详情对话框 -->
|
||
<el-dialog title="项目详情" :visible.sync="detailVisible" width="50%">
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="项目名称">{{ detail.projectName }}</el-descriptions-item>
|
||
<el-descriptions-item label="项目简称">{{ detail.projectShortName }}</el-descriptions-item>
|
||
<el-descriptions-item label="项目类型">{{ detail.projectType }}</el-descriptions-item>
|
||
<el-descriptions-item label="所属地区">{{ detail.region }}</el-descriptions-item>
|
||
<el-descriptions-item label="详细地址">{{ detail.address }}</el-descriptions-item>
|
||
<el-descriptions-item label="管理面积">{{ detail.manageArea }}㎡</el-descriptions-item>
|
||
<el-descriptions-item label="可招商面积">{{ detail.availableArea }}㎡</el-descriptions-item>
|
||
<el-descriptions-item label="总房源数量">{{ detail.totalRooms }}</el-descriptions-item>
|
||
<el-descriptions-item label="可招商房源数量">{{ detail.availableRooms }}</el-descriptions-item>
|
||
<el-descriptions-item label="项目标签" :span="2">
|
||
<el-tag
|
||
v-for="tag in projectTagList"
|
||
:key="tag.id"
|
||
type="success"
|
||
size="medium"
|
||
style="margin-right: 8px; margin-bottom: 5px;">
|
||
{{ tag.tagName }}
|
||
</el-tag>
|
||
<span v-if="!projectTagList || projectTagList.length === 0">暂无标签</span>
|
||
</el-descriptions-item>
|
||
<!-- <el-descriptions-item label="排序值">{{ detail.sort }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="竣工时间">{{ detail.completionTime }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="标准层高">{{ detail.standardHeight }}m</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="物业">{{ detail.property }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="物业费">{{ detail.propertyFee }}元/㎡·月</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="车位数量">{{ detail.parkingSpaces }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="车位月租金">{{ detail.parkingFee }}元/月</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="空调">{{ detail.airConditioning }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="空调费">{{ detail.airConditioningFee }}元/㎡·月</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="空调开放时间">{{ detail.airConditioningTime }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="电梯">{{ detail.elevator }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="网络">{{ detail.network }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="入驻企业">{{ detail.enterprises }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="招商岗位">{{ detail.recruitmentPosition }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="招商部门">{{ detail.recruitmentDepartment }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="VR链接">{{ detail.vrLink }}</el-descriptions-item> -->
|
||
<!-- <el-descriptions-item label="招商时间">{{ detail.recruitmentTime }}</el-descriptions-item> -->
|
||
<el-descriptions-item label="项目图片" :span="2">
|
||
<el-image
|
||
v-if="detail.projectImage"
|
||
:src="detail.projectImage"
|
||
:preview-src-list="[detail.projectImage]"
|
||
fit="cover"
|
||
style="width: 200px; height: 200px">
|
||
</el-image>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="detailVisible = false">关 闭</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { getProjectList, getProjectDetail, addProject, updateProject, deleteProject, getAllProjectStatistics, getTagByType } from '@/api/project'
|
||
import RegionSelector from '@/components/RegionSelector'
|
||
|
||
export default {
|
||
name: 'ProjectList',
|
||
components: {
|
||
RegionSelector
|
||
},
|
||
data() {
|
||
return {
|
||
// 遮罩层
|
||
loading: false,
|
||
// 总条数
|
||
total: 0,
|
||
// 项目列表
|
||
projectList: [],
|
||
// 弹出层标题
|
||
dialogTitle: '',
|
||
// 是否显示弹出层
|
||
dialogVisible: false,
|
||
// 是否显示详情弹出层
|
||
detailVisible: false,
|
||
// 查询参数
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
projectName: undefined,
|
||
projectType: undefined,
|
||
region: undefined
|
||
},
|
||
// 表单参数
|
||
form: {},
|
||
// 详情数据
|
||
detail: {},
|
||
// 标签选项
|
||
tagOptions: [],
|
||
// 表单校验
|
||
rules: {
|
||
projectType: [
|
||
{ required: true, message: '请选择项目类型', trigger: 'change' }
|
||
],
|
||
projectName: [
|
||
{ required: true, message: '请输入项目名称', trigger: 'blur' }
|
||
],
|
||
projectShortName: [
|
||
{ required: true, message: '请输入项目简称', trigger: 'blur' }
|
||
],
|
||
region: [
|
||
{ required: true, message: '请输入所属地区', trigger: 'blur' }
|
||
],
|
||
address: [
|
||
{ required: true, message: '请输入详细地址', trigger: 'blur' }
|
||
]
|
||
},
|
||
// 统计数据
|
||
statistics: {
|
||
managementArea: '37,234.52',
|
||
availableArea: '20,916.2',
|
||
availableRatio: '56.17',
|
||
totalRooms: '637',
|
||
availableRooms: '51'
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
// 项目标签列表,用于在详情页展示
|
||
projectTagList() {
|
||
if (!this.detail.projectTags || !this.tagOptions || this.tagOptions.length === 0) {
|
||
return [];
|
||
}
|
||
|
||
// 将逗号分隔的标签ID字符串转为数组
|
||
const tagIds = typeof this.detail.projectTags === 'string'
|
||
? this.detail.projectTags.split(',')
|
||
: this.detail.projectTags;
|
||
|
||
// 根据ID查找标签对象
|
||
return tagIds.map(tagId => {
|
||
const tag = this.tagOptions.find(t => t.id === tagId);
|
||
return tag || { id: tagId, tagName: `标签${tagId}` };
|
||
});
|
||
}
|
||
},
|
||
created() {
|
||
this.getList()
|
||
this.getTagOptions()
|
||
this.fetchStatistics()
|
||
},
|
||
methods: {
|
||
/** 查询项目列表 */
|
||
getList() {
|
||
this.loading = true
|
||
const params = {
|
||
pageNum: this.queryParams.pageNum,
|
||
pageSize: this.queryParams.pageSize,
|
||
projectName: this.queryParams.projectName,
|
||
projectType: this.queryParams.projectType,
|
||
region: this.queryParams.region
|
||
}
|
||
|
||
getProjectList(params).then(res => {
|
||
this.projectList = res.data.list
|
||
this.total = res.data.total
|
||
this.loading = false
|
||
}).catch(() => {
|
||
this.loading = false
|
||
})
|
||
},
|
||
/** 获取标签选项 */
|
||
getTagOptions() {
|
||
return new Promise((resolve) => {
|
||
getTagByType({ tagType: '项目标签',tagName: '项目标签' }).then(res => {
|
||
this.tagOptions = res.data || [];
|
||
resolve(this.tagOptions);
|
||
}).catch(() => {
|
||
this.tagOptions = [
|
||
{ id: '1', tagName: '标签1' },
|
||
{ id: '2', tagName: '标签2' }
|
||
];
|
||
resolve(this.tagOptions);
|
||
});
|
||
});
|
||
},
|
||
/** 搜索按钮操作 */
|
||
handleQuery() {
|
||
this.queryParams.pageNum = 1
|
||
this.getList()
|
||
},
|
||
/** 重置按钮操作 */
|
||
resetQuery() {
|
||
this.queryParams = {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
projectName: undefined,
|
||
projectType: undefined,
|
||
region: undefined
|
||
}
|
||
this.handleQuery()
|
||
},
|
||
/** 新增按钮操作 */
|
||
handleAdd() {
|
||
this.dialogTitle = '新增项目'
|
||
// 完全重置表单数据
|
||
this.form = {
|
||
region: '' // 初始化region为空字符串以确保组件正确绑定
|
||
}
|
||
this.dialogVisible = true
|
||
},
|
||
/** 修改按钮操作 */
|
||
handleEdit(row) {
|
||
this.dialogTitle = '编辑项目'
|
||
getProjectDetail(row.id).then(res => {
|
||
// 处理从后端返回的项目标签,将逗号分隔的字符串转为数组
|
||
const projectData = { ...res.data };
|
||
|
||
if (typeof projectData.projectTags === 'string' && projectData.projectTags) {
|
||
projectData.projectTags = projectData.projectTags.split(',');
|
||
} else if (!projectData.projectTags) {
|
||
projectData.projectTags = [];
|
||
}
|
||
|
||
this.form = projectData;
|
||
this.dialogVisible = true;
|
||
})
|
||
},
|
||
/** 详情按钮操作 */
|
||
async handleDetail(row) {
|
||
// 确保标签选项已加载
|
||
if (this.tagOptions.length === 0) {
|
||
await this.getTagOptions();
|
||
}
|
||
|
||
getProjectDetail(row.id).then(res => {
|
||
// 处理项目详情数据,确保标签显示友好
|
||
const detailData = { ...res.data };
|
||
|
||
// 如果标签是字符串,则保持原样,用于显示
|
||
if (typeof detailData.projectTags === 'string' && detailData.projectTags) {
|
||
// 可以在这里处理标签的显示格式,如果需要的话
|
||
} else if (Array.isArray(detailData.projectTags)) {
|
||
// 如果是数组,转为逗号分隔的字符串
|
||
detailData.projectTags = detailData.projectTags.join(',');
|
||
}
|
||
|
||
this.detail = detailData;
|
||
this.detailVisible = true;
|
||
})
|
||
},
|
||
/** 删除按钮操作 */
|
||
handleDelete(row) {
|
||
this.$confirm('是否确认删除该项目?', '警告', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
deleteProject(row.id).then(() => {
|
||
this.getList()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
/** 表单提交 */
|
||
submitForm() {
|
||
this.$refs['form'].validate(valid => {
|
||
if (valid) {
|
||
// 处理项目标签,将数组转为逗号分隔的字符串
|
||
const formData = { ...this.form };
|
||
if (Array.isArray(formData.projectTags)) {
|
||
formData.projectTags = formData.projectTags.join(',');
|
||
}
|
||
|
||
// 如果表单有ID则为编辑操作,否则为新增操作
|
||
const isEdit = formData.id !== undefined;
|
||
const method = isEdit ? updateProject(formData.id, formData) : addProject(formData);
|
||
|
||
method.then(() => {
|
||
this.dialogVisible = false;
|
||
this.getList();
|
||
});
|
||
}
|
||
});
|
||
},
|
||
/** 图片上传成功处理 */
|
||
handleImageSuccess(res, file) {
|
||
// 假设后端返回了图片URL
|
||
this.form.projectImage = res.data
|
||
},
|
||
/** 图片上传前处理 */
|
||
beforeImageUpload(file) {
|
||
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
|
||
const isLt2M = file.size / 1024 / 1024 < 2
|
||
|
||
if (!isJPG) {
|
||
this.$message.error('上传图片只能是 JPG/PNG 格式!')
|
||
}
|
||
if (!isLt2M) {
|
||
this.$message.error('上传图片大小不能超过 2MB!')
|
||
}
|
||
return isJPG && isLt2M
|
||
},
|
||
/** 分页大小改变 */
|
||
handleSizeChange(val) {
|
||
this.queryParams.pageSize = val
|
||
this.getList()
|
||
},
|
||
/** 分页页码改变 */
|
||
handleCurrentChange(val) {
|
||
this.queryParams.pageNum = val
|
||
this.getList()
|
||
},
|
||
/** 获取统计数据 */
|
||
async fetchStatistics() {
|
||
try {
|
||
const res = await getAllProjectStatistics()
|
||
this.statistics = {
|
||
managementArea: res.data.manageArea || '0',
|
||
availableArea: res.data.availableArea || '0',
|
||
availableRatio: res.data.availableArea && res.data.manageArea
|
||
? ((res.data.availableArea / res.data.manageArea) * 100).toFixed(2)
|
||
: '0',
|
||
totalRooms: res.data.totalRooms || '0',
|
||
availableRooms: res.data.availableRooms || '0'
|
||
}
|
||
} catch (error) {
|
||
console.error('获取统计数据失败:', error)
|
||
// 使用默认数据
|
||
this.statistics = {
|
||
managementArea: '37,234.52',
|
||
availableArea: '20,916.2',
|
||
availableRatio: '56.17',
|
||
totalRooms: '637',
|
||
availableRooms: '51'
|
||
}
|
||
}
|
||
},
|
||
/** 对话框打开时处理 */
|
||
onDialogOpen() {
|
||
// 只重置校验状态,不清空表单数据
|
||
this.$nextTick(() => {
|
||
this.$refs['form'] && this.$refs['form'].clearValidate()
|
||
})
|
||
},
|
||
/** 对话框关闭时处理 */
|
||
onDialogClose() {
|
||
// 重置表单和校验状态
|
||
this.form = {}
|
||
this.$refs['form'].clearValidate()
|
||
},
|
||
handleRegionChange(regionText) {
|
||
|
||
this.queryParams.region = regionText
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.statistics-row {
|
||
margin-bottom: 20px;
|
||
|
||
.stat-card {
|
||
background: #fff;
|
||
padding: 20px;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||
text-align: center;
|
||
|
||
.stat-label {
|
||
color: #606266;
|
||
font-size: 14px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.stat-value {
|
||
color: #303133;
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 确保下拉框和输入框宽度一致 */
|
||
::v-deep .el-form-item {
|
||
.el-select {
|
||
width: 100%;
|
||
}
|
||
.el-input {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.avatar-uploader .el-upload {
|
||
border: 1px dashed #d9d9d9;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.avatar-uploader .el-upload:hover {
|
||
border-color: #409EFF;
|
||
}
|
||
.avatar-uploader-icon {
|
||
font-size: 28px;
|
||
color: #8c939d;
|
||
width: 178px;
|
||
height: 178px;
|
||
line-height: 178px;
|
||
text-align: center;
|
||
}
|
||
.avatar {
|
||
width: 178px;
|
||
height: 178px;
|
||
display: block;
|
||
}
|
||
.pagination {
|
||
margin-top: 15px;
|
||
text-align: right;
|
||
}
|
||
</style> |