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 = petTypes[e.detail.value].value">
-
- {{ newAppt.petType || '请选择' }}
- ›
+
+ {{ newAppt.petType || '请选择' }}
+ ›
- 服务类型
+ 服务类型
newAppt.serviceType = serviceTypes[e.detail.value].value">
-
- {{ 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 @@
-
+
@@ -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 @@
@@ -546,35 +546,6 @@ const onRemarkBlur = () => {
margin-bottom: 14px;
}
.form-row:last-child { margin-bottom: 0; }
-.form-label {
- font-size: 13px;
- font-weight: 600;
- color: #666660;
- margin-bottom: 6px;
- display: block;
-}
-.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;
-}
-.form-input:focus {
- border-color: #2db96d;
- background: #fff;
-}
-.form-picker {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-.placeholder { color: #bbbbb5; }
-.picker-arrow { color: #bbbbb5; font-size: 18px; font-weight: 300; }
.time-row {
display: flex;
gap: 10px;
@@ -701,37 +672,6 @@ const onRemarkBlur = () => {
}
/* 备注 */
-.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;
-}
-.form-textarea:focus {
- border-color: #2db96d;
- background: #fff;
-}
-
-/* 提交按钮 */
-.submit-btn {
- width: 100%;
- height: 48px;
- border-radius: 14px;
- background: #1a1a1a;
- color: #fff;
- font-size: 16px;
- font-weight: 700;
- border: none;
- margin-top: 0;
- letter-spacing: 0.5px;
- transition: opacity 0.15s;
-}
.submit-fixed {
position: fixed;
left: 0;
@@ -750,8 +690,6 @@ const onRemarkBlur = () => {
.submit-btn--fixed {
box-shadow: 0 5px 16px rgba(0, 0, 0, 0.1);
}
-.submit-btn:active { opacity: 0.85; }
-.submit-btn[disabled] { background: #ddddd8; color: #999993; }
/* Loading */
.loading-mask {