初始版本

This commit is contained in:
贾祥聪
2025-08-19 14:16:51 +08:00
commit f937a1f9b9
4373 changed files with 359728 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<template>
<view class="p-[24rpx]">
<mp-html :content="agreementContent" />
</view>
</template>
<script lang="ts" setup>
import { ref } from "vue"
import { onLoad } from "@dcloudio/uni-app";
import { getPolicy } from "@/api/app"
import { AgreementEnum } from "@/enums/agreementEnums"
let agreementType = ref('') // 协议类型
const agreementContent = ref<string | null>('')
// 获取服务协议
const getPolicyAgreement = async () => {
const res = await getPolicy()
console.log('res=>', res)
if( agreementType == AgreementEnum.SERVICE) {
agreementContent.value = res.service_agreement
uni.setNavigationBarTitle({
title: String(res.service_title)
})
}else {
agreementContent.value = res.privacy_agreement
uni.setNavigationBarTitle({
title: String(res.privacy_title)
})
}
}
onLoad((options:any) => {
if (options.type) {
agreementType = options.type
getPolicyAgreement()
}
})
</script>

View File

@@ -0,0 +1,30 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="as-us flex flex-1 flex-col items-center">
<image :src="appStore.config.logo" mode="" class="img"></image>
<view class="text-content mt-[20rpx]">当前版本{{ appStore.config.version }}</view>
</view>
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
</script>
<style lang="scss" scoped>
.as-us {
.img {
width: 160rpx;
height: 160rpx;
border-radius: 20rpx;
margin-top: 96rpx;
}
}
</style>

View File

@@ -0,0 +1,141 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="register min-h-full flex flex-col items-center px-[30rpx] pt-[60rpx] box-border">
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="true"
:is-fixed="true"
title="更换手机号"
:border-bottom="false"
:title-bold="false"
:background="{ background: `rgba(256,256, 256, 0)` }"
:title-color="'#000'"
>
</u-navbar>
</u-sticky>
<view class="w-full mt-[380rpx]" v-if="bindType == bindMobileEnum.LOOK">
<view class="text-content text-center mb-[8rpx] ">
已绑定手机号
</view>
<view class="text-[48rpx] text-center font-black mb-[180rpx]">
{{ mobile }}
</view>
<u-button type="primary" @click="bindType = bindMobileEnum.CHANGE" class="rounded-[24rpx] w-[380rpx]"> 更换手机号 </u-button>
</view>
<view class="w-full" v-if="bindType == bindMobileEnum.CHANGE">
<view class="bg-white mb-[30rpx] rounded-[24rpx] px-[48rpx] py-[15rpx] flex items-center">
<u-image src="@/static/images/icon/icon_mobile.png" :height="28" :width="28" class="mr-[20rpx]"></u-image>
<u-input
class="flex-1"
v-model="formData.mobile"
:border="false"
placeholder="请输入手机号码"
placeholder-style="color: #999"
/>
</view>
<view class="bg-white rounded-[24rpx] px-[48rpx] py-[15rpx] flex items-center items-center">
<u-image src="@/static/images/icon/icon_code.png" :height="28" :width="28" class="mr-[20rpx]"></u-image>
<u-input
class="flex-1"
v-model="formData.code"
placeholder="请输入验证码"
:border="false"
placeholder-style="color: #999"
/>
<view
class="text-muted leading-4 "
@click="sendSms"
>
<u-verification-code
ref="uCodeRef"
:seconds="60"
@change="codeChange"
change-text="x秒"
/>
<text :class="formData.mobile ? 'text-primary' : 'text-muted'">
{{ codeTips }}
</text>
</view>
</view>
<view class="mt-[112rpx]">
<u-button type="primary" @click="handleConfirm" class="rounded-[24rpx]"> 确定 </u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { userBindMobile } from '@/api/user'
import { smsSend } from '@/api/app'
import { SMSEnum } from '@/enums/appEnums'
import { reactive, ref, shallowRef } from 'vue'
import { useUserStore } from '@/stores/user'
import { onLoad } from '@dcloudio/uni-app'
enum bindMobileEnum {
LOOK = 1, // 查看
CHANGE = 2, // 更改
}
const mobile = ref('')
const bindType = ref(bindMobileEnum.LOOK)
const uCodeRef = shallowRef()
const codeTips = ref('')
const userStore = useUserStore()
const codeChange = (text: string) => {
codeTips.value = text
}
const formData = reactive({
type: 'change',
mobile: '',
code: ''
})
const sendSms = async () => {
if (!formData.mobile) return uni.$u.toast('请输入手机号码')
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: SMSEnum.CHANGE_MOBILE,
mobile: formData.mobile
})
uni.$u.toast('发送成功')
uCodeRef.value?.start()
}
}
const handleConfirm = async () => {
if (!formData.mobile) return uni.$u.toast('请输入手机号码')
if (!formData.code) return uni.$u.toast('请输入验证码')
await userBindMobile(formData, { token: userStore.temToken })
uni.$u.toast('更改成功')
// userStore.login(userStore.temToken!)
setTimeout(()=> {
uni.navigateBack()
}, 500)
}
onLoad((options) => {
// console.log(options.mobile)
mobile.value = options.mobile
})
</script>
<style lang="scss">
page {
height: 100%;
}
</style>

View File

@@ -0,0 +1,101 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view
class="register min-h-full flex flex-col items-center px-[30rpx] pt-[60rpx] box-border"
>
<view class="w-full">
<view v-if="type != 'set'" class="bg-white mb-[30rpx] rounded-[24rpx] px-[48rpx] py-[15rpx] flex">
<u-icon class="mr-[20rpx]" name="lock" color="#999" size="28"></u-icon>
<u-input
class="flex-1"
type="password"
v-model="formData.old_password"
:border="false"
placeholder="请输入原密码"
placeholder-style="color: #999"
/>
</view>
<view class="bg-white mb-[30rpx] rounded-[24rpx] px-[48rpx] py-[15rpx] flex">
<u-icon class="mr-[20rpx]" name="lock" color="#999" size="28"></u-icon>
<u-input
class="flex-1"
type="password"
v-model="formData.password"
placeholder="请输入新密码"
:border="false"
placeholder-style="color: #999"
/>
</view>
<view class="bg-white rounded-[24rpx] px-[48rpx] py-[15rpx] flex">
<u-icon class="mr-[20rpx]" name="lock" color="#999" size="28"></u-icon>
<u-input
class="flex-1"
type="password"
v-model="formData.password_confirm"
placeholder="请再次确认密码"
:border="false"
placeholder-style="color: #999"
/>
</view>
<view class="mt-[112rpx]">
<u-button type="primary" @click="handleConfirm" class="rounded-[24rpx]"> 确定 </u-button>
</view>
<navigator url="/pages/forget_pwd/forget_pwd" class="mt-[40rpx] text-center text-primary" v-if="type != 'set'">
忘记密码?
</navigator>
</view>
</view>
</template>
<script setup lang="ts">
import { userChangePwd } from '@/api/user'
import { onLoad } from '@dcloudio/uni-app'
import { reactive, ref } from 'vue'
import { validateInput } from '@/utils/util'
const type = ref('')
const formData = reactive<any>({
password: '',
password_confirm: '',
})
const handleConfirm = async () => {
if (!formData.old_password && type.value != 'set') return uni.$u.toast('请输入原来的密码')
if (!formData.password) return uni.$u.toast('请输入密码')
if (!formData.password_confirm) return uni.$u.toast('请输入确认密码')
if (formData.password != formData.password_confirm) return uni.$u.toast('两次输入的密码不一致')
if (!validateInput(formData.password) || !validateInput(formData.password_confirm))
return uni.$u.toast('密码应为6-20位数字+英文')
await userChangePwd(formData)
uni.$u.toast('操作成功')
setTimeout(()=> {
uni.navigateBack()
}, 500)
}
onLoad((options) => {
type.value = options.type || ''
if (type.value == 'set') {
uni.setNavigationBarTitle({
title: '设置登录密码',
})
}
})
</script>
<style lang="scss">
page {
height: 100%;
}
</style>

View File

