OfficeSystem/pages/customer/add/index.vue

1116 lines
30 KiB
Vue
Raw Normal View History

2025-11-07 11:09:30 +08:00
<template>
<view class="add-customer-page">
<!-- 自定义导航栏 -->
<view class="custom-navbar">
<view class="navbar-content">
<text class="nav-btn" @click="handleCancel"></text>
<text class="nav-title">客户信息</text>
<text class="nav-btn" style="opacity: 0;">占位</text>
</view>
</view>
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y>
<view class="scroll-content">
<!-- 客户信息部分 -->
<view class="form-section">
<view class="section-title">客户信息</view>
<!-- 客户类型 -->
<view class="form-item">
<text class="form-label">客户类型</text>
<view class="radio-group">
<view
class="radio-item"
:class="{ active: formData.customerType === '个人' }"
@click="formData.customerType = '个人'"
>
<view class="radio-dot" :class="{ checked: formData.customerType === '个人' }"></view>
<text>个人</text>
</view>
<view
class="radio-item"
:class="{ active: formData.customerType === '企业' }"
@click="formData.customerType = '企业'"
>
<view class="radio-dot" :class="{ checked: formData.customerType === '企业' }"></view>
<text>企业</text>
</view>
</view>
</view>
<!-- 客户名称 -->
<view class="form-item">
<input
v-model="formData.name"
class="form-input"
placeholder="输入客户名称"
placeholder-style="color: #999;"
/>
</view>
<!-- 联系电话 -->
<view class="form-item">
<input
v-model="formData.mobile"
class="form-input"
placeholder="输入电话号码"
placeholder-style="color: #999;"
type="number"
/>
</view>
<!-- 微信号 -->
<view class="form-item">
<input
v-model="formData.wechat"
class="form-input"
placeholder="输入微信号"
placeholder-style="color: #999;"
/>
</view>
<!-- 客户来源 -->
<view class="form-item">
<input
v-model="formData.source"
class="form-input"
placeholder="输入客户来源"
placeholder-style="color: #999;"
/>
</view>
<!-- 客户意向 -->
<view class="form-item">
<input
v-model="formData.intents"
class="form-input"
placeholder="输入客户意向"
placeholder-style="color: #999;"
/>
</view>
<!-- 客户状态 -->
<view class="form-item clickable-item" @click="openStatusPicker">
<text v-if="formData.status" class="form-value">{{ getStatusText(formData.status) }}</text>
<text v-else class="form-placeholder">选择客户状态</text>
<text class="arrow"></text>
</view>
<!-- 意向强度 -->
<view class="form-item clickable-item" @click="openIntentLevelPicker">
<text v-if="formData.intentLevel" class="form-value">{{ getIntentLevelText(formData.intentLevel) }}</text>
<text v-else class="form-placeholder">选择意向强度</text>
<text class="arrow"></text>
</view>
<!-- 客户地区 -->
<view class="form-item clickable-item" @click="openRegionPicker" @tap="openRegionPicker">
<text v-if="formData.region" class="form-value">{{ formData.region }}</text>
<text v-else class="form-placeholder">选择客户地区</text>
<text class="arrow"></text>
</view>
<!-- 工作微信 -->
<view class="form-item clickable-item" @click="openWorkWechatPicker">
<text v-if="formData.workWechat" class="form-value">{{ formData.workWechat }}</text>
<text v-else class="form-placeholder">选择工作微信</text>
<text class="arrow"></text>
</view>
</view>
<!-- 其他信息部分 -->
<view class="form-section">
<view class="section-title">其他信息</view>
<!-- 客户星级 -->
<view class="form-item">
<text class="form-label">客户星级</text>
<view class="star-rating">
<text
class="star"
v-for="i in 5"
:key="i"
:class="{ 'filled': i <= formData.rating }"
@click="formData.rating = i"
></text>
</view>
</view>
<!-- 备注 -->
<view class="form-item">
<textarea
v-model="formData.remark"
class="form-textarea"
placeholder="输入备注"
placeholder-style="color: #999;"
auto-height
/>
</view>
<!-- 顾虑点 -->
<view class="form-item">
<textarea
v-model="formData.concern"
class="form-textarea"
placeholder="输入顾虑点"
placeholder-style="color: #999;"
auto-height
/>
</view>
<!-- 痛点 -->
<view class="form-item">
<textarea
v-model="formData.pain"
class="form-textarea"
placeholder="输入痛点"
placeholder-style="color: #999;"
auto-height
/>
</view>
<!-- 关注点 -->
<view class="form-item">
<textarea
v-model="formData.attention"
class="form-textarea"
placeholder="输入关注点"
placeholder-style="color: #999;"
auto-height
/>
</view>
<!-- 需求点 -->
<view class="form-item">
<textarea
v-model="formData.demand"
class="form-textarea"
placeholder="输入需求点"
placeholder-style="color: #999;"
auto-height
/>
</view>
<!-- 下次跟进时间 -->
<view class="form-item clickable-item" @click="openNextFollowTimePicker">
<text v-if="formData.nextFollowTime" class="form-value">{{ formData.nextFollowTime }}</text>
<text v-else class="form-placeholder">选择下次跟进时间可选</text>
<text class="arrow"></text>
</view>
</view>
</view>
</scroll-view>
<!-- 保存按钮 -->
<view class="save-button-wrapper">
<button class="save-button" @click="handleSave" :disabled="saving">保存</button>
</view>
<!-- 客户状态选择弹窗 -->
<view v-if="showStatusPicker" class="modal-mask" @click="showStatusPicker = false">
<view class="modal-content" @click.stop>
<view class="modal-title">选择客户状态</view>
<view class="picker-options">
<view
v-for="item in statusOptions"
:key="item.value"
class="picker-option"
:class="{ active: tempStatus === item.value }"
@click="selectStatus(item.value)"
>
<text>{{ item.label }}</text>
<text v-if="tempStatus === item.value" class="check"></text>
</view>
</view>
<view class="modal-buttons">
<button class="modal-btn" @click="showStatusPicker = false">取消</button>
<button class="modal-btn primary" @click="confirmStatus">确定</button>
</view>
</view>
</view>
<!-- 意向强度选择弹窗 -->
<view v-if="showIntentLevelPicker" class="modal-mask" @click="showIntentLevelPicker = false">
<view class="modal-content" @click.stop>
<view class="modal-title">选择意向强度</view>
<view class="picker-options">
<view
v-for="item in intentLevelOptions"
:key="item.value"
class="picker-option"
:class="{ active: tempIntentLevel === item.value }"
@click="selectIntentLevel(item.value)"
>
<text>{{ item.label }}</text>
<text v-if="tempIntentLevel === item.value" class="check"></text>
</view>
</view>
<view class="modal-buttons">
<button class="modal-btn" @click="showIntentLevelPicker = false">取消</button>
<button class="modal-btn primary" @click="confirmIntentLevel">确定</button>
</view>
</view>
</view>
<!-- 客户地区选择器 - uv-picker -->
<uv-picker
ref="regionPicker"
:columns="addressList"
:loading="regionLoading"
keyName="name"
@confirm="onRegionConfirm"
@change="onRegionChange"
></uv-picker>
<!-- 工作微信选择弹窗 -->
<view v-if="showWorkWechatPicker" class="modal-mask" @click="showWorkWechatPicker = false">
<view class="modal-content" @click.stop>
<view class="modal-title">选择工作微信</view>
<view class="picker-options">
<view
v-for="item in workWechatOptions"
:key="item.id"
class="picker-option"
:class="{ active: tempWorkWechat === item.value }"
@click="selectWorkWechat(item.value)"
>
<text>{{ item.label }}</text>
<text v-if="tempWorkWechat === item.value" class="check"></text>
</view>
</view>
<view class="modal-buttons">
<button class="modal-btn" @click="showWorkWechatPicker = false">取消</button>
<button class="modal-btn primary" @click="confirmWorkWechat">确定</button>
</view>
</view>
</view>
<!-- 下次跟进时间选择弹窗 -->
<view v-if="showNextFollowTimePicker" class="modal-mask" @click="showNextFollowTimePicker = false">
<view class="modal-content" @click.stop>
<view class="modal-title">选择下次跟进时间</view>
<view class="datetime-picker-wrapper" @click.stop>
<picker
mode="date"
:value="tempNextFollowDate"
@change="onNextFollowDateChange"
>
<view class="picker-display">日期: {{ tempNextFollowDate || '请选择日期' }}</view>
</picker>
<picker
mode="time"
:value="tempNextFollowTime"
@change="onNextFollowTimeChange"
>
<view class="picker-display">时间: {{ tempNextFollowTime || '请选择时间' }}</view>
</picker>
</view>
<view class="modal-buttons">
<button class="modal-btn" @click="clearNextFollowTime">清除</button>
<button class="modal-btn" @click="showNextFollowTimePicker = false">取消</button>
<button class="modal-btn primary" @click="confirmNextFollowTime">确定</button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { createCustomer, getRegionTree } from '@/common/api';
import { useUserStore } from '@/store/user';
// 表单数据
const formData = ref({
customerType: '个人', // 客户类型:个人/企业
name: '', // 客户名称
mobile: '', // 联系电话
wechat: '', // 微信号
source: '', // 客户来源
intents: '', // 客户意向(字符串,提交时转换为数组)
status: '', // 客户状态
intentLevel: '', // 意向强度
region: '', // 客户地区(显示名称,如:北京/北京/朝阳)
regionIds: [], // 客户地区ID数组 [省ID, 市ID, 区ID]
workWechat: '', // 工作微信(显示名称)
workWechatId: null, // 工作微信ID
rating: 0, // 客户星级0-5
remark: '', // 备注
concern: '', // 顾虑点
pain: '', // 痛点
attention: '', // 关注点
demand: '', // 需求点
nextFollowTime: '' // 下次跟进时间
});
// 显示状态
const saving = ref(false);
const showStatusPicker = ref(false);
const showIntentLevelPicker = ref(false);
const showWorkWechatPicker = ref(false);
const showNextFollowTimePicker = ref(false);
// 临时选择值
const tempStatus = ref('');
const tempIntentLevel = ref('');
const tempWorkWechat = ref('');
const tempNextFollowDate = ref('');
const tempNextFollowTime = ref('');
// 地区树数据
const regionTree = ref([]);
// uv-picker 的列数据
const provinces = ref([]); // 省
const citys = ref([]); // 市
const areas = ref([]); // 区
const pickerValue = ref([0, 0, 0]); // 当前选中的索引
const regionLoading = ref(true); // 加载状态
// uv-picker 的引用
const regionPicker = ref(null);
// 计算属性:返回地址列表
const addressList = computed(() => {
return [provinces.value, citys.value, areas.value];
});
// 客户状态选项
const statusOptions = [
{ label: '潜在', value: '1' },
{ label: '意向', value: '2' },
{ label: '成交', value: '3' },
{ label: '失效', value: '4' }
];
// 意向强度选项
const intentLevelOptions = [
{ label: '高', value: '1' },
{ label: '中', value: '2' },
{ label: '低', value: '3' }
];
// 加载地区树数据
const loadRegionTree = async () => {
try {
const res = await getRegionTree();
console.log('获取的地区数据:', res);
regionTree.value = res;
// 初始化省份数据
provinces.value = res.sort((left, right) => (Number(left.code || left.id) > Number(right.code || right.id) ? 1 : -1));
// 如果有已选择的地区,设置默认值
if (formData.value.regionIds && formData.value.regionIds.length > 0) {
handlePickValueDefault();
} else {
// 初始化默认显示第一个省份的城市和区县
if (provinces.value.length > 0) {
citys.value = provinces.value[0]?.children || [];
if (citys.value.length > 0) {
areas.value = citys.value[0]?.children || [];
}
}
}
setTimeout(() => {
regionLoading.value = false;
}, 200);
} catch (err) {
console.error('加载地区树失败:', err);
regionLoading.value = false;
uni.showToast({
title: '加载地区数据失败',
icon: 'none'
});
}
};
// 根据已选择的ID设置默认选中项
const handlePickValueDefault = () => {
if (!formData.value.regionIds || formData.value.regionIds.length === 0) {
return;
}
const [provinceId, cityId, districtId] = formData.value.regionIds;
// 设置省
const provinceIndex = provinces.value.findIndex(item => Number(item.id) === Number(provinceId));
if (provinceIndex >= 0) {
pickerValue.value[0] = provinceIndex;
// 设置市
citys.value = provinces.value[provinceIndex]?.children || [];
const cityIndex = citys.value.findIndex(item => Number(item.id) === Number(cityId));
if (cityIndex >= 0) {
pickerValue.value[1] = cityIndex;
// 设置区
areas.value = citys.value[cityIndex]?.children || [];
const districtIndex = areas.value.findIndex(item => Number(item.id) === Number(districtId));
if (districtIndex >= 0) {
pickerValue.value[2] = districtIndex;
}
}
}
// 重置选择器位置
if (regionPicker.value) {
regionPicker.value.setIndexs([pickerValue.value[0], pickerValue.value[1], pickerValue.value[2]], true);
}
};
// 工作微信选项示例数据实际应从API获取包含ID和名称
const workWechatOptions = [
{ label: '工作微信1', value: '工作微信1', id: '1' },
{ label: '工作微信2', value: '工作微信2', id: '2' },
{ label: '工作微信3', value: '工作微信3', id: '3' },
{ label: '工作微信4', value: '工作微信4', id: '4' }
];
// 获取状态文本
const getStatusText = (status) => {
const option = statusOptions.find(opt => opt.value === status);
return option ? option.label : '';
};
// 获取意向强度文本
const getIntentLevelText = (level) => {
return level || '';
};
// 打开状态选择器
const openStatusPicker = () => {
tempStatus.value = formData.value.status;
showStatusPicker.value = true;
};
// 选择状态
const selectStatus = (value) => {
tempStatus.value = value;
};
// 确认状态
const confirmStatus = () => {
formData.value.status = tempStatus.value;
showStatusPicker.value = false;
};
// 打开意向强度选择器
const openIntentLevelPicker = () => {
tempIntentLevel.value = formData.value.intentLevel;
showIntentLevelPicker.value = true;
};
// 选择意向强度
const selectIntentLevel = (value) => {
tempIntentLevel.value = value;
};
// 确认意向强度
const confirmIntentLevel = () => {
formData.value.intentLevel = tempIntentLevel.value;
showIntentLevelPicker.value = false;
};
// 打开地区选择器
const openRegionPicker = () => {
console.log('openRegionPicker called');
// 检查数据是否已加载
if (regionLoading.value || !regionTree.value || regionTree.value.length === 0) {
uni.showToast({
title: '地区数据加载中,请稍候',
icon: 'none'
});
return;
}
// 如果有已选择的地区,恢复选择状态
if (formData.value.regionIds && formData.value.regionIds.length > 0) {
handlePickValueDefault();
} else {
// 如果没有已选择的地区,确保城市和区县数据已初始化
if (citys.value.length === 0 && provinces.value.length > 0) {
citys.value = provinces.value[0]?.children || [];
if (citys.value.length > 0) {
areas.value = citys.value[0]?.children || [];
}
}
}
// 打开选择器
if (regionPicker.value) {
regionPicker.value.open();
// 如果没有已选择的地区,在打开后设置默认索引
if (!formData.value.regionIds || formData.value.regionIds.length === 0) {
setTimeout(() => {
if (regionPicker.value) {
regionPicker.value.setIndexs([0, 0, 0], true);
}
}, 100);
}
}
};
// uv-picker 的 change 事件处理(实现三级联动)
const onRegionChange = (e) => {
if (regionLoading.value) return;
const { columnIndex, index, indexs } = e;
// 改变了省
if (columnIndex === 0) {
citys.value = provinces.value[index]?.children || [];
areas.value = citys.value[0]?.children || [];
if (regionPicker.value) {
regionPicker.value.setIndexs([index, 0, 0], true);
}
} else if (columnIndex === 1) {
// 改变了市
areas.value = citys.value[index]?.children || [];
if (regionPicker.value) {
regionPicker.value.setIndexs(indexs, true);
}
}
};
// uv-picker 的 confirm 事件处理
const onRegionConfirm = (e) => {
console.log('确认选择的地区:', e);
const { value } = e;
if (value && value.length > 0) {
const province = value[0];
const city = value[1];
const district = value[2];
// 构建地区名称和ID数组
const regionNames = [];
const regionIds = [];
if (province) {
regionNames.push(province.name);
regionIds.push(province.id);
if (city) {
regionNames.push(city.name);
regionIds.push(city.id);
if (district) {
regionNames.push(district.name);
regionIds.push(district.id);
}
}
}
formData.value.region = regionNames.join('/');
formData.value.regionIds = regionIds;
} else {
formData.value.region = '';
formData.value.regionIds = [];
}
};
// 打开工作微信选择器
const openWorkWechatPicker = () => {
tempWorkWechat.value = formData.value.workWechat;
showWorkWechatPicker.value = true;
};
// 选择工作微信
const selectWorkWechat = (value) => {
tempWorkWechat.value = value;
};
// 确认工作微信
const confirmWorkWechat = () => {
const selectedWechat = workWechatOptions.find(w => w.value === tempWorkWechat.value);
if (selectedWechat) {
formData.value.workWechat = selectedWechat.value;
formData.value.workWechatId = selectedWechat.id;
}
showWorkWechatPicker.value = false;
};
// 打开下次跟进时间选择器
const openNextFollowTimePicker = () => {
if (formData.value.nextFollowTime) {
const [date, time] = formData.value.nextFollowTime.split(' ');
tempNextFollowDate.value = date || '';
tempNextFollowTime.value = time || '';
} else {
tempNextFollowDate.value = '';
tempNextFollowTime.value = '';
}
showNextFollowTimePicker.value = true;
};
// 日期改变
const onNextFollowDateChange = (e) => {
tempNextFollowDate.value = e.detail.value;
};
// 时间改变
const onNextFollowTimeChange = (e) => {
tempNextFollowTime.value = e.detail.value;
};
// 清除下次跟进时间
const clearNextFollowTime = () => {
formData.value.nextFollowTime = '';
tempNextFollowDate.value = '';
tempNextFollowTime.value = '';
showNextFollowTimePicker.value = false;
};
// 确认下次跟进时间
const confirmNextFollowTime = () => {
if (tempNextFollowDate.value && tempNextFollowTime.value) {
formData.value.nextFollowTime = `${tempNextFollowDate.value} ${tempNextFollowTime.value}:00`;
} else if (tempNextFollowDate.value) {
formData.value.nextFollowTime = `${tempNextFollowDate.value} 09:00:00`;
} else {
formData.value.nextFollowTime = '';
}
showNextFollowTimePicker.value = false;
};
// 取消
const handleCancel = () => {
uni.navigateBack();
};
// 组件挂载时加载地区树数据
onMounted(() => {
loadRegionTree();
});
// 保存
const handleSave = async () => {
// 表单验证
if (!formData.value.name || formData.value.name.trim() === '') {
uni.showToast({
title: '请输入客户名称',
icon: 'none'
});
return;
}
if (!formData.value.mobile || formData.value.mobile.trim() === '') {
uni.showToast({
title: '请输入联系电话',
icon: 'none'
});
return;
}
saving.value = true;
try {
// 获取用户信息
const userStore = useUserStore();
const userId = userStore.userInfo?.id || userStore.userInfo?.userId || '1';
// 格式化当前时间
const now = new Date();
const formatDateTime = (date) => {
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');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
// 处理意向设备数组(如果输入的是逗号分隔的字符串,转换为数组)
let intentsArray = [];
if (formData.value.intents && formData.value.intents.trim()) {
intentsArray = formData.value.intents.split(',').map(item => item.trim()).filter(item => item);
}
// 处理地区ID数组使用已选择的regionIds
const regionIdsArray = formData.value.regionIds || [];
// 构建提交数据(按照接口示例格式)
const submitData = {
id: null,
code: null,
name: formData.value.name.trim(),
status: null,
intentLevel: null,
mobile: formData.value.mobile.trim(),
wechat: formData.value.wechat.trim() || null,
source: formData.value.source.trim() || null,
intents: intentsArray, // 数组格式
followId: userId, // 跟进人ID
remark: formData.value.remark.trim() || null,
type: '2', // 固定为2
workWechatId: formData.value.workWechatId || null,
regionIds: regionIdsArray, // 数组格式
2025-11-07 13:31:19 +08:00
// 添加关注点、顾虑点、需求点、痛点字段
attention: formData.value.attention.trim() || null,
concern: formData.value.concern.trim() || null,
demand: formData.value.demand.trim() || null,
pain: formData.value.pain.trim() || null,
2025-11-07 11:09:30 +08:00
follow: {
followTime: formatDateTime(now), // 当前时间作为跟进时间
nextFollowTime: formData.value.nextFollowTime || null,
customerStatus: formData.value.status || '1',
customerIntentLevel: formData.value.intentLevel || null
},
customerStatus: formData.value.status || '1',
nextFollowTime: formData.value.nextFollowTime || null,
customerIntentLevel: formData.value.intentLevel || null
};
// 调用API创建客户
await createCustomer(submitData);
uni.showToast({
title: '创建成功',
icon: 'success'
});
// 延迟返回上一页
setTimeout(() => {
uni.navigateBack();
}, 1500);
} catch (error) {
console.error('创建客户失败:', error);
uni.showToast({
title: error.message || '创建失败,请重试',
icon: 'none'
});
} finally {
saving.value = false;
}
};
</script>
<style lang="scss" scoped>
.add-customer-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
/* 自定义导航栏 */
.custom-navbar {
background-color: #fff;
padding-top: var(--status-bar-height);
border-bottom: 1px solid #eee;
}
.navbar-content {
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
padding: 0 16px;
}
.nav-btn {
font-size: 20px;
color: #333;
min-width: 44px;
text-align: center;
}
.nav-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
/* 内容区域 */
.content-scroll {
flex: 1;
}
.scroll-content {
padding: 16px;
padding-bottom: 80px; /* 为保存按钮留出空间 */
}
/* 表单部分 */
.form-section {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #eee;
}
.form-item {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
.form-label {
font-size: 14px;
color: #666;
margin-bottom: 8px;
display: block;
}
.form-input {
width: 100%;
height: 44px;
padding: 0 12px;
font-size: 15px;
color: #333;
background-color: #f8f8f8;
border-radius: 6px;
border: 1px solid #e0e0e0;
}
.form-textarea {
width: 100%;
min-height: 80px;
padding: 12px;
font-size: 15px;
color: #333;
background-color: #f8f8f8;
border-radius: 6px;
border: 1px solid #e0e0e0;
}
.clickable-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
padding: 0 12px;
background-color: #f8f8f8;
border-radius: 6px;
border: 1px solid #e0e0e0;
cursor: pointer;
pointer-events: auto;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.form-value {
font-size: 15px;
color: #333;
}
.form-placeholder {
font-size: 15px;
color: #999;
}
.arrow {
font-size: 20px;
color: #999;
}
/* 单选框组 */
.radio-group {
display: flex;
gap: 24px;
}
.radio-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
color: #333;
cursor: pointer;
}
.radio-dot {
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid #ddd;
background-color: #fff;
position: relative;
transition: all 0.3s;
&.checked {
border-color: #1976d2;
background-color: #1976d2;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #fff;
}
}
}
/* 星级评分 */
.star-rating {
display: flex;
gap: 8px;
align-items: center;
}
.star {
font-size: 24px;
color: #ddd;
cursor: pointer;
transition: color 0.2s;
&.filled {
color: #ffc107;
}
}
/* 保存按钮 */
.save-button-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 12px 16px;
padding-bottom: calc(12px + env(safe-area-inset-bottom));
background-color: #fff;
border-top: 1px solid #eee;
z-index: 100;
}
.save-button {
width: 100%;
height: 44px;
background-color: #1976d2;
color: #fff;
font-size: 16px;
font-weight: 600;
border-radius: 6px;
border: none;
&:disabled {
background-color: #ccc;
opacity: 0.6;
}
}
/* 弹窗样式 */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1000;
}
.modal-content {
width: 100%;
max-height: 70vh;
background-color: #fff;
border-radius: 16px 16px 0 0;
padding: 20px;
animation: slideUp 0.3s;
}
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.modal-title {
font-size: 18px;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 20px;
}
.picker-options {
max-height: 400px;
overflow-y: auto;
}
.picker-option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
font-size: 15px;
color: #333;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&.active {
color: #1976d2;
}
}
.check {
color: #1976d2;
font-size: 18px;
}
.modal-buttons {
display: flex;
gap: 12px;
margin-top: 20px;
}
.datetime-picker-wrapper {
padding: 20px 0;
.picker-display {
padding: 12px;
margin-bottom: 12px;
background-color: #f8f8f8;
border-radius: 6px;
font-size: 15px;
color: #333;
text-align: center;
cursor: pointer;
pointer-events: auto;
user-select: none;
}
}
.modal-btn {
flex: 1;
height: 44px;
font-size: 15px;
border-radius: 6px;
border: 1px solid #e0e0e0;
background-color: #fff;
color: #666;
&.primary {
background-color: #1976d2;
color: #fff;
border-color: #1976d2;
}
}
</style>