921 lines
22 KiB
Vue
921 lines
22 KiB
Vue
<template>
|
|
<div class="dashboard">
|
|
<div class="dashboard-header">
|
|
<div class="header-content">
|
|
<h1>管理后台</h1>
|
|
<p>泉友米车友会</p>
|
|
</div>
|
|
<van-button class="logout-btn" size="small" round @click="logout">
|
|
<van-icon name="logout" /> 退出
|
|
</van-button>
|
|
</div>
|
|
|
|
<div class="dashboard-content">
|
|
<div class="content-header">
|
|
<h2>活动管理</h2>
|
|
<van-button type="primary" round icon="plus" @click="createNewActivity">
|
|
创建活动
|
|
</van-button>
|
|
</div>
|
|
|
|
<div class="activity-cards">
|
|
<div v-for="activity in activities" :key="activity.id" class="activity-card" @click="viewActivityDetail(activity)">
|
|
<div class="card-header">
|
|
<h3>{{ activity.title }}</h3>
|
|
<van-tag :type="activity.status === 'upcoming' ? 'primary' : 'default'">
|
|
{{ activity.status === 'upcoming' ? '进行中' : '已结束' }}
|
|
</van-tag>
|
|
</div>
|
|
<p class="card-desc">{{ activity.description }}</p>
|
|
<div class="card-info">
|
|
<div class="info-row">
|
|
<van-icon name="location-o" />
|
|
<span>{{ activity.location }}</span>
|
|
</div>
|
|
<div class="info-row">
|
|
<van-icon name="clock-o" />
|
|
<span>{{ formatDate(activity.startTime) }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<van-icon name="arrow" />
|
|
</div>
|
|
</div>
|
|
|
|
<van-empty v-if="!activities.length" description="暂无活动" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 活动表单弹窗 -->
|
|
<van-popup v-model:show="showActivityForm" position="bottom" round>
|
|
<div class="form-popup">
|
|
<div class="popup-header">
|
|
<h3>创建活动</h3>
|
|
<van-icon name="cross" @click="closeActivityForm" />
|
|
</div>
|
|
<van-form @submit="submitActivity" class="popup-form">
|
|
<van-cell-group>
|
|
<van-field
|
|
v-model="activityForm.title"
|
|
label="活动标题"
|
|
placeholder="请输入活动标题"
|
|
required
|
|
:rules="[{ required: true, message: '请输入活动标题' }]"
|
|
/>
|
|
<van-field
|
|
v-model="activityForm.description"
|
|
label="活动描述"
|
|
type="textarea"
|
|
placeholder="请输入活动描述,包括活动内容、注意事项等"
|
|
rows="3"
|
|
autosize
|
|
maxlength="500"
|
|
show-word-limit
|
|
/>
|
|
<van-field
|
|
v-model="activityForm.price"
|
|
label="人均费用"
|
|
type="number"
|
|
placeholder="请输入人均费用(选填)"
|
|
>
|
|
<template #left-icon>
|
|
<van-icon name="gold-coin-o" style="margin-right: 8px;" />
|
|
</template>
|
|
<template #button>
|
|
<span style="color: #999;">元</span>
|
|
</template>
|
|
</van-field>
|
|
<van-field
|
|
v-model="activityForm.budget"
|
|
label="活动预算"
|
|
type="number"
|
|
placeholder="请输入预算金额(选填)"
|
|
>
|
|
<template #left-icon>
|
|
<van-icon name="balance-o" style="margin-right: 8px;" />
|
|
</template>
|
|
</van-field>
|
|
<van-field
|
|
v-model="activityForm.location"
|
|
label="活动地点"
|
|
placeholder="点击选择地点"
|
|
readonly
|
|
is-link
|
|
required
|
|
:rules="[{ required: true, message: '请选择活动地点' }]"
|
|
@click="openMapPicker"
|
|
>
|
|
<template #left-icon>
|
|
<van-icon name="location-o" style="margin-right: 8px;" />
|
|
</template>
|
|
</van-field>
|
|
<van-field
|
|
v-model="startTimeDisplay"
|
|
label="开始时间"
|
|
placeholder="请选择开始时间"
|
|
readonly
|
|
is-link
|
|
required
|
|
:rules="[{ required: true, message: '请选择开始时间' }]"
|
|
@click="showStartTimePicker = true"
|
|
>
|
|
<template #left-icon>
|
|
<van-icon name="clock-o" style="margin-right: 8px;" />
|
|
</template>
|
|
</van-field>
|
|
<van-field
|
|
v-model="deadlineDisplay"
|
|
label="报名截止"
|
|
placeholder="请选择报名截止时间"
|
|
readonly
|
|
is-link
|
|
required
|
|
:rules="[{ required: true, message: '请选择报名截止时间' }]"
|
|
@click="showDeadlinePicker = true"
|
|
>
|
|
<template #left-icon>
|
|
<van-icon name="calendar-o" style="margin-right: 8px;" />
|
|
</template>
|
|
</van-field>
|
|
</van-cell-group>
|
|
<div class="form-actions">
|
|
<van-button round block type="primary" native-type="submit" size="large">
|
|
创建活动
|
|
</van-button>
|
|
</div>
|
|
</van-form>
|
|
</div>
|
|
</van-popup>
|
|
|
|
<!-- 开始时间选择器 -->
|
|
<van-popup v-model:show="showStartTimePicker" position="bottom" round>
|
|
<div class="datetime-picker-popup">
|
|
<div class="picker-header">
|
|
<van-button size="small" @click="showStartTimePicker = false">取消</van-button>
|
|
<h3>选择开始时间</h3>
|
|
<van-button size="small" type="primary" @click="confirmStartTime">确定</van-button>
|
|
</div>
|
|
<div class="picker-body">
|
|
<input
|
|
type="datetime-local"
|
|
v-model="startTimeInput"
|
|
class="datetime-input"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</van-popup>
|
|
|
|
<!-- 报名截止时间选择器 -->
|
|
<van-popup v-model:show="showDeadlinePicker" position="bottom" round>
|
|
<div class="datetime-picker-popup">
|
|
<div class="picker-header">
|
|
<van-button size="small" @click="showDeadlinePicker = false">取消</van-button>
|
|
<h3>选择报名截止时间</h3>
|
|
<van-button size="small" type="primary" @click="confirmDeadline">确定</van-button>
|
|
</div>
|
|
<div class="picker-body">
|
|
<input
|
|
type="datetime-local"
|
|
v-model="deadlineInput"
|
|
class="datetime-input"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</van-popup>
|
|
|
|
<!-- 地图选点弹窗 -->
|
|
<van-popup v-model:show="showMapPicker" position="bottom" round :style="{ height: '80%' }">
|
|
<div class="map-picker">
|
|
<div class="map-picker-header">
|
|
<van-icon name="arrow-left" @click="showMapPicker = false" />
|
|
<h3>选择地点</h3>
|
|
<van-button type="primary" size="small" @click="confirmLocation">确定</van-button>
|
|
</div>
|
|
<div class="map-search">
|
|
<van-search
|
|
v-model="searchKeyword"
|
|
placeholder="搜索地点"
|
|
@search="searchLocation"
|
|
@clear="clearSearch"
|
|
show-action
|
|
action-text="搜索"
|
|
/>
|
|
</div>
|
|
<div v-if="searchResults.length" class="search-results">
|
|
<div
|
|
v-for="(result, index) in searchResults"
|
|
:key="index"
|
|
class="search-result-item"
|
|
@click="selectSearchResult(result)"
|
|
>
|
|
<div class="result-name">{{ result.name }}</div>
|
|
<div class="result-address">{{ result.address }}</div>
|
|
</div>
|
|
</div>
|
|
<div id="map-picker-container" class="map-container"></div>
|
|
<div class="selected-location">
|
|
<van-icon name="location-o" />
|
|
<span>{{ selectedLocation.name || '请在地图上选择位置' }}</span>
|
|
</div>
|
|
</div>
|
|
</van-popup>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, watch } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { showToast } from 'vant';
|
|
import api from '../../api';
|
|
|
|
const router = useRouter();
|
|
const activities = ref([]);
|
|
|
|
const showActivityForm = ref(false);
|
|
const showMapPicker = ref(false);
|
|
const showStartTimePicker = ref(false);
|
|
const showDeadlinePicker = ref(false);
|
|
const searchKeyword = ref('');
|
|
const searchResults = ref([]);
|
|
const selectedLocation = ref({});
|
|
let pickerMap = null;
|
|
let pickerMarker = null;
|
|
let geocoder = null;
|
|
|
|
const activityForm = ref({
|
|
title: '',
|
|
description: '',
|
|
budget: '',
|
|
location: '',
|
|
locationLat: null,
|
|
locationLng: null,
|
|
startTime: '',
|
|
registrationDeadline: ''
|
|
});
|
|
|
|
// 日期时间选择器的值
|
|
const startTimeInput = ref('');
|
|
const deadlineInput = ref('');
|
|
|
|
// 显示格式化的日期时间
|
|
const startTimeDisplay = computed(() => {
|
|
if (!activityForm.value.startTime) return '';
|
|
return formatDateTime(activityForm.value.startTime);
|
|
});
|
|
|
|
const deadlineDisplay = computed(() => {
|
|
if (!activityForm.value.registrationDeadline) return '';
|
|
return formatDateTime(activityForm.value.registrationDeadline);
|
|
});
|
|
|
|
const formatDate = (date) => {
|
|
return new Date(date).toLocaleDateString('zh-CN', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
};
|
|
|
|
// 格式化日期时间显示
|
|
const formatDateTime = (dateTime) => {
|
|
if (!dateTime) return '';
|
|
const date = new Date(dateTime);
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
};
|
|
|
|
// 开始时间确认
|
|
const confirmStartTime = () => {
|
|
if (!startTimeInput.value) {
|
|
showToast('请选择开始时间');
|
|
return;
|
|
}
|
|
const selectedDate = new Date(startTimeInput.value);
|
|
activityForm.value.startTime = selectedDate.toISOString();
|
|
showStartTimePicker.value = false;
|
|
};
|
|
|
|
// 报名截止时间确认
|
|
const confirmDeadline = () => {
|
|
if (!deadlineInput.value) {
|
|
showToast('请选择报名截止时间');
|
|
return;
|
|
}
|
|
const selectedDate = new Date(deadlineInput.value);
|
|
if (activityForm.value.startTime && selectedDate > new Date(activityForm.value.startTime)) {
|
|
showToast('报名截止时间不能晚于活动开始时间');
|
|
return;
|
|
}
|
|
activityForm.value.registrationDeadline = selectedDate.toISOString();
|
|
showDeadlinePicker.value = false;
|
|
};
|
|
|
|
// 打开地图选择器
|
|
const openMapPicker = () => {
|
|
showMapPicker.value = true;
|
|
};
|
|
|
|
// 关闭活动表单
|
|
const closeActivityForm = () => {
|
|
showActivityForm.value = false;
|
|
activityForm.value = {
|
|
title: '',
|
|
description: '',
|
|
budget: '',
|
|
location: '',
|
|
locationLat: null,
|
|
locationLng: null,
|
|
startTime: '',
|
|
registrationDeadline: ''
|
|
};
|
|
startTimeInput.value = '';
|
|
deadlineInput.value = '';
|
|
};
|
|
|
|
const loadActivities = async () => {
|
|
try {
|
|
const { data } = await api.getActivities();
|
|
activities.value = data;
|
|
} catch (error) {
|
|
showToast('加载失败');
|
|
}
|
|
};
|
|
|
|
const createNewActivity = () => {
|
|
activityForm.value = {
|
|
title: '',
|
|
description: '',
|
|
budget: '',
|
|
location: '',
|
|
locationLat: null,
|
|
locationLng: null,
|
|
startTime: '',
|
|
registrationDeadline: ''
|
|
};
|
|
startTimeInput.value = '';
|
|
deadlineInput.value = '';
|
|
showActivityForm.value = true;
|
|
};
|
|
|
|
const viewActivityDetail = (activity) => {
|
|
router.push(`/admin/activity/${activity.id}`);
|
|
};
|
|
|
|
const initMapPicker = async () => {
|
|
try {
|
|
console.log('开始初始化地图选择器...');
|
|
const AMap = await window.loadAMap();
|
|
console.log('地图API加载完成');
|
|
|
|
if (pickerMap) {
|
|
console.log('销毁旧地图实例');
|
|
pickerMap.destroy();
|
|
}
|
|
|
|
const center = activityForm.value.locationLat && activityForm.value.locationLng
|
|
? [activityForm.value.locationLng, activityForm.value.locationLat]
|
|
: [118.6762, 24.8742];
|
|
|
|
console.log('地图中心点:', center);
|
|
|
|
pickerMap = new AMap.Map('map-picker-container', {
|
|
zoom: 15,
|
|
center: center
|
|
});
|
|
|
|
console.log('地图实例创建完成');
|
|
|
|
geocoder = new AMap.Geocoder();
|
|
console.log('地理编码器创建完成');
|
|
|
|
pickerMarker = new AMap.Marker({
|
|
position: center,
|
|
map: pickerMap,
|
|
draggable: true
|
|
});
|
|
|
|
console.log('地图标记创建完成');
|
|
|
|
if (activityForm.value.location) {
|
|
selectedLocation.value = {
|
|
name: activityForm.value.location,
|
|
lat: activityForm.value.locationLat,
|
|
lng: activityForm.value.locationLng
|
|
};
|
|
}
|
|
|
|
pickerMap.on('click', (e) => {
|
|
const { lng, lat } = e.lnglat;
|
|
pickerMarker.setPosition([lng, lat]);
|
|
|
|
selectedLocation.value = {
|
|
name: '获取地址中...',
|
|
lat: lat,
|
|
lng: lng
|
|
};
|
|
|
|
if (geocoder) {
|
|
geocoder.getAddress([lng, lat], (status, result) => {
|
|
if (status === 'complete' && result.info === 'OK') {
|
|
selectedLocation.value.name = result.regeocode.formattedAddress;
|
|
} else {
|
|
console.warn('逆地理编码失败:', status, result);
|
|
selectedLocation.value.name = `泉州市区 (${lat.toFixed(4)}, ${lng.toFixed(4)})`;
|
|
}
|
|
});
|
|
} else {
|
|
selectedLocation.value.name = `泉州市区 (${lat.toFixed(4)}, ${lng.toFixed(4)})`;
|
|
}
|
|
});
|
|
|
|
pickerMarker.on('dragend', (e) => {
|
|
const { lng, lat } = e.lnglat;
|
|
|
|
selectedLocation.value = {
|
|
name: '获取地址中...',
|
|
lat: lat,
|
|
lng: lng
|
|
};
|
|
|
|
if (geocoder) {
|
|
geocoder.getAddress([lng, lat], (status, result) => {
|
|
if (status === 'complete' && result.info === 'OK') {
|
|
selectedLocation.value.name = result.regeocode.formattedAddress;
|
|
} else {
|
|
console.warn('逆地理编码失败:', status, result);
|
|
selectedLocation.value.name = `泉州市区 (${lat.toFixed(4)}, ${lng.toFixed(4)})`;
|
|
}
|
|
});
|
|
} else {
|
|
selectedLocation.value.name = `泉州市区 (${lat.toFixed(4)}, ${lng.toFixed(4)})`;
|
|
}
|
|
});
|
|
|
|
console.log('地图初始化完成');
|
|
} catch (error) {
|
|
console.error('地图初始化失败:', error);
|
|
showToast('地图加载失败: ' + error.message);
|
|
}
|
|
};
|
|
|
|
const searchLocation = async () => {
|
|
if (!searchKeyword.value.trim()) {
|
|
showToast('请输入搜索关键词');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
console.log('开始搜索地点:', searchKeyword.value);
|
|
const AMap = await window.loadAMap();
|
|
|
|
if (!AMap.PlaceSearch) {
|
|
console.error('PlaceSearch插件未加载');
|
|
showToast('地图搜索功能未就绪');
|
|
return;
|
|
}
|
|
|
|
const placeSearch = new AMap.PlaceSearch({
|
|
pageSize: 10,
|
|
pageIndex: 1,
|
|
city: '泉州',
|
|
citylimit: false,
|
|
extensions: 'base'
|
|
});
|
|
|
|
placeSearch.search(searchKeyword.value, (status, result) => {
|
|
console.log('搜索状态:', status);
|
|
console.log('搜索结果:', result);
|
|
|
|
if (status === 'complete' && result.info === 'OK') {
|
|
if (result.poiList && result.poiList.pois && result.poiList.pois.length > 0) {
|
|
searchResults.value = result.poiList.pois.map(poi => ({
|
|
name: poi.name,
|
|
address: poi.address || poi.district || '',
|
|
lat: poi.location.lat,
|
|
lng: poi.location.lng
|
|
}));
|
|
console.log('搜索成功,找到', searchResults.value.length, '个结果');
|
|
} else {
|
|
searchResults.value = [];
|
|
showToast('未找到相关地点');
|
|
}
|
|
} else if (status === 'no_data') {
|
|
searchResults.value = [];
|
|
showToast('未找到相关地点');
|
|
} else {
|
|
console.error('搜索失败:', status, result);
|
|
searchResults.value = [];
|
|
showToast('搜索失败: ' + (result.info || status));
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('搜索异常:', error);
|
|
showToast('搜索失败: ' + error.message);
|
|
}
|
|
};
|
|
|
|
const selectSearchResult = (result) => {
|
|
selectedLocation.value = result;
|
|
pickerMarker.setPosition([result.lng, result.lat]);
|
|
pickerMap.setCenter([result.lng, result.lat]);
|
|
searchResults.value = [];
|
|
searchKeyword.value = '';
|
|
};
|
|
|
|
const clearSearch = () => {
|
|
searchResults.value = [];
|
|
};
|
|
|
|
const confirmLocation = () => {
|
|
if (selectedLocation.value.lat && selectedLocation.value.lng) {
|
|
activityForm.value.location = selectedLocation.value.name;
|
|
activityForm.value.locationLat = selectedLocation.value.lat;
|
|
activityForm.value.locationLng = selectedLocation.value.lng;
|
|
showMapPicker.value = false;
|
|
} else {
|
|
showToast('请在地图上选择位置');
|
|
}
|
|
};
|
|
|
|
watch(showMapPicker, (val) => {
|
|
if (val) {
|
|
setTimeout(() => {
|
|
initMapPicker();
|
|
}, 300);
|
|
}
|
|
});
|
|
|
|
const submitActivity = async () => {
|
|
try {
|
|
const formData = { ...activityForm.value };
|
|
// 清理空字符串,转换为null
|
|
if (formData.budget === '' || formData.budget === undefined) {
|
|
formData.budget = null;
|
|
}
|
|
if (formData.price === '' || formData.price === undefined) {
|
|
formData.price = null;
|
|
}
|
|
|
|
await api.createActivity(formData);
|
|
showToast('创建成功');
|
|
closeActivityForm();
|
|
loadActivities();
|
|
} catch (error) {
|
|
showToast('创建失败');
|
|
}
|
|
};
|
|
|
|
const logout = () => {
|
|
localStorage.removeItem('token');
|
|
router.push('/admin/login');
|
|
};
|
|
|
|
onMounted(() => {
|
|
loadActivities();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.dashboard {
|
|
min-height: 100vh;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.dashboard-header {
|
|
background: #fff;
|
|
padding: 24px 20px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.header-content h1 {
|
|
font-size: 24px;
|
|
margin: 0 0 4px;
|
|
font-weight: 700;
|
|
color: #1a1a1a;
|
|
}
|
|
|
|
.header-content p {
|
|
font-size: 14px;
|
|
margin: 0;
|
|
color: #666;
|
|
}
|
|
|
|
.logout-btn {
|
|
background: #f5f5f5;
|
|
border: none;
|
|
color: #666;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.logout-btn:active {
|
|
background: #e8e8e8;
|
|
}
|
|
|
|
.dashboard-content {
|
|
padding: 20px 16px;
|
|
}
|
|
|
|
.content-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.content-header h2 {
|
|
font-size: 20px;
|
|
margin: 0;
|
|
color: #1a1a1a;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.activity-cards {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.activity-card {
|
|
background: #fff;
|
|
border-radius: 16px;
|
|
padding: 20px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
border: 1px solid #f0f0f0;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
position: relative;
|
|
}
|
|
|
|
.activity-card:active {
|
|
transform: scale(0.98);
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 12px;
|
|
gap: 12px;
|
|
}
|
|
|
|
.card-header h3 {
|
|
font-size: 17px;
|
|
margin: 0;
|
|
color: #1a1a1a;
|
|
font-weight: 700;
|
|
line-height: 1.4;
|
|
flex: 1;
|
|
}
|
|
|
|
.card-desc {
|
|
font-size: 14px;
|
|
color: #666;
|
|
margin: 0 0 12px;
|
|
line-height: 1.6;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.card-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding-top: 12px;
|
|
border-top: 1px solid #f5f5f5;
|
|
}
|
|
|
|
.info-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 13px;
|
|
color: #666;
|
|
}
|
|
|
|
.info-row .van-icon {
|
|
font-size: 16px;
|
|
color: #FF6700;
|
|
}
|
|
|
|
.card-footer {
|
|
position: absolute;
|
|
right: 20px;
|
|
bottom: 20px;
|
|
color: #ddd;
|
|
font-size: 16px;
|
|
}
|
|
|
|
/* 弹窗 */
|
|
.form-popup {
|
|
max-height: 85vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: #fff;
|
|
border-radius: 16px 16px 0 0;
|
|
}
|
|
|
|
.popup-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.popup-header h3 {
|
|
font-size: 18px;
|
|
margin: 0;
|
|
color: #1a1a1a;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.popup-header .van-icon {
|
|
font-size: 20px;
|
|
color: #999;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.popup-form {
|
|
padding: 0;
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
}
|
|
|
|
.popup-form .van-cell-group {
|
|
margin: 0;
|
|
background: #fff;
|
|
}
|
|
|
|
.popup-form .van-cell {
|
|
padding: 14px 20px;
|
|
}
|
|
|
|
.popup-form .van-field__label {
|
|
width: 90px;
|
|
color: #333;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.popup-form .van-field__control {
|
|
color: #1a1a1a;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.popup-form .van-field__control::placeholder {
|
|
color: #bbb;
|
|
}
|
|
|
|
.form-actions {
|
|
margin: 0;
|
|
padding: 16px 20px 20px;
|
|
flex-shrink: 0;
|
|
background: #fff;
|
|
border-top: 1px solid #f5f5f5;
|
|
}
|
|
|
|
.form-actions .van-button {
|
|
height: 48px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* 地图选点 */
|
|
.map-picker {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.map-picker-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 16px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.map-picker-header h3 {
|
|
flex: 1;
|
|
text-align: center;
|
|
font-size: 16px;
|
|
margin: 0;
|
|
color: #333;
|
|
}
|
|
|
|
.map-picker-header .van-icon {
|
|
font-size: 20px;
|
|
color: #333;
|
|
}
|
|
|
|
.map-search {
|
|
padding: 12px 16px;
|
|
background: #fff;
|
|
}
|
|
|
|
.search-results {
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
background: #fff;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.search-result-item {
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid #f5f5f5;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.search-result-item:active {
|
|
background: #f5f5f5;
|
|
}
|
|
|
|
.result-name {
|
|
font-size: 14px;
|
|
color: #333;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.result-address {
|
|
font-size: 12px;
|
|
color: #999;
|
|
}
|
|
|
|
.map-container {
|
|
flex: 1;
|
|
width: 100%;
|
|
}
|
|
|
|
.selected-location {
|
|
padding: 12px 16px;
|
|
background: #fff;
|
|
border-top: 1px solid #f0f0f0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.selected-location .van-icon {
|
|
color: #FF6700;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.selected-location span {
|
|
flex: 1;
|
|
font-size: 14px;
|
|
color: #333;
|
|
}
|
|
|
|
.datetime-picker-popup {
|
|
background: #fff;
|
|
border-radius: 16px 16px 0 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.picker-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 16px 20px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.picker-header h3 {
|
|
font-size: 16px;
|
|
margin: 0;
|
|
color: #333;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.picker-body {
|
|
padding: 20px;
|
|
}
|
|
|
|
.datetime-input {
|
|
width: 100%;
|
|
padding: 12px;
|
|
font-size: 16px;
|
|
border: 1px solid #e8e8e8;
|
|
border-radius: 8px;
|
|
outline: none;
|
|
color: #333;
|
|
}
|
|
|
|
.datetime-input:focus {
|
|
border-color: #FF6700;
|
|
}
|
|
</style>
|