@@ -0,0 +1,70 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<z-paging
ref="paging"
v-model="collectData"
@query="queryList"
:fixed="false"
height="100%"
use-page-scroll
>
<u-swipe-action
:show="item.show"
:index="index"
v-for="(item, index) in collectData"
:key="item.id"
@click="handleCollect"
:options="options"
btn-width="120"
>
<news-card :item="item" :newsId="item.article_id"></news-card>
</u-swipe-action>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, reactive, shallowRef } from 'vue'
import { getCollect, cancelCollect } from '@/api/news'
const paging = shallowRef()
const options = reactive([
{
text: '取消收藏',
style: {
color: '#FFFFFF',
backgroundColor: '#FF2C3C'
}
}
])
const collectData: any = ref([])
const queryList = async (pageNo, pageSize) => {
const { lists } = await getCollect()
lists.forEach((item: any) => {
item.show = false
})
collectData.value = lists
paging.value.complete(lists)
}
const handleCollect = async (index: number): Promise<void> => {
try {
const article_id: number = collectData.value[index].article_id
await cancelCollect({ id: article_id })
uni.$u.toast('已取消收藏')
paging.value.reload()
} catch (err) {
//TODO handle the exception
console.log('取消收藏报错=>', err)
}
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,254 @@
<template>
<view class="service pt-20">
<view class="service-contain">
<view class="header-image">
<u-image
:src="getImageUrl('/resource/image/shopapi/default/service.png')"
width="100rpx"
height="100rpx"
shape="circle"
style="margin-top: -50rpx"
class="circle"
></u-image>
</view>
<!-- way==1 二维码客服 -->
<template v-if="serviceData.way == 1">
<view class="lg mt-[40rpx] flex justify-center" v-if="serviceData.remarks">
{{ serviceData.remarks }}
</view>
<view class="code flex justify-center">
<u-image
:src="serviceData.qr_code"
width="320rpx"
height="320rpx"
border-radius="20"
></u-image>
</view>
<view
class="mt-[20rpx] mb-[20rpx] xs muted flex justify-center"
v-if="serviceData.phone"
>
客服电话{{ serviceData.phone }}
</view>
<view
class="xs mt-[20rpx] muted mb-[20rpx] flex justify-center"
v-if="serviceData.business_time"
>
服务时间: {{ serviceData.business_time }}
</view>
</template>
<!-- logo -->
<template v-if="serviceData.way != 1">
<view class="flex lg justify-center mt-[40rpx]">
{{ appStore.config.shop_name }}
</view>
<view class="code flex justify-center">
<u-image
:src="appStore.config.shop_logo"
width="320rpx"
height="320rpx"
border-radius="20"
></u-image>
</view>
</template>
<!-- way==2 电话客服 -->
<template v-if="serviceData.way == 2">
<view class="mt-[40rpx] flex justify-center" v-if="serviceData.phone">
<view style="text-align: center">
<view> 拨打客服热线 </view>
<view style="text-decoration: underline" @click="handleCall">
{{ serviceData.service_phone }}
</view>
</view>
</view>
</template>
</view>
<view class="pt-[20rpx]" v-if="serviceData.way == 4">
<!-- #ifdef MP-WEIXIN -->
<button
open-type="contact"
class="copy-btn icon-item text-white rounded-[20rpx] flex justify-center items-center"
style="line-height: 88rpx"
>
<text style="line-height: 32rpx" class="text-[28rpx]">联系微信小程序客服</text>
</button>
<!-- #endif -->
</view>
<view class="pt-[20rpx]" v-else>
<view
class="rounded-[20rpx] copy-btn flex justify-center items-center text-white lg"
v-if="serviceData.way == 1"
@click="saveImageQr"
>
<text class="">保存二维码</text>
</view>
<view
class="rounded-[20rpx] copy-btn flex justify-center items-center text-white lg"
v-if="serviceData.way == 2"
@click="handleCall"
>
<text class="">拨打电话</text>
</view>
<view
class="rounded-[20rpx] copy-btn flex justify-center items-center text-white lg"
v-if="serviceData.way == 3"
@click="handleService"
>
<text class="">联系企业微信客服</text>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { toast } from '@/utils/util'
import { getKefuConfig } from '@/api/user'
import { getClient } from '@/utils/client'
import { serviceEnum } from '@/enums/appEnums'
import { useAppStore } from '@/stores/app'
interface serviceDataObj {
business_time: string
enterprise_id: string
kefu_link: string
name: string
phone: string
qr_code: string
remarks: string
service_phone: string
way: string | number
}
const { getImageUrl } = useAppStore()
const appStore = useAppStore()
const serviceData = ref<serviceDataObj>({
business_time: '',
enterprise_id: '',
kefu_link: '',
name: '',
phone: '',
qr_code: '',
remarks: '',
service_phone: '',
way: ''
})
// 获取客服信息
const getContactService = async (): Promise<void> => {
const res = await getKefuConfig()
serviceData.value = res[serviceEnum[getClient()]]
}
// 保存二维码
const saveImageQr = async (): Promise<void> => {
//#ifdef H5
toast('长按图片保存')
//#endif
//#ifndef H5
try {
const res = await uni.getImageInfo({ src: serviceData.value.qr_code })
try {
await uni.saveImageToPhotosAlbum({ filePath: res.path })
toast('保存成功')
} catch (e) {
const modelRes = await uni.showModal({
title: '图片保存失败',
content: '请确认是否已开启授权'
})
if (modelRes.confirm) uni.openSetting()
}
} catch (err) {
toast('请在小程序后台配置downloadFile')
}
//#endif
}
//拨打电话
const handleCall = () => {
if (!serviceData.value.service_phone) {
toast('请在后台配置客服电话号码')
return
}
uni.makePhoneCall({
phoneNumber: serviceData.value.service_phone,
success(res) {
console.log(res)
},
fail(err) {
console.log(err)
}
})
}
const handleService = () => {
// #ifdef MP-WEIXIN
wx.openCustomerServiceChat({
extInfo: { url: serviceData.value.kefu_link },
corpId: serviceData.value.enterprise_id,
success(res: any) {
console.log(res)
},
fail(err: any) {
console.log(err)
toast('请在后台配置企业微信客服')
}
})
// #endif
// #ifdef H5
if (!serviceData.value.kefu_link) {
toast('请在后台配置企业微信客服')
return
}
window.open(serviceData.value.kefu_link, '_self')
// #endif
}
getContactService()
</script>
<style lang="scss">
.header-image {
display: flex;
justify-content: center;
.circle {
border-radius: 50%;
border: 6rpx solid #ffffff;
}
}
.service {
width: 100vw;
height: 100vh;
background-image: url(../../static/images/service_bg.jpg);
background-size: 100% auto;
&-contain {
width: 620rpx;
// height: 750rpx;
margin: auto;
margin-bottom: 40rpx;
padding-bottom: 80rpx;
border-radius: 10px;
background-color: #ffffff;
.code {
border-radius: 20rpx;
padding-top: 60rpx;
}
.phone {
padding: 0 20rpx;
text-decoration: underline;
}
}
}
.copy-btn {
margin: 0 80rpx;
height: 88rpx;
// @include background_linear(90deg, 0, 100%);
background: #000722;
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<div></div>
</template>
<script setup lang="ts"></script>
<style></style>

View File

@@ -0,0 +1,134 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view
class="register min-h-full flex flex-col items-center px-[30rpx] pt-[60rpx] box-border"
>
<view class="w-full">
<view class="bg-white mb-[30rpx] rounded-[24rpx] px-[48rpx] py-[15rpx] flex items-center">
<u-image src="@/static/images/icon/icon_mobile.png" :height="28" :width="28" class="mr-[20rpx]"></u-image>
<u-input
class="flex-1"
v-model="formData.mobile"
:border="false"
placeholder="请输入手机号码"
placeholder-style="color: #999"
/>
</view>
<view class="bg-white mb-[30rpx] rounded-[24rpx] px-[48rpx] py-[15rpx] flex items-center">
<u-image src="@/static/images/icon/icon_password.png" :height="28" :width="28" class="mr-[20rpx]"></u-image>
<u-input
class="flex-1"
type="password"
v-model="formData.password"
placeholder="请输入新密码"
:border="false"
placeholder-style="color: #999"
/>
</view>
<view class="bg-white rounded-[24rpx] px-[48rpx] py-[15rpx] flex items-center items-center">
<u-image src="@/static/images/icon/icon_code.png" :height="28" :width="28" class="mr-[20rpx]"></u-image>
<u-input
class="flex-1"
v-model="formData.code"
placeholder="请输入验证码"
:border="false"
placeholder-style="color: #999"
/>
<view
class="text-muted leading-4 "
@click="sendSms"
>
<u-verification-code
ref="uCodeRef"
:seconds="60"
@change="codeChange"
change-text="x秒"
/>
<text :class="formData.mobile ? 'text-primary' : 'text-muted'">
{{ codeTips }}
</text>
</view>
</view>
<!-- <view class="bg-white mb-[30rpx] rounded-[24rpx] px-[48rpx] py-[15rpx] flex">
<u-icon class="mr-[20rpx]" name="lock" color="#999" size="28"></u-icon>
<u-input
class="flex-1"
type="password"
v-model="formData.password_confirm"
placeholder="再次输入新密码"
:border="false"
placeholder-style="color: #999"
/>
</view> -->
<view class="mt-[112rpx]">
<u-button type="primary" @click="handleConfirm" class="rounded-[24rpx]"> 确定 </u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { smsSend } from '@/api/app'
import { forgotPassword } from '@/api/user'
import { SMSEnum } from '@/enums/appEnums'
import { reactive, ref, shallowRef } from 'vue'
import { validateInput } from '@/utils/util'
const uCodeRef = shallowRef()
const codeTips = ref('')
const formData = reactive({
mobile: '',
code: '',
password: '',
// password_confirm: '',
})
const codeChange = (text: string) => {
codeTips.value = text
}
const sendSms = async () => {
if (!formData.mobile) return
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: SMSEnum.FIND_PASSWORD,
mobile: formData.mobile,
})
uni.$u.toast('发送成功')
uCodeRef.value?.start()
}
}
const handleConfirm = async () => {
if (!formData.mobile) return uni.$u.toast('请输入手机号码')
if (!formData.password) return uni.$u.toast('请输入密码')
// if (!formData.password_confirm) return uni.$u.toast('请输入确认密码')
// if (formData.password != formData.password_confirm) return uni.$u.toast('两次输入的密码不一致')
// if (!validateInput(formData.password) || !validateInput(formData.password_confirm))
if (!validateInput(formData.password))
return uni.$u.toast('密码应为6-20位数字+英文')
await forgotPassword(formData)
uni.$u.toast('操作成功')
setTimeout(()=> {
uni.navigateBack({delta: 2})
}, 500)
}
</script>
<style lang="scss">
page {
height: 100%;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view class="bg-white px-[20rpx] pt-[30rpx] pb-[20rpx] rounded-t-lg">
<view class="text-sm flex justify-between">
<text class="text-info">订单编号{{ sn }}</text>
<text class="text-[#E86016]">{{ is_settle ? '已结算' : '未结算' }}</text>
</view>
<view v-for="item in order_goods" :key="item.order_id" class="mt-2 flex items-center">
<view class="flex-none">
<u-image
:src="item.goods_image"
width="130rpx"
height="130rpx"
border-radius="16rpx"
></u-image>
</view>
<view class="flex flex-col justify-between ml-2">
<view class="line-clamp-1 font-bold text-lg">
{{ item.goods_name }}
</view>
<view class="text-info text-sm">服务时间{{ item.duration }}分钟</view>
<price font-weight="700" :content="item.goods_price"></price>
</view>
<view class="ml-auto text-info">x{{ total_num }}</view>
</view>
<view class="mt-2 text-info text-xs">服务完成时间{{ true_server_finish_time }}</view>
</view>
<!-- 未结算 -->
<view
class="bg-white px-[20rpx] pb-[30rpx] rounded-b-lg flex items-baseline justify-end"
v-if="type == 0"
>
<view class="text-xs">总金额</view>
<price
:content="order_amount"
font-weight="700"
main-size="40rpx"
sub-size="24rpx"
color="#333333"
/>
</view>
<!-- 已结算 -->
<view class="settle px-[20rpx] py-[30rpx] rounded-b-lg flex justify-between" v-else>
<view class="ml-[20rpx]">
<view class="text-xs mb-[4rpx]"> 总金额: </view>
<price
:content="settle_info.order_amount"
font-weight="700"
main-size="40rpx"
sub-size="24rpx"
color="#333333"
/>
</view>
<view class="ml-[20rpx]">
<view class="text-[#A2704A] text-xs mb-[4rpx]"> 结算佣金: </view>
<price
:content="settle_info.shop_settle"
font-weight="700"
main-size="40rpx"
sub-size="24rpx"
color="#703215"
/>
</view>
<view class="ml-[20rpx]">
<view class="text-[#A2704A] text-xs mb-[4rpx]"> 结算车费: </view>
<price
:content="settle_info.settle_car"
font-weight="700"
main-size="40rpx"
sub-size="24rpx"
color="#703215"
/>
</view>
</view>
</template>
<script lang="ts" setup>
import price from '@/components/price/price.vue'
defineProps<{
sn: string
order_status_desc: string
order_goods: any[]
order_amount: string
true_server_finish_time: string
order_status: number
settle_info: any
type: number
total_num: number
is_settle: number
}>()
</script>
<style lang="scss" scoped>
.settle {
background: linear-gradient(135.6deg, #fff2df 0%, #f4d7ac 100%);
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<view class="flex flex-col min-h-0 h-full">
<option-com
v-model:startDate="startDate"
v-model:endDate="endDate"
:orderAmount="orderAmount"
:type="type"
@change="paging?.reload()"
/>
<view class="h-full">
<z-paging
auto-show-back-to-top
:auto="i == index"
ref="paging"
v-model="dataList"
:data-key="i"
@query="queryList"
:fixed="false"
height="100%"
>
<view
v-for="(item, index) in dataList"
:key="index"
class="px-[30rpx] pb-3"
@click="goToOrder(item.id)"
>
<card
:sn="item.sn"
:order_status_desc="item.order_status_desc"
:order_goods="item.order_goods"
:order_amount="item.order_amount"
:true_server_finish_time="item.true_server_finish_time"
:order_status="item.order_status"
:settle_info="item.settle_info"
:type="type"
:total_num="item.total_num"
:is_settle="item.is_settle"
></card>
</view>
</z-paging>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick, shallowRef } from 'vue'
import cashOutIcon from '../../../static/images/cashOutIcon.png'
import card from './card.vue'
import optionCom from './option.vue'
import { getInComeLists } from '@/api/order'
const props = withDefaults(
defineProps<{
type: number
i: number
index: number
}>(),
{
type: 0
}
)
const paging = shallowRef<any>(null)
const dataList = ref<any>([])
const isFirst = ref<boolean>(true)
const startDate = ref('')
const endDate = ref('')
const orderAmount = ref(0)
watch(
() => props.index,
async () => {
await nextTick()
if (props.i == props.index && isFirst.value) {
isFirst.value = false
paging.value?.reload()
}
},
{ immediate: true }
)
// watch(
// () => [startDate.value, endDate.value],
// () => {
// paging.value?.reload()
// }
// )
// 去到订单详情页面
const goToOrder = (id: number) => {
uni.navigateTo({
url: `/packages/pages/order_detail/order_detail?id=${id}`
})
}
const queryList = async (page_no: number, page_size: number) => {
try {
const { lists, extend } = await getInComeLists({
type: props.type,
start_time: startDate.value,
end_time: endDate.value,
page_no,
page_size
})
orderAmount.value = extend.settle_amount
paging.value.complete(lists)
} catch (e) {
console.log('报错=>', e)
//TODO handle the exception
paging.value.complete(false)
}
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,76 @@
<template>
<view class="px-[30rpx] py-[28rpx]">
<view class="flex justify-between">
<view class="flex items-center" @click="showDataPicker">
<text>{{ startDate }} - {{ endDate }}</text>
<u-icon class="ml-2" name="arrow-down-fill" size="18"></u-icon>
</view>
<view>
<text>{{ type == 0 ? '未结算金额' : '总结算金额' }}</text>
<text class="font-bold text-lg">{{ orderAmount }}</text>
<text></text>
</view>
</view>
<u-calendar
v-model="show"
mode="range"
safe-area-inset-bottom
@change="change"
></u-calendar>
</view>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
const emits = defineEmits<{
(e: 'update:startDate', value: string): void
(e: 'update:endDate', value: string): void
(e: 'change'): void
}>()
const props = defineProps<{
startDate: string
endDate: string
orderAmount: number
type: number // 0: 未结算, 1: 已结算
}>()
const show = ref(false)
const startDate = ref('')
const endDate = ref('')
// 初始化日期
const today = new Date()
const oneMonthAgo = new Date()
oneMonthAgo.setMonth(today.getMonth() - 1)
// 格式化日期为 YYYY-MM-DD 格式
const formatDate = (date: Date): string => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0') // 月份从 0 开始,因此需要加 1
const day = String(date.getDate()).padStart(2, '0') // 确保日期是两位数
return `${year}-${month}-${day}`
}
// 打开日期选择
const showDataPicker = () => {
show.value = true
}
// 日期选择变化
const change = (value: any) => {
startDate.value = value.startDate
endDate.value = value.endDate
emits('update:startDate', value.startDate)
emits('update:endDate', value.endDate)
emits('change')
}
onMounted(() => {
startDate.value = formatDate(oneMonthAgo)
endDate.value = formatDate(today)
emits('update:startDate', startDate.value)
emits('update:endDate', endDate.value)
})
</script>

View File

@@ -0,0 +1,64 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view>
<tabs
:isScroll="false"
:current="current"
@change="handleChange"
height="80"
bar-width="60"
>
<tab v-for="(item, i) in tabList" :key="i" :name="item.name">
<!-- <optionCom></optionCom> -->
<view class="orderList" v-if="userStore.isLogin">
<list :type="item.type" :i="i" :index="current"></list>
</view>
<view v-if="!userStore.isLogin">
<no-login></no-login>
</view>
</tab>
</tabs>
<tabbar />
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import list from './components/list.vue'
import optionCom from './components/option.vue'
import noLogin from '@/components/no-login/no-login.vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const tabList = ref<any>([
{
name: '已结算',
type: 1
},
{
name: '未结算',
type: 0
}
])
const current = ref<number>(0)
const handleChange = (index: number) => {
console.log(index)
current.value = Number(index)
}
</script>
<style lang="scss" scoped>
.orderList {
height: calc(100vh - 100rpx - env(safe-area-inset-bottom));
}
</style>

