Files
sunProduct/src/views/community/AddCommunity.vue
2026-09-10 16:12:22 +08:00

540 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="box">
<div class="header">
<span>智慧物业后台管理系统</span>
<navbar ref="navbarRef" :Showhamburger="false" />
</div>
<div class="container AddBuildingBox">
<el-form ref="formRef" :model="form" :rules="rules" class="formBody" inline label-width="100px">
<div class="AddBuildingBody">
<div class="title">基本信息</div>
<div class="addBuildingFormBody">
<el-form-item label="小区名称" prop="villageName">
<el-input v-model.trim="form.villageName" maxlength="25" placeholder="请输入小区名称" />
</el-form-item>
<el-form-item label="小区编号" prop="villageCode">
<el-input v-model.trim="form.villageCode" maxlength="20" placeholder="请输入小区编号" />
</el-form-item>
<el-form-item label="所在城市" prop="city">
<el-cascader v-model="city" :options="pcaTextArrList" @change="cityChange"> </el-cascader>
</el-form-item>
<el-form-item label="完整详细地址" prop="buildingArea">
<el-input v-model.trim="form.address" :row="3" maxlength="100" placeholder="请输入完整详细地址" show-word-limit type="textarea" />
</el-form-item>
<el-form-item label="建成时间" prop="buildingArea">
<el-date-picker v-model="builtTime" placeholder="选择建成时间" type="date" value-format="YYYY-MM-DD" @change="changeBuiltTime" />
</el-form-item>
<!-- <el-form-item label="房屋用途" prop="houseUse">-->
<!-- <el-select v-model="form.houseUse" placeholder="请选择房屋用途" style="width: 240px">-->
<!-- <el-option v-for="item in houseUserList" :key="item.id" :label="item.name" :value="item.id" />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item label="小区面积" prop="villageArea">
<el-input v-model.trim="form.villageArea" maxlength="8" placeholder="请输入小区面积"><template #append>㎡</template> </el-input>
</el-form-item>
<el-form-item label="小区图片" style="width: 100%">
<el-upload
ref="uploadRef"
:action="uploadFileUrl"
:auto-upload="false"
:file-list="villageImg"
:headers="headerToken()"
:limit="9"
:on-exceed="handleExceed"
accept=".jpg, .png"
list-type="picture-card"
@change="handleUploadChange"
@before-upload="handleBeforeUpload"
>
<el-icon><Plus /></el-icon>
<template #file="{ file }">
<div>
<img :src="file.url" alt="" class="el-upload-list__item-thumbnail" />
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-delete" @click="handleRemove(file)">
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
<template #tip>
<div class="el-upload__tip">支持jpg, png等格式文件上传,文件大小不超过10MB</div>
</template>
</el-upload>
</el-form-item>
<el-form-item label="小区简介" style="width: 100%">
<el-input v-model.trim="form.remark" :rows="5" maxlength="200" placeholder="小区简介" show-word-limit type="textarea" />
</el-form-item>
</div>
</div>
<div class="AddBuildingBody">
<div class="title">负责人信息</div>
<div class="addBuildingFormBody addBuildingForm_User_Body">
<el-form-item label="姓名">
<el-input v-model.trim="form.manageName" maxlength="20" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="电话" prop="managePhone">
<el-input v-model.trim="form.managePhone" placeholder="请输入电话" />
</el-form-item>
<el-form-item label="微信" prop="manageVx">
<el-input v-model.trim="form.manageVx" maxlength="20" placeholder="请输入微信"></el-input>
</el-form-item>
<el-form-item label="QQ" prop="manageQq">
<el-input v-model.trim="form.manageQq" maxlength="20" placeholder="请输入QQ"> </el-input>
</el-form-item>
<el-form-item label="邮箱" prop="manageEmail">
<el-input v-model.trim="form.manageEmail" maxlength="30" placeholder="请输入邮箱"> </el-input>
</el-form-item>
<div class="pl-100px">
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="cancelForm">取消</el-button>
</div>
</div>
</div>
</el-form>
</div>
</div>
</template>
<script lang="ts" setup>
import Navbar from '@/layout/components/Navbar.vue';
import { ref } from 'vue';
import { pcaTextArr } from 'element-china-area-data';
import type { DataItem } from 'element-china-area-data';
import { useUserStore } from '@/store/modules/user';
import { CascaderValue } from 'element-plus';
import { addVillageAPI, editVillageAPI, getVillageByIdAPI, uploadVillageImageAPI } from '@/api/system/community';
import { communitylist_rows_type } from '@/api/system/community/type';
import { Delete, Plus } from '@element-plus/icons-vue';
import { listHouseUseType_add_house } from '@/api/system/houseUse';
const route = useRoute();
const router = useRouter();
const editId = ref(route.query.id as string);
const isEdit = computed(() => Boolean(editId.value));
const apiPrefix = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = `${apiPrefix}/village/uploadImages`;
// 获取省 市 区╬╬╬╬╬╬╬
const pcaTextArrList = ref<DataItem[]>(pcaTextArr);
// 获取小区用途
const houseUserList = ref([]);
const formRef = ref();
const uploadRef = ref();
const form = ref<communitylist_rows_type>({
villageId: null,
villageName: '',
villageCode: '',
address: '',
houseUse: null,
villageArea: '',
remark: '',
parkingSpaceCount: '',
villageImageUrl: '',
city: '',
status: '0',
choiceStatus: 1, // 0 选择 1未选择
manageName: '',
managePhone: '',
manageVx: '',
manageEmail: '',
manageQq: '',
completDate: ''
});
const builtTime = ref(null);
const city = ref<string[]>([]);
const villageImg = ref([]);
// 表单验证
const rules = ref({
villageName: [{ required: true, message: '请输入小区名称', trigger: 'blur' }],
villageArea: [
{ required: false, message: '请输入小区面积', trigger: 'blur' },
{
validator: (rule, value, callback) => {
const num = Number(value);
if (value === '' || value === null) {
return callback();
}
if (Number.isNaN(num)) {
return callback(new Error('请输入正确的小区面积'));
}
if (num < 0) {
return callback(new Error('小区面积不能小于0'));
}
callback();
}
}
],
parkingSpaceCount: [
{ required: false, message: '请输入车位数量', trigger: 'blur' },
{
validator: (rule, value, callback) => {
const num = Number(value);
if (value === '' || value === null) {
return callback();
}
if (Number.isNaN(num)) {
return callback(new Error('请输入正确的车位数量'));
}
if (num < 0) {
return callback(new Error('车位数量不能小于0'));
}
if (num % 1 !== 0) {
return callback(new Error('请输入正确的整数车位数量'));
}
callback();
}
}
],
villageCode: [
{ required: true, message: '请输入小区编号', trigger: 'blur' },
{
validator: (_, value, callback) => {
if (value === '' || value === null) {
return callback(new Error('请输入小区编号'));
}
if (!/^[a-zA-Z0-9_-]{1,20}$/.test(value)) {
callback(new Error('请输入正确的小区编号,只能包含字母、数字、_和-'));
} else {
callback();
}
}
}
],
city: [{ required: true, message: '请选择城市', trigger: 'blur' }],
managePhone: [
{ required: false, message: '请输入电话', trigger: 'blur' },
{
validator: (_, value, callback) => {
// 值为空直接放行(非必填)
if (value === '' || value === null) {
return callback();
}
if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'));
} else {
callback();
}
}
}
],
manageEmail: [
{ required: false, message: '请输入邮箱', trigger: 'blur' },
{
validator: (_, value, callback) => {
if (value === '' || value === null) {
return callback();
}
// 邮箱总长度不超过254个字符,用户名不超过64个字符
if (value.length > 254 || value.split('@')[0].length > 64) {
callback(new Error('邮箱地址过长'));
} else if (!/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/.test(value)) {
callback(new Error('请输入正确的邮箱'));
} else {
callback();
}
}
}
],
manageVx: [
{ required: false, message: '请输入微信', trigger: 'blur' },
{
validator: (_, value, callback) => {
// 值为空直接放行(非必填)
if (value === '' || value === null) {
return callback();
}
if (!/^[a-zA-Z0-9_]{4,20}$/.test(value)) {
callback(new Error('请输入正确的微信'));
} else {
callback();
}
}
}
],
manageQq: [
{ required: false, message: '请输入QQ', trigger: 'blur' },
{
validator: (_, value, callback) => {
// 值为空直接放行(非必填)
if (value === '' || value === null) {
return callback();
}
if (!/^[1-9]\d{4,10}$/.test(value)) {
callback(new Error('请输入正确的QQ'));
} else {
callback();
}
}
}
]
});
/** 获取编辑小区的信息 */
function getCommunityInfo() {
const id = editId.value;
getVillageByIdAPI(id).then((res) => {
city.value = res.data.city ? res.data.city.split('/') : [];
if (res.data.villageImageUrl) {
villageImg.value = res.data.villageImageUrl
.split(',')
.filter((item) => item !== '')
.map((item) => {
return {
name: item.split('/').pop(),
url: item
};
});
}
if (res.data.completDate) {
builtTime.value = res.data.completDate.split(' ')[0];
}
form.value = {
...res.data,
completDate: builtTime.value
};
});
}
if (isEdit.value) {
getCommunityInfo();
}
// 请求头
function headerToken() {
const useStore = useUserStore();
return {
Authorization: 'Bearer ' + useStore.token
};
}
const allowImgSuffix = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'];
/** 检测文件大小和格式 */
function handleBeforeUpload(file: UploadFile): Promise<boolean | { file: UploadFile; type: string }> {
const type = file.raw.type.split('/').pop();
return new Promise((resolve, reject) => {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
reject({
type: '文件大小错误',
file: file
});
ElMessage.warning('图片大小超过10M,请重新上传');
} else if (!allowImgSuffix.includes(type)) {
reject({
type: '文件类型错误',
file: file
});
ElMessage.warning('图片格式不是 png/jpg/jpeg 格式, 请重新上传');
} else {
resolve(true);
}
});
}
/** 上传狗子 */
async function handleUploadChange(file: UploadFile) {
const isUpload = await handleBeforeUpload(file);
if (typeof isUpload === 'object') {
ElMessage.error(isUpload.type);
return;
} else if (typeof isUpload === 'boolean' && isUpload === true) {
villageImg.value.push(file.raw);
}
}
function handleExceed() {
ElMessage.error('最多上传9张图片');
}
/** 移除钩子 */
const handleRemove = (file: UploadFile) => {
const fileToRemoveIndex = villageImg.value.findIndex((item) => item.uid === file.uid);
if (fileToRemoveIndex === -1) {
return;
}
uploadRef.value.handleRemove(villageImg.value[fileToRemoveIndex]);
villageImg.value.splice(fileToRemoveIndex, 1);
};
// 改变时间
const changeBuiltTime = (value: string) => {
form.value.completDate = value;
};
const cityChange = (value: CascaderValue[]) => {
form.value.city = value.join('/');
};
const reg = {
phone: /^1[3-9]\d{9}$/,
email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
qq: /^[1-9]\d{4,11}$/,
wechat: /^[a-zA-Z][a-zA-Z0-9_-]{5,19}$/
};
// 上传图片
const submitForm = () => {
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
// 1) 将图片拆分为“新上传文件”和“已有图片URL”
const newFiles: File[] = [];
const existingUrls: string[] = [];
villageImg.value.forEach((item: any) => {
// 新增图片:handleUploadChange push 进来的是 File
if (item instanceof File) {
newFiles.push(item);
return;
}
// 编辑态已有图片:通常是 { name, url } 结构
if (item && typeof item === 'object' && typeof item.url === 'string') {
existingUrls.push(item.url);
return;
}
// 兜底:如果有字符串URL也兼容
if (typeof item === 'string') {
existingUrls.push(item);
}
});
// 2) 没有新文件:不调用上传接口,直接用已有URL提交
if (newFiles.length === 0) {
form.value.villageImageUrl = existingUrls.join(',');
submitFormInfo();
return;
}
// 3) 有新文件:先上传新文件,再与已有URL合并
const formdata = new FormData();
newFiles.forEach((file) => formdata.append('files', file));
uploadVillageImageAPI(formdata).then((res) => {
const uploadedUrls = res.data.success.map((x) => x.url).filter(Boolean);
// 4) 最终图片 = 已有图片 + 新上传图片(避免覆盖)
form.value.villageImageUrl = [...existingUrls, ...uploadedUrls].join(',');
submitFormInfo();
});
}
});
};
function submitFormInfo() {
if (form.value.managePhone && form.value.managePhone.trim()) {
if (!reg.phone.test(form.value.managePhone)) {
ElMessage.error('请输入正确的手机号码');
return;
}
}
if (form.value.manageVx && form.value.manageVx.trim()) {
if (!reg.wechat.test(form.value.manageVx)) {
ElMessage.error('请输入正确的微信号');
return;
}
}
if (form.value.manageQq && form.value.manageQq.trim()) {
if (!reg.qq.test(form.value.manageQq)) {
ElMessage.error('请输入正确的QQ号码');
return;
}
}
if (form.value.manageEmail && form.value.manageEmail.trim()) {
if (!reg.email.test(form.value.manageEmail)) {
ElMessage.error('请输入正确的邮箱号码');
return;
}
}
if (isEdit.value) {
editVillageAPI<communitylist_rows_type>(form.value).then(() => {
ElMessage.success('编辑成功');
cancelForm();
});
} else {
addVillageAPI<communitylist_rows_type>(form.value).then(() => {
ElMessage.success('添加成功');
cancelForm();
});
}
}
// 返回
const cancelForm = () => {
router.back();
};
// 获取房屋用途
const getHouseUserList = () => {
listHouseUseType_add_house().then((res) => {
houseUserList.value = res.data;
});
};
getHouseUserList();
</script>
<style lang="scss" scoped>
.box {
background: #deedff;
height: 100%;
overflow: auto;
.header {
display: flex;
align-items: center;
height: 50px;
line-height: 50px;
background: transparent;
& > div {
flex: 1;
}
& > span {
padding-left: 20px;
font-size: 20px;
color: #000;
}
}
}
.AddBuildingBox {
padding: 15px;
height: 100%;
width: 100%;
}
.AddBuildingBody {
margin-top: 20px;
display: flex;
flex-direction: column;
justify-content: center;
background-color: white;
padding: 15px;
.title {
height: 40px;
line-height: 40px;
padding-left: 20px;
background-color: rgba(255, 255, 255, 1);
color: rgba(16, 16, 16, 1);
border-bottom: 1px solid #bbbbbb;
margin-bottom: 50px;
}
.addBuildingFormBody {
width: 900px;
margin: 0 auto;
padding-bottom: 40px;
.el-form-item {
width: 46%;
margin-bottom: 20px;
&:deep(.el-input, .el-date-editor.el-input) {
width: 300px;
}
}
.el-button {
width: 80px;
height: 35px;
margin-right: 20px;
}
}
}
.addBuildingForm_User_Body {
width: 900px;
margin: 0 auto;
.el-button {
width: 80px;
height: 35px;
line-height: 35px;
}
}
</style>