hookehuyr

feat(JoinFamily): 更新加入家庭页面样式和输入验证逻辑

refactor(CreateFamily): 优化家训输入框的交互和验证逻辑
style: 统一文件头部注释格式
......@@ -77,7 +77,9 @@
type="text"
v-model="familyMotto[index]"
:placeholder="familyMottoPlaceholder[index]"
maxlength="1"
@input="(e) => handleInputChange(index, e.target.value)"
@focus="focusedIndex = index"
@blur="handleBlur(index)"
class="w-full h-full bg-transparent text-center"
style="font-size: 38rpx;"
/>
......@@ -170,8 +172,7 @@ const familyMotto = ref(['', '', '', '']);
const familyMottoPlaceholder = ref(['孝', '敬', '和', '睦']);
const familySizes = ['2人', '3-5人', '6人+'];
const familyAvatar = ref('');
const focusedIndex = ref(-1);
// 图片预览相关
const previewVisible = ref(false);
......@@ -185,6 +186,26 @@ const previewIndex = ref(0);
// };
/**
* 处理输入变化
*/
const handleInputChange = (index, value) => {
// 只保留第一个有效字符(汉字、数字、大小写字母)
const validChar = value.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] || '';
familyMotto.value[index] = validChar;
};
/**
* 处理失焦事件
*/
const handleBlur = (index) => {
focusedIndex.value = -1;
// 确保只保留有效字符(汉字、数字、大小写字母)
const value = familyMotto.value[index];
const validChar = value.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] || '';
familyMotto.value[index] = validChar;
};
/**
* 显示提示信息
*/
const showToast = (message, type = 'success') => {
......
/*
* @Date: 2025-08-27 18:25:24
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-27 23:18:26
* @FilePath: /lls_program/src/pages/JoinFamily/index.config.js
* @Description: 文件描述
*/
export default {
navigationBarTitleText: '首页'
navigationBarTitleText: '加入家庭'
}
......
<template>
<view class="min-h-screen flex flex-col bg-white">
<AppHeader title="加入家庭" />
<view class="flex-1 px-4 py-6 flex flex-col">
<!-- <AppHeader title="加入家庭" /> -->
<view class="flex-1 px-4 pt-3 pb-6 flex flex-col">
<!-- Title -->
<h2 class="text-xl font-bold text-center mb-2 mt-4">
<h2 class="text-xl font-bold text-center mb-2">
输入家训口令
</h2>
<!-- Description -->
<p class="text-gray-600 text-center text-sm mb-6">
<view class="text-gray-600 text-center text-sm mb-6">
请输入家人提供的家训口令,加入家庭一起参与健康挑战
</p>
</view>
<!-- Input boxes -->
<view class="flex justify-center gap-3 w-full mb-6">
<view v-for="(char, index) in mottoChars" :key="index" class="w-16 h-16">
<view
v-for="(char, index) in mottoChars"
:key="index"
class="w-16 h-16 border border-gray-300 rounded-md flex items-center justify-center"
:style="{
borderColor: focusedIndex === index ? '#3b82f6' : '#d1d5db'
}"
>
<input
:ref="(el) => (inputRefs[index] = el)"
type="text"
v-model="mottoChars[index]"
@input="(e) => handleInputChange(index, e.target.value)"
@keydown="(e) => handleKeyDown(index, e)"
maxlength="1"
class="w-full h-full text-center text-xl border border-gray-300 rounded-md focus:border-blue-500 focus:outline-none"
@focus="focusedIndex = index"
@blur="handleBlur(index)"
class="w-full h-full text-center text-xl bg-transparent outline-none"
style="border: none;"
/>
</view>
</view>
<!-- Help text -->
<p class="text-gray-500 text-center text-sm mb-8">
<view class="text-gray-500 text-center text-sm mb-8">
没有口令?请联系您的大家长获取
</p>
</view>
<!-- Role selection -->
<view class="mb-6">
<h3 class="text-base font-medium mb-4 border-t pt-4">
<h3 class="text-base font-medium mb-4 border-t border-gray-300 pt-4">
选择您的身份
</h3>
<view class="grid grid-cols-2 gap-3">
<button
<view
v-for="role in familyRoles"
:key="role.id"
:class="[
......@@ -61,20 +70,20 @@
{{ role.label }}
</span>
</view>
</button>
</view>
</view>
</view>
<!-- Submit Button -->
<button
<view
@click="handleJoinFamily"
:disabled="!isComplete"
:class="[
'w-full py-4 text-white text-lg font-medium rounded-lg mt-auto',
'w-full py-4 text-white text-lg font-medium rounded-lg mt-auto text-center',
isComplete ? 'bg-blue-500' : 'bg-gray-300'
]"
>
加入家庭
</button>
</view>
</view>
</view>
</template>
......@@ -88,16 +97,23 @@ import AppHeader from '../../components/AppHeader.vue';
const mottoChars = ref(['', '', '', '']);
const selectedRole = ref('');
const inputRefs = ref([]);
const focusedIndex = ref(-1);
const handleInputChange = (index, value) => {
if (value && !/^[\u4e00-\u9fa5]$/.test(value)) {
return;
}
mottoChars.value[index] = value;
if (value && index < 3) {
// 允许输入多个字符,但只保留第一个有效字符(汉字、数字、大小写字母),兼容输入法
if (value) {
// 提取第一个有效字符(汉字、数字、大小写字母)
const firstChar = value.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] || '';
mottoChars.value[index] = firstChar;
// 如果输入了有效字符且不是最后一个输入框,自动聚焦下一个
if (firstChar && index < 3) {
// Taro中无法直接操作DOM进行focus,这里仅保留逻辑
// 在小程序中,可以通过设置 `focus` 属性来控制
}
} else {
mottoChars.value[index] = '';
}
};
const handleKeyDown = (index, e) => {
......@@ -106,6 +122,22 @@ const handleKeyDown = (index, e) => {
}
};
/**
* 处理输入框失焦事件
* @param {number} index - 输入框索引
*/
const handleBlur = (index) => {
// 重置焦点状态
focusedIndex.value = -1;
// 失焦时再次验证输入值,确保只保留有效字符(汉字、数字、大小写字母)
const currentValue = mottoChars.value[index];
if (currentValue) {
const firstChar = currentValue.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] || '';
mottoChars.value[index] = firstChar;
}
};
const familyRoles = [
{ id: 'husband', label: '丈夫' },
{ id: 'wife', label: '妻子' },
......