Browse Source

Merge branch 'wangyi/feature-20251022162725-启动页登录注册' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into dongqian/feature-20251022181325-deepmate简版

wangyi/feature-20251022162725-启动页登录注册
wangyetao 4 weeks ago
parent
commit
6fc67da169
  1. 9
      pages.json
  2. 292
      pages/start/Registration/Registration.vue
  3. 1341
      pages/start/Registration/list.js
  4. 263
      pages/start/login/login.vue
  5. 69
      pages/start/login/verification.js
  6. 985
      pages/start/recoverPassword/recoverPassword.vue
  7. 47
      pages/start/select/select.vue
  8. 4
      pages/start/startup/startup.vue
  9. BIN
      static/icons/To.png
  10. BIN
      static/icons/back.png
  11. BIN
      static/icons/look.png
  12. BIN
      static/icons/start-logo.png
  13. BIN
      static/icons/unlook.png
  14. BIN
      static/select.png
  15. BIN
      static/start-logo.png
  16. 18
      uni_modules/uni-popup/changelog.md
  17. 28
      uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
  18. 9
      uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
  19. 31
      uni_modules/uni-popup/components/uni-popup/uni-popup.vue
  20. 189
      uni_modules/uni-popup/package.json
  21. 7
      uni_modules/uni-transition/changelog.md
  22. 520
      uni_modules/uni-transition/components/uni-transition/uni-transition.vue
  23. 103
      uni_modules/uni-transition/package.json

9
pages.json

@ -137,6 +137,15 @@
"navigationStyle": "custom",
"titleNView": false
}
},
{
"path" : "pages/start/recoverPassword/recoverPassword",
"style" :
{
"navigationBarTitleText": "",
"navigationStyle": "custom",
"titleNView": false
}
}
],
"globalStyle": {

292
pages/start/Registration/Registration.vue

@ -145,17 +145,23 @@
>
</view>
<!-- 注册按钮 -->
<button class="register-btn" @click="register">注册</button>
<!-- 或者 -->
<text class="or-text" @click="goToLogin"
>已有账号登录
</text>
<footerBar class="static-footer" :type="type"></footerBar>
<text class="or-text" @click="goToLogin">已有账号登录 </text>
<!-- 同意弹窗 -->
<uniPopup ref="agreementPopup" type="dialog">
<view class="popup-content">
<text class="popup-title">同意并继续</text>
<text class="popup-message">请阅读并同意服务协议和隐私权限</text>
<button class="agree-button" @click="handleAgree">
<text class="agree-text">同意</text>
</button>
</view>
</uniPopup>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
@ -163,15 +169,17 @@
import { ref } from "vue";
//
import countryList from "../login/list";
import footerBar from '../../../components/footerBar-cn.vue'
import footerBar from "../../../components/footerBar-cn.vue";
import uniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
import { verificationPhone, verificationEmail } from "../login/verification";
const type = ref('member')
const type = ref("member");
const email = ref("");
const password = ref("");
const phone = ref("");
const country = ref("+86");
const agreed = ref(false);
const switchType = ref("Email"); //
const switchType = ref("Phone"); //
const { safeAreaInsets } = uni.getSystemInfoSync();
const codeBtnText = ref("获取验证码");
const isCodeBtnDisabled = ref(false); //
@ -223,37 +231,109 @@ function switchPhone() {
switchType.value = "Phone";
}
// function register() {
// if (switchType.value === "Email") {
// //
// if (!email.value) {
// uni.showToast({
// title: "",
// icon: "none",
// });
// return;
// }
// //
// console.log(":", email.value);
// }
// if (switchType.value === "Phone") {
// //
// if (!phone.value) {
// uni.showToast({
// title: "",
// icon: "none",
// });
// return;
// }
// //
// console.log(":", phone.value);
// }
// }
function register() {
if (switchType.value === "Email") {
if (switchType.value === "Phone") {
//
if (!email.value) {
if (!phone.value) {
uni.showToast({
title: "请输入邮箱地址",
title: "请输入手机号码",
icon: "none",
});
return;
}
if (!password.value) {
uni.showToast({
title: "请输入验证码",
icon: "none",
});
return;
}
const phoneAll = `${country.value}${phone.value}`;
console.log("完整手机号" + phoneAll);
if (validatePhoneNumber(country.value, phone.value)) {
console.log("登录成功:", phoneAll);
}
//
console.log("登录:", email.value);
// console.log(":", phone.value);
}
if (switchType.value === "Phone") {
if (switchType.value === "Email") {
//
if (!phone.value) {
if (!email.value) {
uni.showToast({
title: "请输入手机号码",
title: "请输入邮箱地址",
icon: "none",
});
return;
}
if (!password.value) {
uni.showToast({
title: "请输入验证码",
icon: "none",
});
return;
}
const bool = verificationEmail(email.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "邮箱格式不正确",
icon: "none",
});
return;
}
//
console.log("登录:", phone.value);
console.log("登录:", email.value);
}
}
if (!agreed.value) {
//
agreementPopup.value.open();
return;
}
//
// uni.showToast({
// title: "",
// icon: "success",
// });
}
function goToLogin() {
//
@ -273,7 +353,72 @@ function onPhoneInput(e) {
}
}
// function sendCode() {
// //
// if (isCodeBtnDisabled.value) return;
// //
// isCodeBtnDisabled.value = true;
// codeBtnText.value = "";
// let time = 6;
// const timer = setInterval(() => {
// time--;
// codeBtnText.value = " " + time + "S";
// if (time <= 0) {
// clearInterval(timer);
// codeBtnText.value = "";
// //
// isCodeBtnDisabled.value = false;
// }
// }, 1000);
// return;
// }
function sendCode() {
if (switchType.value === "Phone") {
if (!phone.value) {
uni.showToast({
title: "请输入手机号",
icon: "none",
});
return;
}
const bool = verificationPhone(country.value, phone.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "手机号格式不正确",
icon: "none",
});
return;
}
}
if (switchType.value === "Email") {
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
const bool = verificationEmail(email.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "邮箱格式不正确",
icon: "none",
});
return;
}
}
//
if (isCodeBtnDisabled.value) return;
@ -311,13 +456,61 @@ function openPrivacy() {
});
}
function changeCheckbox() {
agreed.value = !agreed.value;
checkboxUrl.value = agreed.value
? "../../../static/icons/Check-one-true.png"
: "../../../static/icons/Check-one-false.png";
}
//
function validatePhoneNumber(countryCode, phoneNumber) {
//
if (!phoneNumber || phoneNumber.trim() === "") {
uni.showToast({
title: "手机号不能为空",
icon: "none",
});
return false;
}
const bool = verificationPhone(countryCode, phoneNumber);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "手机号格式不正确",
icon: "none",
});
return false;
}
// +715
const cleanNumber = phoneNumber.replace(/^\+/, "");
if (cleanNumber.length < 7 || cleanNumber.length > 15) {
uni.showToast({
title: "手机号长度不正确",
icon: "none",
});
return false;
}
return true;
}
//
const agreementPopup = ref(null);
//
function handleAgree() {
//
agreementPopup.value.close();
//
agreed.value = true;
checkboxUrl.value = "../../../static/icons/Check-one-true.png";
//
}
</script>
<style scoped>
@ -604,7 +797,8 @@ function changeCheckbox() {
flex-direction: column;
font-size: 24rpx;
color: #999999;
margin-bottom: 100rpx;
margin-top: 330rpx;
margin-bottom: -22rpx;
}
.third-party-login {
@ -658,7 +852,57 @@ function changeCheckbox() {
}
.static-footer {
position: fixed;
bottom: 0;
position: fixed;
bottom: 0;
}
/* 弹窗样式 */
.popup-content {
background-color: #ffffff;
padding: 40rpx;
text-align: center;
border-radius: 10rpx;
width: 550rpx;
}
.popup-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 80rpx;
text-align: center; /* 水平居中 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.popup-message {
font-size: 28rpx;
color: #000000;
margin-bottom: 80rpx;
text-align: center; /* 水平居中 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.agree-button {
width: 300rpx;
height: 80rpx;
background-color: #000000;
border-radius: 40rpx;
display: flex; /* 添加flex布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
}
.agree-text {
color: #ffffff;
font-size: 34rpx;
/* 添加垂直居中相关样式 */
display: flex;
align-items: center;
justify-content: center;
line-height: 1; /* 确保文字垂直居中 */
}
</style>

