601 lines
18 KiB
Vue
601 lines
18 KiB
Vue
<template>
|
||
<div class="ResidentMainBox p-2 w-full">
|
||
<PageHeader title="活动"></PageHeader>
|
||
<div class="ResidentMainBody">
|
||
<el-card>
|
||
<template #header>
|
||
<div class="title">基本信息</div>
|
||
</template>
|
||
<div class="addBuildingFormBody">
|
||
<div class="formbox">
|
||
<el-form ref="formRef" :model="form" :rules="rules" label-width="150px">
|
||
<el-row>
|
||
<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"
|
||
@change="
|
||
(data: string[] | null) => {
|
||
if (data) {
|
||
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="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 alt="活动封面" 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>
|
||
<el-row>
|
||
<el-col>
|
||
<el-form-item label="活动详情" prop="content">
|
||
<editor v-model="form.content" :height="500" :min-height="300" :read-only="false" :file-size="5" :type="'url'" />
|
||
</el-form-item>
|
||
</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>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script setup lang="ts">
|
||
import editor from '@/components/Editor/index.vue';
|
||
import { ref } from 'vue';
|
||
import PageHeader from '@/components/Pageheader/index.vue';
|
||
import { addActivity, getActivity, updateActivity } from '@/api/system/Activity';
|
||
import { genFileId, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
|
||
import AMapLoader from '@amap/amap-jsapi-loader';
|
||
import { Plus } from '@element-plus/icons-vue';
|
||
|
||
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': '',
|
||
// 发布状态 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) => {
|
||
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) => {
|
||
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
|
||
};
|
||
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;
|
||
// }
|
||
});
|
||
}
|
||
|
||
// ! 提交========================================
|
||
// 提交
|
||
const submitForm = () => {
|
||
console.log(form.value);
|
||
// if (!isSignin.value) {
|
||
// form.value.SignInfo = null;
|
||
// }
|
||
formRef.value?.validate(async (valid: boolean) => {
|
||
if (valid) {
|
||
if (userid.value) {
|
||
updateActivity(form.value).then(() => {
|
||
ElMessage.success('编辑成功');
|
||
handleBack();
|
||
});
|
||
} else {
|
||
addActivity(form.value).then(() => {
|
||
ElMessage.success('添加成功');
|
||
handleBack();
|
||
});
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
// 返回
|
||
function handleBack() {
|
||
router.push({
|
||
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;
|
||
|
||
.formbox {
|
||
width: 1000px;
|
||
margin: 0 auto;
|
||
}
|
||
&:deep(div.el-step__head.is-process) {
|
||
& > div.el-step__line {
|
||
width: 0;
|
||
border: 1px dashed gray;
|
||
background: transparent;
|
||
}
|
||
}
|
||
&:deep(.el-step.is-vertical .el-step__line) {
|
||
top: 20px;
|
||
}
|
||
&:deep(div.el-step__head.is-finish) {
|
||
& > div.el-step__line {
|
||
width: 0;
|
||
border: 1px solid var(--el-color-primary);
|
||
background: transparent;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|