View File

@@ -0,0 +1,53 @@
<template>
<view class="px-[40rpx] pb-[100rpx] py-[20rpx]">
<!-- #ifndef H5 -->
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="false"
:is-fixed="true"
:title="meta.title"
:custom-title="meta.title_type == 2"
:border-bottom="false"
:title-bold="true"
:background="{ background: `rgba(256,256, 256, ${percent})` }"
:title-color="percent > 0.5 ? '#000' : meta.text_color == 1 ? '#fff' : '#000'"
>
<template #default>
<view> </view>
</template>
<template #title>
<image
class="!h-[40rpx]"
:src="appStore.getImageUrl(meta.title_img)"
mode="heightFix"
></image>
</template>
</u-navbar>
</u-sticky>
<!-- #endif -->
<view class="flex items-center justify-between">
<view class="text-white font-bold text-[40rpx]">
<view>Hello,</view>
<view class="line-clamp-1">欢迎来到{{ appStore.config.shop_name || '-' }}</view>
</view>
<u-image
width="100"
height="100"
:src="appStore.config.shop_logo"
borderRadius="14"
class="flex-none"
></u-image>
</view>
</view>
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
const props = defineProps<{
meta: any
percent: number
}>()
</script>

View File

@@ -0,0 +1,171 @@
<template>
<view class="bg-[#F6F7F8] rounded-t-lg px-[30rpx] py-[40rpx] mt-[-20rpx]">
<view class="bg-white px-[20rpx] py-[28rpx] rounded-lg">
<view class="font-bold text-xl">店铺数据</view>
<view class="grid grid-cols-3 gap-[20rpx] mt-[28rpx]">
<view class="p-[20rpx] bg-[#5565EF] rounded-[12rpx]">
<u-image width="40" height="40" :src="visiter" borderRadius="14"></u-image>
<view class="text-white text-xs mt-1">访客数</view>
<view class="font-bold text-lg text-white">
{{ data?.shop_data.shop_visit || 0 }}
</view>
<view class="mt-1 text-xs">
<text class="text-white mr-1">较昨日</text>
<text :class="handleColor(data?.shop_data.order_income_compare || '0%')">
{{ data?.shop_data.visit_compare || '-' }}
</text>
</view>
</view>
<view class="p-[20rpx] bg-[#14C1DC] rounded-[12rpx]">
<u-image width="40" height="40" :src="order" borderRadius="14"></u-image>
<view class="text-white text-xs mt-1">订单数</view>
<view class="font-bold text-lg text-white">{{ data?.shop_data.order_num || 0 }}</view>
<view class="mt-1 text-xs">
<text class="text-white mr-1">较昨日</text>
<text :class="handleColor(data?.shop_data.order_income_compare || '0%')">
{{ data?.shop_data.order_num_compare || '-' }}
</text>
</view>
</view>
<view class="p-[20rpx] bg-[#0B66EF] rounded-[12rpx]">
<u-image width="40" height="40" :src="income" borderRadius="14"></u-image>
<view class="text-white text-xs mt-1">收入金额()</view>
<view class="font-bold text-lg text-white">
{{ data?.shop_data.order_income || 0}}
</view>
<view class="mt-1 text-xs">
<text class="text-white mr-1">较昨日</text>
<text :class="handleColor(data?.shop_data.order_income_compare || '0%')">
{{ data?.shop_data.order_income_compare || '-' }}
</text>
</view>
</view>
</view>
</view>
<view class="bg-white mt-2 px-[20rpx] py-[28rpx] rounded-lg">
<view class="font-bold text-xl">待服务单</view>
<view
style="background: linear-gradient(92deg, #0b8cef 0%, #2479f9 100%)"
class="grid grid-cols-3 px-[24rpx] py-[22rpx] rounded-lg mt-[28rpx]"
>
<view class="">
<view class="text-white text-xs">今天待服务</view>
<view class="font-bold text-2xl text-white mt-2">
{{ data?.wait_order_data.today_order_num || 0 }}
</view>
</view>
<view class="">
<view class="text-white text-xs">明天待服务</view>
<view class="font-bold text-2xl text-white mt-2">
{{ data?.wait_order_data.tomorrow_order_num || 0 }}
</view>
</view>
<view class="">
<view class="text-white text-xs">后天待服务</view>
<view class="font-bold text-2xl text-white mt-2">
{{ data?.wait_order_data.dayafter_order_num || 0 }}
</view>
</view>
</view>
</view>
<template v-if="userStore.isLogin">
<view class="bg-white mt-2 px-[20rpx] py-[28rpx] rounded-lg">
<view class="font-bold text-xl">订单数据</view>
<view class="grid grid-cols-3 gap-[20rpx] mt-[28rpx]">
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">待服务</view>
<view class="font-bold text-3xl mt-3">{{ data?.order_data.wait_server_num || 0 }}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">待接单</view>
<view class="font-bold text-3xl mt-3">{{ data?.order_data.wait_take_num || 0 }}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">已出发</view>
<view class="font-bold text-3xl mt-3">{{ data?.order_data.depart_num || 0 }}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">已到达</view>
<view class="font-bold text-3xl mt-3">{{ data?.order_data.arrive_num || 0 }}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">服务中</view>
<view class="font-bold text-3xl mt-3">{{ data?.order_data.start_server_num || 0}}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">已完成</view>
<view class="font-bold text-3xl mt-3">{{ data?.order_data.finish_server_num || 0}}</view>
</view>
</view>
</view>
<view class="bg-white mt-2 px-[20rpx] py-[28rpx] rounded-lg">
<view class="font-bold text-xl">技师数据</view>
<view class="grid grid-cols-3 gap-[28rpx] mt-[28rpx]">
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">待审核</view>
<view class="font-bold text-3xl mt-3">{{ data?.coach_data.wait_audtit_num || 0 }}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">上线技师</view>
<view class="font-bold text-3xl mt-3">{{ data?.coach_data.online_coach_num || 0 }}</view>
</view>
<view class="bg-[#F5F8FD] px-[20rpx] py-[28rpx] rounded-md">
<view class="text-[#666666] text-xs">下线技师</view>
<view class="font-bold text-3xl mt-3">{{ data?.coach_data.downline_coach_num || 0 }}</view>
</view>
</view>
</view>
</template>
<template v-if="!userStore.isLogin">
<view class="flex flex-col items-center mb-[100rpx]">
<u-image :width="240" :height="240" :src="empty" />
<view class="mt-2 sm text-muted">您当前未登录登录后可查看信息</view>
<u-button @click="toLogin" class="mt-4 w-[300rpx]" type="primary">去登录</u-button>
</view>
</template>
</view>
</template>
<script setup lang="ts">
import visiter from '@/static/images/index/visiter.png'
import order from '@/static/images/index/order.png'
import income from '@/static/images/index/income.png'
import empty from '@/static/images/empty.png'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const toLogin = () => {
uni.navigateTo({
url: '/pages/login/login'
})
}
const props = defineProps({
data: {
type: Object,
default: () => {}
}
})
//
const handleColor = (str: string) => {
// 去除字符串最后的%
let strNuber = str.slice(0, -1)
let num = Number(strNuber)
let classText = ''
if(num > 0)
classText = 'text-error'
else if (num == 0)
classText = 'text-white'
else
classText = 'text-success'
return classText
}
</script>

View File

@@ -0,0 +1,158 @@
<template>
<!-- modal:隐私授权弹窗-->
<view v-if="show" class="modal-box" @tap.stop>
<view class="dialog" @tap.stop>
<view class="title">隐私政策提示</view>
<view class="content">
欢迎使用{{
appStore.getWebsiteConfig.shop_name
}}小程序请您在使用前点击
<text
class="text-[#243245]"
hover-class="hover"
@click="openContract"
>
{{ name }}
</text>
并仔细阅读如您同意全部内容请点击同意开始使用我们的服务
</view>
<view class="btn-box">
<button
class="btn disagree"
hover-class="hover"
@click="disagreePrivacy"
>
不同意
</button>
<button
class="btn bg-primary text-white"
hover-class="hover"
id="agree-btn"
open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="agreePrivacy"
>
同意
</button>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
const name = ref<string>('')
const show = ref<boolean>(false)
interface PrivacyRes {
errMsg: string
privacyContractName: string
needAuthorization: boolean
}
if (wx.getPrivacySetting) {
wx.getPrivacySetting({
success(res: PrivacyRes) {
console.log(res)
name.value = res.privacyContractName
show.value = res.needAuthorization
}
})
}
const openContract = () => {
wx.openPrivacyContract({
success: () => {},
fail: () => {}
})
}
const disagreeHandle = () => {
// 用户点击拒绝后
show.value = false
}
const disagreePrivacy = () => {
uni.$u.toast('同意隐私政策后可继续使用')
// wx.exitMiniProgram()
}
const agreePrivacy = () => {
show.value = false
}
</script>
<style scoped>
.modal-box {
height: 100vh;
width: 100vw;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 99999;
}
.modal-box .dialog {
box-sizing: border-box;
position: absolute;
bottom: 0;
width: 100%;
padding: 40rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background: #ffffff;
border-radius: 20rpx 20rpx 0 0;
}
.modal-box .title {
text-align: center;
color: #333;
font-weight: bold;
font-size: 34rpx;
}
.modal-box .content {
display: block;
font-size: 28rpx;
color: #666;
margin-top: 20rpx;
text-align: justify;
line-height: 1.6;
padding: 10rpx 20rpx;
}
.modal-box .btn-box {
margin-top: 50rpx;
padding: 0 30rpx;
padding-bottom: 30rpx;
display: flex;
text-align: center;
}
.modal-box .btn::after {
border: none;
display: none;
}
.modal-box .btn-box .btn {
width: 50%;
height: 76rpx;
line-height: 76rpx;
margin: 0 10rpx;
padding: 0;
align-items: center;
justify-content: center;
border-radius: 60px;
font-size: 28rpx;
font-weight: 500;
}
.modal-box .disagree {
color: #0f0f0f;
background: #f5f5f5;
}
</style>

View File

@@ -0,0 +1,104 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor"/>
<!-- #endif -->
</page-meta>
<view class="index" :style="pageStyle">
<!-- 组件 -->
<Logo
:meta="decoratePage.meta"
:percent="percent"
></Logo>
<Main :data="shopData"></Main>
<!-- 返回顶部按钮 -->
<u-back-top
:scroll-top="scrollTop"
:top="100"
:customStyle="{
backgroundColor: '#FFF',
color: '#000',
boxShadow: '0px 3px 6px rgba(0, 0, 0, 0.1)'
}"
>
</u-back-top>
<!-- #ifdef MP -->
<!-- 微信小程序隐私弹窗 -->
<MpPrivacyPopup></MpPrivacyPopup>
<!-- #endif -->
<tabbar/>
</view>
</template>
<script setup lang="ts">
import {getDecoratePage} from '@/api/decorate'
import {getIndexData} from '@/api/shop'
import {onShow, onPageScroll} from '@dcloudio/uni-app'
import {computed, reactive, ref} from 'vue'
import {useAppStore} from '@/stores/app'
import Logo from './component/logoCom.vue'
import Main from './component/main.vue'
// #ifdef MP
import MpPrivacyPopup from './component/mp-privacy-popup.vue'
// #endif
const appStore = useAppStore()
const {getImageUrl} = useAppStore()
const decoratePage = reactive<{
data: any[]
meta: any
}>({
data: [],
meta: {}
})
const scrollTop = ref<number>(0)
const percent = ref<number>(0)
// 根页面样式
const pageStyle = computed(() => {
const {bg_type, bg_color, bg_image} = decoratePage?.meta ?? {}
return bg_type == 1 ?
{'background-color': bg_color} :
{'background-image': `url(${getImageUrl(bg_image)})`}
})
const shopData = ref<any>()
// 初始化页面数据
const initData = async () => {
//获取装修数据
const res = await getDecoratePage({
type: 1
})
decoratePage.data = JSON.parse(res?.data)
decoratePage.meta = JSON.parse(res?.meta)[0].content
uni.setNavigationBarTitle({
title: decoratePage.meta.title
})
//获取首页数据
shopData.value = await getIndexData()
}
onPageScroll((event: any) => {
scrollTop.value = event.scrollTop
const top = uni.upx2px(100)
percent.value = event.scrollTop / top > 1 ? 1 : event.scrollTop / top
})
onShow( () => { initData() })
</script>
<style lang="scss" scoped>
.index {
padding-bottom: 100rpx;
background-size: 100% auto;
background-repeat: no-repeat;
}
</style>

View File