1341
pages/start/Registration/list.js
File diff suppressed because it is too large
View File

263
pages/start/login/login.vue

@ -63,7 +63,7 @@
class="input-field"
type="text"
placeholder="请输入DeepChart ID"
v-model="email"
v-model="deepChartID"
/>
</view>
<view class="input-with-icon">
@ -195,7 +195,8 @@
<!-- 或者 -->
<text class="or-text" @click="goToRegistration"
>如果您还没有账号点击注册 >
>如果您还没有账号点击注册
<image class="to-icon" src="../../../static/icons/To.png"></image>
</text>
<!-- 第三方登录 -->
@ -218,6 +219,17 @@
<text class="third-party-text">通过 Google 登录</text>
</view>
</view>
<!-- 同意弹窗 -->
<uniPopup ref="agreementPopup" type="dialog">
<view class="popup-content">
<text class="popup-title">同意并继续</text>
<text class="popup-message">请阅读并同意服务协议和隐私权限</text>
<button class="agree-button" @click="handleAgree">
<text class="agree-text">同意</text>
</button>
</view>
</uniPopup>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
@ -226,14 +238,19 @@
import { ref } from "vue";
//
import countryList from "./list.js";
import footerBar from '../../../components/footerBar-cn'
import footerBar from "../../../components/footerBar-cn";
import uniPopupDialogVue from "../../../uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue";
import uniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
import { verificationPhone, verificationEmail } from "../login/verification";
const type = ref('member')
const deepChartID = ref("");
const type = ref("member");
const email = ref("");
const password = ref("");
const country = ref("+86");
const phone = ref("");
const agreed = ref(false);
const switchType = ref("Email"); //
const switchType = ref("User"); //
const { safeAreaInsets } = uni.getSystemInfoSync();
const codeBtnText = ref("获取验证码");
const isCodeBtnDisabled = ref(false); //
@ -264,6 +281,7 @@ function showCountryPicker() {
),
success: function (res) {
selectedCountry.value = countries.value[res.tapIndex];
country.value = selectedCountry.value.code;
},
});
}
@ -278,31 +296,38 @@ function goToIndex() {
function switchUser() {
//
switchType.value = "User";
password.value = "";
}
function switchEmail() {
//
switchType.value = "Email";
password.value = "";
}
function switchPhone() {
//
switchType.value = "Phone";
password.value = "";
}
function register() {
if (switchType.value === "Email") {
//
if (!email.value) {
if (switchType.value === "User") {
if (!deepChartID.value) {
uni.showToast({
title: "请输入邮箱地址",
title: "请输入用户名",
icon: "none",
});
return;
}
//
console.log("登录:", email.value);
if (!password.value) {
uni.showToast({
title: "请输入密码",
icon: "none",
});
return;
}
}
if (switchType.value === "Phone") {
@ -315,9 +340,81 @@ function register() {
return;
}
if (!password.value) {
uni.showToast({
title: "请输入验证码",
icon: "none",
});
return;
}
const phoneAll = `${country.value}${phone.value}`;
console.log("完整手机号" + phoneAll);
if (validatePhoneNumber(country.value, phone.value)) {
console.log("登录成功:", phoneAll);
}
//
// console.log(":", phone.value);
}
if (switchType.value === "Email") {
//
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
if (!password.value) {
uni.showToast({
title: "请输入验证码",
icon: "none",
});
return;
}
const bool = verificationEmail(email.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "邮箱格式不正确",
icon: "none",
});
return;
}
//
console.log("登录:", phone.value);
console.log("登录:", email.value);
}
if (!agreed.value) {
//
agreementPopup.value.open();
return;
}
//
// uni.showToast({
// title: "",
// icon: "success",
// });
}
//
const agreementPopup = ref(null);
//
function handleAgree() {
//
agreementPopup.value.close();
//
agreed.value = true;
checkboxUrl.value = "../../../static/icons/Check-one-true.png";
//
}
function loginWithApple() {
@ -383,6 +480,49 @@ function onPhoneInput(e) {
}
function sendCode() {
if (switchType.value === "Phone") {
if (!phone.value) {
uni.showToast({
title: "请输入手机号",
icon: "none",
});
return;
}
const bool = verificationPhone(country.value, phone.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "手机号格式不正确",
icon: "none",
});
return;
}
}
if (switchType.value === "Email") {
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
const bool = verificationEmail(email.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "邮箱格式不正确",
icon: "none",
});
return;
}
}
//
if (isCodeBtnDisabled.value) return;
@ -422,7 +562,10 @@ function openPrivacy() {
function recoverPassword() {
//
console.log("忘记密码");
// console.log("");
uni.navigateTo({
url: "/pages/start/recoverPassword/recoverPassword",
});
}
function changeCheckbox() {
@ -431,6 +574,42 @@ function changeCheckbox() {
? "../../../static/icons/Check-one-true.png"
: "../../../static/icons/Check-one-false.png";
}
//
function validatePhoneNumber(countryCode, phoneNumber) {
//
if (!phoneNumber || phoneNumber.trim() === "") {
uni.showToast({
title: "手机号不能为空",
icon: "none",
});
return false;
}
const bool = verificationPhone(countryCode, phoneNumber);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "手机号格式不正确",
icon: "none",
});
return false;
}
// +715
const cleanNumber = phoneNumber.replace(/^\+/, "");
if (cleanNumber.length < 7 || cleanNumber.length > 15) {
uni.showToast({
title: "手机号长度不正确",
icon: "none",
});
return false;
}
return true;
}
</script>
<style scoped>
@ -720,6 +899,11 @@ function changeCheckbox() {
margin-bottom: 40rpx;
}
.to-icon{
width: 10rpx;
height: 16rpx;
}
.third-party-login {
width: 100%;
margin-bottom: 60rpx;
@ -771,7 +955,56 @@ function changeCheckbox() {
}
.static-footer {
position: fixed;
bottom: 0;
position: fixed;
bottom: 0;
}
/* 弹窗样式 */
.popup-content {
background-color: #ffffff;
padding: 40rpx;
text-align: center;
border-radius: 10rpx;
width: 550rpx;
}
.popup-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 80rpx;
text-align: center; /* 水平居中 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.popup-message {
font-size: 28rpx;
color: #000000;
margin-bottom: 80rpx;
text-align: center; /* 水平居中 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.agree-button {
width: 300rpx;
height: 80rpx;
background-color: #000000;
border-radius: 40rpx;
display: flex; /* 添加flex布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
}
.agree-text {
color: #ffffff;
font-size: 34rpx;
/* 添加垂直居中相关样式 */
display: flex;
align-items: center;
justify-content: center;
line-height: 1; /* 确保文字垂直居中 */
}
</style>

