添加充值功能、优化内容

This commit is contained in:
wangxiaowei
2026-01-10 19:09:01 +08:00
parent d12651fe8a
commit 59859a2363
17 changed files with 959 additions and 105 deletions

10
env/.env vendored
View File

@ -9,18 +9,10 @@ VITE_APP_PUBLIC_BASE=/
# 登录页面
VITE_LOGIN_URL = '/pages/login/login'
# 第一个请求地址
VITE_SERVER_BASEURL = 'https://76458.com'
VITE_UPLOAD_BASEURL = 'https://76458.com/upload'
# h5是否需要配置代理
VITE_APP_PROXY=true
VITE_APP_PROXY_PREFIX = '/storeapi'
# 第二个请求地址 (目前alova中可以使用)
VITE_SERVER_BASEURL = 'https://76458.com'
# 上传图片请求地址
VITE_UPLOAD_BASEURL = 'https://76458.com/storeapi/upload/image'
VITE_UPLOAD_IMAGE_URL = 'https://76458.com/'
# VITE_SERVER_BASEURL = 'https://76458.com'

11
env/.env.development vendored
View File

@ -1,6 +1,17 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true
# 请求地址
VITE_SERVER_BASEURL = 'https://76458.com'
# 图片上传路径
VITE_UPLOAD_BASEURL = 'https://76458.com/upload'
# 上传图片请求地址
VITE_UPLOAD_BASEURL = 'https://76458.com/storeapi/upload/image'
VITE_UPLOAD_IMAGE_URL = 'https://76458.com/'

13
env/.env.production vendored
View File

@ -1,6 +1,17 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
NODE_ENV = 'production'
VITE_NODE_ENV = 'production'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = true
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = false
# 请求地址
VITE_SERVER_BASEURL = 'https://76458.com'
# 图片上传路径
VITE_UPLOAD_BASEURL = 'https://76458.com/upload'
# 上传图片请求地址
VITE_UPLOAD_BASEURL = 'https://76458.com/storeapi/upload/image'
VITE_UPLOAD_IMAGE_URL = 'https://76458.com/'

View File