@@ -0,0 +1,168 @@
<template>
<view>
<u-popup
v-model="showPopup"
mode="bottom"
border-radius="14"
:mask-close-able="false"
safe-area-inset-bottom
closeable
@close="emit('close')"
>
<view class="my-[30rpx] px-[30rpx] flex flex-col justify-center">
<view class="flex-1">
<view class="text-[36rpx]"> 绑定手机号 </view>
<!-- #ifndef MP-WEIXIN -->
<view class="pb-[100rpx] pt-[50rpx]">
<u-form borderBottom :label-width="150">
<u-form-item label="手机号" borderBottom>
<u-input
class="flex-1"
v-model="formData.mobile"
:border="false"
placeholder="请输入手机号码"
/>
</u-form-item>
<u-form-item label="验证码" borderBottom>
<u-input
class="flex-1"
v-model="formData.code"
placeholder="请输入验证码"
:border="false"
/>
<view
class="border-l border-solid border-0 border-light pl-3 text-muted leading-4 ml-3 w-[180rpx]"
@click="sendSms"
>
<u-verification-code
ref="uCodeRef"
:seconds="60"
@change="codeChange"
change-text="x秒"
/>
{{ codeTips }}
</view>
</u-form-item>
</u-form>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="py-[100rpx] flex flex-col items-center">
<view class="text-[36rpx]"> 绑定手机号 </view>
<view class="text-muted mt-[20rpx]">
绑定手机号以获取更好的体验
</view>
</view>
<!-- #endif -->
</view>
<view class="mt-[40rpx]">
<!-- #ifndef MP-WEIXIN -->
<u-button
type="primary"
shape="circle"
@click="handleConfirm"
>
确定
</u-button>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<u-button
type="primary"
shape="circle"
hover-class="none"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
>
<!-- <u-icon name="weixin-fill" size="40" />-->
<text class="ml-[10rpx]"> 授权手机号</text>
</u-button>
<!-- #endif -->
</view>
</view>
</u-popup>
</view>
</template>
<script lang="ts" setup>
import { userBindMobile, userMnpMobile } from '@/api/user'
import { smsSend } from '@/api/app'
import { SMSEnum } from '@/enums/appEnums'
import { computed, reactive, ref, shallowRef } from 'vue'
import { useUserStore } from '@/stores/user'
const props = defineProps({
show: {
type: Boolean
},
userInfo: {
type: Object
}
})
const emit = defineEmits<{
(event: 'update:show', show: boolean): void
(event: 'success'): void
(event: 'close'): void
}>()
const showPopup = computed({
get() {
return props.show
},
set(val) {
emit('update:show', val)
}
})
const uCodeRef = shallowRef()
const codeTips = ref('')
const userStore = useUserStore()
const codeChange = (text: string) => {
codeTips.value = text
}
const formData = reactive({
type: 'bind',
mobile: '',
code: ''
})
const sendSms = async () => {
if (!formData.mobile) return uni.$u.toast('请输入手机号码')
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: SMSEnum.BIND_MOBILE,
mobile: formData.mobile
})
uni.$u.toast('发送成功')
uCodeRef.value?.start()
}
}
const handleConfirm = async () => {
if (!formData.mobile) return uni.$u.toast('请输入手机号码')
if (!formData.code) return uni.$u.toast('请输入验证码')
await userBindMobile(formData, { token: props.userInfo?.token })
uni.$u.toast('绑定成功')
emit('success')
}
const getPhoneNumber = async (e: any): Promise<void> => {
const { encryptedData, iv, code } = e.detail
const data = {
code,
encrypted_data: encryptedData,
iv
}
if (encryptedData) {
await userMnpMobile(
{
...data
},
{ token: props.userInfo?.token }
)
uni.$u.toast('绑定成功')
emit('success')
}
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,265 @@
<template>
<view class="flex flex-col min-h-0 h-full">
<view
class="flex flex-col justify-end items-stretch"
style="height: 25%; padding-left: 32px; padding-bottom: 22px"
>
<view class="text-[40rpx] font-medium"> HELLO, </view>
<view class="mt-2 text-[40rpx] font-medium">
欢迎来到{{ config?.shop_name || '' }}
</view>
</view>
<view class="p-[30rpx] bg-white" style="height: 65%; border-radius: 24rpx 24rpx 0 0">
<view class="grid grid-cols-2 gap-x-2">
<button
v-if="true"
class="account-button"
:class="[isPasswordLogin ? 'active' : 'inactive']"
@click="changeLoginScene(MobileSceneEnum.PASSWORD)"
>
账号登录
</button>
<button
v-if="true"
class="code-button"
:class="[isCodeLogin ? 'active' : 'inactive']"
@click="changeLoginScene(MobileSceneEnum.CODE)"
>
验证码登录
</button>
</view>
<view class="mt-4 login-form">
<u-form :border-bottom="false">
<u-form-item :border-bottom="false">
<u-icon
class="mr-2"
:size="36"
name="/static/images/icon/icon_mobile.png"
/>
<u-input
class="flex-1"
v-model="formData.account"
:border="false"
placeholder="请输入手机号码"
/>
</u-form-item>
<u-form-item v-if="isCodeLogin" class="mt-4" :border-bottom="false">
<u-icon class="mr-2" :size="36" name="/static/images/icon/icon_code.png" />
<u-input
class="flex-1"
v-model="formData.code"
placeholder="请输入验证码"
:border="false"
/>
<view class="px-3 leading-4 w-[200rpx]" @click="sendSms">
<u-verification-code
ref="uCodeRef"
:seconds="60"
@change="codeChange"
change-text="x秒"
/>
<text class="text-primary">
{{ codeTips }}
</text>
</view>
</u-form-item>
<u-form-item v-if="isPasswordLogin" class="mt-4" :border-bottom="false">
<u-icon
class="mr-2"
:size="36"
name="/static/images/icon/icon_password.png"
/>
<u-input
class="flex-1"
v-model="formData.password"
type="password"
placeholder="请输入密码"
:border="false"
/>
<router-navigate to="/pages/forget_pwd/forget_pwd?type=2">
<view class="px-3 text-primary leading-4"> 忘记密码 </view>
</router-navigate>
</u-form-item>
</u-form>
</view>
<view class="flex-1 mt-4">
<u-button
type="primary"
:custom-style="{
flex: 1,
'font-weight': 500
}"
@click="handleLogin"
>
立即登录
</u-button>
<!-- <view class="py-4 text-muted text-center"> OR </view>-->
<!-- <u-button-->
<!-- :custom-style="{-->
<!-- flex: 1,-->
<!-- background: '#fff'-->
<!-- }"-->
<!-- @click="togglePhone"-->
<!-- >-->
<!-- 快捷登录-->
<!-- </u-button>-->
</view>
<view class="flex justify-center py-4 text-muted text-center">
没有账户
<view class="text-primary" @click.stop>
<router-navigate class="text-primary" to="/pages/register/register">
去注册
</router-navigate>
</view>
</view>
</view>
<view class="bg-white py-[30rpx] flex justify-center" style="height: 10%">
<agreement ref="agreementRef" />
</view>
</view>
</template>
<script lang="ts">
export default {
options: {
styleIsolation: 'shared'
}
}
</script>
<script setup lang="ts">
import { shallowRef, ref, reactive, computed } from 'vue'
import { smsSend } from '@/api/app'
import { SMSEnum } from '@/enums/appEnums'
defineProps<{
loading: boolean
config: any
}>()
const emit = defineEmits<{
(event: 'login'): void
(event: 'toggle'): void
}>()
enum MobileSceneEnum {
CODE = 2,
PASSWORD = 1
}
const agreementRef = shallowRef()
const uCodeRef = shallowRef()
const codeTips = ref('')
const formData = reactive({
scene: MobileSceneEnum.PASSWORD,
account: '',
code: '',
password: ''
})
const isCodeLogin = computed(() => formData.scene === MobileSceneEnum.CODE)
const isPasswordLogin = computed(() => formData.scene === MobileSceneEnum.PASSWORD)
const isValidMobile = computed(() => uni.$u.test.mobile(formData.account))
const changeLoginScene = (scene: MobileSceneEnum) => {
formData.scene = scene
}
const codeChange = (text: string) => {
codeTips.value = text
}
const sendSms = async () => {
if (!formData.account || !isValidMobile.value) return
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: SMSEnum.LOGIN,
mobile: formData.account
})
uni.$u.toast('发送成功')
uCodeRef.value?.start()
}
}
const handleLogin = () => {
if (!agreementRef.value?.checkAgreement()) {
return
}
if (!formData.account) return uni.$u.toast('请输入手机号码')
if (formData.scene == MobileSceneEnum.PASSWORD) {
if (!formData.password) return uni.$u.toast('请输入密码')
}
if (formData.scene == MobileSceneEnum.CODE) {
if (!formData.code) return uni.$u.toast('请输入验证码')
}
emit('login', formData)
}
const togglePhone = () => {
emit('toggle')
}
</script>
<style lang="scss" scoped>
.active {
@apply bg-primary text-white;
}
.inactive {
@apply bg-primary-light-9 text-primary;
}
.account-button {
flex: 1;
border: none;
margin: 0 0;
padding: 10rpx 0;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 28rpx;
font-weight: bold;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
border-radius: 16rpx 18rpx 36rpx 16rpx;
clip-path: polygon(0 0, 100% 0, 96% 100%, 0% 100%);
}
.account-button::after {
border: none;
}
.code-button {
flex: 1;
border: none;
margin: 0 0;
padding: 10rpx 0;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 28rpx;
font-weight: bold;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
border-radius: 36rpx 16rpx 16rpx 18rpx;
clip-path: polygon(5% 0, 100% 0, 100% 100%, 0% 100%);
}
.code-button::after {
border: none;
}
.login-form {
:deep(.u-form-item) {
background: #f6f6f6;
border-radius: 24rpx;
padding-left: 30rpx;
margin-bottom: 30rpx;
}
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<view>
<u-popup
v-model="showPopup"
mode="bottom"
border-radius="14"
:mask-close-able="false"
>
<view class="h-[1000rpx] p-[40rpx]">
<view class="flex items-center">
<image
class="w-[100rpx] h-[100rpx] rounded"
mode="heightFix"
:src="logo"
></image>
<text class="text-3xl ml-5 font-medium">{{ title }}</text>
</view>
<view class="mt-5 text-muted">
建议使用您的微信头像和昵称以便获得更好的体验
</view>
<view class="mt-[30rpx]">
<form @submit="handleSubmit">
<u-form-item required label="头像" :labelWidth="120">
<view class="flex-1">
<avatar-upload
v-model="avatar"
@upload="uploadImg"
></avatar-upload>
</view>
</u-form-item>
<u-form-item required label="昵称" :labelWidth="120">
<input
class="flex-1 h-[60rpx]"
name="nickname"
type="nickname"
placeholder="请输入昵称"
/>
</u-form-item>
<view class="mt-[80rpx]">
<button
class="bg-primary rounded-full text-white text-lg h-[80rpx] leading-[80rpx]"
hover-class="none"
form-type="submit"
>
确定
</button>
</view>
<view class="flex justify-center mt-[60rpx]">
<view class="text-muted" @click="showPopup = false">
暂不登录
</view>
</view>
</form>
</view>
</view>
</u-popup>
</view>
</template>
<script lang="ts" setup>
import { uploadImage } from '@/api/app'
import { computed, ref } from 'vue'
const props = defineProps({
show: {
type: Boolean
},
logo: {
type: String
},
title: {
type: String
},
userInfo: {
type: Object
}
})
const emit = defineEmits<{
(event: 'update:show', show: boolean): void
(event: 'update', value: any): void
}>()
const showPopup = computed({
get() {
return props.show
},
set(val) {
emit('update:show', val)
}
})
const avatar = ref()
const uploadImg = async (file: string) => {
uni.showLoading({
title: '正在上传中...'
})
try {
console.log('propsprops', props)
const res: any = await uploadImage(file, props.userInfo?.token)
console.log(res)
avatar.value = res.uri
console.log(res)
uni.hideLoading()
} catch (error) {
console.log(error)
uni.hideLoading()
uni.$u.toast('上传失败,请重试')
}
}
const handleSubmit = (e: any) => {
const { nickname } = e.detail.value
if (!avatar.value) return uni.$u.toast('请添加头像')
if (!nickname) return uni.$u.toast('请输入昵称')
emit('update', {
avatar: avatar.value,
nickname
})
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,80 @@
<template>
<view class="flex flex-col min-h-0 h-full">
<view
class="flex flex-col justify-center items-center"
style="height: 70%;"
>
<view>
<u-image
:src="config.shop_logo"
mode="widthFix"
height="172"
width="172"
border-radius="20"
/>
</view>
<view class="mt-4 text-2xl font-medium">
{{ config.shop_name }}
</view>
</view>
<view
class="px-[30rpx]"
style="height: 20%;"
>
<u-button
type="primary"
:custom-style="{
'flex': 1
}"
@click="handleLogin"
>
<!-- #ifndef MP -->
<u-icon name="weixin-fill" size="40" color="#fff" />
<text class="ml-[10rpx]"> 微信登录</text>
<!-- #endif -->
<!-- #ifdef MP -->
<text>用户一键登录</text>
<!-- #endif -->
</u-button>
<view class="py-4 text-muted text-center">
OR
</view>
<u-button
:custom-style="{
'flex': 1,
'background': '#fff'
}"
@click="togglePhone"
>
手机号登录
</u-button>
</view>
<view class="py-[30rpx] flex justify-center">
<agreement ref="agreementRef" />
</view>
</view>
</template>
<script setup lang="ts">
import { shallowRef } from 'vue'
defineProps<{
loading: boolean,
config: any
}>()
const emit = defineEmits<{
(event: 'login'): void
(event: 'toggle'): void
}>()
const agreementRef = shallowRef()
const handleLogin = () => {
if (agreementRef.value?.checkAgreement()) {
emit('login')
}
}
const togglePhone = () => {
emit('toggle')
}
</script>

View File

