1、我们需要引入一个相关的框架
2、在components中创建一个mymodal的文件夹
创建组件
MyModalComfirm.vue(二次确认弹框)
<template>
<view>
<tui-modal class="tui-modal" :show="props.modal" custom padding="0rpx">
<view class="my-modal-custom">
<view class="my-modal">
<strong class="my-modal-text">{{props.myTip}}</strong>
</view>
<view class="my-modal-button-box">
<view class="my-modal-cancal" @click="hideModal">
<span class="my-modal-span">取消</span>
</view>
<view class="vertical-line"></view>
<view class="my-modal-sure" @click="sureModal">
<span class="my-modal-span">确认</span>
</view>
</view>
</view>
</tui-modal>
</view>
</template>
<script setup>
import {ref,defineEmits} from 'vue'
const emit = defineEmits(['hideModal','sureModal'])
// 取消按钮
const hideModal = () =>{
emit('hideModal')
}
// 确定按钮
const sureModal = () =>{
emit('sureModal')
}
const props = defineProps({
// 提示信息
"myTip":{
type:String,
default:"",
required: true
},
//控制显隐
"modal":{
type:Boolean,
default:false,
required: true
}
})
const handleClick = (e) =>{
let index = e.index;
if (index === 0) {
console.log('你点击了取消按钮');
} else {
console.log('你点击了确定按钮');
}
hideModal();
}
</script>
<style lang="scss" scoped>
.tui-modal{
display: flex;
justify-content: center;
align-items: center;
}
/* 弹窗 */
.my-modal-custom{
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
.my-modal{
// width: 602rpx;
width: 100%;
height: 208rpx;
box-shadow: inset 0rpx -2rpx 0rpx 0rpx #EEEEEE;
display: flex;
justify-content: center;
align-items: center;
.my-modal-text{
/* width: 360rpx; */
height: 56rpx;
font-size: 40rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 549;
color: rgba(0,24,26,0.85);
line-height: 56rpx;
}
}
.my-modal-button-box{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
.my-modal-cancal{
display: flex;
justify-content: center;
align-items: center;
width: 301rpx;
height: 100rpx;
}
.my-modal-cancal:active{
background-color: rgb(229, 229, 229);
}
.vertical-line {
width: 2rpx;
height: 100rpx;
background: #EEEEEE;
// margin-left: 100rpx;
// margin-right: 100rpx;
}
.my-modal-sure{
display: flex;
justify-content: center;
align-items: center;
width: 301rpx;
height: 100rpx;
}
.my-modal-sure:active{
background-color: rgb(229, 229, 229);
}
.my-modal-span{
width: 68rpx;
height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #74BDB0;
line-height: 48rpx;
}
}
}
</style>
在页面上调用:
<!-- 二次确认 -->
<MyModalComfirm :modal="confirmShowModal" :myTip="confirmMyTip" @hideModal="confirmHideModal" @sureModal="submitRequest"></MyModalComfirm>
// 弹窗
const confirmShowModal = ref(false)
const confirmMyTip = ref('你确定提交么?')
// 隐藏
const confirmHideModal = () =>{
confirmShowModal.value = false
}
// 点击显示
const submitClick = () =>{
confirmShowModal.value = true
}
// 二次确认请求
const submitRequest = async () =>{
uni.showLoading({
title: '加载中'
});
//你的请求业务代码
uni.hideLoading();
}
效果:
3、底部提交按钮:
<view class="footer-box">
<view class="footer-button-box">
<span class="footer-button-span">
提交
</span>
</view>
</view>
相关css:
// 底部按钮
.footer-box{
width: 750rpx;
height: 120rpx;
background: #FFFFFF;
box-shadow: 0rpx -8rpx 24rpx 0rpx rgba(0,24,26,0.13);
position: fixed;
bottom: 0rpx;
display: flex;
justify-content: center;
align-items: center;
.footer-button-box{
width: 654rpx;
height: 88rpx;
background: #74BDB0;
border-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
.footer-button-span{
// width: 576rpx;
height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 48rpx;
}
}
.footer-button-box:active{
background-color: rgb(229, 229, 229);
}
}
效果图:
4、弹框提示
在compents的mymodal的文件夹中创建一个组件
使用uni-popup需要导入一些组件
MyModalMessage.vue:
<template>
<view>
<!-- 提示信息弹窗 -->
<uni-popup ref="message" type="message">
<uni-popup-message :type="msgType" :message="messageText" :duration="2000"></uni-popup-message>
</uni-popup>
</view>
</template>
<script setup>
import {ref,defineExpose} from 'vue'
// 弹框
const message = ref(null)
const msgType = ref('')
const messageText = ref('')
// Toggle计时器id
const timerToggle = ref(1)
const messageToggle = (type,value) => {
msgType.value = type
messageText.value = `${value}`
message.value.open('center')
clearTimeout(timerToggle.value)
timerToggle.value = setTimeout(()=>{
// 三秒后关闭提示
message.value.close()
},3000)
}
defineExpose({
messageToggle
})
</script>
<style>
</style>
使用这个组件:
<!-- 提示信息弹窗 -->
<MyModalMessage ref="myModalMessage"></MyModalMessage>
import MyModalMessage from '@/components/mymodal/MyModalMessage.vue'
// 弹窗
const myModalMessage = ref()
myModalMessage.value.messageToggle('error','密码不能为空')
提示类别:success(成功)、error(错误)、warn(警告)、info(信息)
2023-11-19、2023-11-20 start:
4、顶部下拉组件在components/mymodal/ MyModalTopDropdown.vue:
<template>
<view>
<tui-dropdown-list :show="dropdownShow" :top="88" :isMask="true" maskBackground="##737373728d" :height="804">
<template v-slot:selectionbox>
<!-- header -->
<view class="header-box">
<view v-if="props.showAllSpan" class="title-all-box">
<span class="title-span">全部</span>
</view>
<view class="title-box" @click="dropDownShowClick(1)">
<span class="title-span">时间</span>
<view v-if="dropdownShow && selectedButtonIndex === 1" class="title-arrow-up">
<tui-icon name="turningup" :size="18" color="#444"></tui-icon>
</view>
<view v-else class="title-arrow-down">
<tui-icon name="turningdown" :size="18" color="#444"></tui-icon>
</view>
</view>
<view class="title-box" @click="dropDownShowClick(2)">
<span class="title-span">状态</span>
<view v-if="dropdownShow && selectedButtonIndex === 2" class="title-arrow-up">
<tui-icon name="turningup" :size="18" color="#444"></tui-icon>
</view>
<view v-else class="title-arrow-down">
<tui-icon name="turningdown" :size="18" color="#444"></tui-icon>
</view>
</view>
<view class="title-box" @click="dropDownShowClick(3)">
<span class="title-span">咨询形式</span>
<view v-if="dropdownShow && selectedButtonIndex === 3" class="title-arrow-up">
<tui-icon name="turningup" :size="18" color="#444"></tui-icon>
</view>
<view v-else class="title-arrow-down">
<tui-icon name="turningdown" :size="18" color="#444"></tui-icon>
</view>
</view>
</view>
</template>
<template v-slot:dropdownbox>
<!-- 时间 -->
<view class="content-time-box" v-if="selectedButtonIndex === 1">
<view class="content-time-select-box">
<scroll-view scroll-y="true" class="content-time-select-left-scroll-Y-box">
<view class="content-time-select-left-box">
<view v-for="(item,index) in getYears(2023,2050)" :key="index"
:class="{'content-time-select-left-item-box':!checkSelectedYear(item),'content-time-select-left-item-selected-box':checkSelectedYear(item)}"
@click="selectedYearClick(item)">
<span class="item-span">{{item.label}}</span>
</view>
</view>
</scroll-view>
<scroll-view scroll-y="true" class="content-time-select-right-scroll-Y-box">
<view v-if="selectedYear !== 0" v-for="(item,index) in getMonths(selectedYear)" :key="index"
:class="{'content-time-select-right-box':!checkSelectedMonth(item),'content-time-select-right-selected-box':checkSelectedMonth(item)}"
@click="selectedMonthClick(item)">
<span class="item-span">{{item.label}}</span>
<tui-icon v-if="checkSelectedMonth(item)" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
</scroll-view>
</view>
<view class="content-footer-box">
<span class="footer-reset-span" @click="resetTimeClick">重置</span>
<view class="footer-submit-box" @click="submitClick">
<span class="submit">确定</span>
</view>
</view>
</view>
<!-- 状态 -->
<view class="content-status-box" v-if="selectedButtonIndex === 2">
<view class="content-status-select-box">
<view class="status-item-box" @click="selectedStatusClick(1)">
<span class="status-item">待确认</span>
<tui-icon v-if="selectedStatusValue === 1" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
<view class="status-item-box" @click="selectedStatusClick(2)">
<span class="status-item">待咨询</span>
<tui-icon v-if="selectedStatusValue === 2" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
<view class="status-item-box" @click="selectedStatusClick(3)">
<span class="status-item">已完成</span>
<tui-icon v-if="selectedStatusValue === 3" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
<view class="status-item-box" @click="selectedStatusClick(4)">
<span class="status-item">已取消</span>
<tui-icon v-if="selectedStatusValue === 4" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
</view>
<view class="content-footer-box">
<span class="footer-reset-span" @click="resetStatusClick">重置</span>
<view class="footer-submit-box" @click="submitClick">
<span class="submit">确定</span>
</view>
</view>
</view>
<!-- 咨询方式 -->
<view class="content-form-box" v-if="selectedButtonIndex === 3">
<view class="content-form-select-box">
<view class="form-item-box" @click="selectedFormClick(1)">
<span class="form-item">线上预约</span>
<tui-icon v-if="selectedFormValue === 1" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
<view class="form-item-box" @click="selectedFormClick(2)">
<span class="form-item">线下预约</span>
<tui-icon v-if="selectedFormValue === 2" size="{{26}}" unit="rpx" color="#081E42" name="check"></tui-icon>
</view>
</view>
<view class="content-footer-box">
<span class="footer-reset-span" @click="resetFormClick">重置</span>
<view class="footer-submit-box" @click="submitClick">
<span class="submit">确定</span>
</view>
</view>
</view>
</template>
</tui-dropdown-list>
</view>
</template>
<script setup>
import {ref,defineEmits} from 'vue';
const emit = defineEmits(['sure'])
// 控制下拉弹窗的显隐
const dropdownShow = ref(false)
// 0表示未选择 1 时间 2 状态 3 咨询形式
const selectedButtonIndex = ref(0)
const dropDownShowClick = (index) =>{
if (selectedButtonIndex.value === index) {
dropdownShow.value = !dropdownShow.value
return;
}
dropdownShow.value = true
selectedButtonIndex.value = index
}
// 根据起始年份和结束年份返回年份列表
const getYears = (startYear,endYear)=>{
const years = [];
for (let year = startYear; year <= endYear; year++) {
const yearObj = {
label: `${year}年`,
value: year,
children: getMonths(year),
};
years.push(yearObj);
}
return years;
}
// 选中的年份
const selectedYear = ref(0)
// 检查是否选中了传入的年份
const checkSelectedYear = (item) =>{
if (item.value === selectedYear.value) {
return true;
}
return false;
}
// 选中年份的方法
const selectedYearClick = (item) =>{
selectedYear.value = item.value
selectedMonth.value = 0
}
// 选中的月份
const selectedMonth = ref(0)
// 检查是否选中了传入的月份
const checkSelectedMonth = (item) =>{
if (item.monthValue === selectedMonth.value) {
return true;
}
return false;
}
// 选中月份的方法
const selectedMonthClick = (item) =>{
selectedMonth.value = item.monthValue
}
// 根据年份返回月份列表
const getMonths = (year) =>{
const months = [];
for (let month = 1; month <= 12; month++) {
const monthObj = {
label: `${month}月`,
value: `${year}-${month}`,
yearValue: `${year}`,
monthValue: `${month}`
};
months.push(monthObj);
}
return months;
}
// 获取当年的年份
const getCurrentYear = () =>{
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
return currentYear;
}
// 重置时间筛选
const resetTimeClick = () =>{
selectedYear.value = 0
selectedMonth.value = 0
}
// 预约状态, 1:待确认 2:待咨询 3:已完成 4:已取消
const selectedStatusValue = ref(0)
// 选中状态的方法
const selectedStatusClick = (code) => {
selectedStatusValue.value = code
}
// 状态重置
const resetStatusClick = () =>{
selectedStatusValue.value = 0
}
// 预约方式, 1:线下预约 2:线上预约
const selectedFormValue = ref(0)
// 选中预约方式的方法
const selectedFormClick = (code) => {
selectedFormValue.value = code
}
// 预约方式重置
const resetFormClick = () =>{
selectedFormValue.value = 0
}
// 确认方法
const submitClick = () =>{
emit('sure',selectedYear.value,selectedMonth.value,selectedStatusValue.value,selectedFormValue.value)
dropdownShow.value = false
}
const props = defineProps({
//控制“全部”显隐
"showAllSpan":{
type:Boolean,
default:true,
required: false
}
})
</script>
<style lang="scss" scoped>
// 头信息
.header-box{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-around;
align-items: center;
width: 100vw;
height: 88rpx;
background-color: #fff;
.title-all-box{
display: flex;
justify-content: center;
align-items: center;
.title-span{
// width: 60rpx;
// height: 42rpx;
font-size: 30rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.65);
line-height: 42rpx;
}
}
.title-box{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
.title-span{
// width: 60rpx;
// height: 42rpx;
font-size: 30rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
color: rgba(0,24,26,0.85);
line-height: 42rpx;
}
}
}
// 正文信息
.content-time-box{
width: 750rpx;
height: 804rpx;
background: #fff;
border-radius: 0rpx 0rpx 24rpx 24rpx;
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
.content-time-select-box{
width: 750rpx;
height: 664rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
.content-time-select-left-scroll-Y-box{
height: 664rpx;
width: 33%;
.content-time-select-left-box{
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
// height: 500rpx;
width: 100%;
background: #F5F9FC;
overflow-y: scroll;
.content-time-select-left-item-box{
width: 240rpx;
height: 88rpx;
background: #F5F9FC;
display: flex;
justify-content: center;
align-items: center;
.item-span{
width: 116rpx;
height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.65);
line-height: 48rpx;
}
}
.content-time-select-left-item-selected-box{
width: 100%;
height: 88rpx;
background: #FFFFFF;
display: flex;
justify-content: center;
align-items: center;
.item-span{
width: 116rpx;
height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
}
}
}
}
.content-time-select-right-scroll-Y-box{
height: 664rpx;
width: 67%;
.content-time-select-right-box{
display: flex;
justify-content: flex-start;
align-items: center;
width: 240rpx;
height: 88rpx;
background: #FFFFFF;
margin-left: 96rpx;
.item-span{
// width: 48rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.65);
line-height: 48rpx;
}
}
.content-time-select-right-selected-box{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
margin-left: 96rpx;
width: 240rpx;
height: 88rpx;
background: #FFFFFF;
.item-span{
// width: 56rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
margin-right: 44rpx;
}
}
}
}
.content-footer-box{
width: 750rpx;
height: 140rpx;
background: #fff;
border-radius: 0rpx 0rpx 24rpx 24rpx;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
// position: absolute;
// bottom:0rpx;
.footer-reset-span{
// width: 68rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
margin-left: 144rpx;
}
.footer-reset-span:avtive{
background-color: rgb(229, 229, 229);
}
.footer-submit-box{
display: flex;
justify-content: center;
align-items: center;
width: 374rpx;
height: 100rpx;
background: #74BDB0;
border-radius: 50rpx;
margin-left: 124rpx;
.submit{
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 48rpx;
}
}
.footer-submit-box:active{
background-color: rgb(229, 229, 229);
}
}
}
// 状态
.content-status-box {
width: 750rpx;
height: 500rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
.content-status-select-box{
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background: #fff;
width: 100%;
height: 100%;
.status-item-box{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
margin-top: 30rpx;
.status-item{
// width: 68rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
margin-right: 20rpx;
}
}
}
.content-footer-box{
width: 750rpx;
height: 140rpx;
background: #fff;
border-radius: 0rpx 0rpx 24rpx 24rpx;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
// position: absolute;
// bottom:0rpx;
.footer-reset-span{
// width: 68rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
margin-left: 144rpx;
}
.footer-reset-span:avtive{
background-color: rgb(229, 229, 229);
}
.footer-submit-box{
display: flex;
justify-content: center;
align-items: center;
width: 374rpx;
height: 100rpx;
background: #74BDB0;
border-radius: 50rpx;
margin-left: 124rpx;
.submit{
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 48rpx;
}
}
.footer-submit-box:active{
background-color: rgb(229, 229, 229);
}
}
}
// 咨询方式
.content-form-box {
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
.content-form-select-box{
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background: #fff;
width: 100%;
height: 100%;
.form-item-box{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
margin-top: 20rpx;
.form-item{
// width: 68rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
margin-right: 20rpx;
}
}
}
.content-footer-box{
width: 750rpx;
height: 140rpx;
background: #fff;
border-radius: 0rpx 0rpx 24rpx 24rpx;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
// position: absolute;
// bottom:0rpx;
.footer-reset-span{
// width: 68rpx;
// height: 48rpx;
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: rgba(0,24,26,0.85);
line-height: 48rpx;
margin-left: 144rpx;
}
.footer-reset-span:avtive{
background-color: rgb(229, 229, 229);
}
.footer-submit-box{
display: flex;
justify-content: center;
align-items: center;
width: 374rpx;
height: 100rpx;
background: #74BDB0;
border-radius: 50rpx;
margin-left: 124rpx;
.submit{
font-size: 34rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 48rpx;
}
}
.footer-submit-box:active{
background-color: rgb(229, 229, 229);
}
}
}
</style>
使用:
<template>
<view class="content">
<MyModalTopDropdown :showAllSpan="false" @sure="sureSelected"></MyModalTopDropdown>
</view>
</template>
<script setup>
import MyModalTopDropdown from '@/components/mymodal/MyModalTopDropdown.vue'
// 确认按钮
const sureSelected = (selectedYear,selectedMonth,selectedStatusValue,selectedFormValue)=>{
console.log(selectedYear,selectedMonth,selectedStatusValue,selectedFormValue)
}
</script>
<style>
</style>
效果:
end