@ -109,6 +109,7 @@ export interface IEditRoomParams {
price?: number
hours?: number
other_describe?: string
weight?: number
}
export function editRoom(data: IEditRoomParams) {
return http.Post('/storeapi/store/editRoom', data)
@ -225,3 +226,53 @@ export function getStoreIncomeList(data: IGetStoreIncomeListParams) {
export function scanVerifyCoupon(qr_sn: string, store_id: number) {
return http.Post<any>('/storeapi/group/cancelCode', { qr_sn, store_id })
}
/**
* 充值活动列表
*/
export function getRechargeActivityList(data: {store_id}) {
return http.Post<any>('/storeapi/Store/rechargeLists', data)
}
/**
* 添加充值活动
*/
export interface IAddRechargeActivityParams {
store_id: number
title: string
price: number
gift_price: number
}
export function addRechargeActivity(data: IAddRechargeActivityParams) {
return http.Post('/storeapi/Store/addRecharge', data)
}
/**
* 编辑充值活动
*/
export interface IEditRechargeActivityParams {
id: number
store_id: number
title: string
price: number
gift_price: number
}
export function editRechargeActivity(data: IEditRechargeActivityParams) {
return http.Post('/storeapi/Store/editRecharge', data)
}
/**
* 删除充值活动
*/
export function deleteRechargeActivity(id: number) {
return http.Post('/storeapi/Store/delRecharge', { id })
}
/**
* 充值活动状态
*/
export function toggleRechargeActivityStatus(data: {store_id: number, state: number}) {
return http.Post('/storeapi/Store/operateRecharge', data)
}

View File

@ -0,0 +1,281 @@
<route lang="jsonc" type="page">{
"needLogin": true,
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#fff"
}
}</route>
<template>
<view class="pb-40rpx">
<!-- 创建充值活动 -->
<wd-popup v-model="showRechargePopup" lock-scroll custom-style="border-radius: 16rpx;" position="center" @close="Recharge.handleCancel">
<view class="w-592rpx pb-36rpx">
<view class="text-36rpx text-[#121212] leading-50rpx text-center mt-34rpx">创建充值活动</view>
<view class="mt-54rpx">
<view class="flex items-center justify-center">
<view class="font-400 text-30rpx text-[#606266] mr-44rpx">名称</view>
<view class="">
<wd-input v-model="form.title" type="text" placeholder="输入套餐名称" inputmode="numeric" no-border
custom-class="!bg-[#F6F7F8] !rounded-16rpx"
custom-input-class="!px-32rpx !h-72rpx !w-408rpx"/>
</view>
</view>
<view class="mt-20rpx">
<view class="flex items-center justify-center">
<view class="font-400 text-30rpx text-[#606266] mr-44rpx">充值</view>
<view class="">
<wd-input v-model="form.price" type="text" placeholder="输入充值金额" inputmode="numeric" no-border
custom-class="!bg-[#F6F7F8] !rounded-16rpx"
custom-input-class="!px-32rpx !h-72rpx !w-408rpx"/>
</view>
</view>
</view>
<view class="mt-20rpx">
<view class="flex items-center justify-center">
<view class="font-400 text-30rpx text-[#606266] mr-44rpx">赠送</view>
<view>
<wd-input v-model="form.gift_price" type="text" placeholder="输入赠送金额" inputmode="numeric" no-border
custom-class="!bg-[#F6F7F8] !rounded-16rpx"
custom-input-class="!px-32rpx !h-72rpx !w-408rpx"/>
</view>
</view>
</view>
</view>
<view class="flex items-center justify-center mt-58rpx">
<view class="w-240rpx h-80rpx text-center leading-80rpx text-32rpx text-[#303133] rounded-8rpx bg-[#F6F7F8] mr-30rpx" @click="Recharge.handleCancel">取消</view>
<view class="w-240rpx h-80rpx text-center leading-80rpx text-32rpx text-[#fff] rounded-8rpx bg-[#4C9F44]" @click="Recharge.handleAdd">确定</view>
</view>
</view>
</wd-popup>
<navbar title="充值活动" custom-class='!bg-[#F6F7F9]'></navbar>
<view class="mt-64rpx" v-if="list.length == 0">
<view class="flex justify-center">
<wd-img :src="`${OSS}images/store/recharge/image1.png`" width="362rpx" height="292rpx"></wd-img>
</view>
<view class="mt-18rpx font-400 text-28rpx leading-40rpx text-center">暂无活动</view>
<view class="font-bold text-30rpx leading-42rpx bg-[#4C9F44] text-#fff rounded-8rpx mx-60rpx h-90rpx leading-90rpx text-center mt-50rpx" @click="showRechargePopup = true">
创建充值活动
</view>
</view>
<view class="bg-white rounded-16rpx mt-20rpx mx-30rpx px-32rpx py-44rpx" v-if="list.length > 0">
<view class="flex items-center justify-between">
<view>
<view class="text-40rpx leading-36rpx text-[#171B2E] font-bold mb-22rpx">门店充值活动</view>
<wd-switch v-model="rechargeStatus" size="36rpx" active-color="#4C9F44" @change="Recharge.handleToggle"/>
</view>
<view>
<wd-img :src="`${OSS}images/store/recharge/image2.png`" width="354rpx" height="152rpx"></wd-img>
</view>
</view>
<view class="mt-20rpx">
<view class="" v-for="(item, index) in list" :key="item.id">
<view class="text-26rpx leading-38rpx text-[#909399]">{{ item.title }}</view>
<view class="flex items-center justify-between mt-12rpx">
<view class="">充值{{ item.price }}赠送{{ item.gift_price }}</view>
<view class="font-400 text-26rpx leading-36rpx text-[#606266]">
<text @click="Recharge.handleDelete(item.id)">删除</text>
<wd-divider vertical />
<text @click="Recharge.handleEdit(item)">修改</text>
</view>
</view>
<view v-if="index !== list.length - 1" class="h-2rpx bg-[#F6F6F6] my-22rpx"></view>
</view>
</view>
<view class="flex items-center justify-center mt-100rpx">
<view class="w-630rpx h-90rpx text-center leading-90rpx text-32rpx text-[#fff] rounded-46rpx bg-[#4C9F44]" @click="showRechargePopup = true">新增</view>
</view>
</view>
<!-- 取消订单 -->
<wd-message-box selector="wd-message-box-slot"></wd-message-box>
</view>
</template>
<script lang="ts" setup>
import { router } from '@/utils/tools'
import { useStoreStore } from '@/store'
import { amount } from '@/utils/test'
import { useMessage, useToast } from 'wot-design-uni'
import { getRechargeActivityList, addRechargeActivity, editRechargeActivity, deleteRechargeActivity, toggleRechargeActivityStatus } from '@/api/store'
const OSS = inject('OSS')
const useStore = useStoreStore()
const toast = useToast()
const message = useMessage('wd-message-box-slot')
// 充值活动状态
const rechargeStatus = ref<boolean>(false) // 充值活动开启状态
const list = ref<Array<any>>([]) // 茶室列表
const showRechargePopup = ref<boolean>(false)
// 表单
const form = ref({
title: '', // 套餐名称
price: '', // 充值金额
gift_price: '', // 赠送金额
})
const rechargeId = ref<number>(0)
onLoad((args) => {
Recharge.handleInit()
})
const Recharge = {
/**
* 获取已添加的充值套餐
*/
handleInit: async () => {
const res = await getRechargeActivityList({
store_id: useStore.defaultStore.id,
})
rechargeStatus.value = res.recharge_state == 1 ? true : false
list.value = res.recharge
},
/**
* 创建充值活动
*/
handleAdd: async () => {
if (!form.value.title) {
toast.show('请输入名称')
return
}
if (!amount(form.value.price)) {
toast.show('请输入正确的充值金额')
return
}
if (!amount(form.value.gift_price)) {
toast.show('请输入正确的充值赠送金额')
return
}
toast.loading({
loadingType: 'ring',
loadingColor: '#4C9F44',
msg: '充值中...'
})
try {
if (rechargeId.value === 0) {
await addRechargeActivity({
store_id: useStore.defaultStore.id,
title: form.value.title,
price: Number(form.value.price),
gift_price: Number(form.value.gift_price),
})
} else {
await editRechargeActivity({
id: rechargeId.value,
store_id: useStore.defaultStore.id,
title: form.value.title,
price: Number(form.value.price),
gift_price: Number(form.value.gift_price),
})
}
toast.close()
Recharge.handleInit()
Recharge.handleCancel()
return true
} catch (error) {
toast.close()
return false
}
},
/**
* 编辑充值活动
*/
handleEdit: (item) => {
showRechargePopup.value = true
rechargeId.value = item.id
form.value = {
title: item.title,
price: item.price,
gift_price: item.gift_price,
}
},
/**
* 删除充值活动
*/
handleDelete: async (id) => {
message.confirm({
title: '确定删除活动?',
msg: '删除活动后无法恢复,是否删除',
confirmButtonText: '确定',
cancelButtonText: '取消',
cancelButtonProps: {
customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
},
confirmButtonProps: {
customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
}
}).then(async (res) => {
// 点击确认按钮回调事件
if (res.action == 'confirm') {
await deleteRechargeActivity(id)
Recharge.handleInit()
}
}).catch(() => {
// 点击取消按钮回调事件
})
},
/**
* 切换充值活动状态
*/
handleToggle: async (item) => {
await toggleRechargeActivityStatus({
store_id: useStore.defaultStore.id,
state: item.value ? 1 : 0,
})
},
/**
* 取消创建充值活动
*/
handleCancel: () => {
showRechargePopup.value = false
rechargeId.value = 0
form.value = {
title: '',
price: '',
gift_price: '',
}
},
}
</script>
<style lang="scss">
page {
background-color: $cz-page-background;
}
.datetime-picker {
:deep() {
.wd-cell {
background-color: transparent !important;
}
}
}
</style>

View File

@ -91,7 +91,7 @@
<view class="mt-32rpx">
<view class="font-bold text-32rpx text-[#303133] leading-44rpx">门店图片</view>
<view class="mt-32rpx">
<wd-upload :header="{'token': token}" :file-list="fileList" image-mode="aspectFill" :limit="9" multiple :action="action"
<!-- <wd-upload :header="{'token': token}" :file-list="fileList" image-mode="aspectFill" :limit="9" multiple :action="action"
@change="EditStore.handleUploadFile">
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
@ -101,6 +101,16 @@
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">添加图片</view>
</view>
</wd-upload> -->
<wd-upload :header="{'token': token}" v-model:file-list="fileList" image-mode="aspectFill" :limit="9" multiple :action="action" >
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view class="">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`"
mode="aspectFill" />
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">添加图片</view>
</view>
</wd-upload>
</view>
</view>
@ -117,7 +127,7 @@
import { toast } from '@/utils/toast'
import { getStoreDetails, editStoreInfo } from '@/api/store'
import { useStoreStore } from '@/store'
import { router } from '@/utils/tools'
import { router, removeImageUrlPrefix } from '@/utils/tools'
const OSS = inject('OSS')
const useStore = useStoreStore()
@ -236,25 +246,25 @@
/**
* 图片选择/删除
*/
handleUploadFile: ({ fileList: files }) => {
let url = ''
let name = ''
let response = null
// handleUploadFile: ({ fileList: files }) => {
// let url = ''
// let name = ''
// let response = null
const res = files.map(item => {
if (item.response) {
response = JSON.parse(item.response)
url = response.data.url
name = response.data.name
}
return {
name: name || item.name,
url: url || item.url
}
})
// const res = files.map(item => {
// if (item.response) {
// response = JSON.parse(item.response)
// url = response.data.url
// name = response.data.name
// }
// return {
// name: name || item.name,
// url: url || item.url
// }
// })
EditStore.fileList = res
},
// EditStore.fileList = res
// },
/**
* 保存门店信息
@ -290,12 +300,7 @@
return
}
form.value.image_arr = EditStore.fileList.map(item => {
if (typeof item.url === 'string') {
return item.url.replace(import.meta.env.VITE_UPLOAD_IMAGE_URL, '')
}
return item.url
})
form.value.image_arr = removeImageUrlPrefix(fileList.value)
form.value.image = form.value.image_arr[0]
form.value.id = useStore.defaultStore.id

View File

@ -40,13 +40,19 @@
</view>
<view >
<!-- 使用说明 -->
<!-- 其他说明 -->
<view class="bg-white rounded-16rpx py-26rpx px-30rpx mt-24rpx mx-30rpx">
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">使用说明</view>
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">其他说明</view>
<view class="">
<rich-text :nodes="teaRoom.textarea1"></rich-text>
</view>
</view>
<!-- 其他说明 -->
<view class="bg-white rounded-16rpx py-26rpx px-30rpx mt-24rpx mx-30rpx">
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">起订时间</view>
<view class="text-[26rpx] text-[#606266] leading-36rpx">{{ teaRoom?.room?.hours }}小时起订</view>
</view>
</view>
</view>
</view>
@ -63,7 +69,14 @@
// 包间内容
const roomId = ref<number>(0) // 门店ID
const teaRoom = ref<any>({})
const teaRoom = ref<any>({
room: {
price: 0,
sold: 0,
title: '',
hours: 0
}
})
onLoad((args) => {
roomId.value = Number(args.id)
@ -79,7 +92,7 @@
console.log("🚀 ~ res:", res)
teaRoom.value = res.details
console.log("🚀 ~ teaRoom.value:", teaRoom.value)
swiperList.value = teaRoom.value.img_arr
swiperList.value = teaRoom.value.room.room_arr
}
}
</script>

View File

@ -0,0 +1,177 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#fff"
}
}</route>
<template>
<view>
<navbar title="订单详情" custom-class='!bg-[#fff]'></navbar>
<view class="mt-38rpx">
<view class="flex flex-col items-center justify-center">
<view class="font-bold text-30rpx leading-42rpx text-[#303133]">门店充值1000送500</view>
<view class="mt-18rpx text-40rpx leading-56rpx text-[#303133]">1000.00</view>
<view class="flex items-center mt-8rpx">
<wd-img :src="`${OSS}icon/icon_pay_success.png`" width="28rpx" height="28rpx"></wd-img>
<view class="text-26rpx leading-36rpx text-[#606266] ml-12rpx">充值成功</view>
</view>
</view>
<view class="mt-112rpx text-28rpx leading-40rpx text-[#606266] px-30rpx">
<view class="flex items-center justify-between">
<view>充值门店</view>
<view>茶址.24小时智能茶室(中新店)</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>用户昵称</view>
<view>茶址.24小时智能茶室(中新店)</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>手机</view>
<view>茶址.24小时智能茶室(中新店)</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>充值金额</view>
<view>1000</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>赠金</view>
<view>1000</view>
</view>
<view class="h-2rpx bg-[#E5E5E5] mt-12rpx mb-32rpx"></view>
<view class="flex items-center justify-between">
<view>订单编号</view>
<view>
<view>
<text>11</text>
<wd-divider vertical />
<text class="text-[#4C9F44]" @click="copy(1)">复制</text>
</view>
</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>交易方式</view>
<view>微信支付</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>创建时间</view>
<view>微信支付</view>
</view>
<view class="flex items-center justify-between mt-24rpx">
<view>付款时间</view>
<view>微信支付</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { router } from '@/utils/tools'
import { toTimes, copy } from '@/utils/tools'
import { getStoreMemberList } from '@/api/store'
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
import { useStoreStore } from '@/store'
const OSS = inject('OSS')
const useStore = useStoreStore()
// mescroll
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
const upOption = {
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const list = ref<Array<any>>([]) // 茶室列表
// 日期过滤
const value = ref<number>(Date.now())
onLoad((args) => {
})
const RechargeDetail = {
/**
* 分页加载
* @param mescroll
*/
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
// const filter = {
// page: mescroll.num,
// size: mescroll.size,
// store_id: useStore.defaultStore.id,
// }
// getStoreMemberList(filter).then((res) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
// list.value = list.value.concat(curPageData) //追加新数据
// mescroll.endSuccess(curPageData.length, Boolean(res.more))
// }).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
// })
},
/**
* 确认日期
* @param date
*/
handleConfirmDate: (date: {value: number}) => {
list.value = []
getMescroll().resetUpScroll();
},
/**
* 跳转用户详情
* @param id
*/
handleToDetail: (id) => {
uni.$on('refreshUserDetail', () => {
uni.$off('refreshUserDetail')
list.value = []
getMescroll().resetUpScroll()
})
router.navigateTo(`/bundle/user/user-detail?id=${id}`)
},
/**
* 搜索
*/
handleSearch: () => {
list.value = []
getMescroll().resetUpScroll()
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.datetime-picker {
:deep() {
.wd-cell {
background-color: transparent !important;
}
}
}
</style>

View File

@ -0,0 +1,155 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#fff"
}
}</route>
<template>
<view>
<navbar title="充值明细" custom-class='!bg-[#F6F7F8]'></navbar>
<view class="">
<view class="flex items-center">
<view class="font-bold text-[#303133] datetime-picker">
<wd-datetime-picker v-model="value" :maxDate="Date.now()" type="year-month" @confirm="RechargeList.handleConfirmDate"></wd-datetime-picker>
</view>
</view>
<view class="flex items-center font-400 text-24rpx leading-34rpx text-[#606266] mx-30rpx">
<view class="mr-130rpx">充值¥2000.46</view>
<view class="">赠金¥500.00</view>
</view>
<view class="mx-30rpx mt-20rpx">
<mescroll-body ref="mescrollItem0" @init="mescrollInit" @down="downCallback" @up="RechargeList.upCallback" :down="downOption" :up="upOption">
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mb-20rpx" @click="router.navigateTo(`/bundle/user/recharge-detail?id=1`)">
<!-- <view class="bg-white rounded-16rpx px-30rpx py-28rpx mb-20rpx" @click="router.navigateTo(`/bundle/user/recharge-detail?id=${item.id}`)" v-for="item in list" :key="item.id"> -->
<view class="flex items-center justify-between">
<view class="font-bold text-30rpx text-[#303133] leading-42rpx">门店充值1000送500</view>
<view class="flex items-center">
<view class="text-[#FF5951] font-bold text-30rpx leading-42rpx">+1000.00</view>
<view class="">
<wd-icon name="arrow-right" size="32rpx" color="#666"></wd-icon>
</view>
</view>
</view>
<view class="mt-12rpx text-24rpx leading-34rpx text-[#606266] flex items-center">
<view class="w-300rpx mr-52rpx">客户昵称茶址客户昵称</view>
<view class="w-240rpx">手机13585423654</view>
</view>
<view class="mt-12rpx text-24rpx leading-34rpx text-[#F29747] flex items-center">
<view class="w-300rpx mr-52rpx">充值金额1000</view>
<view class="w-240rpx">赠金500</view>
</view>
<view class="mt-12rpx text-24rpx leading-34rpx text-[#909399]">
充值时间2025-08-05 19:09:52
</view>
</view>
</mescroll-body>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { router } from '@/utils/tools'
import { toTimes, copy } from '@/utils/tools'
import { getStoreMemberList } from '@/api/store'
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
import { useStoreStore } from '@/store'
const OSS = inject('OSS')
const useStore = useStoreStore()
// mescroll
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
const upOption = {
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const list = ref<Array<any>>([]) // 茶室列表
// 日期过滤
const value = ref<number>(Date.now())
onLoad((args) => {
})
const RechargeList = {
/**
* 分页加载
* @param mescroll
*/
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
// const filter = {
// page: mescroll.num,
// size: mescroll.size,
// store_id: useStore.defaultStore.id,
// }
// getStoreMemberList(filter).then((res) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
// list.value = list.value.concat(curPageData) //追加新数据
// mescroll.endSuccess(curPageData.length, Boolean(res.more))
// }).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
// })
},
/**
* 确认日期
* @param date
*/
handleConfirmDate: (date: {value: number}) => {
list.value = []
getMescroll().resetUpScroll();
},
/**
* 跳转用户详情
* @param id
*/
handleToDetail: (id) => {
uni.$on('refreshUserDetail', () => {
uni.$off('refreshUserDetail')
list.value = []
getMescroll().resetUpScroll()
})
router.navigateTo(`/bundle/user/user-detail?id=${id}`)
},
/**
* 搜索
*/
handleSearch: () => {
list.value = []
getMescroll().resetUpScroll()
}
}
</script>
<style lang="scss">
page {
background-color: $cz-page-background;
}
.datetime-picker {
:deep() {
.wd-cell {
background-color: transparent !important;
}
}
}
</style>

View File

@ -7,9 +7,77 @@
}</route>
<template>
<view class="user-list-bg">
<view class="user-list-bg w-[100%] sticky top-0 left-0 z-50">
<wd-navbar safeAreaInsetTop :bordered="false" custom-style="background-color: transparent !important;">
<template #left>
<view class="flex items-center" @click="router.navigateBack()">
<view class=" mt-4rpx">
<wd-icon name="thin-arrow-left" size="30rpx"></wd-icon>
</view>
<view class="text-[#303133] text-36rpx ml-24rpx leading-48rpx">客户列表</view>
</view>
</template>
</wd-navbar>
</view>
<view class="mt-12rpx">
<view class="relative flex justify-center">
<wd-img :src="`${OSS}images/store/user/image4.png`" width="690rpx" height="280rpx"></wd-img>
<view class="absolute left-50% top-50% -translate-x-1/2 -translate-y-1/2 w-full">
<view>
<view>
<navbar title="用户列表" custom-class="!bg-[transparent]"></navbar>
<view class="flex items-center justify-between px-30rpx">
<view class="ml-48rpx">
<view class="font-500 text-30rpx leading-42rpx text-[#121212]">门店预充值(未消费</view>
<view class="mt-6rpx text-24rpx leading-34rpx text-[#7B8290]">{{ useStore.defaultStore.name }}</view>
</view>
<view class="relative mt-20rpx" @click="router.navigateTo('/bundle/user/recharge-list')">
<wd-img :src="`${OSS}images/store/user/image5.png`" width="178rpx" height="50rpx"></wd-img>
<view class="absolute left-50% top-50% -translate-x-1/2 -translate-y-1/2 flex justify-center items-center w-full">
<view class="font-400 text-26rpx text-[#333] mt-[-8rpx]">充值明细</view>
<view class="mt-[-12rpx]">
<wd-icon name="arrow-right" size="32rpx" color="#666"></wd-icon>
</view>
</view>
</view>
</view>
<view class="text-[#7B4FE1] flex items-center ml-78rpx mt-30rpx">
<view class="flex flex-col items-center justify-center mr-100rpx">
<view class="font-bold text-32rpx leading-44rpx">150,000.00</view>
<view class="mt-12rpx font-400 text-28rpx leading-40rpx">实际充值()</view>
</view>
<view class="flex flex-col items-center justify-center">
<view class="font-bold text-32rpx leading-44rpx">10,000.00</view>
<view class="mt-12rpx font-400 text-28rpx leading-40rpx">赠金()</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="mt-40rpx">
<view class="font-bold text-32rpx leading-44rpx mx-30rpx">客户信息</view>
<view class="search-box relative">
<wd-search placeholder="搜索客户信息" cancel-txt="搜索" placeholder-left hide-cancel custom-input-class="!h-72rpx !pr-160rpx" v-model="keywords" light >
</wd-search>
<view
class="absolute top-1/2 -translate-y-1/2 right-34rpx w-142rpx h-64rpx leading-64rpx text-center rounded-32rpx bg-#4C9F44 text-#fff font-400 text-32rpx"
@click="UserList.handleSearch">
搜索
</view>
</view>
</view>
<view class="flex items-center mx-30rpx">
<view
class="mr-12rpx bg-white rounded-12rpx w-132rpx h-56rpx text-center leading-50rpx font-400 text-28rpx leading-40rpx text-[#303133] border-2rpx border-solid border-[#fff]"
:class="{active: item.type === currentTab }"
v-for="item in menuTab" :key="item.type"
@click="UserList.handleChangeTab(item.type)">
{{ item.name }}
</view>
</view>
<view>
@ -66,7 +134,13 @@
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const list = ref<Array<any>>([]) // 茶室列表
const keywords = ref<string>('') // 搜索关键词
const menuTab = ref([
{type: 'all', name: '全部'},
{type: 'recharge', name: '充值卡'},
{type: 'vip', name: '会员卡'},
])
const currentTab = ref<string>('all')
onLoad((args) => {
})
@ -78,20 +152,20 @@
*/
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
const filter = {
page: mescroll.num,
size: mescroll.size,
store_id: useStore.defaultStore.id,
}
// const filter = {
// page: mescroll.num,
// size: mescroll.size,
// store_id: useStore.defaultStore.id,
// }
getStoreMemberList(filter).then((res) => {
const curPageData = res.list || [] // 当前页数据
if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
list.value = list.value.concat(curPageData) //追加新数据
mescroll.endSuccess(curPageData.length, Boolean(res.more))
}).catch(() => {
// getStoreMemberList(filter).then((res) => {
// const curPageData = res.list || [] // 当前页数据
// if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
// list.value = list.value.concat(curPageData) //追加新数据
// mescroll.endSuccess(curPageData.length, Boolean(res.more))
// }).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
})
// })
},
/**
@ -103,7 +177,6 @@
uni.$off('refreshUserDetail')
list.value = []
getMescroll().resetUpScroll()
})
router.navigateTo(`/bundle/user/user-detail?id=${id}`)
},
@ -116,6 +189,23 @@
uni.makePhoneCall({
phoneNumber: phone
})
},
/**
* 切换tab
*/
handleChangeTab: (type: string) => {
currentTab.value = type
list.value = []
getMescroll().resetUpScroll()
},
/**
* 搜索
*/
handleSearch: () => {
list.value = []
getMescroll().resetUpScroll()
}
}
</script>
@ -124,4 +214,26 @@
page {
background-color: #fff;
}
.user-list-bg {
background-color: $cz-page-background;
background-image: url(#{$OSS}images/store/user/image3.png);
background-size: 100%;
background-repeat: no-repeat;
}
.search-box {
:deep() {
.wd-search {
background: transparent !important;
}
}
}
.active {
border-radius: 12rpx;
border: 2rpx solid #4C9F44;
background-color: #F0F6EF;
color: #4C9F44;
}
</style>

View File

@ -59,24 +59,29 @@
<!-- 菜单栏 -->
<view class="flex justify-between mt-40rpx">
<view class="flex flex-col justify-center items-center w-[25%]" @click="showScanMenu = true">
<view class="flex flex-col justify-center items-center w-[20%]" @click="showScanMenu = true">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image3.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">扫码验券</view>
</view>
<view class="flex flex-col justify-center items-center w-[25%]" @click="router.navigateTo('/bundle/finance/finance')">
<view class="flex flex-col justify-center items-center w-[20%]" @click="router.navigateTo('/bundle/finance/finance')">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image4.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">财务管理</view>
</view>
<view class="flex flex-col justify-center items-center w-[25%]">
<view class="flex flex-col justify-center items-center w-[20%]">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image5.png`" mode="aspectFit" @click="router.navigateTo('/bundle/setmeal/setmeal')" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">套餐管理</view>
</view>
<view class="flex flex-col justify-center items-center w-[25%]" @click="router.navigateTo('/bundle/user/user-list')">
<view class="flex flex-col justify-center items-center w-[20%]" @click="router.navigateTo('/bundle/user/user-list')">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image6.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">户列表</view>
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">户列表</view>
</view>
<view class="flex flex-col justify-center items-center w-[20%]" @click="router.navigateTo('/bundle/recharge/recharge')">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/home/image8.png`" mode="aspectFit" />
<view class="font-400 text-24rpx text-[#303133] leading-34rpx">充值活动</view>
</view>
</view>

View File

@ -89,23 +89,24 @@
handleToLogin: async () => {
// TODO 如果是edit的话就是修改手机号
if (!testMobile(model.mobile)) {
toast.info('请输入正确的账号')
toast.show('请输入正确的账号')
return
}
if (!model.passowrd) {
toast.info('请输入密码')
toast.show('请输入密码')
return
}
uni.showLoading({
title: '登录中...'
toast.loading({
loadingType: 'ring',
loadingColor: '#4C9F44',
msg: '登录中...'
})
try {
const userStore = useUserStore()
const res = await userStore.mobileLogin(model.mobile, model.passowrd, 1, 1, 1)
uni.hideLoading()
toast.close()
// 这里记录用户账号密码,下次自动填充
uni.setStorageSync('loginMobile', model.mobile)
@ -116,11 +117,9 @@
router.reLaunch('/pages/index/index')
}, 1000)
} catch (error) {
toast.info('登录失败,请稍后重试')
uni.hideLoading()
toast.close()
return
}
}
}
</script>

View File

@ -1,5 +1,6 @@
<route lang="jsonc" type="page">{
"layout": "tabbar",
"needLogin": true,
"style": {
"navigationStyle": "custom"
}
@ -67,7 +68,7 @@
</view>
<view class="mb-16rpx flex items-center text-24rpx text-[#606266] leading-40rpx">
<text class="w-140rpx">门店ID:</text>
<text class="flex-1 text-[#000]">{{ storeInfo.id }}</text>
<text class="flex-1 text-[#000]">{{ storeInfo.id > 0 ? storeInfo.id : '-'}}</text>
</view>
<view class="mb-16rpx flex items-center text-24rpx text-[#606266] leading-40rpx">
<text class="w-140rpx">门店地址:</text>

View File

@ -1,5 +1,6 @@
<!-- 使用 type="home" 属性设置首页其他页面不需要设置默认为page -->
<route lang="jsonc">{
"needLogin": true,
"layout": "tabbar",
"style": {
// 'custom' 表示开启自定义导航栏,默认 'default'

View File

@ -80,6 +80,8 @@
</view>
</view>
<!-- 团购视频 -->
<!-- <view class="mt-28rpx">
<view class="mb-28rpx flex items-center">
@ -122,20 +124,47 @@
</view>
</view>
<view class="text-26rpx text-[#9CA3AF] font-400 leading-36rpx">
可添加1张图片
可添加1-9张图片
</view>
</view>
<view class="flex flex-wrap items-center gap-16rpx">
<wd-upload
:header="{'token': token}"
:file-list="fileList"
:limit="1"
v-model:file-list="fileList"
:limit="9"
multiple
image-mode="scaleToFill"
:action="action"
@success="RoomDetail.handleUploadSuccess">
:action="action">
</wd-upload>
</view>
</view>
<!-- 排序 -->
<view class="mt-28rpx add-textarea">
<view class="flex items-center justify-between mb-20rpx">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
包间排序
</view>
<view class="text-24rpx text-[#9CA3AF] font-400 leading-34rpx">
数字越大排名越靠前
</view>
</view>
<wd-input v-model="form.weight" no-border placeholder="请输入包间排序" :maxlength="10"
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
</view>
<!-- 推荐人数 -->
<view class="mt-28rpx add-textarea">
<view class="flex items-center justify-between mb-20rpx">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
推荐人数
</view>
</view>
<wd-input v-model="form.people_number" no-border placeholder="推荐人数1-5人" :maxlength="10"
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
</view>
</view>
<!-- 规格与价格 -->
@ -397,7 +426,7 @@
<script lang="ts" setup>
import { getRoomDetails, getRoomLabelList, handleCreateTag, handleDeleteTag, editRoom } from '@/api/store'
import { updateUserInfo } from '@/api/user'
import { router } from '@/utils/tools'
import { router, removeImageUrlPrefix } from '@/utils/tools'
import { toast } from '@/utils/toast'
import { useStoreStore } from '@/store'
@ -431,12 +460,15 @@
// 表单
const form = reactive({
title: '',
image: '',
image_arr: [] as string[],
img: '',
tags: [] as string[],
price: '',
hours: '',
video: null as any,
other_describe: '',
weight: 0,
people_number: ''
})
const tags = ref<Array<{ id: number, label_name: string, index: number }>>([])
const roomLabelId = ref<string>('')
@ -455,21 +487,25 @@
*/
handleGetRoomDetails: async () => {
const res = await getRoomDetails(roomId.value)
fileList.value = [{url: res.details.room.img, name: res.details.room.img }]
fileList.value =res.details.room.room_arr.map((url: string) => ({
url,
}))
roomLabelId.value = res.details.room.label_id
form.title = res.details.room.title
form.image = res.details.room.img
form.img = res.details.room.img
form.price = res.details.room.price.toString()
form.hours = res.details.room.hours.toString()
form.other_describe = res.details.room.other_describe || ''
form.weight = res.details.room.weight || 0
form.people_number = res.details.room.people_number || ''
},
/**
* 获取包间标签列表
*/
handleGetRoomLabels: async () => {
const res = await getRoomLabelList(useStore.defaultStore.id)
availableTags.value = res.list
@ -494,8 +530,6 @@
}
})
}
console.log("🚀 ~ availableTags.value:", availableTags.value)
},
/**
@ -514,23 +548,6 @@
showTagSelectPopup.value = true
},
/**
* 上传图片
*/
handleUploadSuccess: async (e: any) => {
try {
const response = JSON.parse(e.file.response)
if (response.code) {
const avatarUrl = response.data.url
form.image = avatarUrl
} else {
throw new Error('上传失败')
}
} catch (error) {
toast.info('上传失败')
}
},
/**
* 关闭选择标签弹窗
*/
@ -712,8 +729,8 @@
return
}
if (!form.image) {
toast.info('请上传团购图片')
if (fileList.value.length === 0) {
toast.info('请上传包间图片')
return
}
@ -727,14 +744,18 @@
return
}
const imgArr = removeImageUrlPrefix(fileList.value)
let params = {
id: roomId.value,
img: form.image.replace(import.meta.env.VITE_UPLOAD_IMAGE_URL, ''),
img: imgArr[0],
img_arr: imgArr.join(','),
title: form.title,
label_id: tags.value.map(tag => tag.id).join(','),
price: Number(form.price),
hours: Number(form.hours),
other_describe: form.other_describe,
weight: Number(form.weight),
people_number: form.people_number
}
uni.showLoading({
@ -745,13 +766,13 @@
await editRoom(params)
uni.hideLoading()
toast.info('修改成功')
router.navigateBack(1, 500)
// router.navigateBack(1, 500)
} catch (e) {
toast.info('修改失败,请稍后重试')
uni.hideLoading()
return
}
},
}
}
</script>

View File

@ -1,5 +1,6 @@
<route lang="jsonc" type="page">{
"layout": "tabbar",
"needLogin": true,
"style": {
"navigationStyle": "custom"
}

View File

@ -1,6 +1,7 @@
import Decimal from 'decimal.js'
import { allowedNodeEnvironmentFlags } from 'process'
import { toast } from './toast'
import { isArray } from 'wot-design-uni/components/common/util';
/**
* 页面跳转方法
@ -158,3 +159,20 @@ export function getCurrentDate() {
return `${year}-${month}-${day}`;
}
/**
* 格式化上传图片的URL去除前缀
* @param file 上传图片文件列表
* @returns 去除前缀后的图片URL列表
*/
export function removeImageUrlPrefix(file: any) {
const fileList = file.map(item => {
if (item.response) {
const response = JSON.parse(item.response)
if (response.code) {
return response.data.uri.replace(import.meta.env.VITE_UPLOAD_IMAGE_URL, '')
}
}
return item.url.replace(import.meta.env.VITE_UPLOAD_IMAGE_URL, '')
})
return fileList
}