69
pages/start/login/verification.js

@ -0,0 +1,69 @@
function verificationPhone(countryCode,phoneNumber) {
switch (countryCode) {
case '+86':
return verificationChina(phoneNumber);
case '+1':
return verificationAmerica(phoneNumber);
case '+65':
return verificationSingapore(phoneNumber);
case '+60':
return verificationMalaysia(phoneNumber);
case '+66':
return verificationThailand(phoneNumber);
case '+852':
return verificationHongKong(phoneNumber);
case '+84':
return verificationVietnam(phoneNumber);
default:
return true;
}
}
function verificationChina(phoneNumber){
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(phoneNumber);
}
function verificationAmerica(phoneNumber){
const phoneRegex = /^[2-9]\d{2}[- ]?\d{4}$/;
return phoneRegex.test(phoneNumber);
}
function verificationSingapore(phoneNumber){
const phoneRegex = /^[89]\d{7}$/;
return phoneRegex.test(phoneNumber);
}
function verificationMalaysia(phoneNumber){
const phoneRegex = /^01\d{8}$/;
return phoneRegex.test(phoneNumber);
}
function verificationHongKong(phoneNumber){
const phoneRegex = /^0[896]\d{8}$/;
return phoneRegex.test(phoneNumber);
}
function verificationThailand(phoneNumber){
const phoneRegex = /^[5-9]\d{7}$/;
return phoneRegex.test(phoneNumber);
}
function verificationVietnam(phoneNumber){
const phoneRegex = /^(0)?[3-9]\d{8}$/;
return phoneRegex.test(phoneNumber);
}
function verificationEmail(email) {
const emailRegex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
return emailRegex.test(email);
}
export {verificationPhone,verificationEmail}

985
pages/start/recoverPassword/recoverPassword.vue

