feat: 商品详情交互开发
This commit is contained in:
@@ -1,44 +1,169 @@
|
||||
<template>
|
||||
<view class="form-wrapper">
|
||||
<view class="form-header">
|
||||
<image class="form-icon" src="./images/icon_minus.png"></image>
|
||||
<text class="form-title">游客1</text>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">姓 名</text>
|
||||
<input class="form-input" v-model="name" placeholder="请输入姓名" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input
|
||||
class="form-input"
|
||||
v-model="phone"
|
||||
placeholder="请输入手机号"
|
||||
@blur="validatePhone"
|
||||
<uni-icons class="minus" color="#00A6FF" size="22" type="minus" />
|
||||
<text class="form-title">{{ title }}</text>
|
||||
<uni-icons
|
||||
v-if="showDeleteIcon"
|
||||
class="delete"
|
||||
color="#00A6FF"
|
||||
size="22"
|
||||
type="trash"
|
||||
@click="handleDelete"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-item-wrapper">
|
||||
<view class="form-item">
|
||||
<view class="form-item-row">
|
||||
<text class="form-label">姓 名</text>
|
||||
<input
|
||||
class="form-input"
|
||||
:class="{ 'form-input-error': nameError }"
|
||||
v-model="nameValue"
|
||||
placeholder="请输入姓名"
|
||||
@blur="validateName"
|
||||
/>
|
||||
</view>
|
||||
<text v-if="nameError" class="form-error">{{ nameError }}</text>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="form-item-row">
|
||||
<text class="form-label">手机号</text>
|
||||
<input
|
||||
class="form-input"
|
||||
:class="{ 'form-input-error': phoneError }"
|
||||
v-model="phoneValue"
|
||||
placeholder="请输入手机号"
|
||||
type="tel"
|
||||
maxlength="11"
|
||||
@blur="validatePhone"
|
||||
/>
|
||||
</view>
|
||||
<text v-if="phoneError" class="form-error">{{ phoneError }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
// Local state
|
||||
const name = ref("");
|
||||
const phone = ref("");
|
||||
// 常量定义
|
||||
const PHONE_REGEX = /^1[3-9]\d{9}$/;
|
||||
const ERROR_MESSAGES = {
|
||||
NAME_REQUIRED: "请输入姓名",
|
||||
PHONE_REQUIRED: "手机号不能为空",
|
||||
PHONE_INVALID: "请输入正确的手机号",
|
||||
};
|
||||
|
||||
/**
|
||||
* FormCard 组件 Props
|
||||
* @typedef {Object} FormCardProps
|
||||
* @property {string} title - 表单标题
|
||||
* @property {Object} form - 表单数据对象
|
||||
* @property {string} form.name - 姓名
|
||||
* @property {string} form.phone - 手机号
|
||||
* @property {boolean} showDeleteIcon - 是否显示删除图标
|
||||
*/
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: "游客1",
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
name: "",
|
||||
phone: "",
|
||||
}),
|
||||
validator: (value) => {
|
||||
return value && typeof value === 'object' &&
|
||||
'name' in value && 'phone' in value;
|
||||
},
|
||||
},
|
||||
showDeleteIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* FormCard 组件事件
|
||||
* @typedef {Object} FormCardEmits
|
||||
* @property {Function} update:name - 更新姓名事件
|
||||
* @property {Function} update:phone - 更新手机号事件
|
||||
* @property {Function} delete - 删除表单事件
|
||||
*/
|
||||
const emit = defineEmits(["update:name", "update:phone", "delete"]);
|
||||
|
||||
// 响应式状态
|
||||
const nameError = ref("");
|
||||
const phoneError = ref("");
|
||||
|
||||
// Methods
|
||||
const validatePhone = () => {
|
||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||
if (!phone.value) {
|
||||
phoneError.value = "手机号不能为空";
|
||||
} else if (!phoneRegex.test(phone.value)) {
|
||||
phoneError.value = "请输入正确的手机号";
|
||||
} else {
|
||||
phoneError.value = "";
|
||||
// 计算属性 - 双向绑定
|
||||
const nameValue = computed({
|
||||
get: () => props.form?.name || "",
|
||||
set: (value) => emit("update:name", value?.trim() || ""),
|
||||
});
|
||||
|
||||
const phoneValue = computed({
|
||||
get: () => props.form?.phone || "",
|
||||
set: (value) => {
|
||||
// 只允许数字输入
|
||||
const numericValue = value.replace(/\D/g, "");
|
||||
emit("update:phone", numericValue);
|
||||
},
|
||||
});
|
||||
|
||||
// 工具函数
|
||||
/**
|
||||
* 验证姓名是否有效
|
||||
* @param {string} name - 姓名
|
||||
* @returns {string} 错误信息,空字符串表示验证通过
|
||||
*/
|
||||
const getNameError = (name) => {
|
||||
if (!name || name.trim() === "") {
|
||||
return ERROR_MESSAGES.NAME_REQUIRED;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* 验证手机号是否有效
|
||||
* @param {string} phone - 手机号
|
||||
* @returns {string} 错误信息,空字符串表示验证通过
|
||||
*/
|
||||
const getPhoneError = (phone) => {
|
||||
if (!phone) {
|
||||
return ERROR_MESSAGES.PHONE_REQUIRED;
|
||||
}
|
||||
if (!PHONE_REGEX.test(phone)) {
|
||||
return ERROR_MESSAGES.PHONE_INVALID;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
// 验证方法
|
||||
const validateName = () => {
|
||||
nameError.value = getNameError(props.form?.name);
|
||||
};
|
||||
|
||||
const validatePhone = () => {
|
||||
phoneError.value = getPhoneError(props.form?.phone);
|
||||
};
|
||||
|
||||
// 事件处理
|
||||
const handleDelete = () => {
|
||||
emit("delete");
|
||||
};
|
||||
|
||||
// 暴露给模板的方法(用于测试或外部调用)
|
||||
defineExpose({
|
||||
validateName,
|
||||
validatePhone,
|
||||
getNameError,
|
||||
getPhoneError,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user