添加页面

This commit is contained in:
wangxiaowei
2025-12-13 17:15:30 +08:00
parent 0ab8464612
commit 775a93f766
22 changed files with 1329 additions and 432 deletions

47
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,47 @@
# Copilot Instructions for chazhi_store
## 项目架构与核心约定
- 本项目基于 UniAppUnibest 3.8.2),采用 Vue 3 + TypeScript目录结构以 `src/` 为主分为页面、组件、API、服务、工具等模块。
- 页面与组件的方法命名规范:对外暴露方法统一以 `handle` 前缀命名(如 `handleLogin`),内部方法无需加前缀,所有方法集中在对象底部。
- 组件/页面对外方法对象名建议与文件名一致,若冲突可加 `s` 后缀。
- 路由、页面配置见 `src/pages.json``src/router/`,布局在 `src/layouts/`
- 样式采用 `unocss`,全局样式在 `src/uni.scss`,图标需在 `uno.config.ts``safelist` 配置。
## 网络请求与数据流
- 支持三种请求库:
- 简单版 http`src/http/http.ts`),示例见 `src/api/foo.ts`
- alova`src/http/alova.ts`),示例见 `src/api/foo-alova.ts`
- vue-query`src/http/vue-query.ts`),自动生成接口,示例见 `src/service/`
- 选择建议:简单场景用 http复杂/自动化用 alova 或 vue-query。
- 状态管理采用 `pinia`,入口在 `src/store/`
## Tabbar 策略
- 支持 4 种 tabbar无、原生、自定义有缓存、自定义无缓存详见 `src/tabbar/README.md`
- tabbar 配置需与接口返回的 path 对齐,使用 unocss 图标需在 `uno.config.ts` safelist 配置。
## 关键开发流程
- 构建/运行:使用 HBuilderX 或 `vite`,配置见 `vite.config.ts`
- 依赖管理:使用 `pnpm`,依赖锁定在 `pnpm-lock.yaml`
- 约定优于配置,所有新增页面/组件/服务请遵循现有命名与结构。
## 重要文件/目录参考
- `src/pages/` 页面目录
- `src/components/` 通用组件
- `src/api/` 接口定义
- `src/http/` 请求库实现
- `src/service/` 业务服务层
- `src/store/` 状态管理
- `src/utils/` 工具函数
- `src/layouts/` 布局
- `src/tabbar/` tabbar 策略与配置
## 其他说明
- 代码注释与文档以中文为主。
- 详细用法、特殊约定见各模块下 README.md。
- 遇到不明确的约定,优先参考现有实现和 README。

2
env/.env vendored
View File

@ -1,5 +1,5 @@
VITE_APP_TITLE = '茶址'
VITE_APP_PORT = 9000
VITE_APP_PORT = 9002
VITE_UNI_APPID = '__UNI__D1E5001'
VITE_WX_APPID = 'wx63e106209b842919'

View File

@ -0,0 +1,54 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#F6F7F9"
}
}</route>
<template>
<view class="mt-54rpx">
<view class="font-bold text-44rpx text-[#303133] leading-60rpx text-center">切换账号</view>
<view class="mx-30rpx mt-48rpx">
<view class="bg-white rounded-16rpx px-30rpx py-36rpx flex justify-between items-center mb-20rpx">
<view class="flex items-center">
<wd-img width="70rpx" height="70rpx" round src="https://shchazhi.oss-cn-hangzhou.aliyuncs.com/fronted/icon/icon_avatar.png" mode="aspectFit" />
<view class="w-360rpx line-1 ml-24rpx">茶址24小时智能茶室(...</view>
</view>
<view class="flex-1 flex justify-end">
<template v-if="2 < 1">
<view class="font-400 text-28rpx text-[#4C9F44] leading-40rpx">当前登录</view>
</template>
<template v-if="2 > 1">
<view class="w-120rpx h-60rpx bg-[#4C9F44] text-center leading-60rpx text-[#fff] font-400 text-28rpx leading-40rpx rounded-8rpx text-center">切换</view>
</template>
</view>
</view>
<view class="bg-white rounded-16rpx px-30rpx py-36rpx flex justify-between items-center mb-20rpx">
<view class="flex items-center">
<wd-img width="70rpx" height="70rpx" round src="https://shchazhi.oss-cn-hangzhou.aliyuncs.com/fronted/icon/icon_avatar.png" mode="aspectFit" />
<view class="w-360rpx line-1 ml-24rpx">茶址24小时智能茶室(...</view>
</view>
<view class="flex-1 flex justify-end">
<template v-if="2 > 1">
<view class="font-400 text-28rpx text-[#4C9F44] leading-40rpx">当前登录</view>
</template>
<template v-if="2 < 1">
<view class="w-120rpx h-60rpx bg-[#4C9F44] text-center leading-60rpx text-[#fff] font-400 text-28rpx leading-40rpx rounded-8rpx text-center">切换</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
</script>
<style lang="scss" scoped>
page {
background-color: $cz-page-background;
}
</style>

View File

@ -0,0 +1,133 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F6F7F8"
}
}</route>
<template>
<view class="home-bg pb-80rpx">
<view class="home-bg w-[100%] fixed top-0 left-0 z-100">
<wd-navbar left-arrow safeAreaInsetTop title="财务管理" :bordered="false" custom-style="background-color: transparent !important;" @click-left="router.navigateBack()"></wd-navbar>
</view>
<view :style="{ paddingTop: navbarHeight + 'px' }">
<view class="relative mx-30rpx mt-16rpx">
<wd-img width="692rpx" height="334rpx" :src="`${OSS}images/store/finance/image1.png`" mode="aspectFit" />
<view class="absolute top-50rpx left-40rpx">
<view class="font-400 text-26rpx text-[#B4CEFF] leading-36rpx">经营资产</view>
<view class="mt-20rpx font-bold text-36rpx text-[#fff] leading-34rpx">5,662.46</view>
</view>
<view class="flex items-center justify-around w-100% absolute bottom-40rpx left-0 px-40rpx">
<view class="flex flex-col items-center justify-start">
<view class="font-400 text-24rpx text-[#606266] text-34rpx">已提现</view>
<view class="font-bold text-32rpx text-[#303133] leading-34rpx mt-14rpx">3,662.46</view>
</view>
<view class="h-98rpx">
<wd-divider vertical color="#EAEEF2" custom-class="!h-98rpx"></wd-divider>
</view>
<view class="flex flex-col items-center justify-start">
<view class="font-400 text-24rpx text-[#606266] text-34rpx">待提现</view>
<view class="font-bold text-32rpx text-[#303133] leading-34rpx mt-14rpx">3,662.46</view>
</view>
</view>
</view>
<view class="mx-30rpx">
<view class="flex items-center">
<view class="font-bold text-[#303133]">
<text class="text-44rpx">8</text>
<text class="text-32rpx"></text>
</view>
<view class="mt-16rpx">
<wd-icon name="fill-arrow-down" size="40rpx" color="#BFC2CC"></wd-icon>
</view>
</view>
<view class="font-400 text-24rpx text-[#606266] leading-34rpx">
收入5,562.46
</view>
<view class="mt-20rpx">
<mescroll-body ref="mescrollItem0" @init="mescrollInit" @down="downCallback" @up="Finance.upCallback" :down="downOption" :up="upOption">
<view class="bg-white rounded-16rpx px-16rpx py-28rpx">
<view class="flex items-center justify-between">
<view class="line-1 w-480rpx font-bold text-30rpx text-[#303133] leading-42rpx">榻榻米双人包间惬意茶室商务...</view>
<view class="flex items-center">
<view class="text-[#FF5951] font-bold text-30rpx leading-42rpx">+ 181.35</view>
<wd-icon name="arrow-right" size="40rpx" color="#000"></wd-icon>
</view>
</view>
<view class="mt-12rpx">
<view class="flex items-center">
<view class="rounded-4rpx w-60rpx text-center text-[#40AE36] border-2rpx border-solid border-[#40AE36] text-22rpx pb-4rpx">团购</view>
<view class="text-24rpx text-[#606266] leading-34rpx ml-10rpx">上海.茶址24小时智能茶室中新店</view>
</view>
</view>
<view class="mt-14rpx text-24rpx text-[#909399] leading-34rpx">
核销时间2025-08-05 19:09:52
</view>
</view>
</mescroll-body>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { router } from '@/utils/tools'
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
const OSS = inject('OSS')
const navbarHeight = inject('navbarHeight')
// mescroll相关
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
const upOption = {
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const list = ref<Array<any>>([]) // 茶艺师列表
onLoad((args) => {
})
const Finance = {
// 上拉加载的回调: 其中num:当前页 从1开始, size:每页数据条数,默认10
upCallback: (mescroll) => {
// getTeaSpecialistOrderList(filter).then((res) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
// list.value = list.value.concat(curPageData) //追加新数据
// mescroll.endSuccess(curPageData.length, Boolean(res.more))
// }).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
// })
},
}
</script>
<style lang="scss" scoped>
page {
background-color: $cz-page-background;
// background: linear-gradient( 176deg, #D2EDFF 0%, rgba(119,199,255,0) 100%);
}
.home-bg {
background: linear-gradient( 176deg, #D2EDFF 0%, rgba(119,199,255,0) 100%);
// background-image: url(#{$OSS}images/store/finance/image1.png);
background-size: 100% 588rpx;
background-repeat: no-repeat;
}
</style>

View File

@ -0,0 +1,73 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F6F7F9"
}
}</route>
<template>
<view class="mx-30rpx">
<view>
<navbar :title="storeName" custom-class='!bg-[#F6F7F8]' :leftArrow="false"></navbar>
</view>
<view class="mt-76rpx">
<view class="font-bold text-44rpx text-[#303133] leading-60rpx text-center">输入验券</view>
</view>
<wd-form ref="form" :model="model">
<view>
<view class="mt-20rpx">
<wd-input
v-model="model.code"
type="text"
placeholder="请输入券码"
inputmode="numeric"
no-border
custom-class="!bg-[#fff] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx"
/>
</view>
</view>
</wd-form>
<view class="w-630rpx h-90rpx text-center leading-90rpx mx-auto rounded-8rpx bg-[#4C9F44] text-[#fff] mt-112rpx" @click="VerifyCode.handleVerifyCode">
验券
</view>
</view>
</template>
<script lang="ts" setup>
import { toast } from '@/utils/toast'
const storeName = ref<string>('')
// 表单相关
const model = reactive<{
code: string
}>({
code: ''
})
onLoad((args) => {
storeName.value = args.storeName || ''
})
const VerifyCode = {
// 验券
handleVerifyCode() {
if (!model.code) {
return
}
}
}
</script>
<style lang="scss" scoped>
page {
background-color: $cz-page-background;
}
</style>