@@ -0,0 +1,336 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="login flex flex-col min-h-0 flex-1">
<!-- #ifndef H5 -->
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="true"
:is-fixed="true"
title="登录"
:border-bottom="false"
:title-bold="false"
:background="{ background: `rgba(256,256, 256, 0)` }"
:title-color="'#000'"
>
</u-navbar>
</u-sticky>
<!-- #endif -->
<!-- 微信登陆 -->
<!-- <wechat
v-if="LoginWayEnum.WECHAT == loginWay"
class="flex flex-col min-h-0 flex-1 h-full"
:loading="wechatLock"
:config="appStore.config"
@login="wxLoginLock"
@toggle="loginWay = LoginWayEnum.MOBILE"
/> -->
<!-- 手机/验证码登陆 -->
<mobile
class="flex flex-col min-h-0 flex-1 h-full"
:loading="mobileLock"
:config="appStore.config"
@login="mobileLoginLock"
@toggle="loginWay = LoginWayEnum.WECHAT"
/>
<!-- 绑定手机号 -->
<!-- <bind-mobile
v-model:show="showBindMobilePopup"
:userInfo="loginData"
@close="removeWxQuery"
@success="bindMobileSuccess"
/> -->
<!-- 更新用户信息 -->
<!-- <update-user-info
v-model:show="showLoginPopup"
:logo="appStore.config.shop_logo"
:title="appStore.config.shop_name"
:userInfo="loginData"
@update="handleUpdateUser"
/> -->
<!-- 审核状态 -->
<modal-popup
v-model:show="showModel"
:title="auditStatus == AuditStatusEnum.WAIT ? '审核中' : '审核不通过'"
>
<template #content>
<view :class="auditStatus == AuditStatusEnum.WAIT ? 'login-bg-wait' : 'login-bg-reject'">
<!-- 审核中 -->
<view v-if="auditStatus == AuditStatusEnum.WAIT ">
<view class="">
谢您对我们平台工作的支持我们将
</view>
<view class="">
在1~3个工作日内审核
</view>
</view>
<!-- 审核不通过 -->
<view v-if="auditStatus == AuditStatusEnum.REJECT">
<view class="">
抱歉您的申请未通过请完善您的商
</view>
<view class="">
家申请信息后再次提交
</view>
</view>
</view>
</template>
<template #footer>
<!-- 审核中 -->
<view v-if="auditStatus == AuditStatusEnum.WAIT ">
<u-button @click="showModel = false">确认</u-button>
</view>
<!-- 审核不通过 -->
<view v-if="auditStatus == AuditStatusEnum.REJECT">
<u-button type="primary" @click="toApply(auditStatus)">重新申请</u-button>
</view>
</template>
</modal-popup>
</view>
</template>
<script setup lang="ts">
import {
login,
mnpLogin as mnpLoginApi,
apiWechatAuthLogin as OALogin,
updateUser
} from '@/api/account'
import { BACK_URL } from '@/enums/cacheEnums'
import { useLockFn } from '@/hooks/useLockFn'
import { useAppStore } from '@/stores/app'
import { useUserStore } from '@/stores/user'
import cache from '@/utils/cache'
import { TOKEN_KEY } from '@/enums/constantEnums'
import { isWeixinClient } from '@/utils/client'
import { series } from '@/utils/util'
// #ifdef H5
import wechatOa, { UrlScene } from '@/utils/wechat'
// #endif
import { onLoad } from '@dcloudio/uni-app'
import { useRoute, useRouter } from 'uniapp-router-next'
import { ref, watch, nextTick } from 'vue'
import wechat from './component/wechat.vue'
import mobile from './component/mobile.vue'
import updateUserInfo from './component/update-user-info.vue'
import BindMobile from './component/bind-mobile.vue'
import { AuditStatusEnum } from '@/enums/auditEnum'
enum LoginWayEnum {
ACCOUNT = 1,
MOBILE = 2,
WECHAT = 3
}
const isWeixin = ref(true)
// #ifdef H5
isWeixin.value = isWeixinClient()
// #endif
const userStore = useUserStore()
const appStore = useAppStore()
const router = useRouter()
const route = useRoute()
const loginData = ref()
const showLoginPopup = ref(false)
const showBindMobilePopup = ref(false)
const loginWay = ref<string | number>(0)
const showModel = ref(false)
// 商家审核状态
const auditStatus = ref(AuditStatusEnum.NO)
// 去重新申请
const toApply = (auditStatus?: number) => {
showModel.value = false
uni.navigateTo({
url: `/packages/pages/apply/apply?auditStatus=${auditStatus}`
})
}
const checkIsBindMobile = async () => {
loginHandle()
}
const loginHandle = async () => {
const { token } = loginData.value
userStore.login(token)
userStore.getUser()
// #ifdef APP-PLUS
router.navigateBack()
// #endif
// #ifndef APP-PLUS
const pages = getCurrentPages()
if (pages.length > 1) {
const prevPage = pages[pages.length - 2]
await router.navigateBack()
// @ts-ignore
const { onLoad, options } = prevPage
// 刷新上一个页面
onLoad && onLoad(options)
} else if (cache.get(BACK_URL)) {
try {
router.redirectTo(cache.get(BACK_URL))
} finally {
router.switchTab(cache.get(BACK_URL))
}
} else {
router.reLaunch('/pages/index/index')
}
cache.remove(BACK_URL)
// #endif
}
const loginAfter = (() => {
const updateUsers = async () => {
if (loginData.value.is_new_user && !showLoginPopup.value) {
try {
await updateUser(
{
avatar: loginData.value.avatar,
nickname: loginData.value.nickname
},
{ token: loginData.value.token }
)
} catch (error) {}
} else if (showLoginPopup.value) {
return Promise.reject()
}
}
return series(updateUsers, checkIsBindMobile)
})()
const { lockFn: mobileLoginLock, isLock: mobileLock } = useLockFn(async (formData: any) => {
uni.showLoading({
title: '请稍后...'
})
try {
const data = await login(formData)
loginData.value = data
auditStatus.value = data.audit_status
console.log('auditStatus.value', auditStatus.value)
// 入驻审核通过直接登录
if(auditStatus.value === AuditStatusEnum.PASS){
await loginAfter()
uni.hideLoading()
}else if (auditStatus.value === AuditStatusEnum.WAIT || auditStatus.value === AuditStatusEnum.REJECT) {
// 入驻审核待审核、拒绝,不可登录,打开弹窗
// 使用临时token,申请页面需要用到
userStore.temToken = loginData.value.token
await nextTick()
console.log('userStore.temToken=>', userStore.temToken)
uni.hideLoading()
showModel.value = true
}else { // 未提交入驻不可登录,跳转申请入驻
// 使用临时token申请页面需要用到
userStore.temToken = loginData.value.token
await nextTick()
console.log('userStore.temToken=>', userStore.temToken)
uni.hideLoading()
toApply(auditStatus.value)
}
// await loginAfter()
// uni.hideLoading()
} catch (error: any) {
uni.hideLoading()
uni.$u.toast(error)
}
})
watch(
() => appStore.config,
(value) => {
// if (value.third_auth && value.wechat_auth == 1) {
// loginWay.value = LoginWayEnum.WECHAT
// return
// }
// if (value.login_way) {
// loginWay.value = value.login_way[0]
// return
// }
},
{
immediate: true
}
)
const removeWxQuery = () => {
const options = route.query
if (options.code && options.state) {
delete options.code
delete options.state
router.redirectTo({ path: route.path, query: options })
}
}
onLoad(async () => {
//#ifdef H5
const options = wechatOa.getAuthData()
try {
if (options.code && options.scene === UrlScene.LOGIN) {
uni.showLoading({
title: '请稍后...'
})
const data = await oaLogin(options)
if (data) {
loginData.value = data
await loginAfter()
}
}
} catch (error) {
removeWxQuery()
} finally {
uni.hideLoading()
//清除保存的授权数据
wechatOa.setAuthData()
}
//#endif
})
</script>
<style lang="scss">
page {
height: 100%;
}
.login {
height: 100%;
background-image: url('@/static/images/user/login_bg.png');
background-size: cover;
background-repeat: no-repeat;
}
.login-bg-wait {
height: 386rpx;
background-image: url('@/static/images/login/bg_audit_wait.png');
background-size: cover;
background-repeat: no-repeat;
}
.login-bg-reject {
height: 386rpx;
background-image: url('@/static/images/login/bg_audit_reject.png');
background-size: cover;
background-repeat: no-repeat;
}
</style>

View File