@ -0,0 +1,985 @@
<template>
<view class="login-registration-container">
<!-- 自定义导航栏 -->
<view
class="custom-navbar"
:style="{ paddingTop: safeAreaInsets?.top + 'px' }"
>
<view class="nav-left">
<image
class="icons"
@click="goToBack"
src="../../../static/icons/back.png"
alt="返回首页"
/>
</view>
<view class="nav-right">
<image
class="icons"
src="../../../static/icons/Headset.png"
alt="联系客服"
/>
</view>
</view>
<!-- Logo -->
<!-- <image class="logo" src="/static/logo.png" mode="aspectFit"></image> -->
<!-- 欢迎语 -->
<text class="welcome-text">找回密码</text>
<!-- 邮箱/手机号切换 -->
<view v-if="!isRecovering" class="switch-container">
<text
class="switch-item"
:class="{ active: switchType === 'Email' }"
@click="switchEmail"
>邮箱</text
>
<text
class="switch-item"
:class="{ active: switchType === 'Phone' }"
@click="switchPhone"
>手机号</text
>
</view>
<view v-else class="switch-container-occupy"> </view>
<!-- 输入框 -->
<view v-if="isRecovering" class="input-container">
<view v-if="switchType === 'Email'">
<!-- 修改邮箱输入框容器将图标包含在内 -->
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Unlock.png"
alt=""
/>
<input
class="input-field"
:type="newPasswordLookFirst ? 'text' : 'password'"
placeholder="输入新密码"
v-model="newPasswordFirst"
/>
<image
class="input-icon-eye"
@click="newPasswordLookFirst = !newPasswordLookFirst"
:src="
!newPasswordLookFirst
? '../../../static/icons/unlook.png'
: '../../../static/icons/look.png'
"
alt=""
/>
</view>
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Unlock.png"
alt=""
/>
<input
class="input-field"
:type="newPasswordLookSecond ? 'text' : 'password'"
placeholder="再次确认"
v-model="newPasswordSecond"
/>
<image
class="input-icon-eye"
@click="newPasswordLookSecond = !newPasswordLookSecond"
:src="
!newPasswordLookSecond
? '../../../static/icons/unlook.png'
: '../../../static/icons/look.png'
"
alt=""
/>
</view>
</view>
</view>
<view v-else class="input-container">
<view v-if="switchType === 'Email'">
<!-- 修改邮箱输入框容器将图标包含在内 -->
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Mail.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入邮箱"
v-model="email"
/>
<view>
<button
class="send-code-btn-email"
:disabled="isCodeBtnDisabled"
:class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
@click="sendCode"
>
<text
class="send-code-text"
:class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
>{{ codeBtnText }}</text
>
</button>
</view>
</view>
<view class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Text-recognition.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入验证码"
v-model="password"
/>
</view>
</view>
<view v-if="switchType === 'Phone'" class="phone-input-container">
<view class="country-code-selector" @click="showCountryPicker">
<image
class="country-flag-img"
src="../../../static/icons/Iphone.png"
mode="aspectFit"
></image>
<text class="country-code">{{ selectedCountry.code }}</text>
<!-- <text class="arrow-down"></text> -->
</view>
<input
class="input-field phone-input"
type="number"
placeholder="输入手机号"
v-model="phone"
@input="onPhoneInput"
/>
<view>
<button
class="send-code-btn"
:disabled="isCodeBtnDisabled"
:class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
@click="sendCode"
>
<text
class="send-code-text"
:class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
>{{ codeBtnText }}</text
>
</button>
</view>
</view>
<view v-if="switchType === 'Phone'" class="input-with-icon">
<image
class="input-icon"
src="../../../static/icons/Text-recognition.png"
alt=""
/>
<input
class="input-field"
type="text"
placeholder="请输入验证码"
v-model="password"
/>
</view>
</view>
<!-- 用户协议 -->
<view @click="changeCheckbox" class="agreement-container-one">
<text
class="agreement-text-one"
:style="!isRecovering ? 'visibility: hidden' : 'visibility: visible'"
>
密码最少8位数
</text>
</view>
<!-- 注册按钮 -->
<button class="register-btn" @click="register">
{{ isRecovering ? "确认" : "下一步" }}
</button>
<!-- 或者 -->
<text class="or-text-one" @click="goToRegistration"
>如果您还没有账号点击注册
<image class="to-icon" src="../../../static/icons/To.png"></image>
</text>
<!-- 或者 -->
<text class="or-text" @click="goToLogin">已有账号登录 </text>
<!-- 同意弹窗 -->
<uniPopup ref="agreementPopup" type="dialog">
<view class="popup-content">
<text class="popup-title">同意并继续</text>
<text class="popup-message">请阅读并同意服务协议和隐私权限</text>
<button class="agree-button" @click="handleAgree">
<text class="agree-text">同意</text>
</button>
</view>
</uniPopup>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
<script setup>
import { ref } from "vue";
//
import countryList from "../login/list";
import footerBar from "../../../components/footerBar-cn.vue";
import uniPopup from "../../../uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
import { verificationPhone, verificationEmail } from "../login/verification";
const type = ref("member");
const email = ref("");
const password = ref("");
const newPasswordFirst = ref("");
const newPasswordSecond = ref("");
const phone = ref("");
const country = ref("+86");
const agreed = ref(false);
const switchType = ref("Phone"); //
const { safeAreaInsets } = uni.getSystemInfoSync();
const codeBtnText = ref("获取验证码");
const isCodeBtnDisabled = ref(false); //
const checkboxUrl = ref("../../../static/icons/Check-one-false.png");
const isRecovering = ref(false);
const newPasswordLookFirst = ref(false);
const newPasswordLookSecond = ref(false);
// 使list.js
const countries = ref(
countryList.list.map((item) => ({
name: item.name,
code: `+${item.tel}`,
flag: item.flag,
short: item.short,
en: item.en,
}))
);
//
const selectedCountry = ref(
countries.value.find((country) => country.short === "CN") ||
countries.value[0]
);
//
function showCountryPicker() {
uni.showActionSheet({
itemList: countries.value.map(
(country) => `${country.name} ${country.code}`
),
success: function (res) {
selectedCountry.value = countries.value[res.tapIndex];
},
});
}
function goToBack() {
//
uni.navigateBack(-1);
}
function switchEmail() {
//
switchType.value = "Email";
}
function switchPhone() {
//
switchType.value = "Phone";
}
function register() {
if (isRecovering.value) {
if(!newPasswordFirst.value || !newPasswordSecond.value){
uni.showToast({
title: "密码不能为空",
icon: "none",
});
return;
}
if (newPasswordFirst.value !== newPasswordSecond.value) {
uni.showToast({
title: "前后密码不一致",
icon: "none",
});
return;
}
//
return;
}
if (switchType.value === "Phone") {
//
if (!phone.value) {
uni.showToast({
title: "请输入手机号码",
icon: "none",
});
return;
}
if (!password.value) {
uni.showToast({
title: "请输入验证码",
icon: "none",
});
return;
}
const phoneAll = `${country.value}${phone.value}`;
console.log("完整手机号" + phoneAll);
if (validatePhoneNumber(country.value, phone.value)) {
console.log("登录成功:", phoneAll);
}
//
// console.log(":", phone.value);
}
if (switchType.value === "Email") {
//
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
if (!password.value) {
uni.showToast({
title: "请输入验证码",
icon: "none",
});
return;
}
const bool = verificationEmail(email.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "邮箱格式不正确",
icon: "none",
});
return;
}
//
console.log("登录:", email.value);
}
isRecovering.value = !isRecovering.value;
//
// uni.showToast({
// title: "",
// icon: "success",
// });
}
function goToLogin() {
//
uni.navigateTo({
url: "/pages/start/login/login",
});
}
function onPhoneInput(e) {
//
const value = e.detail.value;
// 使 isNaN
if (isNaN(value)) {
phone.value = "";
} else {
phone.value = value;
}
}
// function sendCode() {
// //
// if (isCodeBtnDisabled.value) return;
// //
// isCodeBtnDisabled.value = true;
// codeBtnText.value = "";
// let time = 6;
// const timer = setInterval(() => {
// time--;
// codeBtnText.value = " " + time + "S";
// if (time <= 0) {
// clearInterval(timer);
// codeBtnText.value = "";
// //
// isCodeBtnDisabled.value = false;
// }
// }, 1000);
// return;
// }
function sendCode() {
if (switchType.value === "Phone") {
if (!phone.value) {
uni.showToast({
title: "请输入手机号",
icon: "none",
});
return;
}
const bool = verificationPhone(country.value, phone.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "手机号格式不正确",
icon: "none",
});
return;
}
}
if (switchType.value === "Email") {
if (!email.value) {
uni.showToast({
title: "请输入邮箱地址",
icon: "none",
});
return;
}
const bool = verificationEmail(email.value);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "邮箱格式不正确",
icon: "none",
});
return;
}
}
//
if (isCodeBtnDisabled.value) return;
//
isCodeBtnDisabled.value = true;
codeBtnText.value = "重新发送";
let time = 6;
const timer = setInterval(() => {
time--;
codeBtnText.value = "重新发送 " + time + "s";
if (time <= 0) {
clearInterval(timer);
codeBtnText.value = "重新发送";
//
isCodeBtnDisabled.value = false;
}
}, 1000);
return;
}
function openAgreement() {
//
console.log("打开用户协议");
uni.navigateTo({
url: "/pages/start/agreement/agreement",
});
}
function openPrivacy() {
//
console.log("打开隐私政策");
uni.navigateTo({
url: "/pages/start/privacy/privacy",
});
}
function changeCheckbox() {
agreed.value = !agreed.value;
checkboxUrl.value = agreed.value
? "../../../static/icons/Check-one-true.png"
: "../../../static/icons/Check-one-false.png";
}
//
function validatePhoneNumber(countryCode, phoneNumber) {
//
if (!phoneNumber || phoneNumber.trim() === "") {
uni.showToast({
title: "手机号不能为空",
icon: "none",
});
return false;
}
const bool = verificationPhone(countryCode, phoneNumber);
console.log("验证是否成功", bool);
//
if (!bool) {
uni.showToast({
title: "手机号格式不正确",
icon: "none",
});
return false;
}
// +715
const cleanNumber = phoneNumber.replace(/^\+/, "");
if (cleanNumber.length < 7 || cleanNumber.length > 15) {
uni.showToast({
title: "手机号长度不正确",
icon: "none",
});
return false;
}
return true;
}
//
const agreementPopup = ref(null);
//
function handleAgree() {
//
agreementPopup.value.close();
//
agreed.value = true;
checkboxUrl.value = "../../../static/icons/Check-one-true.png";
//
}
</script>
<style scoped>
.login-registration-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 70rpx;
height: 100vh;
background-color: #ffffff;
}
/* 自定义导航栏样式 */
.custom-navbar {
position: absolute;
top: 0;
left: 0;
/* z-index: 999; */
width: 90%;
height: 80rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10rpx 40rpx;
margin-bottom: 20rpx;
}
.nav-left,
.nav-right {
flex: 1;
}
.nav-right {
display: flex;
justify-content: flex-end;
}
.nav-left {
display: flex;
justify-content: flex-start;
}
.icons {
margin: 20rpx;
width: 40rpx;
height: 40rpx;
/* margin-right: 10rpx; */
}
.back-btn,
.headphone-btn {
font-size: 36rpx;
font-weight: bold;
color: #333333;
padding: 10rpx;
}
.logo {
width: 120rpx;
height: 120rpx;
margin-bottom: 60rpx;
border-radius: 20%;
}
.welcome-text {
font-size: 48rpx;
font-weight: bold;
color: #333333;
margin-bottom: 60rpx;
/* text-align: left; */
/* align-self: flex-start; */
}
.switch-container {
display: flex;
margin-bottom: 40rpx;
align-self: flex-start;
}
.switch-item {
font-size: 28rpx;
color: #999999;
padding: 10rpx 20rpx;
position: relative;
}
.switch-item::after {
content: "";
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60%;
/* 控制边框宽度 */
height: 2rpx;
background-color: transparent;
}
.switch-item.active {
color: #333333;
font-weight: 700;
}
.switch-item.active::after {
content: "";
position: absolute;
top: 60rpx;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 30%;
/* 控制边框宽度 */
height: 7rpx;
background-color: #333333;
}
.input-container {
width: 100%;
}
/* 添加图标输入框样式 */
.input-with-icon {
display: flex;
align-items: center;
width: 100%;
height: 80rpx;
border-bottom: 2rpx solid #e5e5e5;
margin-bottom: 20rpx;
}
.input-icon {
width: 40rpx;
height: 40rpx;
margin: 0 20rpx;
}
.input-icon-eye {
width: 40rpx;
height: 20rpx;
margin: 0 20rpx;
}
.input-field {
flex: 1;
height: 80rpx;
padding: 15rpx 0;
font-size: 28rpx;
color: #333333;
border: none;
background-color: transparent;
}
.phone-input-container {
display: flex;
align-items: center;
width: 95.8%;
height: 80rpx;
/* border-radius: 20rpx; */
/* border: 2rpx solid #e5e5e5; */
/* background-color: #f5f5f5; */
padding: 0 10rpx;
border-bottom: 2rpx solid #e5e5e5;
margin-bottom: 20rpx;
}
.country-code-selector {
display: flex;
align-items: center;
padding: 0 10rpx;
padding-bottom: 1rpx;
height: 100%;
/* border-right: 2rpx solid #e5e5e5; */
/* background-color: #f5f5f5; */
border-radius: 20rpx 0 0 20rpx;
}
.country-code {
font-size: 28rpx;
color: #333333;
margin-right: 10rpx;
}
.country-flag-img {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.arrow-down {
font-size: 20rpx;
color: #999999;
}
.phone-input {
flex: 1;
width: auto;
height: 100%;
border: none;
background-color: transparent;
padding: 0 0rpx;
}
.send-code-btn {
width: 200rpx;
height: 60rpx;
display: inline-flex;
padding: 0rpx 10rpx;
justify-content: center;
align-items: center;
gap: 10px;
border-radius: 4px;
background: #000;
}
.send-code-btn-email {
width: 200rpx;
height: 60rpx;
display: inline-flex;
padding: 0rpx 10rpx;
justify-content: center;
align-items: center;
gap: 10px;
border-radius: 4px;
background: #000;
margin-right: 15rpx;
}
.send-code-btn-disabled {
background: #e6e6e6;
/* 禁用状态下的灰色背景 */
}
.send-code-btn-disabled-text {
color: #999999 !important;
}
.send-code-text {
color: #fff;
font-size: 28rpx;
}
.agreement-container-one {
display: flex;
align-items: center;
align-self: flex-start;
margin-bottom: 80rpx;
}
.agreement-container {
display: flex;
align-items: center;
margin-bottom: 40rpx;
margin-top: -75.5rpx;
align-self: flex-start;
}
.checkbox {
width: 30rpx;
height: 30rpx;
margin-left: 20rpx;
/* flex: content; */
}
.agreement-text-one {
font-size: 22rpx;
color: #666666;
text-align: center;
margin-left: 10rpx;
}
.agreement-text {
margin-left: 20rpx;
font-size: 24rpx;
color: #666666;
}
.link {
color: #333333;
font-weight: bold;
text-decoration: underline;
}
.register-btn {
width: 100%;
height: 80rpx;
background-color: #000000;
color: white;
font-size: 32rpx;
font-weight: bold;
border-radius: 40rpx;
margin-bottom: 40rpx;
}
.or-text {
flex-direction: column;
font-size: 24rpx;
color: #999999;
margin-top: 294rpx;
margin-bottom: -22rpx;
}
.to-icon {
width: 10rpx;
height: 16rpx;
}
.or-text-one {
flex-direction: column;
font-size: 24rpx;
color: #999999;
}
.third-party-login {
width: 100%;
margin-bottom: 60rpx;
}
.third-party-text {
color: #ffffff;
font-weight: bold;
white-space: pre;
}
.third-party-btn {
width: 100%;
height: 80rpx;
background-color: rgb(0, 0, 0);
border: 2rpx solid #e5e5e5;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
font-size: 28rpx;
color: #333333;
}
.google-icon,
.apple-icon {
width: 60rpx;
height: 60rpx;
margin-right: 20rpx;
}
.existing-account {
display: flex;
align-items: center;
}
.account-text {
font-size: 24rpx;
color: #666666;
}
.login-link {
font-size: 24rpx;
font-weight: bold;
color: #333333;
margin-left: 10rpx;
text-decoration: underline;
}
.static-footer {
position: fixed;
bottom: 0;
}
/* 弹窗样式 */
.popup-content {
background-color: #ffffff;
padding: 40rpx;
text-align: center;
border-radius: 10rpx;
width: 550rpx;
}
.popup-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 80rpx;
text-align: center; /* 水平居中 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.popup-message {
font-size: 28rpx;
color: #000000;
margin-bottom: 80rpx;
text-align: center; /* 水平居中 */
display: flex; /* 使用flex布局 */
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.agree-button {
width: 300rpx;
height: 80rpx;
background-color: #000000;
border-radius: 40rpx;
display: flex; /* 添加flex布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
}
.agree-text {
color: #ffffff;
font-size: 34rpx;
/* 添加垂直居中相关样式 */
display: flex;
align-items: center;
justify-content: center;
line-height: 1; /* 确保文字垂直居中 */
}
.switch-container-occupy {
margin-top: 100rpx;
}
</style>

47
pages/start/select/select.vue

@ -17,11 +17,22 @@
<button class="register-button" @click="toRegistration">注册</button>
</view>
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
<script setup>
import footerBar from '../../../components/footerBar-cn'
import { ref, reactive, toRefs, watch } from 'vue'
const type = ref('member')
function toRegistration() {
uni.navigateTo({
url: "/pages/start/Registration/Registration",
@ -42,8 +53,11 @@ function toLogin() {
align-items: center;
/* justify-content: space-between; */
padding: 40rpx;
height: 100vh;
height: 90vh;
background-color: #ffffff;
overflow: hidden;
max-height: 100vh;
-webkit-overflow-scrolling: none;
}
.title-section {
@ -56,30 +70,32 @@ function toLogin() {
.main-title {
text-align: center;
font-size: 64rpx;
font-weight: 800;
font-weight: 300;
color: #000000;
margin-bottom: 10rpx;
}
.subtitle-section {
margin-bottom: 100rpx;
margin-bottom: 80rpx;
}
.subtitle {
font-weight: bold;
/* font-weight: bold; */
font-size: 32rpx;
color: #333333;
}
.phone-card {
background-image: url("/static/images/card.jpg");
background-size: 100% 100%;
background-image: url("/static/select.png");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
min-width: 400rpx;
min-height: 700rpx;
width: 400rpx;
height: 700rpx;
border-radius: 40rpx;
/* background-position: center; */
/* min-width: 300rpx; */
width: 420rpx;
height: 786rpx;
border-radius: 50rpx;
padding: 40rpx;
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.3);
/* box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.3); */
position: relative;
overflow: hidden;
}
@ -149,4 +165,11 @@ function toLogin() {
.nav-item.active .nav-text {
color: #000000;
}
.static-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
</style>

4
pages/start/startup/startup.vue

@ -16,11 +16,13 @@ onShow(() => {
});
}, 3000);
});
</script>
<style>
.background {
background: linear-gradient(180deg, #2a0c4d, #1d0836, #0d0218);
background: linear-gradient(180deg, #FB6967, #FB6967);
width: 100vw;
height: 100vh;
}

BIN
static/icons/To.png

After

Width: 11  |  Height: 18  |  Size: 442 B

BIN
static/icons/back.png

After

Width: 18  |  Height: 32  |  Size: 576 B

BIN
static/icons/look.png

After

Width: 40  |  Height: 24  |  Size: 1.5 KiB

BIN
static/icons/start-logo.png

After

Width: 326  |  Height: 200  |  Size: 20 KiB

BIN
static/icons/unlook.png

After

Width: 40  |  Height: 18  |  Size: 1.0 KiB

BIN
static/select.png

After

Width: 420  |  Height: 786  |  Size: 156 KiB

BIN
static/start-logo.png

After

Width: 326  |  Height: 200  |  Size: 20 KiB

18
uni_modules/uni-popup/changelog.md

@ -1,3 +1,21 @@
## 1.9.11(2025-08-20)
- 修复 uni-popup-dialog组件设置 borderRadius 不生效的 Bug
## 1.9.10(2025-07-18)
- 修复 nvue 下弹窗样式错乱的问题 ,更新依赖 uni-transition 组件
- 更新 示例取消 borderRadius 属性 ,如需内容圆角,用户应该直接在内容插槽中实现
## 1.9.9(2025-06-11)
- 修复 uni-popup-dialog 中 setVal 方法报错的问题
- 修复 uni-popup-dialog 数据双向绑定问题。
## 1.9.8(2025-04-16)
- 修复 更新组件示例 ,解决更新数据或保存项目导致弹窗消失的问题
## 1.9.7(2025-04-14)
- 修复 uni-popup-dialog 弹出框在vue3中双向绑定问题
## 1.9.6(2025-01-08)
- 修复 示例中过期图片地址
## 1.9.5(2024-10-15)
- 修复 微信小程序中的getSystemInfo警告
## 1.9.2(2024-09-21)
- 修复 uni-popup在android上的重复点击弹出位置不正确的bug
## 1.9.1(2024-04-02)
- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
## 1.9.0(2024-03-28)

28
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue

@ -1,5 +1,5 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-popup-dialog" :style="{ borderRadius }">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
</view>
@ -124,6 +124,10 @@
focus: {
type: Boolean,
default: true,
},
borderRadius: {
type: String,
default: '11px',
}
},
data() {
@ -156,12 +160,13 @@
}
},
value(val) {
if (this.maxlength != -1 && this.mode === 'input') {
this.val = val.slice(0, this.maxlength);
} else {
this.val = val
}
this.setVal(val)
},
// #ifdef VUE3
modelValue(val) {
this.setVal(val)
},
// #endif
val(val) {
// #ifdef VUE2
// TODO vue2
@ -189,6 +194,16 @@
},
methods: {
/**
* 给val属性赋值
*/
setVal(val) {
if (this.maxlength != -1 && this.mode === 'input') {
this.val = val.slice(0, this.maxlength);
} else {
this.val = val
}
},
/**
* 点击确认按钮
*/
onOk() {
@ -218,7 +233,6 @@
<style lang="scss">
.uni-popup-dialog {
width: 300px;
border-radius: 11px;
background-color: #fff;
}

9
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue

@ -39,24 +39,25 @@
},
data() {
return {
// TODO
bottomData: [{
text: '微信',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'wx'
},
{
text: '支付宝',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'ali'
},
{
text: 'QQ',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'qq'
},
{
text: '新浪',
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'sina'
},
// {

31
uni_modules/uni-popup/components/uni-popup/uni-popup.vue

@ -192,6 +192,17 @@
},
mounted() {
const fixSize = () => {
// #ifdef MP-WEIXIN
const {
windowWidth,
windowHeight,
windowTop,
safeArea,
screenHeight,
safeAreaInsets
} = uni.getWindowInfo()
// #endif
// #ifndef MP-WEIXIN
const {
windowWidth,
windowHeight,
@ -200,6 +211,7 @@
screenHeight,
safeAreaInsets
} = uni.getSystemInfoSync()
// #endif
this.popupWidth = windowWidth
this.popupHeight = windowHeight + (windowTop || 0)
// TODO fix by mehaotian ,ios app ios
@ -350,6 +362,7 @@
this.showPopup = true
this.showTrans = true
this.$nextTick(() => {
this.showPoptrans()
if (this.messageChild && this.type === 'message') {
this.messageChild.timerClose()
}
@ -372,8 +385,7 @@
}
// TODO type
if (type) return
this.showPopup = true
this.showTrans = true
this.showPoptrans()
},
/**
* 中间弹出样式处理
@ -403,8 +415,7 @@
}
// TODO type
if (type) return
this.showPopup = true
this.showTrans = true
this.showPoptrans()
},
left(type) {
this.popupstyle = 'left'
@ -423,8 +434,7 @@
}
// TODO type
if (type) return
this.showPopup = true
this.showTrans = true
this.showPoptrans()
},
right(type) {
this.popupstyle = 'right'
@ -443,8 +453,13 @@
}
// TODO type
if (type) return
this.showPopup = true
this.showTrans = true
this.showPoptrans()
},
showPoptrans(){
this.$nextTick(()=>{
this.showPopup = true
this.showTrans = true
})
}
}
}

189
uni_modules/uni-popup/package.json

@ -1,88 +1,107 @@
{
"id": "uni-popup",
"displayName": "uni-popup 弹出层",
"version": "1.9.1",
"description": " Popup 组件,提供常用的弹层",
"keywords": [
"uni-ui",
"弹出层",
"弹窗",
"popup",
"弹框"
"id": "uni-popup",
"displayName": "uni-popup 弹出层",
"version": "1.9.11",
"description": " Popup 组件,提供常用的弹层",
"keywords": [
"uni-ui",
"弹出层",
"弹窗",
"popup",
"弹框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "",
"uni-app": "^4.07",
"uni-app-x": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-transition"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-transition"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"uni-app": {
"vue": {
"vue2": "√",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "√",
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√",
"alipay": "√",
"toutiao": "√",
"baidu": "√",
"kuaishou": "-",
"jd": "-",
"harmony": "-",
"qq": "√",
"lark": "-"
},
"quickapp": {
"huawei": "-",
"union": "-"
}
},
"uni-app-x": {
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√"
}
}
}
}
}
}

7
uni_modules/uni-transition/changelog.md

@ -1,3 +1,10 @@
## 1.3.6(2025-07-18)
- 修复 nvue 页面,样式错误问题
## 1.3.5(2025-06-11)
- 修复 第一次执行不显示动画的问题
## 1.3.4(2025-04-16)
- 修复 页面数据更新到底动画复原的问题
- 修复 示例页面打开报错的问题
## 1.3.3(2024-04-23)
- 修复 当元素会受变量影响自动隐藏的bug
## 1.3.2(2023-05-04)

520
uni_modules/uni-transition/components/uni-transition/uni-transition.vue

@ -1,286 +1,292 @@
<template>
<!-- #ifndef APP-NVUE -->
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick">
<slot></slot>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick">
<slot></slot>
</view>
<!-- #endif -->
</template>
<script>
import { createAnimation } from './createAnimation'
import { createAnimation } from './createAnimation'
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
emits:['click','change'],
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: [Array, String],
default() {
return 'fade'
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default() {
return {}
}
},
customClass:{
type: String,
default: ''
},
onceRender:{
type:Boolean,
default:false
},
},
data() {
return {
isShow: false,
transform: '',
opacity: 1,
animationData: {},
durationTime: 300,
config: {}
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
// close,
if (this.isShow) {
this.close()
}
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
emits: ['click', 'change'],
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: [Array, String],
default () {
return 'fade'
}
},
immediate: true
}
},
computed: {
//
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transform = ''
for (let i in styles) {
let line = this.toLine(i)
transform += line + ':' + styles[i] + ';'
}
return transform
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default () {
return {}
}
},
customClass: {
type: String,
default: ''
},
onceRender: {
type: Boolean,
default: false
},
},
//
transformStyles() {
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
},
created() {
//
this.config = {
duration: this.duration,
timingFunction: 'ease',
transformOrigin: '50% 50%',
delay: 0
}
this.durationTime = this.duration
},
methods: {
/**
* ref 触发 初始化动画
*/
init(obj = {}) {
if (obj.duration) {
this.durationTime = obj.duration
data() {
return {
isShow: false,
transform: '',
opacity: 0,
animationData: {},
durationTime: 300,
config: {}
}
this.animation = createAnimation(Object.assign(this.config, obj),this)
},
/**
* 点击组件触发回调
*/
onClick() {
this.$emit('click', {
detail: this.isShow
})
},
/**
* ref 触发 动画分组
* @param {Object} obj
*/
step(obj, config = {}) {
if (!this.animation) return
for (let i in obj) {
try {
if(typeof obj[i] === 'object'){
this.animation[i](...obj[i])
}else{
this.animation[i](obj[i])
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
// close,
if (this.isShow) {
this.close()
}
}
} catch (e) {
console.error(`方法 ${i} 不存在`)
}
},
immediate: true
}
this.animation.step(config)
return this
},
/**
* ref 触发 执行动画
*/
run(fn) {
if (!this.animation) return
this.animation.run(fn)
},
//
open() {
clearTimeout(this.timer)
this.transform = ''
this.isShow = true
let { opacity, transform } = this.styleInit(false)
if (typeof opacity !== 'undefined') {
this.opacity = opacity
computed: {
//
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transform = ''
for (let i in styles) {
let line = this.toLine(i)
transform += line + ':' + styles[i] + ';'
}
return transform
},
//
transformStyles() {
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
this.transform = transform
// nextTick wx
this.$nextTick(() => {
// TODO
this.timer = setTimeout(() => {
this.animation = createAnimation(this.config, this)
this.tranfromInit(false).step()
this.animation.run()
this.$emit('change', {
detail: this.isShow
})
}, 20)
})
},
//
close(type) {
if (!this.animation) return
this.tranfromInit(true)
.step()
.run(() => {
this.isShow = false
this.animationData = null
this.animation = null
let { opacity, transform } = this.styleInit(false)
this.opacity = opacity || 1
this.transform = transform
this.$emit('change', {
detail: this.isShow
})
})
},
//
styleInit(type) {
let styles = {
transform: ''
created() {
//
this.config = {
duration: this.duration,
timingFunction: 'ease',
transformOrigin: '50% 50%',
delay: 0
}
let buildStyle = (type, mode) => {
if (mode === 'fade') {
styles.opacity = this.animationType(type)[mode]
} else {
styles.transform += this.animationType(type)[mode] + ' '
this.durationTime = this.duration
},
methods: {
/**
* ref 触发 初始化动画
*/
init(obj = {}) {
if (obj.duration) {
this.durationTime = obj.duration
}
}
if (typeof this.modeClass === 'string') {
buildStyle(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildStyle(type, mode)
this.animation = createAnimation(Object.assign(this.config, obj), this)
},
/**
* 点击组件触发回调
*/
onClick() {
this.$emit('click', {
detail: this.isShow
})
}
return styles
},
//
tranfromInit(type) {
let buildTranfrom = (type, mode) => {
let aniNum = null
if (mode === 'fade') {
aniNum = type ? 0 : 1
} else {
aniNum = type ? '-100%' : '0'
if (mode === 'zoom-in') {
aniNum = type ? 0.8 : 1
}
if (mode === 'zoom-out') {
aniNum = type ? 1.2 : 1
},
/**
* ref 触发 动画分组
* @param {Object} obj
*/
step(obj, config = {}) {
if (!this.animation) return this
Object.keys(obj).forEach(key => {
const value = obj[key]
if (typeof this.animation[key] === 'function') {
Array.isArray(value) ?
this.animation[key](...value) :
this.animation[key](value)
}
if (mode === 'slide-right') {
aniNum = type ? '100%' : '0'
})
this.animation.step(config)
return this
},
/**
* ref 触发 执行动画
*/
run(fn) {
if (!this.animation) return
this.animation.run(fn)
},
//
open() {
clearTimeout(this.timer)
this.isShow = true
//
this.transform = this.styleInit(false).transform || ''
this.opacity = this.styleInit(false).opacity || 0
// nextTick wx
this.$nextTick(() => {
// TODO
this.timer = setTimeout(() => {
this.animation = createAnimation(this.config, this)
this.tranfromInit(false).step()
this.animation.run(() => {
// #ifdef APP-NVUE
this.transform = this.styleInit(false).transform || ''
this.opacity = this.styleInit(false).opacity || 1
// #endif
// #ifndef APP-NVUE
this.transform = ''
this.opacity = this.styleInit(false).opacity || 1
// #endif
this.$emit('change', {
detail: this.isShow
})
})
}, 80)
})
},
//
close(type) {
if (!this.animation) return
this.tranfromInit(true)
.step()
.run(() => {
this.isShow = false
this.animationData = null
this.animation = null
let { opacity, transform } = this.styleInit(false)
this.opacity = opacity || 1
this.transform = transform
this.$emit('change', {
detail: this.isShow
})
})
},
//
styleInit(type) {
let styles = { transform: '', opacity: 1 }
const buildStyle = (type, mode) => {
const value = this.animationType(type)[mode] // 使 type
if (mode.startsWith('fade')) {
styles.opacity = value
} else {
styles.transform += value + ' '
}
if (mode === 'slide-bottom') {
aniNum = type ? '100%' : '0'
}
if (typeof this.modeClass === 'string') {
buildStyle(type, this.modeClass)
} else {
this.modeClass.forEach(mode => buildStyle(type, mode))
}
return styles
},
//
tranfromInit(type) {
let buildTranfrom = (type, mode) => {
let aniNum = null
if (mode === 'fade') {
aniNum = type ? 0 : 1
} else {
aniNum = type ? '-100%' : '0'
if (mode === 'zoom-in') {
aniNum = type ? 0.8 : 1
}
if (mode === 'zoom-out') {
aniNum = type ? 1.2 : 1
}
if (mode === 'slide-right') {
aniNum = type ? '100%' : '0'
}
if (mode === 'slide-bottom') {
aniNum = type ? '100%' : '0'
}
}
this.animation[this.animationMode()[mode]](aniNum)
}
if (typeof this.modeClass === 'string') {
buildTranfrom(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildTranfrom(type, mode)
})
}
this.animation[this.animationMode()[mode]](aniNum)
}
if (typeof this.modeClass === 'string') {
buildTranfrom(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildTranfrom(type, mode)
})
}
return this.animation
},
animationType(type) {
return {
fade: type ? 0 : 1,
'slide-top': `translateY(${type ? '0' : '-100%'})`,
'slide-right': `translateX(${type ? '0' : '100%'})`,
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
'slide-left': `translateX(${type ? '0' : '-100%'})`,
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
}
},
//
animationMode() {
return {
fade: 'opacity',
'slide-top': 'translateY',
'slide-right': 'translateX',
'slide-bottom': 'translateY',
'slide-left': 'translateX',
'zoom-in': 'scale',
'zoom-out': 'scale'
return this.animation
},
animationType(type) {
return {
fade: type ? 1 : 0,
'slide-top': `translateY(${type ? '0' : '-100%'})`,
'slide-right': `translateX(${type ? '0' : '100%'})`,
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
'slide-left': `translateX(${type ? '0' : '-100%'})`,
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
}
},
//
animationMode() {
return {
fade: 'opacity',
'slide-top': 'translateY',
'slide-right': 'translateX',
'slide-bottom': 'translateY',
'slide-left': 'translateX',
'zoom-in': 'scale',
'zoom-out': 'scale'
}
},
// 线
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
},
// 线
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
}
}
</script>
<style></style>

103
uni_modules/uni-transition/package.json

@ -1,7 +1,7 @@
{
"id": "uni-transition",
"displayName": "uni-transition 过渡动画",
"version": "1.3.3",
"version": "1.3.6",
"description": "元素的简单过渡动画",
"keywords": [
"uni-ui",
@ -12,12 +12,14 @@
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
"HBuilderX": "",
"uni-app": "^4.12",
"uni-app-x": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
@ -35,49 +37,74 @@
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
"type": "component-vue",
"darkmode": "x",
"i18n": "x",
"widescreen": "x"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"dependencies": [
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
"uni-app": {
"vue": {
"vue2": "√",
"vue3": "√"
},
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"vue": "√",
"nvue": "√",
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": {
},
"alipay": {
},
"toutiao": {
},
"baidu": {
},
"kuaishou": {
},
"jd": {
},
"harmony": "-",
"qq": "√",
"lark": "-"
},
"quickapp": {
"huawei": "√",
"union": "√"
}
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
"uni-app-x": {
"web": {
"safari": "-",
"chrome": "-"
},
"app": {
"android": "-",
"ios": "-",
"harmony": "-"
},
"mp": {
"weixin": "-"
}
}
}
}

Loading…
Cancel
Save