View File

@ -0,0 +1,79 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F6F7F8"
}
}</route>
<template>
<view>
<view>
<navbar title="验券记录" custom-class='!bg-[#F6F7F8]' :leftArrow="false"></navbar>
</view>
<view class="bg-white rounded-16rpx px-30rpx py-28rpx">
<view>
<mescroll-body ref="mescrollItem0" @init="mescrollInit" @down="downCallback" @up="VerifyRecord.upCallback" :down="downOption" :up="upOption">
<view v-for="i in 4" :key="i">
<view class="flex items-center justify-between">
<view class="mr-26rpx">
<view class="font-400 text-26rpx text-[#303133] leading-40rpx w-440rpx line-2">这是团购套餐的名字</view>
<view class="font-400 text-22rpx text-[#909399] leading-32rpx">2025-08-09 17:21</view>
</view>
<view>
<price-format color="#303133" :first-size="36" :second-size="36" :subscript-size="36" :price="23.02"></price-format>
</view>
</view>
<view class="mt-22rpx mb-30rpx">
<wd-gap bg-color="#F6F7F9" height="2rpx"></wd-gap>
</view>
</view>
</mescroll-body>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
const OSS = inject('OSS')
// mescroll相关
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
const upOption = {
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const list = ref<Array<any>>([]) // 茶艺师列表
onLoad((args) => {
})
const VerifyRecord = {
// 上拉加载的回调: 其中num:当前页 从1开始, size:每页数据条数,默认10
upCallback: (mescroll) => {
// getTeaSpecialistOrderList(filter).then((res) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
// list.value = list.value.concat(curPageData) //追加新数据
// mescroll.endSuccess(curPageData.length, Boolean(res.more))
// }).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
// })
},
}
</script>
<style lang="scss" scoped>
page {
background-color: #fff;
}
</style>

View File

@ -1,6 +1,6 @@
{
"name": "茶址",
"appid": "__UNI__D1E5001",
"name": "",
"appid": "",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
@ -18,93 +18,22 @@
"modules": {},
"distribute": {
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion": 30,
"targetSdkVersion": 30,
"abiFilters": [
"armeabi-v7a",
"arm64-v8a"
]
"permissions": []
},
"ios": {},
"sdkConfigs": {},
"icons": {
"android": {
"hdpi": "static/app/icons/72x72.png",
"xhdpi": "static/app/icons/96x96.png",
"xxhdpi": "static/app/icons/144x144.png",
"xxxhdpi": "static/app/icons/192x192.png"
},
"ios": {
"appstore": "static/app/icons/1024x1024.png",
"ipad": {
"app": "static/app/icons/76x76.png",
"app@2x": "static/app/icons/152x152.png",
"notification": "static/app/icons/20x20.png",
"notification@2x": "static/app/icons/40x40.png",
"proapp@2x": "static/app/icons/167x167.png",
"settings": "static/app/icons/29x29.png",
"settings@2x": "static/app/icons/58x58.png",
"spotlight": "static/app/icons/40x40.png",
"spotlight@2x": "static/app/icons/80x80.png"
},
"iphone": {
"app@2x": "static/app/icons/120x120.png",
"app@3x": "static/app/icons/180x180.png",
"notification@2x": "static/app/icons/40x40.png",
"notification@3x": "static/app/icons/60x60.png",
"settings@2x": "static/app/icons/58x58.png",
"settings@3x": "static/app/icons/87x87.png",
"spotlight@2x": "static/app/icons/80x80.png",
"spotlight@3x": "static/app/icons/120x120.png"
}
}
}
},
"compatible": {
"ignoreVersion": true
"sdkConfigs": {}
}
},
"quickapp": {},
"mp-weixin": {
"appid": "wx63e106209b842919",
"appid": "",
"setting": {
"urlCheck": false,
"es6": true,
"minified": true
"urlCheck": false
},
"usingComponents": true,
"requiredPrivateInfos": [
"getLocation"
],
"optimization": {
"subPackages": true
},
"permission": {
"scope.userLocation": {
"desc": "我们需要获取您的位置,以方便推荐附近茶室给您"
}
}
"usingComponents": true
},
"mp-alipay": {
"usingComponents": true,
"styleIsolation": "shared"
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
@ -116,7 +45,126 @@
"enable": false
},
"vueVersion": "3",
"h5": {
"router": {}
"default": {
"name": "茶址",
"appid": "__UNI__D1E5001",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"h5": {
"router": {}
},
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"compatible": {
"ignoreVersion": true
},
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
"modules": {},
"distribute": {
"android": {
"minSdkVersion": 30,
"targetSdkVersion": 30,
"abiFilters": [
"armeabi-v7a",
"arm64-v8a"
],
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios": {},
"sdkConfigs": {},
"icons": {
"android": {
"hdpi": "static/app/icons/72x72.png",
"xhdpi": "static/app/icons/96x96.png",
"xxhdpi": "static/app/icons/144x144.png",
"xxxhdpi": "static/app/icons/192x192.png"
},
"ios": {
"appstore": "static/app/icons/1024x1024.png",
"ipad": {
"app": "static/app/icons/76x76.png",
"app@2x": "static/app/icons/152x152.png",
"notification": "static/app/icons/20x20.png",
"notification@2x": "static/app/icons/40x40.png",
"proapp@2x": "static/app/icons/167x167.png",
"settings": "static/app/icons/29x29.png",
"settings@2x": "static/app/icons/58x58.png",
"spotlight": "static/app/icons/40x40.png",
"spotlight@2x": "static/app/icons/80x80.png"
},
"iphone": {
"app@2x": "static/app/icons/120x120.png",
"app@3x": "static/app/icons/180x180.png",
"notification@2x": "static/app/icons/40x40.png",
"notification@3x": "static/app/icons/60x60.png",
"settings@2x": "static/app/icons/58x58.png",
"settings@3x": "static/app/icons/87x87.png",
"spotlight@2x": "static/app/icons/80x80.png",
"spotlight@3x": "static/app/icons/120x120.png"
}
}
}
}
},
"quickapp": {},
"mp-weixin": {
"appid": "wx63e106209b842919",
"setting": {
"urlCheck": false,
"es6": true,
"minified": true
},
"requiredPrivateInfos": [
"getLocation"
],
"optimization": {
"subPackages": true
},
"usingComponents": true,
"permission": {
"scope.userLocation": {
"desc": "我们需要获取您的位置,以方便推荐附近茶室给您"
}
}
},
"mp-alipay": {
"usingComponents": true,
"styleIsolation": "shared"
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "3"
}
}

View File

@ -32,10 +32,16 @@
"text": "首页"
},
{
"iconPath": "static/tabbar/reserve.png",
"selectedIconPath": "static/tabbar/reserve_s.png",
"iconPath": "static/tabbar/order.png",
"selectedIconPath": "static/tabbar/order_s.png",
"pagePath": "pages/reserve/reserve",
"text": "预约"
"text": "订单"
},
{
"iconPath": "static/tabbar/store.png",
"selectedIconPath": "static/tabbar/store_s.png",
"pagePath": "pages/login/login",
"text": "门店管理"
},
{
"iconPath": "static/tabbar/my.png",
@ -102,13 +108,30 @@
"path": "pages/login/login",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/login/mobile",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "pages/login/mobile",
"path": "pages/login/password",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "pages/login/set-password",
"type": "page",
"layout": "default",
"style": {
@ -181,6 +204,15 @@
{
"root": "bundle",
"pages": [
{
"path": "account/switch",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#F6F7F9"
}
},
{
"path": "collect/collect",
"type": "page",
@ -216,6 +248,15 @@
"navigationBarTitleText": "分包页面"
}
},
{
"path": "finance/finance",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F6F7F8"
}
},
{
"path": "profile/profile",
"type": "page",
@ -249,6 +290,24 @@
"navigationStyle": "custom"
}
},
{
"path": "store/verify-code",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F6F7F9"
}
},
{
"path": "store/verify-record",
"type": "page",
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F6F7F8"
}
},
{
"path": "store-recharge/store-recharge",
"type": "page",

View File

@ -9,121 +9,126 @@
}</route>
<template>
<view class="home-bg">
<view class="home-bg pb-80rpx">
<view class="home-bg w-[100%] fixed top-0 left-0 z-100">
<wd-navbar safeAreaInsetTop :bordered="false" custom-style="background-color: transparent !important;">
<template #left>
<view class="flex items-center line-1 w-130rpx" @click="Index.handleToCity">
<view class="mr-10rpx font-400 leading-44rpx text-32rpx pl-10rpx line-1">{{ city || LOCATION_DEFAULT_CITY }}</view>
<view class="flex items-center line-1 w-450rpx" @click="router.navigateTo('/bundle/account/switch')">
<view class="mr-10rpx font-400 leading-44rpx text-32rpx pl-10rpx line-1">茶址24小时智能茶室(中新店)</view>
<wd-img width="14rpx" height="9rpx" :src="`${OSS}icon/icon_arrow_down.png`" />
</view>
</template>
<template #title>
<view class="search-box flex items-center ml-26rpx" @click="Index.handleToSearch">
<wd-search placeholder="搜索茶址名称" hide-cancel disabled :placeholder-left="true"
placeholderStyle="text-align:left;padding-left: 24rpx;line-heigt: 44rpx;color: #C9C9C9; font-size: 32rpx;font-weight: normal;">
</wd-search>
</view>
</template>
</wd-navbar>
</view>
<view :style="{ paddingTop: navbarHeight + 'px' }">
<view class="mt-32rpx mx-30rpx">
<wd-swiper height="240rpx" indicatorPosition="bottom-left"
:indicator="{ type: 'dots-bar' }" :list="swiperList" v-model:current="current" mode="aspectFit"></wd-swiper>
</view>
<view class="mt-32rpx mx-30rpx relative">
<wd-img width="690rpx" height="240rpx" :src="`${OSS}images/store/home/image1.png`" mode="aspectFit" />
<view class="mt-40rpx flex items-center h-36rpx mx-30rpx">
<wd-img width="160rpx" height="36rpx" :src="`${OSS}images/home/home_image1.png`" mode="aspectFit" />
<text class="text-22rpx leading-32rpx text-[#818CA9] ml-36rpx">更多茶艺师点击预约</text>
</view>
<!-- 本月收入 -->
<view class="flex flex-col justify-center items-center absolute top-42rpx left-46rpx">
<view class="font-400 text-26rpx text-[#825F37] leading-36rpx">本月收入</view>
<view class="mt-42rpx">
<price-format color="#825F37" :first-size="36" :second-size="36" :subscript-size="24" :price="23.02"></price-format>
</view>
</view>
<view class="mt-16rpx relative w-690rpx h-180rpx mx-30rpx" @click="Index.handleToWxOfficialAccount">
<wd-img width="690rpx" height="180rpx" :src="`${OSS}images/home/home_image2.png`" mode="scaleToFill" />
<view class="h-64rpx absolute bottom-0 right-0 bg-[#4C9F44] text-[#fff] flex items-center px-26rpx rounded">
<text class="mr-8rpx">一键约</text>
<wd-img width="22rpx" height="18.06rpx" :src="`${OSS}icon/icon_arrow_right.png`" mode="aspectFit" />
<view class="absolute top-42rpx left-174rpx">
<wd-img width="6rpx" height="188rpx" :src="`${OSS}images/store/home/image2.png`" mode="aspectFit" />
</view>
<!-- 下单金额 -->
<view class="flex flex-col justify-center items-center absolute top-42rpx left-208rpx">
<view class="font-400 text-26rpx text-[#825F37] leading-36rpx">下单金额</view>
<view class="mt-26rpx">
<price-format color="#825F37" :first-size="36" :second-size="36" :subscript-size="24" :price="23.02"></price-format>
</view>
<view class="font-400 text-24rpx text-[#825F37] leading-34rpx mt-10rpx">昨日223</view>
</view>
<!-- 核销金额 -->
<view class="flex flex-col justify-center items-center absolute top-42rpx left-368rpx">
<view class="font-400 text-26rpx text-[#825F37] leading-36rpx">核销金额</view>
<view class="mt-26rpx">
<price-format color="#825F37" :first-size="36" :second-size="36" :subscript-size="24" :price="23.02"></price-format>
</view>
<view class="font-400 text-24rpx text-[#825F37] leading-34rpx mt-10rpx">昨日223</view>
</view>
</view>
<view class="relative mt-40rpx h-44rpx mx-30rpx">
<view class="absolute ele-center" >
<wd-img width="252.04rpx" height="24.43rpx" :src="`${OSS}images/home/home_image3.png`" mode="aspectFit" />
<!-- 菜单栏 -->
<view class="flex justify-between mt-40rpx">
<view class="flex flex-col justify-center items-center w-[25%]" @click="showScanMenu = true">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image3.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">扫码验券</view>
</view>
<view class="flex flex-col justify-center items-center w-[25%]" @click="router.navigateTo('/bundle/finance/finance')">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image4.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">财务管理</view>
</view>
<view class="flex flex-col justify-center items-center w-[25%]">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image5.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">套餐管理</view>
</view>
<view class="flex flex-col justify-center items-center w-[25%]">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image6.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">用户列表</view>
</view>
<view class="text-32rpx text[#303133] font-500 absolute top-0 ele-center">预约茶室</view>
</view>
<view>
<mescroll-body @init="mescrollInit" :down="downOption" @down="downCallback" :up="upOption" @up="Index.upCallback" top="28rpx"
:fixed="true">
<view class="relative p-20rp mb-24rpx" v-for="(item, index) in list" :key="index" @click="Index.handleToReserveRoom(item.id, item.operation_type)">
<view class="absolute top--28rpx left-0 z-1" v-if="item.operation_type == 1">
<wd-img width="110rpx" height="110rpx" :src="`${OSS}images/home/home_image4.png`"/>
</view>
<view class="mx-30rpx p-30rpx flex bg-white rounded-10rpx">
<wd-img width="200rpx" height="200rpx" :src="item.image" radius="10rpx" />
<view class="flex-1 ml-28rpx flex justify-between line-1 items-start relative">
<view class="line-1">
<view class="font-bold text-30rpx leading-42rpx line-1">
{{ item.name }}
</view>
<view class="flex items-center mt-12rpx leading-34rpx">
<view class="font-400 text-[#F29747] text-24rpx mr-18rpx">半年预约{{ item.half_year_nums > 10 ? item.half_year_nums + '+' : item.half_year_nums }}</view>
<view class="font-400 bg-[#F3F3F3] text-[#818CA9] text-22rpx px-8rpx rounded-4rpx">刚有人预约了</view>
</view>
<view class="flex items-center mt-12rpx leading-34rpx">
<view class="font-400 text-[#606266] text-24rpx mr-10rpx">
营业时间:{{ item.start_time }}-{{ item.end_time }}
</view>
<view class="font-400 bg-[#FFEEED] text-[#FF5951] text-22rpx px-4rpx rounded-4rpx border-[#F2E2E1]" v-if="item.shop_status == 0">
打烊了
</view>
</view>
<view class="flex items-center mt-20rpx">
<wd-img width="26rpx" height="26rpx" :src="`${OSS}icon/icon_location.png`"
mode="aspectFit" />
<view class="ml-4rpx line-1 font-400 text-22rpx text-[#606266] leading-32rpx">
{{ item.address }}
</view>
</view>
</view>
<view class="absolute bottom-0 right-0">
<view class="flex justify-end">
<view class="bg-[#4C9F44] w-72rpx h-40rpx rounded-18rpx flex items-center justify-center">
<wd-icon name="add" color="#fff" size="20rpx" custom-style="font-weight: bold;" />
</view>
</view>
<view class="text-24rpx text-[#92928C] font-400 mt-12rpx">距您{{ item.distance }}km</view>
</view>
<!-- 商家培训 -->
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mx-30rpx mt-40rpx">
<view class="flex items-center justify-between">
<view class="flex items-center">
<wd-img width="48rpx" height="48rpx" :src="`${OSS}images/store/home/image7.png`" mode="aspectFit" />
<view class="flex items-center ml-10rpx">
<view class="text-32rpx text-[#303133] leading-44rpx">商家培训</view>
<view class="">
<wd-divider vertical color="#D9D9D9"></wd-divider>
</view>
<view class="font-400 text-24rpx text-[#909399] leading-34rpx">商家运营赋能</view>
</view>
</view>
</mescroll-body>
<view class="">
<text class="font-400 text-24rpx text-[#909399] leading-34rpx">更多</text>
<wd-icon name="arrow-right" size="20rpx" color="#C9C9C9" class="ml-4rpx"></wd-icon>
</view>
</view>
<view class="mt-30rpx">
<view v-for="i in 4" :key="i">
<view class="flex items-center justify-between">
<view class="mr-26rpx">
<view class="font-400 text-26rpx text-[#303133] leading-40rpx w-440rpx line-2">必备课如何做好门店评价管理客户为什么给好评</view>
<view class="font-400 text-22rpx text-[#909399] leading-32rpx">2025-08-09 17:21</view>
</view>
<view>
<wd-img width="164rpx" height="108rpx" radius="4rpx" :src="`${OSS}images/banner1.png`" mode="aspectFit" />
</view>
</view>
<view class="mt-22rpx mb-30rpx" v-if="i !== 4">
<wd-gap bg-color="#F6F7F9" height="2rpx"></wd-gap>
</view>
</view>
</view>
</view>
</view>
<!-- 扫码验券弹出框 -->
<wd-action-sheet v-model="showScanMenu" :actions="scanActions" @select="Index.handleSelectScanMenu" />
</view>
</template>
<script lang="ts" setup>
import { router } from '@/utils/tools'
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
import { LOCATION_DENY_TIME_KEY, handleEnsureLocationAuthHooks, LOCATION_DEFAULT_CITY, handleGetLocationCity, LOCATION_CITY_KEY } from '@/hooks/useLocation'
import { getHomeBannerList } from '@/api/home'
import { getHomeTeaStoreList } from '@/api/tea-room'
import { useUserStore } from '@/store'
const OSS = inject('OSS')
const navbarHeight = inject('navbarHeight')
/** 轮播图 **/
const swiperList = ref<string[]>([])
const current = ref<number>(0)
// 分页
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
@ -131,171 +136,63 @@
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const latitude = ref<number>(0)
const longitude = ref<number>(0)
const city = ref<string>('')
const keywords = ref<string>('')
const list = ref<Array<any>>([])
let lastLocation = { lat: 0, lng: 0 }
onShow(async () => {
if (uni.getStorageSync(LOCATION_DENY_TIME_KEY)) {
const location = await checkLocationAuthWithModal()
if (location) {
// 只有经纬度变化时才刷新
if (location.lat !== lastLocation.lat || location.lng !== lastLocation.lng) {
const loc = await handleGetLocationCity(location.lat, location.lng)
city.value = loc.city
latitude.value = location.lat
longitude.value = location.lng
lastLocation.lat = location.lat
lastLocation.lng = location.lng
console.log("🚀 ~ city.value:", 'xxxxx', city.value)
Index.handleResetSearch()
}
}
// 扫码验券弹出框
const showScanMenu = ref<boolean>(false)
const scanActions = ref([
{
name: '扫码验券',
},
{
name: '输入券码',
},
{
name: '验券记录',
}
})
])
onLoad(async() => {
// 初始化页面数据
Index.handleInit()
// 获取用户经纬度(带缓存和授权逻辑)
const { lat, lng } = await handleEnsureLocationAuthHooks()
latitude.value = lat
longitude.value = lng
const location = await handleGetLocationCity(lat, lng)
city.value = location.city
latitude.value = location.latitude
longitude.value = location.longitude
Index.handleResetSearch()
})
const Index = {
handleInit: async() => {
},
/**
* 茶室门店列表
* @param mescroll
* 选择扫码验券菜单
*/
upCallback: (mescroll) => {
const userStore = useUserStore()
const userId = userStore.userInfo?.id || 0
const filter = {
page: mescroll.num,
size: mescroll.size,
latitude: latitude.value,
longitude: longitude.value,
search: keywords.value,
user_id: userId
}
uni.showLoading({ title: '加载中...' })
try {
getHomeTeaStoreList(filter).then( res => {
const curPageData = res.list || [] // 当前页数据
if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
list.value = list.value.concat(curPageData) //追加新数据
mescroll.endSuccess(curPageData.length, Boolean(res.more))
}).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
})
uni.hideLoading()
} catch (error) {
uni.hideLoading()
handleSelectScanMenu: (item: { item: { name: string }}) => {
console.log("🚀 ~ item:", item)
if (item.item.name === '扫码验券') {
Index.handleScanCode()
} else if (item.item.name === '输入券码') {
const storeName = '茶址24小时智能茶室(中新店)'
router.navigateTo(`/bundle/store/verify-code?storeName=${storeName}`)
} else if (item.item.name === '验券记录') {
router.navigateTo('/bundle/store/verify-record')
}
},
/**
* 初始化首页数据
* 扫码验券
*/
handleInit: () => {
getHomeBannerList().then(res => {
swiperList.value = res.list.map(item => item.address)
})
},
/**
* 跳转城市选择
*/
handleToCity: () => {
uni.$on('locationUpdate', params => {
console.log("🚀 ~ params:", params)
uni.$off('locationUpdate')
city.value = params.city
latitude.value = params.latitude
longitude.value = params.longitude
Index.handleResetSearch()
})
router.navigateTo(`/pages/city/city?lat=${latitude.value}&lng=${longitude.value}`)
},
/**
* 跳转茶室搜索
*/
handleToSearch: () => {
uni.$on('refreshTeaRoomList', params => {
keywords.value = params.keywords
Index.handleResetSearch()
uni.$off('refreshTeaRoomList')
})
router.navigateTo(`/pages/search/search?keywords=${keywords.value}`)
},
/**
* 跳转到预约茶室页面
*/
handleToReserveRoom: (id: number = 0, type: number = 1) => {
router.navigateTo( `/bundle/tea-room/room?id=${id}&type=${type}`)
},
/**
* 重置搜索
*/
handleResetSearch: () => {
console.log("🚀 ~ location:", 456)
list.value = []
getMescroll().resetUpScroll()
},
/**
* 跳转公众号
*/
handleToWxOfficialAccount: () => {
wx.openOfficialAccountProfile({
username: '', // 此处填写公众号的原始 ID
success: res => {
handleScanCode: () => {
uni.scanCode({
success: (res) => {
console.log('scanCode res:', res)
},
fail: res => {
fail: (err) => {
console.log('scanCode err:', err)
}
})
},
/**
* 更新定位信息
*/
handleLocationUpdate: (params: any) => {
console.log("🚀 ~ locationUpdate:")
if (
city.value !== params.city ||
latitude.value !== params.latitude ||
longitude.value !== params.longitude
) {
city.value = params.city
latitude.value = params.latitude
longitude.value = params.longitude
Index.handleResetSearch()
}
}
}
</script>
@ -311,36 +208,6 @@ page {
background-repeat: no-repeat;
}
.search-box {
display: flex;
height: 100%;
margin-right: 40px;
--wot-search-padding: 0;
--wot-search-side-padding: 0;
:deep() {
.wd-search {
background: transparent !important;
width: 100% !important;
}
.wd-search__block {
background-color: #fff !important;
}
.wd-search__input {
// #ifdef MP
padding-left: 32px !important;
padding-right: 32px !important;
// #endif
// #ifndef MP
padding-right: 0 !important;
// #endif
}
}
}
.rounded {
border-radius: 20rpx 0rpx 20rpx 0rpx;
}

View File

@ -1,112 +1,202 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#fff"
"navigationStyle": "custom"
}
}</route>
<template>
<view>
<view class="mx-48rpx mt-50rpx">
<view class="text-[#303133] text-40rpx leading-56rpx">
<text class="font-400 mr-24rpx">欢迎使用</text>
<text class="font-700">茶址</text>
<view class="login-bg">
<!-- <view class="login-bg w-[100%] fixed top-0 left-0 z-100"> -->
<wd-navbar safeAreaInsetTop :bordered="false"
custom-style="background-color: transparent !important;"></wd-navbar>
<!-- </view> -->
<view class="px-68rpx">
<view class="font-bold text-44rpx text-[#333333] leading-60rpx">你好欢迎使用</view>
<view class="font-bold text-44rpx text-[#333333] leading-60rpx mt-18rpx">茶址管理平台</view>
<view class="mt-18rpx font-400 text-26rpx text-[#606266] leading-40rpx w-354rpx">此小程序仅供企业内部员工登录请凭管理员账号密码登录
</view>
<view class="font-400 text-26rpx leading-36rpx text-[#606266] mt-20rpx">登录后可进行茶室预约开启您的专属茶席</view>
</view>
<view class="mt-176rpx w-162rpx h-160rpx mx-auto">
<wd-img :src="`${OSS}images/logo.png`" width="100%" height="100%" mode="aspectFill"></wd-img>
</view>
<view class="mt-124rpx mx-60rpx box-border">
<wd-button custom-class="!bg-[#4C9F44] !rounded-8rpx !text-[#fff] !text-30rpx !leading-42rpx !h-90rpx !w-[100%] box-border" @click="Login.handleLogin">立即登录</wd-button>
<wd-button custom-class="!bg-[#4C9F44] !rounded-8rpx !text-[#fff] !text-30rpx !leading-42rpx !h-90rpx !w-[100%] box-border" @click="Login.handleMobileLogin">测试-账号登录</wd-button>
<!-- <wd-button open-type="getUserInfo" @getuserinfo="Login.handleWxLogin" custom-class="!bg-[#4C9F44] !rounded-8rpx !text-[#fff] !text-30rpx !leading-42rpx !h-90rpx !w-[100%] box-border">立即登录</wd-button> -->
<!-- <view class="text-30rpx font-400 text-[#303133] leading-42rpx text-center mt-32rpx">其它手机号登录</view> -->
</view>
<view class="flex items-center mx-32rpx mt-64rpx">
<view class="w-32rpx h-32rpx">
<wd-checkbox v-model="agree" @change="Login.handleAgree" checked-color="#4C9F44" size="large"> </wd-checkbox>
</view>
<view class="font-400 text-26rpx leading-40rpx text-[#8F959E] ml-14rpx flex-1" @click="agree = !agree">
我已阅读并同意 <text class="text-[#4C9F44]" @click.stop="Login.handleToService">服务协议</text> <text class="text-[#4C9F44]" @click.stop="Login.handleToPrivacy">隐私政策</text>未注册手机号登录后将自动你为您创建账号
</view>
<!-- 登录表单 -->
<view class="mt-100rpx px-58rpx">
<wd-form ref="form" :model="model">
<view>
<view class="font-400 text-30rpx text-[#606266] leading-44rpx">账号</view>
<view class="mt-20rpx">
<wd-input v-model="model.mobile" type="text" placeholder="请输入账号" inputmode="numeric" no-border
custom-class="!bg-[#F6F7F8] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx" @input="Login.handleInputMobile" />
</view>
</view>
<view class="mt-40rpx">
<view class="font-400 text-30rpx text-[#606266] leading-44rpx">密码</view>
<view class="mt-20rpx">
<wd-input v-model="model.passowrd" type="safe-password" placeholder="请输入密码" no-border
custom-class="!bg-[#F6F7F8] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx" />
</view>
</view>
<view class="mt-74rpx">
<wd-button custom-class="!bg-[#4C9F44] !rounded-8rpx !text-[#fff] !text-30rpx !leading-42rpx !h-90rpx !w-[100%] box-border" @click="Login.handleToLogin">立即登录</wd-button>
</view>
<view class="mt-44rpx font-400 text-28rpx text-[#4C9F44] leading-40rpx" @click="router.navigateTo('/pages/login/password')">
忘记密码
</view>
</wd-form>
</view>
</view>
</template>
<script lang="ts" setup>
import { toast } from '@/utils/toast'
import { getWxCode } from '@/api/login'
import { useUserStore } from '@/store'
import { router } from '@/utils/tools'
import { mobile as testMobile } from '@/utils/test'
import { router } from '@/utils/tools'
import { useToast } from 'wot-design-uni'
const OSS = inject('OSS')
const OSS = inject('OSS')
const toast = useToast()
const disabled = ref<boolean>(true)
// 服务协议条款
const agree = ref<boolean>(false)
/** 页面 **/
let pageType = 'login' // 页面类型 login:登录 edit:修改手机号
const page = ref<{ title: string, desc: string }>({ title: '其他手机号登录', desc: '请输入你要登录的手机号' })
const showEditSuccessPopup = ref<boolean>(false) // 显示手机号修改成功弹窗
const userId = ref<number>(0) // 用户ID修改手机号时需要传
const Login = {
// 获取手机号
handleLogin: async (e: object) => {
if (!agree.value) {
toast.info('请同意服务协议和隐私政策')
return
}
/** 验证码倒计时 **/
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
const userStore = useUserStore()
const res = await userStore.wxLogin()
if (res) {
toast.info('登录成功')
router.navigateBack(1, 500)
}
},
/** 表单相关 **/
const model = reactive<{
mobile: string
passowrd: string
}>({
mobile: '',
passowrd: ''
})
/** 结束 **/
// 手机登录
handleMobileLogin: async () => {
const userStore = useUserStore()
console.log("🚀 ~ userStore:", userStore)
const res = await userStore.mobileLogin('18868040087', 1, 2)
if (res) {
uni.setStorageSync('latitude', '30.74744')
uni.setStorageSync('longitude', '120.78483')
toast.info('登录成功')
router.navigateBack(1, 500)
}
},
/** 服务协议和隐私政策 **/
const agree = ref<boolean>(false)
/** 结束 **/
handleAgree: async (e: any) => {
},
onLoad((args) => {
// 从个人登录页面进入
if (args.type === 'edit') {
userId.value = Number(args.userId) || 0 // userId仅做测试使用实际请传真实用户ID
// 跳转到服务协议页面
handleToService: () => {
},
// 跳转到隐私政策页面
handleToPrivacy: () => {
},
page.value.title = '修改手机号'
page.value.desc = '手机号一年内可修改2次'
pageType = 'edit'
}
})
const Login = {
// 验证手机号
handleInputMobile: (e: { value: string }) => {
model.mobile = e.value
disabled.value = !testMobile(model.mobile)
},
// 发送验证码
handleCountDown: () => {
if (disabled.value) {
toast.show({
iconClass: 'info-circle',
msg: '手机号码错误请重新输入',
direction: 'vertical'
})
return
}
startCountDown.value = true
nextTick(() => {
countDown.value?.start()
// 发送验证码请求
})
},
// 验证码倒计时结束
handleFinishCountDown: () => {
startCountDown.value = false
},
// 登录
handleToLogin: () => {
// TODO 如果是edit的话就是修改手机号
if (pageType === 'login' && !agree.value) {
toast.show({
iconClass: 'info-circle',
msg: '请同意服务协议和隐私政策',
direction: 'vertical'
})
return
}
if (!testMobile(model.mobile)) {
toast.show({
iconClass: 'info-circle',
msg: '手机号码错误请重新输入',
direction: 'vertical'
})
return
}
if (!model.code) {
toast.show({
iconClass: 'info-circle',
msg: '验证码错误',
direction: 'vertical'
})
return
}
},
// 获取手机号
handleGetPhoneNumber: (e: object) => {
console.log("🚀 ~ e:", e)
},
handleAgree: (e: any) => {
console.log('e', e)
},
// 跳转到服务协议页面
handleToService: () => {
disabled.value = !disabled.value
console.log("🚀 ~ disabled:", disabled)
},
// 跳转到隐私政策页面
handleToPrivacy: () => {
},
// 修改手机成功后返回
handleToBack: () => {
uni.navigateBack()
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
page {
background-color: #fff;
}
.service {
:deep() {
.wd-checkbox {
display: flex;
align-content: flex-start;
}
.wd-checkbox__label {
margin-left: 6rpx;
}
}
}
.login-bg {
background-color: #fff;
background-image: url(#{$OSS}images/store/login/image1.png);
background-size: 100% 420rpx;
background-repeat: no-repeat;
}
</style>

View File

@ -0,0 +1,224 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#fff"
}
}</route>
<template>
<view>
<view class="mx-60rpx mt-20rpx">
<view class="text-[#303133] text-48rpx leading-80rpx font-600">
找回密码
</view>
<view class="font-400 text-28rpx leading-44rpx text-[#6B7280] mt-12rpx">需要验证绑定手机</view>
</view>
<view class="mt-106rpx mx-48rpx">
<wd-form ref="form" :model="model">
<view>
<view class="font-400 text-30rpx text-[#606266] leading-44rpx">手机号</view>
<view class="mt-20rpx">
<wd-input
v-model="model.mobile"
type="text"
placeholder="请输入手机号码"
inputmode="numeric"
no-border
custom-class="!bg-[#F6F7F8] !border !border-solid !border-[#EAECF0] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx"
@input="mobile.handleInputMobile"
/>
</view>
</view>
<view class="mt-40rpx">
<view class="font-400 text-30rpx text-[#606266] leading-44rpx">验证码</view>
<view class="mt-20rpx">
<wd-input type="text" placeholder="请输入验证码" v-model="model.code" inputmode="numeric" no-border custom-class="!bg-[#F6F7F8] !border !border-solid !border-[#EAECF0] !rounded-16rpx" custom-input-class="!px-32rpx !h-104rpx">
<template #suffix>
<view class="flex items-center mr-34rpx">
<view class="flex items-center">
<wd-divider color="#C9C9C9" vertical />
</view>
<view class="flex items-center">
<view class="text-[#4C9F44] text-32rpx font-400 leading-44rpx" v-if="!startCountDown" @click="mobile.handleCountDown">发送验证码</view>
<view class="!text-[#C9C9C9] text-32rpx font-400 leading-44rpx flex items-center" v-if="startCountDown">
<wd-count-down ref="countDown" :time="countDownTime" millisecond :auto-start="false" format="ss" custom-class="!text-[#C9C9C9] !text-32rpx" @finish="mobile.handleFinishCountDown"></wd-count-down>
<view> S后重发</view>
</view>
</view>
</view>
</template>
</wd-input>
</view>
</view>
</wd-form>
</view>
<view class="h-90rpx leading-90rpx mx-60rpx rounded-8rpx text-center mt-112rpx bg-[#4C9F44] text-[#fff]" :class="disabled ? 'opacity-40' : ''" @click="mobile.handleToLogin">下一步</view>
<!-- 手机号修改成功 -->
<wd-popup v-model="showEditSuccessPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
<view class="relative pt-64rpx pb-74rpx">
<view class="flex justify-center items-center">
<view class="bg-[#4C9F44] w-280rpx rounded-280rpx">
<wd-img width="280rpx" height="280rpx" :src="`${OSS}images/reserve_room/reserve_room_image7.png`"/>
</view>
</view>
<view class="text-[#303133] text-36rpx leading-46rpx text-center mt-48rpx">手机号修改成功</view>
<view class="text-[#9CA3AF] text-28rpx leading-44rpx mt-16rpx text-center">{{ page.desc }}</view>
<view class="w-630rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff] mt-174rpx mx-auto" @click="mobile.handleToBack">好的</view>
</view>
</wd-popup>
</view>
</template>
<script lang="ts" setup>
import {mobile as testMobile} from '@/utils/test'
import { useToast } from 'wot-design-uni'
import { router } from '@/utils/tools'
const OSS = inject('OSS')
const toast = useToast()
const disabled = ref<boolean>(true)
/** 页面 **/
let pageType = 'login' // 页面类型 login:登录 edit:修改手机号
const page = ref<{title: string, desc: string}>({title: '其他手机号登录', desc: '请输入你要登录的手机号'})
const showEditSuccessPopup = ref<boolean>(false) // 显示手机号修改成功弹窗
const userId = ref<number>(0) // 用户ID修改手机号时需要传
/** 验证码倒计时 **/
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
/** 表单相关 **/
const model = reactive<{
mobile: string
code: string
}>({
mobile: '',
code: ''
})
/** 结束 **/
/** 服务协议和隐私政策 **/
const agree = ref<boolean>(false)
/** 结束 **/
onLoad((args) => {
// 从个人登录页面进入
if (args.type === 'edit') {
userId.value = Number(args.userId) || 0 // userId仅做测试使用实际请传真实用户ID
page.value.title = '修改手机号'
page.value.desc = '手机号一年内可修改2次'
pageType = 'edit'
}
})
const mobile = {
// 验证手机号
handleInputMobile: (e: {value: string}) => {
model.mobile = e.value
disabled.value = !testMobile(model.mobile)
},
// 发送验证码
handleCountDown: () => {
if (disabled.value) {
toast.show({
iconClass: 'info-circle',
msg: '手机号码错误请重新输入',
direction: 'vertical'
})
return
}
startCountDown.value = true
nextTick(() => {
countDown.value?.start()
// 发送验证码请求
})
},
// 验证码倒计时结束
handleFinishCountDown: () => {
startCountDown.value = false
},
// 登录
handleToLogin: () => {
// TODO 如果是edit的话就是修改手机号
if (!testMobile(model.mobile)) {
toast.show({
iconClass: 'info-circle',
msg: '手机号码错误请重新输入',
direction: 'vertical'
})
return
}
if (!model.code) {
toast.show({
iconClass: 'info-circle',
msg: '验证码错误',
direction: 'vertical'
})
return
}
router.navigateTo( '/pages/login/set-password')
},
// 获取手机号
handleGetPhoneNumber: (e: object) => {
console.log("🚀 ~ e:", e)
},
handleAgree: (e: any) => {
console.log('e', e)
},
// 跳转到服务协议页面
handleToService: () => {
disabled.value = !disabled.value
console.log("🚀 ~ disabled:", disabled)
},
// 跳转到隐私政策页面
handleToPrivacy: () => {
},
// 修改手机成功后返回
handleToBack: () => {
uni.navigateBack()
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.service {
:deep() {
.wd-checkbox {
display: flex;
align-content: flex-start;
}
.wd-checkbox__label {
margin-left: 6rpx;
}
}
}
</style>

View File

@ -0,0 +1,223 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#fff"
}
}</route>
<template>
<view>
<view class="mx-60rpx mt-20rpx">
<view class="text-[#303133] text-48rpx leading-80rpx font-600">
设置新密码
</view>
<view class="font-400 text-28rpx leading-44rpx text-[#6B7280] mt-12rpx">请设置你得新密码</view>
</view>
<view class="mt-106rpx mx-48rpx">
<wd-form ref="form" :model="model">
<view>
<view class="font-400 text-30rpx text-[#606266] leading-44rpx">密码</view>
<view class="mt-20rpx">
<wd-input
v-model="model.mobile"
type="text"
placeholder="请输入密码"
inputmode="numeric"
no-border
custom-class="!bg-[#F6F7F8] !border !border-solid !border-[#EAECF0] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx"
@input="mobile.handleInputMobile"
/>
</view>
</view>
<view>
<view class="font-400 text-30rpx text-[#606266] leading-44rpx">再次确认</view>
<view class="mt-20rpx">
<wd-input
v-model="model.mobile"
type="text"
placeholder="再次确认新的密码"
inputmode="numeric"
no-border
custom-class="!bg-[#F6F7F8] !border !border-solid !border-[#EAECF0] !rounded-16rpx"
custom-input-class="!px-32rpx !h-104rpx"
@input="mobile.handleInputMobile"
/>
</view>
</view>
</wd-form>
</view>
<view class="h-90rpx leading-90rpx mx-60rpx rounded-8rpx text-center mt-112rpx bg-[#4C9F44] text-[#fff]" :class="disabled ? 'opacity-40' : ''" @click="mobile.handleToLogin">确定</view>
<!-- 手机号修改成功 -->
<wd-popup v-model="showEditSuccessPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
<view class="relative pt-64rpx pb-74rpx">
<view class="flex justify-center items-center">
<view class="bg-[#4C9F44] w-280rpx rounded-280rpx">
<wd-img width="280rpx" height="280rpx" :src="`${OSS}images/reserve_room/reserve_room_image7.png`"/>
</view>
</view>
<view class="text-[#303133] text-36rpx leading-46rpx text-center mt-48rpx">密码修改成功</view>
<view class="text-[#9CA3AF] text-28rpx leading-44rpx mt-16rpx text-center">请记住你的登录密码</view>
<view class="w-630rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff] mt-174rpx mx-auto" @click="mobile.handleToBack">好的</view>
</view>
</wd-popup>
</view>
</template>
<script lang="ts" setup>
import {mobile as testMobile} from '@/utils/test'
import { useToast } from 'wot-design-uni'
const OSS = inject('OSS')
const toast = useToast()
const disabled = ref<boolean>(true)
/** 页面 **/
let pageType = 'login' // 页面类型 login:登录 edit:修改手机号
const page = ref<{title: string, desc: string}>({title: '其他手机号登录', desc: '请输入你要登录的手机号'})
const showEditSuccessPopup = ref<boolean>(false) // 显示手机号修改成功弹窗
const userId = ref<number>(0) // 用户ID修改手机号时需要传
/** 验证码倒计时 **/
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
/** 表单相关 **/
const model = reactive<{
mobile: string
code: string
}>({
mobile: '',
code: ''
})
/** 结束 **/
/** 服务协议和隐私政策 **/
const agree = ref<boolean>(false)
/** 结束 **/
onLoad((args) => {
// 从个人登录页面进入
if (args.type === 'edit') {
userId.value = Number(args.userId) || 0 // userId仅做测试使用实际请传真实用户ID
page.value.title = '修改手机号'
page.value.desc = '手机号一年内可修改2次'
pageType = 'edit'
}
})
const mobile = {
// 验证手机号
handleInputMobile: (e: {value: string}) => {
model.mobile = e.value
disabled.value = !testMobile(model.mobile)
},
// 发送验证码
handleCountDown: () => {
if (disabled.value) {
toast.show({
iconClass: 'info-circle',
msg: '手机号码错误请重新输入',
direction: 'vertical'
})
return
}
startCountDown.value = true
nextTick(() => {
countDown.value?.start()
// 发送验证码请求
})
},
// 验证码倒计时结束
handleFinishCountDown: () => {
startCountDown.value = false
},
// 登录
handleToLogin: () => {
// TODO 如果是edit的话就是修改手机号
if (pageType === 'login' && !agree.value) {
toast.show({
iconClass: 'info-circle',
msg: '请同意服务协议和隐私政策',
direction: 'vertical'
})
return
}
if (!testMobile(model.mobile)) {
toast.show({
iconClass: 'info-circle',
msg: '手机号码错误请重新输入',
direction: 'vertical'
})
return
}
if (!model.code) {
toast.show({
iconClass: 'info-circle',
msg: '验证码错误',
direction: 'vertical'
})
return
}
},
// 获取手机号
handleGetPhoneNumber: (e: object) => {
console.log("🚀 ~ e:", e)
},
handleAgree: (e: any) => {
console.log('e', e)
},
// 跳转到服务协议页面
handleToService: () => {
disabled.value = !disabled.value
console.log("🚀 ~ disabled:", disabled)
},
// 跳转到隐私政策页面
handleToPrivacy: () => {
},
// 修改手机成功后返回
handleToBack: () => {
uni.navigateBack()
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.service {
:deep() {
.wd-checkbox {
display: flex;
align-content: flex-start;
}
.wd-checkbox__label {
margin-left: 6rpx;
}
}
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/static/tabbar/order.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/static/tabbar/store.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -42,23 +42,23 @@ export const nativeTabbarList: NativeTabBarItem[] = [
text: '首页',
},
{
iconPath: 'static/tabbar/reserve.png',
selectedIconPath: 'static/tabbar/reserve_s.png',
iconPath: 'static/tabbar/order.png',
selectedIconPath: 'static/tabbar/order_s.png',
pagePath: 'pages/reserve/reserve',
text: '预约',
text: '订单',
},
{
iconPath: 'static/tabbar/store.png',
selectedIconPath: 'static/tabbar/store_s.png',
pagePath: 'pages/login/login',
text: '门店管理',
},
{
iconPath: 'static/tabbar/my.png',
selectedIconPath: 'static/tabbar/my_s.png',
pagePath: 'pages/my/my',
text: '我的',
},
// {
// iconPath: 'static/tabbar/example.png',
// selectedIconPath: 'static/tabbar/exampleHL.png',
// pagePath: 'pages/about/about',
// text: '关于',
// },
}
]
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!