@@ -0,0 +1,65 @@
<template>
<z-paging
auto-show-back-to-top
:auto="i == index"
ref="paging"
v-model="dataList"
:data-key="i"
@query="queryList"
:fixed="false"
height="100%"
>
<block v-for="(newsItem, newsIndex) in dataList" :key="newsIndex">
<news-card :item="newsItem" :newsId="newsItem.id"></news-card>
</block>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick, shallowRef } from 'vue'
import { getArticleList } from '@/api/news'
const props = withDefaults(
defineProps<{
cid: number
i: number
index: number
}>(),
{
cid: 0
}
)
const paging = shallowRef<any>(null)
const dataList = ref([])
const isFirst = ref<boolean>(true)
watch(
() => props.index,
async () => {
await nextTick()
if (props.i == props.index && isFirst.value) {
isFirst.value = false
paging.value?.reload()
}
},
{ immediate: true }
)
const queryList = async (page_no, page_size) => {
try {
const { lists } = await getArticleList({
cid: props.cid,
page_no,
page_size
})
paging.value.complete(lists)
} catch (e) {
console.log('报错=>', e)
//TODO handle the exception
paging.value.complete(false)
}
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,85 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<!-- #ifndef H5 -->
<u-sticky
h5-nav-height="0"
bg-color="transparent"
>
<u-navbar
:is-back="false"
:is-fixed="false"
title="资讯"
:border-bottom="false"
:title-bold="true"
:title-color="$theme.navColor"
:background="{ background: $theme.navBgColor }"
>
</u-navbar>
</u-sticky>
<!-- #endif -->
<view class="news">
<!-- 搜索 -->
<navigator class="news-search px-[24rpx] py-[14rpx] bg-white" url="/pages/search/search">
<u-search placeholder="请输入关键词搜索" disabled :show-action="false"></u-search>
</navigator>
<!-- 内容 -->
<tabs
:current="current"
@change="handleChange"
height="80"
bar-width="60"
:barStyle="{ bottom: '0' }"
>
<tab v-for="(item, i) in tabList" :key="i" :name="item.name">
<view class="news-list pt-[20rpx]">
<news-list :cid="item.id" :i="i" :index="current"></news-list>
</view>
</tab>
</tabs>
<tabbar />
</view>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { onLoad, onShow, onReady } from '@dcloudio/uni-app'
import NewsList from './component/news-list.vue'
import { getArticleCate } from '@/api/news'
const tabList = ref<any>([])
const current = ref<number>(0)
const handleChange = (index: number) => {
console.log(index)
current.value = Number(index)
}
const getData = async () => {
const data = await getArticleCate()
tabList.value = [{ name: '全部', id: '' }].concat(data)
}
onLoad((options) => {
getData()
})
</script>
<style lang="scss">
.news {
&-search {
margin-bottom: 2rpx;
}
&-list {
height: calc(100vh - 272rpx - env(safe-area-inset-bottom));
}
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="news-detail bg-white">
<!-- 标题信心 -->
<view class="news-detail-header py-[20rpx] px-[30rpx]">
<view class="text-3xl font-medium">{{ newsData.title }}</view>
<view class="flex mt-[20rpx] text-xs">
<view class="mr-[40rpx]" v-if="newsData.author">作者: {{ newsData.author }}</view>
<view class="text-muted mr-[40rpx] flex-1">{{ newsData.create_time }}</view>
<view class="flex items-center text-muted flex-none">
<image
src="/static/images/icon/icon_visit.png"
class="w-[30rpx] h-[30rpx]"
></image>
<view class="ml-[10rpx]">{{ newsData.click }}</view>
</view>
</view>
</view>
<!-- 咨询内容 -->
<view class="news-detail-section bg-white p-[24rpx]">
<!-- 摘要 -->
<view class="summary p-[20rpx] text-base" v-if="newsData.abstract">
<text class="font-medium">摘要: </text> {{ newsData.abstract }}
</view>
<!-- 内容 -->
<view class="mt-[20rpx]">
<u-parse :html="newsData.content"></u-parse>
</view>
</view>
<view class="panel-btn flex items-center px-[34rpx]" @click="handleAddCollect(newsData.id)">
<u-icon
:name="newsData.collect ? 'star-fill' : 'star'"
size="40"
:color="newsData.collect ? '#F7BA47' : '#333'"
></u-icon>
<text class="ml-[10rpx]">收藏</text>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getArticleDetail, addCollect, cancelCollect } from '@/api/news'
const newsData = ref<any>({})
let newsId = ''
const getData = async (id) => {
newsData.value = await getArticleDetail({ id })
}
const handleAddCollect = async (id: number) => {
try {
if (newsData.value.collect) {
await cancelCollect({ id })
uni.$u.toast('已取消收藏')
} else {
await addCollect({ id })
uni.$u.toast('收藏成功')
}
getData(newsId)
} catch (e) {
//TODO handle the exception
}
}
onLoad((options: any) => {
newsId = options.id
getData(newsId)
})
</script>
<style lang="scss">
.news-detail {
height: 100%;
&-header {
border-bottom: 2rpx solid #f8f8f8;
}
&-section {
.summary {
border-radius: 12rpx;
background-color: #f7f7f7;
}
}
.panel-btn {
position: fixed;
right: 30rpx;
height: 80rpx;
bottom: 80rpx;
border-radius: 40rpx;
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.16);
}
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<z-paging
auto-show-back-to-top
:auto="i == index"
ref="paging"
v-model="dataList"
:data-key="i"
@query="queryList"
:fixed="false"
height="100%"
>
<block v-for="(item, index) in dataList" :key="index">
<orderCard :data="item"></orderCard>
</block>
</z-paging>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick, shallowRef } from 'vue'
import orderCard from '@/components/orderCard/index.vue'
import { getOrderList } from '@/api/app'
const props = withDefaults(
defineProps<{
cid: number
i: number
index: number
}>(),
{
cid: 0
}
)
const paging = shallowRef<any>(null)
const dataList = ref([])
const isFirst = ref<boolean>(true)
watch(
() => props.index,
async () => {
await nextTick()
if (props.i == props.index && isFirst.value) {
isFirst.value = false
paging.value?.reload()
}
},
{ immediate: true }
)
const queryList = async (page_no: any, page_size: any) => {
try {
const { lists } = await getOrderList({ page_no, page_size, order_status: props.index })
paging.value.complete(lists)
} catch (e) {
console.log('报错=>', e)
//TODO handle the exception
paging.value.complete(false)
}
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,74 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor"/>
<!-- #endif -->
</page-meta>
<view class="">
<tabs
:isScroll="false"
:current="current"
@change="handleChange"
height="80"
bar-width="60"
:barStyle="{ bottom: '0' }"
>
<tab v-for="(item, i) in tabList" :key="i" :name="item.name">
<view v-if="userStore.isLogin" class="orderList pt-[20rpx] px-[30rpx]">
<orderList :cid="item.id" :i="i+1" :index="current + 1"></orderList>
</view>
<view v-if="!userStore.isLogin">
<no-login></no-login>
</view>
</tab>
</tabs>
<tabbar />
</view>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { onLoad, onShow, onReady } from '@dcloudio/uni-app'
import orderList from './components/orderList.vue'
import noLogin from '@/components/no-login/no-login.vue'
import { getArticleCate } from '@/api/news'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const tabList = ref<any>([
{
name: '待接单'
},
{
name: '进行中'
},
{
name: '已完成'
},
{
name: '售后单'
},
])
const current = ref<number>(0)
const handleChange = (index: number) => {
console.log(index)
current.value = Number(index)
}
// const getData = async () => {
// const data = await getArticleCate()
// tabList.value = [{ name: '全部', id: '' }].concat(data)
// }
onLoad((options) => {
// getData()
})
</script>
<style lang="scss" scoped>
.orderList {
height: calc(100vh - 180rpx - env(safe-area-inset-bottom));
}
</style>

View File

@@ -0,0 +1,172 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<!-- 页面状态 -->
<page-status :status="status">
<template #error>
<u-empty text="订单不存在" mode="order"></u-empty>
</template>
<template #default>
<view class="payment-result p-[20rpx]">
<view class="result bg-white p-[20rpx] rounded-md">
<view class="flex flex-col items-center my-[40rpx]">
<!-- 支付状态图片 -->
<u-image
class="status-image"
:src="paymentStatus['image']"
width="100"
height="100"
shape="circle"
/>
<!-- 支付状态文字 -->
<text class="text-2xl font-medium mt-[20rpx]">{{
paymentStatus['text']
}}
</text>
<view class="text-3xl font-medium mt-[20rpx]">
¥ {{ orderInfo?.order_amount }}
</view>
</view>
<!-- 支付信息 -->
<view class="result-info">
<view class="result-info__item">
<text>订单编号</text>
<text>{{ orderInfo?.sn || '-' }}</text>
</view>
<view class="result-info__item">
<text>付款时间</text>
<text>{{ orderInfo.pay_time || '-' }}</text>
</view>
<view class="result-info__item">
<text>支付方式</text>
<template v-if="orderInfo.pay_status">
<text>{{ orderInfo?.pay_way_desc || '-' }}</text>
</template>
<template v-else>
<text>未支付</text>
</template>
</view>
</view>
</view>
<view class="mt-[40rpx]">
<view class="mb-[20rpx]">
<u-button
v-if="pageOptions.from == 'deposit'"
type="primary"
shape="circle"
hover-class="none"
@click="goDeposit"
>
继续充值
</u-button>
</view>
<view class="mb-[20rpx]">
<u-button
type="primary"
plain
shape="circle"
hover-class="none"
@click="goHome"
>
返回首页
</u-button>
</view>
</view>
</view>
</template>
</page-status>
</template>
<script lang="ts" setup>
import {getPayResult} from '@/api/pay'
import {PageStatusEnum} from '@/enums/appEnums'
import {onLoad} from '@dcloudio/uni-app'
import {computed, reactive, ref} from 'vue'
import {useRouter} from "uniapp-router-next";
const router = useRouter()
const mapStatus = {
succeed: {
text: '支付成功',
image: '/static/images/payment/icon_succeed.png'
},
waiting: {
text: '等待支付',
image: '/static/images/payment/icon_waiting.png'
}
}
const status = ref(PageStatusEnum['LOADING'])
const pageOptions = ref({
id: '',
from: ''
})
const orderInfo = reactive<any>({
order: {}
})
const paymentStatus = computed(() => {
const status = !!orderInfo.pay_status
return mapStatus[status ? 'succeed' : 'waiting']
})
const initPageData = () => {
return new Promise((resolve, reject) => {
getPayResult({
order_id: pageOptions.value.id,
from: pageOptions.value.from
})
.then((data) => {
Object.assign(orderInfo, data)
resolve(data)
})
.catch((err) => {
reject(err)
})
})
}
const goHome = () => {
router.reLaunch('/pages/index/index')
}
const goOrder = () => {
switch (pageOptions.value.from) {
case 'recharge':
router.navigateBack()
break
}
}
const goDeposit = () => {
router.redirectTo('/packages/pages/bond/bond')
}
onLoad(async (options: any) => {
try {
if (!options.id) throw new Error('订单不存在')
pageOptions.value = options
await initPageData()
status.value = PageStatusEnum['NORMAL']
} catch (err) {
console.log(err)
status.value = PageStatusEnum['ERROR']
}
})
</script>
<style lang="scss" scoped>
.result-info {
.result-info__item {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
}
}
</style>

View File

@@ -0,0 +1,197 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
<!-- #endif -->
</page-meta>
<view class="register flex flex-col min-h-0 flex-1">
<!-- #ifndef H5 -->
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="true"
:is-fixed="true"
title="注册"
:border-bottom="false"
:title-bold="false"
:background="{ background: `rgba(256,256, 256, 0)` }"
:title-color="'#000'"
>
</u-navbar>
</u-sticky>
<!-- #endif -->
<view
class="flex flex-col justify-end items-stretch"
style="height: 25%; padding-left: 32px; padding-bottom: 22px"
>
<view class="text-[40rpx] font-medium"> HELLO, </view>
<view class="mt-2 text-[40rpx] font-medium">
欢迎来到{{ appStore.config.shop_name }}
</view>
</view>
<view class="p-[30rpx] bg-white" style="height: 65%; border-radius: 24rpx 24rpx 0 0">
<view class="mt-4 register-form">
<u-form :border-bottom="false">
<u-form-item :border-bottom="false">
<u-icon
class="mr-2"
:size="36"
name="/static/images/icon/icon_mobile.png"
/>
<u-input
class="flex-1"
v-model="formData.account"
:border="false"
placeholder="请输入手机号码"
/>
</u-form-item>
<u-form-item :border-bottom="false">
<u-icon
class="mr-2"
:size="36"
name="/static/images/icon/icon_password.png"
/>
<u-input
class="flex-1"
v-model="formData.password"
:border="false"
placeholder="请输入密码"
/>
</u-form-item>
<u-form-item class="mt-4" :border-bottom="false">
<u-icon class="mr-2" :size="36" name="/static/images/icon/icon_code.png" />
<u-input
class="flex-1"
v-model="formData.code"
placeholder="请输入验证码"
:border="false"
/>
<view class="px-3 leading-4 w-[200rpx]" @click="sendSms">
<u-verification-code
ref="uCodeRef"
:seconds="60"
@change="codeChange"
change-text="x秒"
/>
<text class="text-primary">
{{ codeTips }}
</text>
</view>
</u-form-item>
</u-form>
</view>
<view class="flex-1 mt-4">
<u-button
type="primary"
:custom-style="{
flex: 1,
'font-weight': 500
}"
@click="accountRegister"
>
立即注册
</u-button>
</view>
<view class="flex justify-center py-4 text-muted text-center">
已有账号
<view class="text-primary" @click.stop="router.navigateBack()"> 去登录 </view>
</view>
</view>
<view class="bg-white py-[30rpx] flex justify-center" style="height: 10%">
<agreement ref="agreementRef" />
</view>
</view>
</template>
<script lang="ts">
export default {
options: {
styleIsolation: 'shared'
}
}
</script>
<script setup lang="ts">
import { register } from '@/api/account'
import { useAppStore } from '@/stores/app'
import { reactive, shallowRef, ref } from 'vue'
import { validateInput } from '@/utils/util'
import { useRouter } from 'uniapp-router-next'
import { smsSend } from '@/api/app'
import { SMSEnum } from '@/enums/appEnums'
const appStore = useAppStore()
const router = useRouter()
const agreementRef = shallowRef()
const uCodeRef = shallowRef()
const codeTips = ref('')
const formData = reactive({
account: '',
password: '',
code: ''
})
const codeChange = (text: string) => {
codeTips.value = text
}
const sendSms = async () => {
if (!formData.account) return uni.$u.toast('请输入手机号码')
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: SMSEnum.REGISTER,
mobile: formData.account
})
uni.$u.toast('发送成功')
uCodeRef.value?.start()
}
}
const accountRegister = async () => {
if (!agreementRef.value?.checkAgreement()) {
return
}
if (!formData.account) return uni.$u.toast('请输入账号')
if (!formData.password) return uni.$u.toast('请输入密码')
if (!formData.code) return uni.$u.toast('请输入验证码')
// if (formData.password != formData.password_confirm) return uni.$u.toast('两次输入的密码不一致')
if (!validateInput(formData.password)) return uni.$u.toast('密码应为6-20位数字+英文')
await register(formData)
uni.$u.toast('注册成功!')
setTimeout(() => {
uni.redirectTo({
// url: '/packages/pages/apply/apply'
url: '/pages/login/login'
})
}, 1000)
}
</script>
<style lang="scss">
page {
height: 100%;
}
.register {
height: 100%;
background-image: url('@/static/images/user/login_bg.png');
background-size: cover;
background-repeat: no-repeat;
}
</style>
<style lang="scss" scoped>
.register-form {
:deep(.u-form-item) {
background: #f6f6f6;
border-radius: 24rpx;
padding-left: 30rpx;
margin-bottom: 30rpx;
}
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="suggest bg-white">
<!-- 热门搜索 -->
<view class="hot" v-if="hot_search.status == 1 && searchData.length">
<view class="font-medium pl-[24rpx] pt-[26rpx] pb-[6rpx] text-lg">热门搜索</view>
<view class="w-full px-[24rpx]">
<block v-for="(hotItem, index) in searchData" :key="index">
<view
class="keyword truncate max-w-full"
@click="handleHistoreSearch(hotItem.name)"
>
{{ hotItem.name }}
</view>
</block>
</view>
</view>
<view
class="mx-[24rpx] my-[40rpx] border-b border-solid border-0 border-light"
v-if="hot_search.status == 1 && searchData.length && his_search.length"
></view>
<!-- 历史搜索 -->
<view class="history" v-if="his_search.length">
<view class="flex justify-between px-[24rpx] pb-[6rpx] pt-[26rpx]">
<view class="text-lg font-medium">历史搜索</view>
<view class="text-xs text-muted" @click="() => emit('clear')">清空</view>
</view>
<view class="w-full px-[24rpx]">
<block v-for="(hisItem, index) in his_search" :key="index">
<view class="keyword truncate" @click="handleHistoreSearch(hisItem)">{{
hisItem
}}</view>
</block>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
const emit = defineEmits<{
(event: 'search', value: string): void
(event: 'clear', value: void): void
}>()
const props = withDefaults(
defineProps<{
hot_search?: {
data: any[]
status: number
}
his_search?: string[]
}>(),
{
hot_search: () => ({
data: [],
status: 0
}),
his_search: () => []
}
)
const searchData = computed(() => {
return props.hot_search.data.filter((item) => item.name)
})
const handleHistoreSearch = (text: string) => {
emit('search', text)
}
</script>
<style lang="scss" scoped>
.suggest {
height: 100%;
.keyword {
display: inline-block;
margin: 24rpx 16rpx 0 0;
padding: 8rpx 24rpx;
border-radius: 26rpx;
background-color: #f4f4f4;
}
}
</style>

View File

@@ -0,0 +1,142 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="search">
<!-- 搜索框 -->
<view class="px-[24rpx] py-[14rpx] bg-white">
<u-search
v-model="keyword"
placeholder="请输入关键词搜索"
height="72"
@search="handleSearch"
@custom="handleSearch"
@clear="search.searching = false"
></u-search>
</view>
<!-- 搜索 -->
<view class="search-content">
<!-- -->
<suggest
v-show="!search.searching"
@search="handleSearch"
@clear="handleClear"
:hot_search="search.hot_search"
:his_search="search.his_search"
></suggest>
<!-- -->
<view class="search-content-s pt-[20rpx]" v-show="search.searching">
<z-paging
ref="paging"
v-model="search.result"
@query="queryList"
:fixed="false"
height="100%"
>
<block v-for="item in search.result" :key="item.id">
<news-card :item="item" :newsId="item.id"></news-card>
</block>
</z-paging>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive, shallowRef } from 'vue'
import Suggest from './component/suggest.vue'
import { HISTORY } from '@/enums/constantEnums'
import { getHotSearch } from '@/api/shop'
import cache from '@/utils/cache'
import { getArticleList } from '@/api/news'
interface Search {
hot_search: {
data: any[]
status: number
}
his_search: string[]
result: any
searching: boolean
}
const search = reactive<Search>({
hot_search: {
data: [],
status: 1
},
his_search: [],
result: [],
searching: false
})
const keyword = ref<string>('')
const paging = shallowRef()
const handleSearch = (value: string) => {
keyword.value = value
if (keyword.value) {
if (!search.his_search.includes(keyword.value)) {
search.his_search.unshift(keyword.value)
cache.set(HISTORY, search.his_search)
}
}
paging.value.reload()
search.searching = true
}
const getHotSearchFunc = async () => {
try {
search.hot_search = await getHotSearch()
} catch (e) {
//TODO handle the exception
console.log('获取热门搜索失败=>', e)
}
}
const handleClear = async (): Promise<void> => {
const resModel: any = await uni.showModal({
title: '温馨提示',
content: '是否清空历史记录?'
})
if (resModel.confirm) {
cache.set(HISTORY, '')
search.his_search = []
}
}
const queryList = async (page_no: number, page_size: number) => {
try {
const { lists } = await getArticleList({
keyword: keyword.value,
page_no,
page_size
})
paging.value.complete(lists)
} catch (e) {
console.log('报错=>', e)
//TODO handle the exception
paging.value.complete(false)
}
}
getHotSearchFunc()
search.his_search = cache.get(HISTORY) || []
</script>
<style lang="scss" scoped>
.search {
&-content {
height: calc(100vh - 46px - env(safe-area-inset-bottom));
&-s {
height: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<view class="p-[15rpx] rounded-lg bg-white mt-[-15rpx] mx-[20rpx]">
<view class="flex items-center justify-between">
<view class="flex flex-col items-center justify-center flex-1" @click="toMyMaster">
<text class="font-bold text-lg">{{ userStore?.userInfo?.coach_count || '0' }}</text>
<text class="text-info mt-1">我的技师</text>
</view>
<view
class="flex flex-col items-center justify-center flex-1"
@click="toPlatformProject"
>
<text class="font-bold text-lg">{{ userStore?.userInfo?.goods_count || '0' }}</text>
<text class="text-info mt-1">平台项目</text>
</view>
<view class="flex flex-col items-center justify-center flex-1" @click="toMyProject">
<text class="font-bold text-lg">{{
userStore?.userInfo?.shop_goods_count || '-'
}}</text>
<text class="text-info mt-1">店铺项目</text>
</view>
</view>
<view class="flex mt-4">
<view class="flex-1 p-[24rpx] rounded-lg bg-[#d9e6fd] text-[#4A65A3]" @click="toBond">
<view class="font-bold text-sm">{{ userStore?.userInfo?.deposit || '0' }}</view>
<view class="mt-1">我的保证金 ></view>
</view>
<view
class="flex-1 p-[24rpx] rounded-lg bg-[#bcecfb] ml-2 text-[#2DA9BC]"
@click="toBalance"
>
<view class="font-bold text-sm">{{ userStore?.userInfo?.money || '-' }}</view>
<view class="mt-1">我的账户余额 ></view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
//跳转至我的技师列表
const toMyMaster = () => {
uni.navigateTo({
url: '/packages/pages/my_master/my_master'
})
}
//跳转至保证金页面
const toBond = () => {
uni.navigateTo({
url: '/packages/pages/bond/bond'
})
}
//跳转至余额
const toBalance = () => {
uni.navigateTo({
url: '/packages/pages/balance/balance'
})
}
//跳转至平台项目
const toPlatformProject = () => {
uni.navigateTo({
url: '/packages/pages/platform_project/platform_project'
})
}
//跳转至我的项目
const toMyProject = () => {
uni.navigateTo({
url: '/packages/pages/my_project/my_project'
})
}
</script>

View File

@@ -0,0 +1,119 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="user h-full bg-no-repeat" :style="pageStyle">
<!-- #ifndef H5 -->
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="false"
:is-fixed="true"
:title="state.meta.title"
:custom-title="state.meta.title_type == 2"
:border-bottom="false"
:title-bold="true"
:background="{ background: `rgba(256,256, 256, 0)` }"
:title-color="state.meta.text_color == 1 ? '#fff' : '#000'"
>
<template #title>
<image
class="!h-[54rpx]"
:src="state.meta.title_img"
mode="widthFix"
></image>
</template>
</u-navbar>
</u-sticky>
<!-- #endif -->
<view v-for="(item, index) in state.pages" :key="index">
<template v-if="item.name == 'shop-user-info'">
<w-user-info
:content="item.content"
:styles="item.styles"
:user-info="userInfo"
:is-login="userStore.isLogin"
/>
</template>
<template v-if="item.name == 'shop-user-service'">
<w-user-service
:content="item.content"
:styles="item.styles"
/>
</template>
<template v-if="item.name == 'shop-user-banner'">
<w-user-banner
:content="item.content"
:styles="item.styles"
/>
</template>
</view>
<tabbar/>
</view>
</template>
<script setup lang="ts">
import {getDecoratePage} from '@/api/decorate'
import {useUserStore} from '@/stores/user'
import {useAppStore} from '@/stores/app'
import {onShow} from '@dcloudio/uni-app'
import {reactive, ref, computed} from 'vue'
const userStore = useUserStore()
const {getImageUrl} = useAppStore()
let userInfo = ref<any>({
nickname: '',
is_staff: 0,
avatar: '',
mobile: '',
service_num: 0,
wait_pay_num: 0,
staff_wait_num: 0,
finish_num: 0,
appoint_num: 0,
decorate_page: []
})
const state = reactive<{
pages: any[]
meta: any
}>({
pages: [],
meta: {}
})
userInfo = computed(() => userStore.userInfo)
// 根页面样式
const pageStyle = computed(() => {
const {bg_type, bg_color, bg_image} = state.meta ?? {}
return bg_type == 1 ?
{'background-color': bg_color} :
{'background-image': `url(${getImageUrl(bg_image)})`}
})
const initUserInfo = async (): Promise<void> => {
await userStore.getUser()
userInfo.value = userStore.userInfo
}
const getData = async () => {
const data = await getDecoratePage({type: 2})
state.meta = JSON.parse(data.meta)[0].content
state.pages = JSON.parse(data.data)
console.log(state.meta)
uni.setNavigationBarTitle({
title: state.meta.title
})
}
onShow(() => {
initUserInfo()
getData()
})
</script>
<style></style>

View File

@@ -0,0 +1,262 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<!-- Main Start -->
<view class="user-data p-[30rpx]">
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="true"
:is-fixed="true"
title="个人资料"
:border-bottom="false"
:title-bold="false"
:background="{ background: `rgba(256,256, 256, 0)` }"
:title-color="'#000'"
>
</u-navbar>
</u-sticky>
<!-- 头像 -->
<view
class="item rounded-t-[20rpx] mt-[20rpx] btn-border flex flex-1 justify-between items-center"
>
<view class="label">头像</view>
<u-image :src="userInfo?.logo" width="80" height="80" border-radius="50%"></u-image>
</view>
<!-- 昵称 -->
<view
class="item flex flex-1 justify-between items-center"
>
<view class="label">姓名</view>
<view class="content text-right">{{ userInfo?.name }}</view>
</view>
<!-- 手机号 -->
<view class="item flex flex-1 justify-between items-center">
<view class="label">手机号</view>
<!-- #ifdef MP-WEIXIN -->
<!-- <view class="content">
{{ userInfo?.mobile == '' ? '未绑定手机号' : userInfo?.mobile }}
</view>
<u-button
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
type="primary"
shape="circle"
size="mini"
:plain="true"
>
{{ userInfo?.mobile == '' ? '绑定手机号' : '更换手机号' }}
</u-button> -->
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<!-- <view class="content" @click="showMobilePop = true">
{{ userInfo?.mobile == '' ? '未绑定手机号' : userInfo?.mobile }}
</view>
<view @click="showMobilePop = true">
<u-icon name="arrow-right" size="22" color="#666"></u-icon>
</view> -->
<!-- #endif -->
<view class="content" @click="toBindMobile">
{{ userInfo?.mobile }}
</view>
<view @click="toBindMobile">
<u-icon name="arrow-right" size="22" color="#666"></u-icon>
</view>
</view>
<!-- 入驻时间 -->
<view
class="item flex flex-1 justify-between items-center rounded-b-[20rpx]"
>
<view class="label">入驻时间</view>
<view class="content">{{ userInfo?.create_time }}</view>
</view>
</view>
<!-- 账号修改组件 -->
<!-- <u-popup v-model="showMobilePop" :closeable="true" mode="center" border-radius="20">
<view class="px-[50rpx] py-[40rpx] bg-white" style="width: 85vw">
<view class="mb-[70rpx] text-xl text-center">{{ userInfo?.mobile == '' ? '绑定手机号' : '更换手机号' }}</view>
<u-form-item borderBottom>
<u-input
class="flex-1"
v-model="newMobile"
placeholder="请输入新的手机号码"
:border="false"
/>
</u-form-item>
<u-form-item borderBottom>
<u-input
class="flex-1"
v-model="mobileCode"
placeholder="请输入验证码"
:border="false"
/>
<view
class="border-l border-solid border-0 border-light pl-3 text-muted leading-4 ml-3 w-[180rpx]"
@click="sendSms"
>
<u-verification-code
ref="uCodeRef"
:seconds="60"
@change="codeChange"
change-text="x秒"
/>
{{ codeTips }}
</view>
</u-form-item>
<view class="mt-[80rpx]">
<u-button @click="changeCodeMobile" type="primary" shape="circle"> 确定 </u-button>
</view>
</view>
</u-popup> -->
</template>
<script lang="ts" setup>
import { ref, shallowRef } from 'vue'
import { onShow, onUnload } from '@dcloudio/uni-app'
import { getUserInfo, userEdit, userBindMobile, userMnpMobile } from '@/api/user'
import { smsSend } from '@/api/app'
import { FieldType, SMSEnum } from '@/enums/appEnums'
// 用户信息
const userInfo = ref<any>({})
// 用户信息的枚举
const fieldType = ref(FieldType.NONE)
//显示性别选择弹窗
const showPicker = ref<boolean | null>(false)
// 显示手机号验证码调整弹窗 非小程序才需要
const showMobilePop = ref<boolean | null>(false)
//新的手机号码
const newMobile = ref<string>('')
//修改手机验证码
const mobileCode = ref<string>('')
const codeTips = ref('')
const uCodeRef = shallowRef()
// 跳转更换手机号码页面
const toBindMobile = () => {
uni.navigateTo({
url: `/pages/bind_mobile/bind_mobile?mobile=${userInfo.value.mobile}`
})
}
// 获取用户信息
const getUser = async (): Promise<void> => {
userInfo.value = await getUserInfo()
}
// 获取验证码显示字段
const codeChange = (text: string) => {
codeTips.value = text
}
// 发送验证码
const sendSms = async () => {
if (!newMobile.value) return uni.$u.toast('请输入新的手机号码')
if (uCodeRef.value?.canGetCode) {
await smsSend({
scene: userInfo.value.mobile ? SMSEnum.CHANGE_MOBILE : SMSEnum.BIND_MOBILE,
mobile: newMobile.value
})
uni.$u.toast('发送成功')
uCodeRef.value?.start()
}
}
// 验证码修改手机号-非微信小程序
const changeCodeMobile = async () => {
await userBindMobile({
type: userInfo.value.mobile ? 'change' : 'bind',
mobile: newMobile.value,
code: mobileCode.value
})
uni.$u.toast('操作成功')
showMobilePop.value = false
getUser()
}
// 修改用户信息
const setUserInfoFun = async (value: string): Promise<void> => {
await userEdit({
field: fieldType.value,
value: value
})
uni.$u.toast('操作成功')
getUser()
}
// 显示修改用户性别弹窗
const changeSex = () => {
showPicker.value = true
fieldType.value = FieldType.SEX
}
// 微信小程序 绑定||修改用户手机号
const getPhoneNumber = async (e): Promise<void> => {
const { encryptedData, iv, code } = e.detail
const data = {
code,
encrypted_data: encryptedData,
iv
}
if (encryptedData) {
await userMnpMobile({
...data
})
uni.$u.toast('操作成功')
getUser()
}
}
onShow(async () => {
getUser()
})
</script>
<style lang="scss" scoped>
.header {
width: 100%;
image {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
}
}
.item {
margin-top: 2rpx;
padding: 40rpx 20rpx;
background-color: #ffffff;
.label {
width: 150rpx;
@apply text-main text-base font-medium;
}
.content {
flex: 1;
width: 80%;
padding-right: 12rpx;
@apply text-content text-base font-medium text-right;
}
}
</style>

View File

@@ -0,0 +1,257 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
<!-- #endif -->
</page-meta>
<view class="user-set">
<navigator :url="`/pages/user_data/user_data`">
<view class="item flex bg-white mt-[20rpx]">
<u-avatar :src="userInfo.logo" shape="square" :size="100"></u-avatar>
<view class="ml-[20rpx] flex flex-1 justify-between items-center">
<view>
<view class="mb-[15rpx] text-xl font-medium">{{ userInfo.name }}</view>
<view class="text-content text-xs">账号{{ userInfo.sn }}</view>
</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
</view>
</navigator>
<view
class="item bg-white mt-[20rpx] btn-border flex flex-1 justify-between"
@click="handlePwd"
>
<view class="">登录密码</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
<!-- #ifdef H5 || MP-WEIXIN -->
<view
v-if="isWeixin"
class="item bg-white flex flex-1 justify-between"
@click="bindWechatLock"
>
<view class="">绑定微信</view>
<view class="flex justify-between">
<view class="text-muted mr-[20rpx]">
{{ userInfo.is_auth ? '已绑定' : '未绑定' }}
</view>
<u-icon v-if="userInfo.is_auth == 0" name="arrow-right" color="#666"></u-icon>
</view>
</view>
<!-- #endif -->
<navigator url="/packages/pages/entry_info/entry_info">
<view class="item bg-white btn-border flex flex-1 justify-between mt-[20rpx]">
<view class="">入驻信息</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
</navigator>
<navigator url="/packages/pages/shop_info/shop_info">
<view class="item bg-white btn-border flex flex-1 justify-between">
<view class="">店铺资料</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
</navigator>
<navigator :url="`/pages/agreement/agreement?type=${AgreementEnum.PRIVACY}`">
<view class="item bg-white btn-border flex flex-1 justify-between">
<view class="">隐私政策</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
</navigator>
<navigator :url="`/pages/agreement/agreement?type=${AgreementEnum.SERVICE}`">
<view class="item bg-white btn-border flex flex-1 justify-between">
<view class="">服务协议</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
</navigator>
<navigator url="/pages/as_us/as_us">
<view class="item bg-white flex flex-1 justify-between">
<view class="">关于我们</view>
<view class="flex justify-between">
<view class="text-muted mr-[20rpx]">
{{ appStore.config.version }}
</view>
<u-icon name="arrow-right" color="#666"></u-icon>
</view>
</view>
</navigator>
<view class="mt-[60rpx] mx-[26rpx]">
<u-button type="error" @click="showLogout = true"> 退出登录</u-button>
</view>
<u-action-sheet
:list="list"
v-model="show"
@click="handleClick"
:safe-area-inset-bottom="true"
></u-action-sheet>
<u-popup
class="pay-popup"
v-model="showLogout"
round
mode="center"
borderRadius="10"
:maskCloseAble="false"
>
<view class="content bg-white w-[560rpx] p-[40rpx]">
<view class="text-2xl font-medium text-center"> 温馨提示</view>
<view class="pt-[30rpx] pb-[40rpx]">
<view> 是否清除当前登录信息退出登录</view>
</view>
<view class="flex">
<view class="flex-1 mr-[20rpx]">
<u-button
shape="circle"
type="primary"
plain
size="medium"
hover-class="none"
:customStyle="{ width: '100%' }"
@click="showLogout = false"
>
取消
</u-button>
</view>
<view class="flex-1">
<u-button
shape="circle"
type="primary"
size="medium"
hover-class="none"
:customStyle="{ width: '100%' }"
@click="logoutHandle"
>
确认
</u-button>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import { useAppStore } from '@/stores/app'
import { useUserStore } from '@/stores/user'
import { AgreementEnum } from '@/enums/agreementEnums'
import { isWeixinClient } from '@/utils/client'
import { mnpAuthBind, oaAuthBind } from '@/api/account'
import { useLockFn } from '@/hooks/useLockFn'
import { useRouter } from 'uniapp-router-next'
// #ifdef H5
import wechatOa from '@/utils/wechat'
// #endif
const router = useRouter()
const appStore = useAppStore()
const userStore = useUserStore()
const userInfo = computed(() => userStore.userInfo)
const list = ref([
{
text: '修改密码'
},
{
text: '忘记密码'
}
])
const isWeixin = ref(true)
// #ifdef H5
isWeixin.value = isWeixinClient()
// #endif
const show = ref(false)
const showLogout = ref(false)
// 修改/忘记密码
const handleClick = (index: number) => {
switch (index) {
case 0:
router.navigateTo('/pages/change_password/change_password')
break
case 1:
router.navigateTo('/pages/forget_pwd/forget_pwd')
break
}
}
const handlePwd = () => {
if (!userInfo.value.has_password)
return router.navigateTo('/pages/change_password/change_password?type=set')
show.value = true
}
// 退出登录
const logoutHandle = () => {
userStore.logout()
router.redirectTo('/pages/login/login')
}
const bindWechat = async () => {
if (userInfo.value.is_auth) return
try {
uni.showLoading({
title: '请稍后...'
})
// #ifdef MP-WEIXIN
const { code }: any = await uni.login({
provider: 'weixin'
})
await mnpAuthBind({
code: code
})
//#endif
// #ifdef H5
if (isWeixin.value) {
wechatOa.getUrl()
}
// #endif
await userStore.getUser()
uni.hideLoading()
} catch (e) {
uni.hideLoading()
uni.$u.toast(e)
}
}
const { lockFn: bindWechatLock } = useLockFn(bindWechat)
onShow(() => {
userStore.getUser()
})
onLoad(async (options) => {
// #ifdef H5
const { code } = options
if (!isWeixin.value) return
if (code) {
uni.showLoading({
title: '请稍后...'
})
try {
await oaAuthBind({ code })
await userStore.getUser()
} catch (error) {}
//用于清空code
router.redirectTo('/pages/user_set/user_set')
}
// #endif
})
</script>
<style lang="scss" scoped>
.user-set {
.item {
padding: 30rpx;
}
.btn-border {
border-bottom: 2rpx solid #f8f8f8;
}
}
</style>

View File

@@ -0,0 +1,269 @@
<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar
:front-color="$theme.navColor"
:background-color="$theme.navBgColor"
/>
<!-- #endif -->
</page-meta>
<view class="user-set flex flex-col min-h-0 h-full">
<u-sticky h5-nav-height="0" bg-color="transparent">
<u-navbar
:is-back="true"
:is-fixed="true"
title="个人设置"
:border-bottom="false"
:title-bold="false"
:background="{ background: `rgba(256,256, 256, 0)` }"
:title-color="'#000'"
>
</u-navbar>
</u-sticky>
<view class="flex-1 px-[30rpx]">
<navigator :url="`/pages/user_data/user_data`" v-if="userInfo.audit_status != null">
<view class="item flex bg-white rounded-[20rpx] my-[20rpx]">
<u-avatar :src="userInfo.logo" shape="square" :size="100"></u-avatar>
<view class="ml-[20rpx] flex flex-1 justify-between items-center">
<view>
<view class="mb-[15rpx] text-xl font-medium">{{ userInfo.name }}</view>
<view class="text-content text-xs">工号{{ userInfo.sn }}</view>
</view>
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
</view>
</navigator>
<view
class="item bg-white rounded-[24rpx] mt-[20rpx] btn-border flex flex-1 justify-between"
@click="handlePwd"
>
<view class="">登录密码</view>
<view class="flex justify-between">
<!-- <view class="text-muted mr-[20rpx]">
{{ userInfo.has_password ? '已设置' : '未设置' }}
</view> -->
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
</view>
<!-- #ifdef H5 || MP-WEIXIN -->
<!-- <view-->
<!-- class="item bg-white rounded-b-[20rpx] flex flex-1 justify-between"-->
<!-- v-if="isWeixin"-->
<!-- @click="bindWechatLock"-->
<!-- >-->
<!-- <view class="">绑定微信</view>-->
<!-- <view class="flex justify-between">-->
<!-- <view class="text-muted mr-[20rpx]">-->
<!-- {{ userInfo.is_auth ? '已绑定' : '未绑定' }}-->
<!-- </view>-->
<!-- <u-icon v-if="userInfo.is_auth == 0" name="arrow-right" color="#999"></u-icon>-->
<!-- </view>-->
<!-- </view>-->
<!-- #endif -->
<view
v-if="userInfo.audit_status !== null"
class="item bg-white rounded-t-[20rpx] mt-[20rpx] btn-border flex flex-1 justify-between"
@click="router.navigate('/packages/pages/shop_info/shop_info')"
>
<view class="">店铺资料</view>
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
<view
v-if="userInfo.audit_status !== null"
class="item bg-white btn-border flex flex-1 justify-between"
@click="router.navigate('/packages/pages/entry_info/entry_info')"
>
<view class="">入驻信息</view>
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
<view
class="item bg-white btn-border flex flex-1 justify-between"
@click="router.navigate(`/pages/agreement/agreement?type=${AgreementEnum.PRIVACY}`)"
>
<view class="">隐私政策</view>
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
<view
class="item bg-white btn-border flex flex-1 justify-between"
@click="router.navigate(`/pages/agreement/agreement?type=${AgreementEnum.SERVICE}`)"
>
<view class="">服务协议</view>
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
<view
class="item bg-white rounded-b-[20rpx] flex flex-1 justify-between"
@click="router.navigate('/pages/as_us/as_us')"
>
<view class="">关于我们</view>
<view class="flex justify-between">
<view class="text-muted mr-[20rpx]"> v{{ appStore.config.version }}</view>
<u-icon name="arrow-right" color="#999"></u-icon>
</view>
</view>
</view>
<view
class="footer bg-white p-[30rpx]"
style="box-shadow: 0 -4rpx 48rpx 0 #1423191f;"
>
<u-button type="error" @click="showLogout = true">
退出登录
</u-button>
</view>
<u-action-sheet
:list="list"
v-model="show"
@click="handleClick"
:safe-area-inset-bottom="true"
></u-action-sheet>
<!-- 推出登录弹窗 -->
<modal-popup
v-model:show="showLogout"
title="温馨提示"
content="确认退出当前登录吗?"
@confirm="logoutHandle"
>
</modal-popup>
</view>
</template>
<script setup lang="ts">
import {onLoad, onShow} from '@dcloudio/uni-app'
import {computed, ref} from 'vue'
import {useAppStore} from '@/stores/app'
import {useUserStore} from '@/stores/user'
import {AgreementEnum} from '@/enums/agreementEnums'
import {isWeixinClient} from '@/utils/client'
import {mnpAuthBind, oaAuthBind} from '@/api/account'
import {useLockFn} from '@/hooks/useLockFn'
import {useRouter} from 'uniapp-router-next'
// #ifdef H5
import wechatOa from '@/utils/wechat'
// #endif
const router = useRouter()
const appStore = useAppStore()
const userStore = useUserStore()
const userInfo = computed(() => userStore.userInfo)
const list = ref([
{
text: '修改密码'
},
{
text: '忘记密码'
}
])
const isWeixin = ref(true)
// #ifdef H5
isWeixin.value = isWeixinClient()
// #endif
const show = ref(false)
const showLogout = ref(false)
// 修改/忘记密码
const handleClick = (index: number) => {
switch (index) {
case 0:
router.navigateTo('/pages/change_password/change_password')
break
case 1:
router.navigateTo('/pages/forget_pwd/forget_pwd')
break
}
}
const handlePwd = () => {
// if (!userInfo.value.has_password)
// return router.navigateTo('/pages/change_password/change_password?type=set')
// show.value = true
// 去修改密码
handleClick(0)
}
// 退出登录
const logoutHandle = () => {
userStore.logout()
router.redirectTo('/pages/login/login')
}
const bindWechat = async () => {
if (userInfo.value.is_auth) return
try {
uni.showLoading({
title: '请稍后...'
})
// #ifdef MP-WEIXIN
const {code}: any = await uni.login({
provider: 'weixin'
})
await mnpAuthBind({
code: code
})
//#endif
// #ifdef H5
if (isWeixin.value) {
wechatOa.getUrl()
}
// #endif
await userStore.getUser()
uni.hideLoading()
} catch (e) {
uni.hideLoading()
uni.$u.toast(e)
}
}
const {lockFn: bindWechatLock} = useLockFn(bindWechat)
onShow(() => {
userStore.getUser()
})
onLoad(async (options) => {
// #ifdef H5
const {code} = options
if (!isWeixin.value) return
if (code) {
uni.showLoading({
title: '请稍后...'
})
try {
await oaAuthBind({code})
await userStore.getUser()
} catch (error) {
}
//用于清空code
router.redirectTo('/pages/user_set/user_set')
}
// #endif
})
</script>
<style lang="scss" scoped>
.user-set {
.item {
padding: 30rpx;
}
.btn-border {
border-bottom: 2rpx solid #f8f8f8;
}
// 底部按钮
.footer {
box-shadow: 0 -4rpx 48rpx 0 #1423191f;
padding-bottom: calc(env(safe-area-inset-bottom) + 30rpx);
}
}
</style>

View File

@@ -0,0 +1,16 @@
<template>
<web-view :src="url" />
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
const url = ref('')
onLoad((options) => {
url.value = decodeURIComponent(options.url!)
})
</script>
<style></style>