feat: 商家入驻&员工注册增加微信授权获取手机号,手动填号需验证码;按钮移至输入框右侧
This commit is contained in:
parent
1e10ebc759
commit
b387f6d944
@ -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;
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
<view class="c-form-card">
|
||||
<view class="c-field">
|
||||
<text class="c-label">预约门店</text>
|
||||
<text class="app-form-label">预约门店</text>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="orderedStores"
|
||||
@ -22,50 +22,50 @@
|
||||
:disabled="!orderedStores.length"
|
||||
@change="onStorePickerChange"
|
||||
>
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': !orderedStores.length }">{{ storePickerDisplay }}</text>
|
||||
<text class="c-arrow">›</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !orderedStores.length }">{{ storePickerDisplay }}</text>
|
||||
<text class="app-form-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
<text v-if="storeHintLine" class="c-store-hint">{{ storeHintLine }}</text>
|
||||
</view>
|
||||
<view v-if="myPets.length" class="c-field">
|
||||
<text class="c-label">使用档案(可选)</text>
|
||||
<text class="app-form-label">使用档案(可选)</text>
|
||||
<picker mode="selector" :range="petPickerOptions" range-key="label" @change="onPickMyPet">
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': selectedPetIndex === 0 }">{{ petPickerLabel }}</text>
|
||||
<text class="c-arrow">›</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': selectedPetIndex === 0 }">{{ petPickerLabel }}</text>
|
||||
<text class="app-form-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">宠物名字</text>
|
||||
<input v-model="newAppt.petName" class="c-input" placeholder="请输入宠物名字" />
|
||||
<text class="app-form-label">宠物名字</text>
|
||||
<input v-model="newAppt.petName" class="app-form-input" placeholder="请输入宠物名字" />
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">宠物类型</text>
|
||||
<text class="app-form-label">宠物类型</text>
|
||||
<picker mode="selector" :range="petTypes" range-key="label" @change="e => newAppt.petType = petTypes[e.detail.value].value">
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': !newAppt.petType }">{{ newAppt.petType || '请选择' }}</text>
|
||||
<text class="c-arrow">›</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !newAppt.petType }">{{ newAppt.petType || '请选择' }}</text>
|
||||
<text class="app-form-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">服务类型</text>
|
||||
<text class="app-form-label">服务类型</text>
|
||||
<picker mode="selector" :range="serviceTypes" range-key="label" @change="e => newAppt.serviceType = serviceTypes[e.detail.value].value">
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': !newAppt.serviceType }">{{ newAppt.serviceType || '请选择' }}</text>
|
||||
<text class="c-arrow">›</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !newAppt.serviceType }">{{ newAppt.serviceType || '请选择' }}</text>
|
||||
<text class="app-form-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">预约时间</text>
|
||||
<text class="app-form-label">预约时间</text>
|
||||
<view class="c-time-row">
|
||||
<picker mode="date" :value="appointmentDate" @change="onAppointmentDateChange" class="c-time-half">
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': !appointmentDate }">{{ appointmentDate || '日期' }}</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !appointmentDate }">{{ appointmentDate || '日期' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
<picker
|
||||
@ -75,17 +75,17 @@
|
||||
@change="onAppointmentTimeOnlyChange"
|
||||
class="c-time-half"
|
||||
>
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': !appointmentTime }">{{ appointmentTime || '时间' }}</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !appointmentTime }">{{ appointmentTime || '时间' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">备注(可选)</text>
|
||||
<input v-model="newAppt.remark" class="c-input" placeholder="可选" />
|
||||
<text class="app-form-label">备注(可选)</text>
|
||||
<input v-model="newAppt.remark" class="app-form-input" placeholder="可选" />
|
||||
</view>
|
||||
<button class="c-submit" :loading="creatingAppt" @click="confirmNewAppt">提交预约</button>
|
||||
<button class="app-btn-main app-btn-primary" style="margin-top:20px" :loading="creatingAppt" @click="confirmNewAppt">提交预约</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@ -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;
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
</view>
|
||||
<view class="order-time">{{ item.time }}</view>
|
||||
<view v-if="item.status === 'new'" class="order-actions">
|
||||
<button class="btn-sm btn-outline" @click.stop="cancelService(item)">取消预约</button>
|
||||
<button class="app-btn-sm app-btn-outline" @click.stop="cancelService(item)">取消预约</button>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="filteredOrders.length === 0" class="empty"><text>暂无预约</text></view>
|
||||
@ -96,10 +96,12 @@
|
||||
<view class="order-time">{{ item.time }}</view>
|
||||
<view class="order-actions">
|
||||
<view v-if="item.status === 'new'" class="order-btns">
|
||||
<button class="btn-sm btn-fill" @click.stop="startService(item)">开始服务</button>
|
||||
<button class="btn-sm btn-outline" @click.stop="cancelService(item)">取消</button>
|
||||
<button class="app-btn-sm app-btn-primary" @click.stop="startService(item)">开始服务</button>
|
||||
<button class="app-btn-sm app-btn-outline" @click.stop="cancelService(item)">取消</button>
|
||||
</view>
|
||||
<view v-else-if="item.status === 'doing'" class="order-btns">
|
||||
<button class="app-btn-sm app-btn-primary" @click.stop="goReport(item)">填写报告</button>
|
||||
</view>
|
||||
<button v-else-if="item.status === 'doing'" class="btn-sm btn-fill" @click.stop="goReport(item)">填写报告</button>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="filteredOrders.length === 0" class="empty"><text>暂无数据</text></view>
|
||||
@ -167,8 +169,8 @@
|
||||
</view>
|
||||
<view class="popup-footer">
|
||||
<view class="popup-actions">
|
||||
<button class="btn-sm btn-outline" style="flex:0 0 80px" @click="showNewAppt = false">取消</button>
|
||||
<button class="c-submit" style="flex:1;margin:0" :loading="creatingAppt" @click="confirmNewAppt">确认创建</button>
|
||||
<button class="app-btn-sm app-btn-outline" style="flex:0 0 80px" @click="showNewAppt = false">取消</button>
|
||||
<button class="app-btn-main app-btn-primary" style="flex:1;margin:0" :loading="creatingAppt" @click="confirmNewAppt">确认创建</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -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;
|
||||
|
||||
@ -11,9 +11,9 @@
|
||||
<input v-model="loginForm.phone" type="tel" class="field" placeholder="手机号" maxlength="11" />
|
||||
<view class="sms-row">
|
||||
<input v-model="loginForm.code" type="digit" class="field sms-input" placeholder="验证码" maxlength="6" />
|
||||
<button class="sms-btn" :disabled="smsCountdown > 0" @click="handleSendSms">{{ smsCountdown > 0 ? smsCountdown + 's' : '获取验证码' }}</button>
|
||||
<button class="app-btn-code app-btn-code--neutral sms-code-btn" :disabled="smsCountdown > 0" @click="handleSendSms">{{ smsCountdown > 0 ? smsCountdown + 's' : '获取验证码' }}</button>
|
||||
</view>
|
||||
<button class="primary-btn" :disabled="loginLoading" @click="handleLogin">{{ loginLoading ? '登录中…' : '登录' }}</button>
|
||||
<button class="app-btn-main app-btn-primary login-main-btn" :disabled="loginLoading" @click="handleLogin">{{ loginLoading ? '登录中…' : '登录' }}</button>
|
||||
|
||||
<view class="divider">
|
||||
<view class="divider-line"></view>
|
||||
@ -23,14 +23,14 @@
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button
|
||||
class="ghost-btn"
|
||||
class="app-btn-main app-btn-soft"
|
||||
open-type="getPhoneNumber"
|
||||
:disabled="loginLoading"
|
||||
@getphonenumber="onWxGetPhoneNumber"
|
||||
>{{ loginLoading ? '登录中…' : '微信授权登录' }}</button>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<button class="ghost-btn" @click="onWechatLoginNotInMp">微信授权登录</button>
|
||||
<button class="app-btn-main app-btn-soft" @click="onWechatLoginNotInMp">微信授权登录</button>
|
||||
<!-- #endif -->
|
||||
|
||||
<view class="footer-links">
|
||||
@ -45,9 +45,23 @@
|
||||
<text class="card-title">商家入驻</text>
|
||||
<input v-model="bossForm.storeName" class="field" placeholder="店铺名称" />
|
||||
<input v-model="bossForm.bossName" class="field" placeholder="您的姓名" />
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="phone-row">
|
||||
<input v-model="bossForm.phone" type="tel" class="field phone-input" placeholder="手机号" maxlength="11" />
|
||||
<button class="app-btn-code app-btn-code--brand phone-auth-btn" open-type="getPhoneNumber" @getphonenumber="onWxGetPhoneNumberBoss">授权手机号</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<input v-model="bossForm.phone" type="tel" class="field" placeholder="手机号" maxlength="11" />
|
||||
<!-- #endif -->
|
||||
<!-- 手机号手动填写时需要验证码 -->
|
||||
<view v-if="!bossForm.wxPhoneAuto" class="sms-row">
|
||||
<input v-model="bossForm.smsCode" type="digit" class="field sms-input" placeholder="验证码" maxlength="6" />
|
||||
<button class="app-btn-code app-btn-code--neutral sms-code-btn" :disabled="bossSmsCountdown > 0" @click="handleSendSmsBoss">{{ bossSmsCountdown > 0 ? bossSmsCountdown + 's' : '获取验证码' }}</button>
|
||||
</view>
|
||||
<view v-else class="wx-verified-hint">✓ 微信已认证手机号</view>
|
||||
<input v-model="bossForm.password" type="password" class="field" placeholder="登录密码(至少6位)" />
|
||||
<button class="primary-btn" :disabled="regLoading" @click="handleRegisterBoss">{{ regLoading ? '提交中…' : '提交申请' }}</button>
|
||||
<button class="app-btn-main app-btn-primary login-main-btn" :disabled="regLoading" @click="handleRegisterBoss">{{ regLoading ? '提交中…' : '提交申请' }}</button>
|
||||
<text class="back-link" @click="backToLoginForm">← 返回登录</text>
|
||||
</view>
|
||||
|
||||
@ -57,9 +71,23 @@
|
||||
<view class="hint-box">请输入店长提供的邀请码加入团队</view>
|
||||
<input v-model="staffForm.inviteCode" class="field" placeholder="8位邀请码" maxlength="8" />
|
||||
<input v-model="staffForm.name" class="field" placeholder="您的姓名" />
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="phone-row">
|
||||
<input v-model="staffForm.phone" type="tel" class="field phone-input" placeholder="手机号" maxlength="11" />
|
||||
<button class="app-btn-code app-btn-code--brand phone-auth-btn" open-type="getPhoneNumber" @getphonenumber="onWxGetPhoneNumberStaff">授权手机号</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<input v-model="staffForm.phone" type="tel" class="field" placeholder="手机号" maxlength="11" />
|
||||
<!-- #endif -->
|
||||
<!-- 手机号手动填写时需要验证码 -->
|
||||
<view v-if="!staffForm.wxPhoneAuto" class="sms-row">
|
||||
<input v-model="staffForm.smsCode" type="digit" class="field sms-input" placeholder="验证码" maxlength="6" />
|
||||
<button class="app-btn-code app-btn-code--neutral sms-code-btn" :disabled="staffSmsCountdown > 0" @click="handleSendSmsStaff">{{ staffSmsCountdown > 0 ? staffSmsCountdown + 's' : '获取验证码' }}</button>
|
||||
</view>
|
||||
<view v-else class="wx-verified-hint">✓ 微信已认证手机号</view>
|
||||
<input v-model="staffForm.password" type="password" class="field" placeholder="登录密码(至少6位)" />
|
||||
<button class="primary-btn" :disabled="regLoading" @click="handleRegisterStaff">{{ regLoading ? '提交中…' : '注册' }}</button>
|
||||
<button class="app-btn-main app-btn-primary login-main-btn" :disabled="regLoading" @click="handleRegisterStaff">{{ regLoading ? '提交中…' : '注册' }}</button>
|
||||
<text class="back-link" @click="backToLoginForm">← 返回登录</text>
|
||||
</view>
|
||||
</view>
|
||||
@ -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)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -344,36 +464,33 @@ onUnmounted(() => { if (smsTimer) clearInterval(smsTimer) })
|
||||
margin-bottom: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.sms-btn {
|
||||
.sms-code-btn {
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0 16px;
|
||||
border-radius: 12px;
|
||||
background: #f5f5f2;
|
||||
color: #2db96d;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sms-btn[disabled] { color: #bbbbb5; }
|
||||
|
||||
/* 主按钮 */
|
||||
.primary-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border-radius: 14px;
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
margin-top: 4px;
|
||||
/* 微信获取手机号按钮 */
|
||||
.phone-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.primary-btn:active { opacity: 0.85; }
|
||||
.primary-btn[disabled] { background: #ddddd8; color: #999993; }
|
||||
.phone-input { flex: 1; margin-bottom: 0; }
|
||||
.phone-auth-btn {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.wx-verified-hint {
|
||||
font-size: 13px;
|
||||
color: #2db96d;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: #eaf8f0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.login-main-btn { margin-top: 4px !important; }
|
||||
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
@ -385,19 +502,6 @@ onUnmounted(() => { if (smsTimer) clearInterval(smsTimer) })
|
||||
.divider-line { flex: 1; height: 1px; background: #ebebea; }
|
||||
.divider-text { font-size: 12px; color: #bbbbb5; }
|
||||
|
||||
/* 幽灵按钮 */
|
||||
.ghost-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border-radius: 14px;
|
||||
background: #f5f5f2;
|
||||
color: #1a1a1a;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
.ghost-btn:active { opacity: 0.85; }
|
||||
|
||||
/* 底部链接 */
|
||||
.footer-links {
|
||||
display: flex;
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
</view>
|
||||
|
||||
<view class="logout-wrap">
|
||||
<button class="logout-btn" @click="logout">退出登录</button>
|
||||
<button class="app-btn-main app-btn-danger-outline" @click="logout">退出登录</button>
|
||||
</view>
|
||||
<TabBar current-page="mine" @change="goPage" />
|
||||
</view>
|
||||
@ -235,18 +235,4 @@ const logout = () => {
|
||||
.logout-wrap {
|
||||
padding: 24px 16px;
|
||||
}
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
border-radius: 14px;
|
||||
background: #fff;
|
||||
color: #e5534b;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
border: 1.5px solid #f0e0de;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.logout-btn:active { opacity: 0.85; }
|
||||
</style>
|
||||
|
||||
@ -77,28 +77,28 @@
|
||||
</view>
|
||||
<scroll-view scroll-y class="pet-form-body">
|
||||
<view class="c-field">
|
||||
<text class="c-label">名字</text>
|
||||
<input v-model="form.name" class="c-input" placeholder="宠物名字" maxlength="32" />
|
||||
<text class="app-form-label">名字</text>
|
||||
<input v-model="form.name" class="app-form-input" placeholder="宠物名字" maxlength="32" />
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">类型</text>
|
||||
<text class="app-form-label">类型</text>
|
||||
<picker mode="selector" :range="petTypes" range-key="label" @change="onPetTypeChange">
|
||||
<view class="c-input c-picker">
|
||||
<text :class="{ 'c-placeholder': !form.petType }">{{ form.petType || '请选择' }}</text>
|
||||
<text class="c-arrow">›</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !form.petType }">{{ form.petType || '请选择' }}</text>
|
||||
<text class="app-form-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">品种(可选)</text>
|
||||
<input v-model="form.breed" class="c-input" placeholder="如:金毛" maxlength="32" />
|
||||
<text class="app-form-label">品种(可选)</text>
|
||||
<input v-model="form.breed" class="app-form-input" placeholder="如:金毛" maxlength="32" />
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">备注(可选)</text>
|
||||
<input v-model="form.remark" class="c-input" placeholder="习性、注意事项等" maxlength="200" />
|
||||
<text class="app-form-label">备注(可选)</text>
|
||||
<input v-model="form.remark" class="app-form-input" placeholder="习性、注意事项等" maxlength="200" />
|
||||
</view>
|
||||
<view class="c-field">
|
||||
<text class="c-label">头像</text>
|
||||
<text class="app-form-label">头像</text>
|
||||
<view class="avatar-tap" @click="pickAvatar">
|
||||
<image v-if="form.avatarDisplay" :src="form.avatarDisplay" class="avatar-prev" mode="aspectFill" />
|
||||
<text v-else class="avatar-tap-text">点击上传</text>
|
||||
@ -106,7 +106,7 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="popup-footer">
|
||||
<button class="c-submit" :loading="saving" @click="savePet">{{ saving ? '保存中…' : '保存' }}</button>
|
||||
<button class="app-btn-main app-btn-primary" :loading="saving" @click="savePet">{{ saving ? '保存中…' : '保存' }}</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -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;
|
||||
|
||||
@ -15,24 +15,24 @@
|
||||
</view>
|
||||
<view v-show="!basicInfoCollapsed">
|
||||
<view class="form-row">
|
||||
<text class="form-label">宠物名字</text>
|
||||
<input v-model="report.petName" class="form-input" placeholder="请输入宠物名字" />
|
||||
<text class="app-form-label">宠物名字</text>
|
||||
<input v-model="report.petName" class="app-form-input" placeholder="请输入宠物名字" />
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">服务类型</text>
|
||||
<text class="app-form-label">服务类型</text>
|
||||
<picker mode="selector" :range="serviceColumns" range-key="label" @change="onServiceConfirm">
|
||||
<view class="form-input form-picker">
|
||||
<text :class="{ placeholder: !report.serviceType }">{{ report.serviceType || '请选择' }}</text>
|
||||
<text class="picker-arrow">›</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !report.serviceType }">{{ report.serviceType || '请选择' }}</text>
|
||||
<text class="app-form-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">服务时间</text>
|
||||
<text class="app-form-label">服务时间</text>
|
||||
<view class="time-row">
|
||||
<picker mode="date" :value="serviceDate" @change="onServiceDateChange" class="time-picker">
|
||||
<view class="form-input form-picker">
|
||||
<text :class="{ placeholder: !serviceDate }">{{ serviceDate || '日期' }}</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !serviceDate }">{{ serviceDate || '日期' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
<picker
|
||||
@ -42,8 +42,8 @@
|
||||
@change="onServiceTimeChange"
|
||||
class="time-picker"
|
||||
>
|
||||
<view class="form-input form-picker">
|
||||
<text :class="{ placeholder: !serviceTimePart }">{{ serviceTimePart || '时间' }}</text>
|
||||
<view class="app-form-input app-form-picker">
|
||||
<text :class="{ 'app-form-placeholder': !serviceTimePart }">{{ serviceTimePart || '时间' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
@ -135,7 +135,7 @@
|
||||
</view>
|
||||
<textarea
|
||||
v-model="report.remark"
|
||||
class="form-textarea"
|
||||
class="app-form-textarea"
|
||||
placeholder="服务细节、注意事项(可选)"
|
||||
@focus="onRemarkFocus"
|
||||
@blur="onRemarkBlur"
|
||||
@ -149,7 +149,7 @@
|
||||
:class="{ 'submit-fixed--scrolled': hasScrolled }"
|
||||
:style="submitFixedStyle"
|
||||
>
|
||||
<button class="submit-btn submit-btn--fixed" :disabled="submitting" @click="submitReport">
|
||||
<button class="app-btn-main app-btn-primary submit-btn--fixed" :disabled="submitting" @click="submitReport">
|
||||
{{ submitting ? '提交中…' : '生成报告' }}
|
||||
</button>
|
||||
</view>
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user