同步6/16

This commit is contained in:
Zy
2026-06-01 18:12:58 +08:00
parent 38274ab612
commit 50e7b14fd6
183 changed files with 8956 additions and 4803 deletions

View File

@@ -8,16 +8,122 @@
</template>
<div class="addBuildingFormBody">
<div class="formbox">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="150px">
<el-row>
<el-col :span="12">
<el-col :span="13">
<el-form-item label="活动标题" prop="title">
<el-input v-model.trim="form.title" placeholder="请输入活动标题" />
</el-form-item>
</el-col>
<el-col :span="11">
<el-form-item label="活动作者" prop="author">
<el-input v-model.trim="form.author" placeholder="请输入活动作者" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="13">
<el-form-item label="活动时间" prop="startTime">
<el-date-picker
v-model="activityTime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
@update:model-value="
(data) => {
if (data && data.length > 0) {
form.startTime = data[0];
form.endTime = data[1];
} else {
form.startTime = '';
form.endTime = '';
}
}
"
type="datetimerange"
range-separator=""
start-placeholder="开始活动"
end-placeholder="结束活动"
/>
</el-form-item>
</el-col>
<el-col :span="11">
<el-form-item label="活动地点" prop="address">
<el-input v-model.trim="form.address" placeholder="请输入活动地点" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="11">
<el-form-item label="报名截止日期" prop="deadline">
<el-date-picker
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
v-model="deadline"
@update:model-value="
(data) => {
if (data) {
form.applyDeadline = data;
} else {
form.applyDeadline = '';
}
}
"
type="date"
placeholder="请选择报名截止日期"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="作者" prop="author">
<el-input v-model.trim="form.author" placeholder="请输入作者" />
<el-form-item label="主办方" prop="organizer">
<el-input v-model.trim="form.organizer" placeholder="请输入主办方" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="联系人" prop="contactName">
<el-input v-model.trim="form.contactName" placeholder="请输入联系人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="contactName">
<el-input v-model.trim="form.contactPhone" placeholder="请输入联系方式" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="活动名额" prop="quota">
<div class="flex items-center">
<el-input-number v-model.trim.number="form.quota" :min="0" placeholder="0代表无限" />
<span class="ml-2 text-gray-500 text-sm" style="font-size: 12px">(填 <b>0</b> 代表<u>不限制名额</u></span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="活动封面" prop="title">
<el-upload
ref="upload"
class="avatar-uploader"
:action="uploadUrl"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:on-exceed="handleExceed"
:on-error="handleError"
:limit="1"
>
<img v-if="form.coverImage" :src="form.coverImage" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="招募要求">
<el-input :row="8" type="textarea" v-model.trim="form.requirements" placeholder="请输入招募要求" />
</el-form-item>
</el-col>
</el-row>
@@ -29,6 +135,69 @@
</el-col>
</el-row>
<!-- <el-row>
<el-col>
<el-form-item label="活动签到">
<el-switch
@change="
(val) => {
if (val) {
initSignMap();
}
}
"
v-model="isSignin"
active-text="签到"
inactive-text="不签到"
:active-value="true"
:inactive-value="false"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="13">
<el-form-item label="签到时间" prop="signinStartTime">
<el-date-picker
v-model="SigninTime"
type="datetimerange"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator=""
@update:model-value="
(data) => {
if (data && data.length > 0) {
form.signinStartTime = data[0];
form.signinEndTime = data[1];
} else {
form.signinStartTime = '';
form.signinEndTime = '';
}
}
"
start-placeholder="开始签到"
end-placeholder="结束签到"
/>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="isSignin">
<el-col>
<el-form-item label="签到设置">
<div>
<div>
<el-button type="primary" v-if="isDraw === 0" @click="startDrawCircle">开始设置签到范围</el-button>
<el-button type="success" v-if="isDraw === 1" @click="editCircle">编辑签到范围</el-button>
<el-button type="warning" v-if="isDraw === 2" @click="stopDrawCircle">结束设置签到范围</el-button>
<el-button type="danger" @click="clearCircle">清空签到范围</el-button>
</div>
<div v-loading="mapReady" class="SignMapbox">
<div id="SignMap" style="height: 100%; width: 100%"></div>
</div>
</div>
</el-form-item>
</el-col>
</el-row> -->
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="handleBack">返回</el-button>
@@ -44,44 +213,281 @@
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { addActivity, getActivity, updateActivity } from '@/api/system/Activity';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { com_repair_project_status } = toRefs<any>(proxy?.useDict('com_repair_project_status'));
import { genFileId, UploadFiles, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import AMapLoader from '@amap/amap-jsapi-loader';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/activity/uploadImage';
const aloaderkey = import.meta.env.VITE_GCJ02_KEY_GD;
const VITE_GCJ02_JS_CODE_GD = import.meta.env.VITE_GCJ02_JS_CODE_GD;
const route = useRoute();
const router = useRouter();
const formRef = ref();
const form = ref({
'id': null,
// 标题
'title': '',
// 作者
'author': '',
// 内容
'content': '',
'status': '0'
// 发布状态 0未发布 1己发布
'status': '0',
// 报名截止日期
'applyDeadline': '',
// 活动时间
'startTime': '',
'endTime': '',
// 活动地点
'address': '',
// 主办方
'organizer': '',
// 联系人
'contactName': '',
// 联系方式
'contactPhone': '',
// 开始签到时间
'signinStartTime': '',
// 结束签到时间
'signinEndTime': '',
// 活动名额
'quota': 0,
// 积分奖励
'pointsReward': 0,
// 活动封面
'coverImage': '',
// 招募要求
'requirements': ''
// 'SignInfo': {
// lng: null, // 经度
// lat: null, // 纬度
// radius: null // 半径 m
// }
});
const rules = {
title: [{ required: true, message: '请输入活动标题', trigger: 'blur' }],
author: [{ required: true, message: '请输入作者', trigger: 'blur' }],
applyDeadline: [{ required: true, message: '请输入报名截止日期', trigger: 'blur' }],
startTime: [{ required: true, message: '请选择开始日期', trigger: 'blur' }],
endTime: [{ required: true, message: '请选择结束日期', trigger: 'blur' }],
address: [{ required: true, message: '请输入活动地点', trigger: 'blur' }],
organizer: [{ required: true, message: '请输入主办方', trigger: 'blur' }],
contactName: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
contactPhone: [{ required: true, message: '请输入联系方式', trigger: 'blur' }],
coverImage: [{ required: true, message: '请上传活动封面', trigger: 'blur' }],
content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
};
const userid = ref();
// 报名截止日期
const deadline = ref([]);
// 有效签到时间
const SigninTime = ref([]);
// 活动时间
const activityTime = ref([]);
// 上传文件
const upload = ref<UploadInstance>();
// 上传成功
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
if (response.data && response.data.url) {
form.value.coverImage = response.data.url;
}
};
// 超出文件限制
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
upload.value!.handleStart(file);
upload.value?.submit();
};
// 上传失败
const handleError: UploadProps['onError'] = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
console.log(error);
};
// ! 投诉 ====================================================================================================
// ! 签到 =======================================
const isSignin = ref(false);
let AMapLi = null;
let map: any = null;
let circle: any = null;
let circleEditor: any = null;
let geocoder: any = null;
const mapReady = ref(false);
let mouseTool = null;
// 0 开始 1编辑 2结束
const isDraw = ref(0);
onMounted(() => {
if (route.query.id) {
userid.value = route.query.id;
init();
}
});
async function initSignMap() {
try {
mapReady.value = true;
window._AMapSecurityConfig = { securityJsCode: VITE_GCJ02_JS_CODE_GD };
console.log('开始执行 AMapLoader.load');
const AMap = await AMapLoader.load({
key: aloaderkey,
version: '2.0',
plugins: ['AMap.MouseTool', 'AMap.CircleEditor', 'AMap.Geolocation', 'AMap.Geocoder']
});
mapReady.value = false;
console.log('AMapLoader.load 执行完毕');
AMapLi = AMap;
map = new AMap.Map('SignMap', {
viewMode: '3D',
zoom: 11,
center: [116.397428, 39.90923]
});
// 解析本地存储的城市信息
const villageInfoStr = localStorage.getItem('villageInfo') || '{}';
let city = '';
try {
const villageInfo = JSON.parse(villageInfoStr);
city = (villageInfo?.city || '').trim();
} catch (err) {
console.warn('解析 villageInfo 失败', err);
}
// 初始化地理编码工具
if (city.trim() !== '') {
geocoder = new AMap.Geocoder({
city: city.trim(),
radius: 1000
});
getGeoCode(city.trim(), map);
}
// 鼠标绘制工具
mouseTool = new AMap.MouseTool(map);
mouseTool.on('draw', (e: any) => {
// 移除旧围栏
if (circle) {
map.remove(circle);
}
circle = e.obj;
});
} catch (error) {
console.error('高德地图初始化失败:', error);
}
}
// 获取 城市经纬度并定位中心
const getGeoCode = (address, map) => {
geocoder.getLocation(address, (status: string, result: any) => {
if (status === 'complete' && result.geocodes.length > 0) {
const geo = result.geocodes[0];
const lng = geo.location.lng;
const lat = geo.location.lat;
map.setCenter([lng, lat]);
map.setZoom(15);
} else {
console.log('未找到该地址');
}
});
};
// 开始绘制圆形
const startDrawCircle = () => {
resetCircle();
if (!mouseTool) return;
isDraw.value = 1; // 编辑
mouseTool.circle({
strokeColor: '#1890ff',
strokeWeight: 3,
fillColor: 'rgba(24, 144, 255, 0.2)',
fillOpacity: 0.4
});
};
// 停止绘制圆形
const stopDrawCircle = () => {
if (!mouseTool) return;
if (mouseTool) {
mouseTool.close(false);
}
if (circleEditor) {
circleEditor.close();
circleEditor = null;
}
isDraw.value = 0; // 开始
mouseTool.close(false);
if (circle) {
const center = circle.getCenter();
const radius = circle.getRadius();
console.log('中心点:', [center.lng, center.lat]);
console.log('半径:', radius, typeof radius);
// form.value.SignInfo.lat = center.lat;
// form.value.SignInfo.lng = center.lng;
// form.value.SignInfo.radius = center.radius;
}
};
// 编辑 圆形
const editCircle = () => {
if (!circle) return;
if (mouseTool) {
mouseTool.close(false);
}
isDraw.value = 2; // 结束
circleEditor = new AMapLi.CircleEditor(map, circle);
circleEditor.open();
};
// 删除圆形
const clearCircle = () => {
// resetCircle();
isDraw.value = 0; // 开始
ElMessage.success('全部清除');
};
// 重置
const resetCircle = () => {
// 不能绘画
if (mouseTool) {
mouseTool.close(false);
}
// 清除圆形
if (circle) {
map.remove(circle);
circle = null;
}
// 不能编辑
if (circleEditor) {
circleEditor.close();
circleEditor = null;
}
};
function init() {
getActivity(userid.value).then((res) => {
form.value = {
...form.value,
...res.data
};
console.log(form.value);
activityTime.value = [res.data.startTime, res.data.endTime];
SigninTime.value = [res.data.signinStartTime, res.data.signinEndTime];
deadline.value = [res.data.applyDeadline];
// if (res.data?.SignInfo !== null) {
// isSignin.value = true;
// }
});
}
if (route.query.id) {
userid.value = route.query.id;
init();
}
// ! 提交========================================
// 提交
const submitForm = () => {
console.log(form.value);
// if (!isSignin.value) {
// form.value.SignInfo = null;
// }
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (userid.value) {
@@ -105,12 +511,63 @@ function handleBack() {
path: '/repair/Activity'
});
}
/**
* 销毁地图及所有工具实例(防内存泄漏、重复初始化)
*/
function destroyMapInstance() {
if (map) {
map.destroy();
map = null;
}
mouseTool = null;
geocoder = null;
circle = null;
AMapLi = null;
mapReady.value = false;
}
onUnmounted(() => {
// destroyMapInstance();
});
</script>
<style scoped lang="scss">
.SignMapbox {
height: 500px;
width: 500px;
margin-top: 10px;
border: 1px solid #ccc;
}
.ResidentMainBox {
display: flex;
flex-direction: column;
.avatar-uploader {
border: 1px solid #ccc;
border-radius: 6px;
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 158px;
height: 158px;
text-align: center;
}
.avatar-uploader .avatar {
width: 158px;
height: 158px;
display: block;
object-fit: cover;
}
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.ResidentMainBody {
margin-top: 20px;
flex: 1;