From b387f6d94477bd609562b5e19d68e1aac00c7705 Mon Sep 17 00:00:00 2001 From: MaDaLei Date: Thu, 16 Apr 2026 23:09:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=95=86=E5=AE=B6=E5=85=A5=E9=A9=BB&?= =?UTF-8?q?=E5=91=98=E5=B7=A5=E6=B3=A8=E5=86=8C=E5=A2=9E=E5=8A=A0=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=8E=88=E6=9D=83=E8=8E=B7=E5=8F=96=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E5=8F=B7=EF=BC=8C=E6=89=8B=E5=8A=A8=E5=A1=AB=E5=8F=B7=E9=9C=80?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=EF=BC=9B=E6=8C=89=E9=92=AE=E7=A7=BB?= =?UTF-8?q?=E8=87=B3=E8=BE=93=E5=85=A5=E6=A1=86=E5=8F=B3=E4=BE=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/global.css | 177 +++++++++++++++ .../appointment/CustAppointmentCreate.vue | 85 +++----- src/pages/home/Home.vue | 47 +--- src/pages/login/Login.vue | 202 +++++++++++++----- src/pages/mine/Mine.vue | 16 +- src/pages/mine/MyPets.vue | 47 ++-- src/pages/report/Report.vue | 88 ++------ 7 files changed, 390 insertions(+), 272 deletions(-) diff --git a/src/assets/global.css b/src/assets/global.css index 8976866..0c2f949 100644 --- a/src/assets/global.css +++ b/src/assets/global.css @@ -58,6 +58,57 @@ page { } .van-field::placeholder { color: var(--c-text-4) !important; } +/* ---- 通用表单(业务页复用) ---- */ +.app-form-label { + display: block; + font-size: 13px; + font-weight: 600; + color: #666660; + margin-bottom: 6px; +} +.app-form-input { + background: #f5f5f2; + border: 1.5px solid transparent; + border-radius: 12px; + padding: 11px 14px; + font-size: 15px; + color: #1a1a1a; + width: 100%; + box-sizing: border-box; + min-height: 44px; +} +.app-form-input:focus { + border-color: #2db96d; + background: #ffffff; +} +.app-form-picker { + display: flex; + align-items: center; + justify-content: space-between; +} +.app-form-placeholder { color: #bbbbb5; } +.app-form-arrow { + color: #bbbbb5; + font-size: 18px; + font-weight: 300; +} +.app-form-textarea { + background: #f5f5f2; + border: 1.5px solid transparent; + border-radius: 12px; + padding: 12px 14px; + font-size: 14px; + color: #1a1a1a; + width: 100%; + box-sizing: border-box; + min-height: 100px; + line-height: 1.6; +} +.app-form-textarea:focus { + border-color: #2db96d; + background: #fff; +} + /* ---- 按钮 ---- * 微信小程序原生 button 默认白底,会盖住 class 背景;且仅写 var(--c-brand) 在部分基础库下可能不生效, * 导致「白底 + 白字」。此处用实色 + button 元素选择器强制覆盖。 */ @@ -119,6 +170,132 @@ button::after { border: none !important; } +/* ---- 通用操作按钮(业务页复用) ---- */ +.app-btn-sm, +button.app-btn-sm { + height: 34px !important; + min-height: 34px !important; + line-height: 34px !important; + padding: 0 16px !important; + margin: 0 !important; + border-radius: 10px !important; + font-size: 13px !important; + font-weight: 600 !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + border: none !important; + box-sizing: border-box !important; + flex-shrink: 0; + transition: opacity 0.15s, transform 0.15s, box-shadow 0.15s; +} +.app-btn-sm:active { + opacity: 0.9; + transform: translateY(1px); +} +.app-btn-primary, +button.app-btn-primary { + background: linear-gradient(135deg, #32c877 0%, #2db96d 100%) !important; + color: #ffffff !important; + box-shadow: 0 4px 12px rgba(45, 185, 109, 0.28); +} +.app-btn-outline, +button.app-btn-outline { + background: transparent !important; + color: #999993 !important; + border: 1.5px solid #ebebea !important; +} +.app-btn-danger-outline, +button.app-btn-danger-outline { + background: #ffffff !important; + color: #e5534b !important; + border: 1.5px solid #f0e0de !important; +} +.app-btn-sm[disabled], +button.app-btn-sm[disabled] { + background: #ddddd8 !important; + color: #999993 !important; + box-shadow: none !important; + border: none !important; +} +.app-btn-main, +button.app-btn-main { + width: 100% !important; + height: 48px !important; + min-height: 48px !important; + line-height: 48px !important; + padding: 0 18px !important; + margin: 0 !important; + border-radius: 14px !important; + font-size: 16px !important; + font-weight: 700 !important; + letter-spacing: 0.4px; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + border: none !important; + box-sizing: border-box !important; + transition: opacity 0.15s, transform 0.15s, box-shadow 0.15s; +} +.app-btn-main:active { + opacity: 0.9; + transform: translateY(1px); +} +.app-btn-main[disabled], +button.app-btn-main[disabled] { + background: #ddddd8 !important; + color: #999993 !important; + box-shadow: none !important; + border: none !important; +} +.app-btn-soft, +button.app-btn-soft { + background: #f5f5f2 !important; + color: #1a1a1a !important; + border: none !important; + box-shadow: none !important; +} +.app-btn-code, +button.app-btn-code { + height: 48px !important; + min-height: 48px !important; + line-height: 48px !important; + padding: 0 16px !important; + margin: 0 !important; + border-radius: 12px !important; + font-size: 14px !important; + font-weight: 600 !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + white-space: nowrap; + box-sizing: border-box !important; + border: none !important; + transition: opacity 0.15s, transform 0.15s; +} +.app-btn-code:active { + opacity: 0.9; + transform: translateY(1px); +} +.app-btn-code--neutral, +button.app-btn-code--neutral { + background: #f5f5f2 !important; + color: #2db96d !important; + border: none !important; +} +.app-btn-code--brand, +button.app-btn-code--brand { + background: #f0fcf5 !important; + color: #2db96d !important; + border: 1.5px solid #c0f0d8 !important; +} +.app-btn-code--neutral[disabled], +button.app-btn-code--neutral[disabled] { + background: #f5f5f2 !important; + color: #bbbbb5 !important; + border: none !important; +} + /* ---- 单元格组 ---- */ .van-cell-group { background: var(--c-surface) !important; diff --git a/src/pages/appointment/CustAppointmentCreate.vue b/src/pages/appointment/CustAppointmentCreate.vue index ffdf371..1addec0 100644 --- a/src/pages/appointment/CustAppointmentCreate.vue +++ b/src/pages/appointment/CustAppointmentCreate.vue @@ -13,7 +13,7 @@ - 预约门店 + 预约门店 - - {{ storePickerDisplay }} - + + {{ storePickerDisplay }} + {{ storeHintLine }} - 使用档案(可选) + 使用档案(可选) - - {{ petPickerLabel }} - + + {{ petPickerLabel }} + - 宠物名字 - + 宠物名字 + - 宠物类型 + 宠物类型 - - {{ newAppt.petType || '请选择' }} - + + {{ newAppt.petType || '请选择' }} + - 服务类型 + 服务类型 - - {{ newAppt.serviceType || '请选择' }} - + + {{ newAppt.serviceType || '请选择' }} + - 预约时间 + 预约时间 - - {{ appointmentDate || '日期' }} + + {{ appointmentDate || '日期' }} - - {{ appointmentTime || '时间' }} + + {{ appointmentTime || '时间' }} - 备注(可选) - + 备注(可选) + - + @@ -370,41 +370,8 @@ onMounted(async () => { box-shadow: 0 2px 8px rgba(0,0,0,0.04); } .c-field { margin-bottom: 16px; } -.c-label { - display: block; - font-size: 13px; - font-weight: 600; - color: #666660; - margin-bottom: 6px; -} -.c-input { - background: #f5f5f2; - border: 1.5px solid transparent; - border-radius: 12px; - padding: 11px 14px; - font-size: 15px; - color: #1a1a1a; - width: 100%; - box-sizing: border-box; - min-height: 44px; -} -.c-input:focus { border-color: #2db96d; background: #fff; } -.c-picker { display: flex; align-items: center; justify-content: space-between; } -.c-placeholder { color: #bbbbb5; } -.c-arrow { color: #bbbbb5; font-size: 18px; } .c-time-row { display: flex; gap: 10px; } .c-time-half { flex: 1; } -.c-submit { - width: 100%; - height: 48px; - border-radius: 14px; - background: #1a1a1a; - color: #fff; - font-size: 16px; - font-weight: 700; - border: none; - margin-top: 20px; -} .c-store-hint { display: block; margin-top: 6px; diff --git a/src/pages/home/Home.vue b/src/pages/home/Home.vue index 601cda3..828af98 100644 --- a/src/pages/home/Home.vue +++ b/src/pages/home/Home.vue @@ -46,7 +46,7 @@ {{ item.time }} - + 暂无预约 @@ -96,10 +96,12 @@ {{ item.time }} - - + + + + + - 暂无数据 @@ -167,8 +169,8 @@ - - + + @@ -493,20 +495,6 @@ onShow(() => { .c-time-half { flex: 1; } .c-textarea { min-height: 80px; line-height: 1.5; } -.c-submit { - width: 100%; - height: 48px; - border-radius: 14px; - background: #1a1a1a; - color: #fff; - font-size: 16px; - font-weight: 700; - border: none; - margin-top: 20px; - letter-spacing: 0.3px; -} -.c-submit:active { opacity: 0.85; } - /* B端 Tab */ .tabs-wrap { display: flex; @@ -599,25 +587,6 @@ onShow(() => { .order-actions { display: flex; justify-content: flex-end; } .order-btns { display: flex; gap: 8px; } -.btn-sm { - height: 34px; - padding: 0 16px; - border-radius: 10px; - font-size: 13px; - font-weight: 600; - display: inline-flex; - align-items: center; - justify-content: center; - border: none; -} -.btn-fill { background: #1a1a1a; color: #fff; } -.btn-fill:active { opacity: 0.85; } -.btn-outline { - background: transparent; - color: #999993; - border: 1.5px solid #ebebea; -} - /* 弹窗 */ .popup-mask { position: fixed; diff --git a/src/pages/login/Login.vue b/src/pages/login/Login.vue index 5577811..52032f8 100644 --- a/src/pages/login/Login.vue +++ b/src/pages/login/Login.vue @@ -11,9 +11,9 @@ - + - + @@ -23,14 +23,14 @@ - + @@ -45,9 +45,23 @@ 商家入驻 + + + + + + + + + + + + + + ✓ 微信已认证手机号 - + ← 返回登录 @@ -57,9 +71,23 @@ 请输入店长提供的邀请码加入团队 + + + + + + + + + + + + + + ✓ 微信已认证手机号 - + ← 返回登录 @@ -74,11 +102,15 @@ import { openAppPage } from '../../utils/globalState.js' const view = ref('login') const loginForm = reactive({ phone: '13800138001', code: '123456' }) -const bossForm = reactive({ storeName: '', bossName: '', phone: '', password: '' }) -const staffForm = reactive({ inviteCode: '', name: '', phone: '', password: '' }) +const bossForm = reactive({ storeName: '', bossName: '', phone: '', password: '', smsCode: '', wxPhoneAuto: false }) +const staffForm = reactive({ inviteCode: '', name: '', phone: '', password: '', smsCode: '', wxPhoneAuto: false }) const loginLoading = ref(false) const regLoading = ref(false) const smsCountdown = ref(0) +const bossSmsCountdown = ref(0) +const staffSmsCountdown = ref(0) +let bossSmsTimer = null +let staffSmsTimer = null let smsTimer = null @@ -143,6 +175,36 @@ const handleSendSms = async () => { } } +const handleSendSmsBoss = async () => { + if (!bossForm.phone || bossForm.phone.length !== 11) return showToast('请输入正确的手机号') + const res = await sendSms(bossForm.phone) + if (res.code === 200) { + showToast('验证码已发送') + bossSmsCountdown.value = 60 + bossSmsTimer = setInterval(() => { + bossSmsCountdown.value-- + if (bossSmsCountdown.value <= 0) clearInterval(bossSmsTimer) + }, 1000) + } else { + showToast(res.message || '发送失败') + } +} + +const handleSendSmsStaff = async () => { + if (!staffForm.phone || staffForm.phone.length !== 11) return showToast('请输入正确的手机号') + const res = await sendSms(staffForm.phone) + if (res.code === 200) { + showToast('验证码已发送') + staffSmsCountdown.value = 60 + staffSmsTimer = setInterval(() => { + staffSmsCountdown.value-- + if (staffSmsCountdown.value <= 0) clearInterval(staffSmsTimer) + }, 1000) + } else { + showToast(res.message || '发送失败') + } +} + const handleLogin = async () => { if (!loginForm.phone || loginForm.phone.length !== 11) return showToast('请输入正确的手机号') if (!loginForm.code || loginForm.code.length !== 6) return showToast('请输入6位验证码') @@ -201,14 +263,65 @@ const onWxGetPhoneNumber = async (e) => { } } +/** 商家入驻:微信获取手机号并填入 */ +const onWxGetPhoneNumberBoss = async (e) => { + const d = e.detail || {} + const errMsg = d.errMsg || '' + if (errMsg.includes('deny') || errMsg.includes('cancel')) return + const code = d.code + if (!code) { showToast('未获取到授权,请手动输入'); return } + loginLoading.value = true + try { + const res = await wxPhoneLogin(code) + if (res.code === 200 && res.data?.phone) { + bossForm.phone = res.data.phone + bossForm.wxPhoneAuto = true + showToast('手机号已获取') + } else { + showToast('未获取到手机号,请手动输入') + } + } catch { + showToast('网络异常') + } finally { + loginLoading.value = false + } +} + +/** 员工注册:微信获取手机号并填入 */ +const onWxGetPhoneNumberStaff = async (e) => { + const d = e.detail || {} + const errMsg = d.errMsg || '' + if (errMsg.includes('deny') || errMsg.includes('cancel')) return + const code = d.code + if (!code) { showToast('未获取到授权,请手动输入'); return } + loginLoading.value = true + try { + const res = await wxPhoneLogin(code) + if (res.code === 200 && res.data?.phone) { + staffForm.phone = res.data.phone + staffForm.wxPhoneAuto = true + showToast('手机号已获取') + } else { + showToast('未获取到手机号,请手动输入') + } + } catch { + showToast('网络异常') + } finally { + loginLoading.value = false + } +} + const handleRegisterBoss = async () => { const f = bossForm if (!f.storeName) return showToast('请输入店铺名称') if (!f.bossName) return showToast('请输入您的姓名') if (!f.phone || f.phone.length !== 11) return showToast('请输入正确的手机号') + if (!f.wxPhoneAuto && (!f.smsCode || f.smsCode.length !== 6)) return showToast('请输入6位验证码') if (!f.password || f.password.length < 6) return showToast('密码至少6位') + const payload = { ...f } + if (f.wxPhoneAuto) delete payload.smsCode regLoading.value = true - const res = await registerBoss(f) + const res = await registerBoss(payload) regLoading.value = false if (isHttpOk(res)) { const parsed = parseBossRegData(res.data) @@ -243,9 +356,12 @@ const handleRegisterStaff = async () => { if (!f.inviteCode || f.inviteCode.length !== 8) return showToast('请输入8位邀请码') if (!f.name) return showToast('请输入您的姓名') if (!f.phone || f.phone.length !== 11) return showToast('请输入正确的手机号') + if (!f.wxPhoneAuto && (!f.smsCode || f.smsCode.length !== 6)) return showToast('请输入6位验证码') if (!f.password || f.password.length < 6) return showToast('密码至少6位') + const payload = { ...f } + if (f.wxPhoneAuto) delete payload.smsCode regLoading.value = true - const res = await registerStaff(f) + const res = await registerStaff(payload) regLoading.value = false if (isHttpOk(res)) { const parsed = parseStaffRegData(res.data) @@ -267,7 +383,11 @@ const handleRegisterStaff = async () => { } } -onUnmounted(() => { if (smsTimer) clearInterval(smsTimer) }) +onUnmounted(() => { + if (smsTimer) clearInterval(smsTimer) + if (bossSmsTimer) clearInterval(bossSmsTimer) + if (staffSmsTimer) clearInterval(staffSmsTimer) +}) diff --git a/src/pages/mine/MyPets.vue b/src/pages/mine/MyPets.vue index 194a544..fe707e4 100644 --- a/src/pages/mine/MyPets.vue +++ b/src/pages/mine/MyPets.vue @@ -77,28 +77,28 @@ - 名字 - + 名字 + - 类型 + 类型 - - {{ form.petType || '请选择' }} - + + {{ form.petType || '请选择' }} + - 品种(可选) - + 品种(可选) + - 备注(可选) - + 备注(可选) + - 头像 + 头像 点击上传 @@ -106,7 +106,7 @@ - + @@ -515,29 +515,6 @@ onShow(() => loadPets()) .popup-footer { padding: 12px 20px max(16px, env(safe-area-inset-bottom)); border-top: 1px solid #f0f0f0; } .c-field { margin-bottom: 14px; } -.c-label { font-size: 13px; font-weight: 600; color: #666660; display: block; margin-bottom: 6px; } -.c-input { - background: #f5f5f2; - border-radius: 12px; - padding: 11px 14px; - font-size: 15px; - width: 100%; - box-sizing: border-box; - min-height: 44px; -} -.c-picker { display: flex; justify-content: space-between; align-items: center; } -.c-placeholder { color: #bbbbb5; } -.c-arrow { color: #bbbbb5; } -.c-submit { - width: 100%; - height: 48px; - border-radius: 14px; - background: #1a1a1a; - color: #fff; - font-size: 16px; - font-weight: 700; - border: none; -} .avatar-tap { width: 72px; height: 72px; diff --git a/src/pages/report/Report.vue b/src/pages/report/Report.vue index 8d958eb..8353f1e 100644 --- a/src/pages/report/Report.vue +++ b/src/pages/report/Report.vue @@ -15,24 +15,24 @@ - 宠物名字 - + 宠物名字 + - 服务类型 + 服务类型 - - {{ report.serviceType || '请选择' }} - + + {{ report.serviceType || '请选择' }} + - 服务时间 + 服务时间 - - {{ serviceDate || '日期' }} + + {{ serviceDate || '日期' }} - - {{ serviceTimePart || '时间' }} + + {{ serviceTimePart || '时间' }} @@ -135,7 +135,7 @@