初始版本
This commit is contained in:
58
business_uniapp/src/App.vue
Normal file
58
business_uniapp/src/App.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch } from '@dcloudio/uni-app'
|
||||
import { useAppStore } from './stores/app'
|
||||
import { useUserStore } from './stores/user'
|
||||
import { useThemeStore } from './stores/theme'
|
||||
import { useRoute, useRouter } from 'uniapp-router-next'
|
||||
const appStore = useAppStore()
|
||||
const { getUser } = useUserStore()
|
||||
const { getTheme } = useThemeStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
//#ifdef H5
|
||||
const setH5WebIcon = () => {
|
||||
const config = appStore.getWebsiteConfig
|
||||
let favicon: HTMLLinkElement = document.querySelector('link[rel="icon"]')!
|
||||
if (favicon) {
|
||||
favicon.href = config.h5_favicon
|
||||
return
|
||||
}
|
||||
favicon = document.createElement('link')
|
||||
favicon.rel = 'icon'
|
||||
favicon.href = config.h5_favicon
|
||||
document.head.appendChild(favicon)
|
||||
}
|
||||
//#endif
|
||||
|
||||
const getConfig = async () => {
|
||||
await appStore.getConfig()
|
||||
//#ifdef H5
|
||||
setH5WebIcon()
|
||||
//#endif
|
||||
const { status, page_status, page_url } = appStore.getH5Config
|
||||
if (route.meta.webview) return
|
||||
//处理关闭h5渠道
|
||||
//#ifdef H5
|
||||
if (status == 0) {
|
||||
if (page_status == 1) return (location.href = page_url)
|
||||
router.reLaunch('/pages/empty/empty')
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
|
||||
onLaunch(async () => {
|
||||
getTheme()
|
||||
getConfig()
|
||||
//#ifdef H5
|
||||
setH5WebIcon()
|
||||
//#endif
|
||||
await getUser()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
background-color: #f6f7f8;
|
||||
}
|
||||
</style>
|
||||
45
business_uniapp/src/api/account.ts
Normal file
45
business_uniapp/src/api/account.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { client } from '@/utils/client'
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录
|
||||
export function login(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/account', data: { ...data, terminal: client } })
|
||||
}
|
||||
|
||||
//注册
|
||||
export function register(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/register', data: { ...data, channel: client } })
|
||||
}
|
||||
|
||||
//向微信请求code的链接
|
||||
export function getWxCodeUrl(data: Record<string, any>) {
|
||||
return request.get({ url: '/login/codeUrl', data })
|
||||
}
|
||||
|
||||
export function OALogin(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/oaLogin', data })
|
||||
}
|
||||
|
||||
export function mnpLogin(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/mnpLogin', data })
|
||||
}
|
||||
|
||||
//更新微信小程序头像昵称
|
||||
export function updateUser(data: Record<string, any>, header: any) {
|
||||
return request.post({ url: '/login/updateUser', data, header })
|
||||
}
|
||||
|
||||
//小程序绑定微信
|
||||
export function mnpAuthBind(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/mnpAuthBind', data })
|
||||
}
|
||||
|
||||
//公众号绑定微信
|
||||
export function oaAuthBind(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/oaAuthBind', data })
|
||||
}
|
||||
|
||||
//佣金明细列表
|
||||
export function userMoneyList(data?: any) {
|
||||
return request.get({ url: '/finance/lists', data })
|
||||
}
|
||||
143
business_uniapp/src/api/app.ts
Normal file
143
business_uniapp/src/api/app.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//发送短信
|
||||
export function smsSend(data: any) {
|
||||
return request.post({ url: '/sms/sendCode', data: data })
|
||||
}
|
||||
|
||||
export function getConfig() {
|
||||
return request.get({ url: '/config/config' })
|
||||
}
|
||||
|
||||
export function getPolicy() {
|
||||
return request.get({ url: '/config/agreement'})
|
||||
}
|
||||
|
||||
export function uploadImage(file: any, token?: string) {
|
||||
return request.uploadFile({
|
||||
url: '/upload/image',
|
||||
filePath: file,
|
||||
name: 'file',
|
||||
header: {
|
||||
token
|
||||
},
|
||||
fileType: 'image'
|
||||
})
|
||||
}
|
||||
|
||||
export function wxJsConfig(data: any) {
|
||||
return request.get({ url: '/wechat/jsConfig', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 公众号登录
|
||||
*/
|
||||
export const apiOALogin = (params: any) => request.post({ url: '/login/oaLogin', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 公众号-获取授权url
|
||||
*/
|
||||
export const apiCodeUrlGet = () =>
|
||||
request.get({
|
||||
url: '/login/codeUrl',
|
||||
data: {
|
||||
url: location.href
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 微信sdk配置
|
||||
*/
|
||||
export const apiJsConfig = () =>
|
||||
request.get({ url: '/wechat/jsConfig', data: { url: getSignLink() } })
|
||||
|
||||
//申请
|
||||
export function apply(data: any, header?: any) {
|
||||
return request.post({ url: '/shop/apply', data, header })
|
||||
}
|
||||
|
||||
//获取分类
|
||||
export function getCategory(header?: any) {
|
||||
return request.get({ url: '/goods/categoryLists' , header})
|
||||
}
|
||||
|
||||
//获取项目列表
|
||||
export function getProjectList(data: any, header?: any) {
|
||||
return request.get({ url: '/goods/lists', data, header })
|
||||
}
|
||||
|
||||
//获取订单列表
|
||||
export function getOrderList(data?: any) {
|
||||
return request.get({ url: '/order/lists', data })
|
||||
}
|
||||
//获取商家服务
|
||||
export function getShopGoodsList(data?: any) {
|
||||
return request.get({ url: '/goods/shopGoodsLists', data })
|
||||
}
|
||||
//获取服务项目列表
|
||||
export function getGoodsList(data?: any) {
|
||||
return request.get({ url: '/goods/lists', data })
|
||||
}
|
||||
|
||||
//获取服务项目列表
|
||||
export function getMyGoodsLists(data?: any) {
|
||||
return request.get({ url: '/goods/myGoodsLists', data })
|
||||
}
|
||||
|
||||
//获取服务项目详情
|
||||
export function getGoodsDetail(data?: any) {
|
||||
return request.get({ url: '/goods/detail', data })
|
||||
}
|
||||
|
||||
//获取技能列表
|
||||
export function getSkillList(data?: any) {
|
||||
return request.get({ url: '/coach/skillLists', data })
|
||||
}
|
||||
|
||||
//提现配置
|
||||
export function withdrawConfig(data?: any) {
|
||||
return request.get({ url: '/withdraw/getWithDrawWay', data })
|
||||
}
|
||||
|
||||
//设置提现配置
|
||||
export function setWithdrawConfig(data?: any) {
|
||||
return request.post({ url: '/withdraw/setWithDrawWay', data })
|
||||
}
|
||||
|
||||
export const getAddress = (params: any) =>
|
||||
request.get({ url: '/city/getNearbyLocation', data: params })
|
||||
|
||||
export const getCityListApi = (params?: any, header?: any) =>
|
||||
request.get({ url: '/city/getCityLists', data: params, header })
|
||||
|
||||
|
||||
/**
|
||||
* @param { Object } params { location: xxx,xxx }
|
||||
* @return { Promise }
|
||||
* @description 地址逆解析
|
||||
*/
|
||||
export const getGeocoderCoordinate = (params: any, header?: any) =>
|
||||
request.get({ url: '/city/geocoderCoordinate', data: params, header })
|
||||
|
||||
//获取附近地址
|
||||
export const getNearbyLocation = (params: any, header?: any) =>
|
||||
request.get({ url: '/city/getNearbyLocation', data: params, header })
|
||||
|
||||
//财务明细列表
|
||||
export function apiFinanceLists(data?: any) {
|
||||
return request.get({ url: '/finance/lists', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取地级市列表 以首字母排序
|
||||
*/
|
||||
export const apiRegionCity = (params: any, header?: any) =>
|
||||
request.get({ url: '/city/city', data: params, header })
|
||||
18
business_uniapp/src/api/cashOut.ts
Normal file
18
business_uniapp/src/api/cashOut.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//获取提现配置
|
||||
export function getCashOutConfig(data?: any) {
|
||||
return request.post({ url: '/withdraw/getWithDrawInfo', data })
|
||||
}
|
||||
|
||||
export function toCashOut(data?: any) {
|
||||
return request.post({ url: '/withdraw/apply', data })
|
||||
}
|
||||
|
||||
export function listData(data?: any) {
|
||||
return request.get({ url: '/withdraw/logLists', data })
|
||||
}
|
||||
|
||||
export function userWithdrawDetail(data?: any) {
|
||||
return request.get({ url: '/withdraw/detail', data })
|
||||
}
|
||||
20
business_uniapp/src/api/decorate.ts
Normal file
20
business_uniapp/src/api/decorate.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 装修数据
|
||||
*/
|
||||
export const getDecoratePage = (params: any) => request.get({ url: '/decorate/page', data: params })
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 装修风格
|
||||
*/
|
||||
export const getDecorateStyle = () => request.get({ url: '/decorate/style' })
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 底部导航
|
||||
*/
|
||||
export const getDecorateTabbar = () => request.get({ url: '/decorate/tabbar' })
|
||||
68
business_uniapp/src/api/goods.ts
Normal file
68
business_uniapp/src/api/goods.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取商品评价列表
|
||||
*/
|
||||
export const apiEvaluateGoodsLists = (params: any) =>
|
||||
request.get({ url: '/goodsComment/lists', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取商品评价分类列表
|
||||
*/
|
||||
export const apiEvaluateGoodsCategory = (params: any) =>
|
||||
request.get({ url: '/goodsComment/commentCategory', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取服务分类
|
||||
*/
|
||||
export const apiGetCategoryList = (params?: any) =>
|
||||
request.get({ url: '/goods/categoryLists', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取技能列表
|
||||
*/
|
||||
export const apiGetSkillList = (params?: any) =>
|
||||
request.get({ url: '/goods/skillLists', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取技能列表
|
||||
*/
|
||||
export const apiAddGoods = (params?: any) => request.post({ url: '/goods/add', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 修改商品状态
|
||||
*/
|
||||
export const apiEditStaus = (params?: any) => request.post({ url: '/goods/status', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 删除商品
|
||||
*/
|
||||
export const apiDelGoods = (params?: any) => request.post({ url: '/goods/del', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取详情数据
|
||||
*/
|
||||
export const apiGetDetail = (params?: any) => request.get({ url: '/goods/detail', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 编辑商品
|
||||
*/
|
||||
export const apiEditGoods = (params?: any) => request.post({ url: '/goods/edit', data: params })
|
||||
41
business_uniapp/src/api/master.ts
Normal file
41
business_uniapp/src/api/master.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//技师申请列表
|
||||
export function getMasterApplyList(data: any) {
|
||||
return request.get({ url: '/coach/applyLists', data })
|
||||
}
|
||||
|
||||
//旗下技师列表
|
||||
export function getMasterList(data: any) {
|
||||
return request.get({ url: '/coach/shopCoachLists', data })
|
||||
}
|
||||
|
||||
//获取申请入驻技师详情
|
||||
export function getMasterInfo(data: any) {
|
||||
return request.get({ url: '/coach/applyDetail', data })
|
||||
}
|
||||
|
||||
//技师审核
|
||||
export function auditMaster(data: any) {
|
||||
return request.post({ url: '/coach/applyAudit', data })
|
||||
}
|
||||
|
||||
//获取技师服务时间
|
||||
export function getWorkTime(data: any) {
|
||||
return request.get({ url: '/coach/getServerTime', data })
|
||||
}
|
||||
|
||||
//设置技师服务时间
|
||||
export function setWorkTime(data: any) {
|
||||
return request.post({ url: '/coach/setServerTime', data })
|
||||
}
|
||||
|
||||
//设置技师工作状态
|
||||
export function coachSetWorkStatus(data: any) {
|
||||
return request.post({ url: '/coach/setWorkStatus', data })
|
||||
}
|
||||
|
||||
//获取技师详情
|
||||
export function getCoachInfo(data: any) {
|
||||
return request.get({ url: '/coach/info', data })
|
||||
}
|
||||
52
business_uniapp/src/api/news.ts
Normal file
52
business_uniapp/src/api/news.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* @description 获取文章分类
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getArticleCate() {
|
||||
return request.get({ url: '/article/cate' })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文章列表
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getArticleList(data: Record<string, any>) {
|
||||
return request.get({ url: '/article/lists', data: data })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文章详情
|
||||
* @param { number } id
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getArticleDetail(data: { id: number }) {
|
||||
return request.get({ url: '/article/detail', data: data })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 加入收藏
|
||||
* @param { number } id
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function addCollect(data: { id: number }) {
|
||||
return request.post({ url: '/article/addCollect', data: data }, { isAuth: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 取消收藏
|
||||
* @param { number } id
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function cancelCollect(data: { id: number }) {
|
||||
return request.post({ url: '/article/cancelCollect', data: data }, { isAuth: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取收藏列表
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getCollect() {
|
||||
return request.get({ url: '/article/collect' })
|
||||
}
|
||||
47
business_uniapp/src/api/order.ts
Normal file
47
business_uniapp/src/api/order.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//获取订单详情
|
||||
export function getOrderDetail(data: Record<string, any>) {
|
||||
return request.get({ url: '/order/detail', data })
|
||||
}
|
||||
|
||||
//接单
|
||||
export function takeOrder(data: Record<string, any>) {
|
||||
return request.post({ url: '/order/take', data })
|
||||
}
|
||||
//出发
|
||||
export function depart(data: Record<string, any>) {
|
||||
return request.post({ url: '/order/depart', data })
|
||||
}
|
||||
//到达
|
||||
export function arrive(data: Record<string, any>) {
|
||||
return request.post({ url: '/order/arrive', data })
|
||||
}
|
||||
//服务开始
|
||||
export function startServer(data: Record<string, any>) {
|
||||
return request.post({ url: '/order/startServer', data })
|
||||
}
|
||||
//服务完成
|
||||
export function finishServer(data: Record<string, any>) {
|
||||
return request.post({ url: '/order/finishServer', data })
|
||||
}
|
||||
|
||||
//更换技师
|
||||
export function changeService(data: Record<string, any>) {
|
||||
return request.post({ url: '/order/dispatchCoach', data })
|
||||
}
|
||||
|
||||
//获取可以更换的技师
|
||||
export function getOrderCoachLists(data: Record<string, any>) {
|
||||
return request.get({ url: '/order/coachLists', data })
|
||||
}
|
||||
|
||||
//技师的订单列表
|
||||
export function getDispatchList(data: Record<string, any>) {
|
||||
return request.get({ url: '/order/coachOrderLists', data })
|
||||
}
|
||||
|
||||
//收入列表
|
||||
export function getInComeLists(data: Record<string, any>) {
|
||||
return request.get({ url: '/order/incomeLists', data })
|
||||
}
|
||||
25
business_uniapp/src/api/pay.ts
Normal file
25
business_uniapp/src/api/pay.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import request from '@/utils/request'
|
||||
import { client } from '@/utils/client'
|
||||
|
||||
//支付方式
|
||||
export function getPayWay(data?: any) {
|
||||
return request.get({ url: '/pay/payWay', data: { ...data, scene: client } }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 预支付
|
||||
export function prepay(data: any) {
|
||||
return request.post({ url: '/pay/prepay', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 预支付
|
||||
export function getPayResult(data: any) {
|
||||
return request.get({ url: '/pay/getPayResult', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
//跳转支付
|
||||
export const apiJumpPayPrepay = (params: any,token:string) => request.post({ url: '/pay/prepay', data: params, header: {token:token}})
|
||||
|
||||
//跳转支付结果
|
||||
export function apiJumpPayResult(params: any,token:string) {
|
||||
return request.get({ url: '/pay/getPayResult', data: params, header: {token:token} })
|
||||
}
|
||||
16
business_uniapp/src/api/recharge.ts
Normal file
16
business_uniapp/src/api/recharge.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//充值
|
||||
export function recharge(data: any) {
|
||||
return request.post({ url: '/recharge/recharge', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
//充值记录
|
||||
export function rechargeRecord(data: any) {
|
||||
return request.get({ url: '/recharge/lists', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 充值配置
|
||||
export function rechargeConfig() {
|
||||
return request.get({ url: '/recharge/config' }, { isAuth: true })
|
||||
}
|
||||
14
business_uniapp/src/api/shop.ts
Normal file
14
business_uniapp/src/api/shop.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
//首页数据
|
||||
export function getIndexData() {
|
||||
return request.get({ url: '/index/shopData' })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 热门搜索
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getHotSearch() {
|
||||
return request.get({ url: '/search/hotLists' })
|
||||
}
|
||||
81
business_uniapp/src/api/user.ts
Normal file
81
business_uniapp/src/api/user.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getUserCenter(header?: any) {
|
||||
return request.get({ url: '/shop/centre', header }, { ignoreCancel: true })
|
||||
}
|
||||
|
||||
export function getShopDetail(header?: any) {
|
||||
return request.get({ url: '/shop/detail', header })
|
||||
}
|
||||
|
||||
// 商家更新资料详情
|
||||
export function getShopUpdatInfo() {
|
||||
return request.get({ url: '/shop/updateInfoDetail' })
|
||||
}
|
||||
|
||||
// 商家更新资料
|
||||
export function shopUpdateInfo(data: any) {
|
||||
return request.post({ url: '/shop/updateInfo', data })
|
||||
}
|
||||
|
||||
// 个人信息
|
||||
export function getUserInfo() {
|
||||
return request.get({ url: '/shop/info' }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 个人编辑
|
||||
export function userEdit(data: any) {
|
||||
return request.post({ url: '/user/setInfo', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 绑定手机
|
||||
export function userBindMobile(data: any, header?: any) {
|
||||
return request.post({ url: '/shop/bindMobile', data, header }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 微信电话
|
||||
export function userMnpMobile(data: any) {
|
||||
return request.post({ url: '/user/getMobileByMnp', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 更改密码
|
||||
export function userChangePwd(data: any) {
|
||||
return request.post({ url: '/login/changePassword', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
//忘记密码
|
||||
export function forgotPassword(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/resetPassword', data })
|
||||
}
|
||||
|
||||
//余额明细
|
||||
export function accountLog(data: any) {
|
||||
return request.get({ url: '/account_log/lists', data })
|
||||
}
|
||||
|
||||
// //修改定位
|
||||
// export function editLocation(data?: any) {
|
||||
// return request.get({ url: '/shop/updateLocation', data })
|
||||
// }
|
||||
|
||||
// 获取营业时间
|
||||
export function getBusiness() {
|
||||
return request.get({ url: '/shop/getBusiness' })
|
||||
}
|
||||
|
||||
// 设置营业时间
|
||||
export function setBusiness(data: any) {
|
||||
return request.post({ url: '/shop/setBusiness', data })
|
||||
}
|
||||
|
||||
//获取保证金套餐
|
||||
export function getDepositList(data?: any) {
|
||||
return request.get({ url: '/deposit/depositPackage', data })
|
||||
}
|
||||
|
||||
//充值保证金
|
||||
export function apiRechargeDeposit(data: any) {
|
||||
return request.post({ url: '/deposit/sumbitOrder', data }, { isAuth: true })
|
||||
}
|
||||
export const getKefuConfig = (params?: any) =>
|
||||
request.post({ url: '/config/getKefuConfig', data: params })
|
||||
90
business_uniapp/src/components/agreement/agreement.vue
Normal file
90
business_uniapp/src/components/agreement/agreement.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<view class="agreement" :class="{ shake: isShake }">
|
||||
<view>
|
||||
<u-checkbox v-model="isActive" shape="circle">
|
||||
<view class="text-base flex">
|
||||
已阅读并同意
|
||||
<view class="text-primary" @click.stop>
|
||||
<router-navigate
|
||||
class="text-primary"
|
||||
to="/pages/agreement/agreement?type=service"
|
||||
>
|
||||
《服务协议》
|
||||
</router-navigate>
|
||||
</view>
|
||||
|
||||
和
|
||||
<view class="text-primary" @click.stop>
|
||||
<router-navigate
|
||||
class="text-primary"
|
||||
to="/pages/agreement/agreement?type=privacy"
|
||||
>
|
||||
《隐私协议》
|
||||
</router-navigate>
|
||||
</view>
|
||||
</view>
|
||||
</u-checkbox>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<modal-popup
|
||||
v-model:show="showPopup"
|
||||
title="服务协议及隐私协议"
|
||||
@confirm="isActive = true"
|
||||
>
|
||||
<template #content>
|
||||
<view>
|
||||
为了更好的保障您的权益,请您阅读并同意 <text class="text-primary">《服务协议》与《隐私政策》</text>
|
||||
</view>
|
||||
</template>
|
||||
</modal-popup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { computed, ref } from 'vue'
|
||||
import ModalPopup from '../modal-popup/modal-popup.vue'
|
||||
const appStore = useAppStore()
|
||||
const showPopup = ref(false)
|
||||
const isActive = ref(false)
|
||||
const isShake = ref(false)
|
||||
|
||||
const checkAgreement = () => {
|
||||
if (!isActive.value) {
|
||||
showPopup.value = true
|
||||
isShake.value = true
|
||||
setTimeout(() => {
|
||||
isShake.value = false
|
||||
}, 1000)
|
||||
}
|
||||
return isActive.value
|
||||
}
|
||||
defineExpose({
|
||||
checkAgreement
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.shake {
|
||||
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
@keyframes shake {
|
||||
10%,
|
||||
90% {
|
||||
transform: translate3d(-1px, 0, 0);
|
||||
}
|
||||
20%,
|
||||
80% {
|
||||
transform: translate3d(2px, 0, 0);
|
||||
}
|
||||
30%,
|
||||
50%,
|
||||
70% {
|
||||
transform: translate3d(-4px, 0, 0);
|
||||
}
|
||||
40%,
|
||||
60% {
|
||||
transform: translate3d(4px, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
108
business_uniapp/src/components/avatar-upload/avatar-upload.vue
Normal file
108
business_uniapp/src/components/avatar-upload/avatar-upload.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<button
|
||||
class="avatar-upload p-0 m-0 rounded"
|
||||
:style="styles"
|
||||
hover-class="none"
|
||||
open-type="chooseAvatar"
|
||||
@click="chooseAvatar"
|
||||
@chooseavatar="chooseAvatar"
|
||||
>
|
||||
<image class="w-full h-full" mode="heightFix" :src="modelValue" v-if="modelValue" />
|
||||
<slot v-else>
|
||||
<div
|
||||
:style="styles"
|
||||
class="border border-dotted border-light flex w-full h-full flex-col items-center justify-center text-muted text-xs box-border rounded"
|
||||
>
|
||||
<u-icon name="plus" :size="36" />
|
||||
添加图片
|
||||
</div>
|
||||
</slot>
|
||||
</button>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { uploadImage } from '@/api/app'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { addUnit } from '@/utils/util'
|
||||
import { isBoolean } from 'lodash-es'
|
||||
import { computed, CSSProperties, onUnmounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String
|
||||
},
|
||||
fileKey: {
|
||||
type: String,
|
||||
default: 'uri'
|
||||
},
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: 120
|
||||
},
|
||||
round: {
|
||||
type: [Boolean, String, Number],
|
||||
default: false
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: string): void
|
||||
}>()
|
||||
const userStore = useUserStore()
|
||||
const styles = computed<CSSProperties>(() => {
|
||||
const size = addUnit(props.size)
|
||||
return {
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: isBoolean(props.round) ? (props.round ? '50%' : '') : addUnit(props.round)
|
||||
}
|
||||
})
|
||||
|
||||
const chooseAvatar = (e: any) => {
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.navigateTo({
|
||||
url: '/uni_modules/vk-uview-ui/components/u-avatar-cropper/u-avatar-cropper?destWidth=300&rectWidth=200&fileType=jpg'
|
||||
})
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
const path = e.detail?.avatarUrl
|
||||
if (path) {
|
||||
uploadImageIng(path)
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
const uploadImageIng = async (file: string) => {
|
||||
uni.showLoading({
|
||||
title: '正在上传中...'
|
||||
})
|
||||
try {
|
||||
const res: any = await uploadImage(file, userStore.temToken!)
|
||||
uni.hideLoading()
|
||||
console.log(res)
|
||||
emit('update:modelValue', res[props.fileKey])
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.$u.toast(error)
|
||||
}
|
||||
}
|
||||
// 监听从裁剪页发布的事件,获得裁剪结果
|
||||
uni.$on('uAvatarCropper', (path) => {
|
||||
uploadImageIng(path)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
uni.$off('uAvatarCropper')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar-upload {
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
30
business_uniapp/src/components/checkbox-mark/index.vue
Normal file
30
business_uniapp/src/components/checkbox-mark/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<view>
|
||||
<view
|
||||
v-if="select"
|
||||
class="bg-primary p-1 w-[50rpx] h-[50rpx] flex items-center justify-center rounded-full"
|
||||
:style="{ background: background }"
|
||||
>
|
||||
<u-icon color="#fff" name="checkbox-mark"></u-icon>
|
||||
</view>
|
||||
<view
|
||||
v-if="!select"
|
||||
class="rounded-full w-[50rpx] h-[50rpx]"
|
||||
style="border: 2px solid #ccc"
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
select: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
344
business_uniapp/src/components/editor/editor.vue
Normal file
344
business_uniapp/src/components/editor/editor.vue
Normal file
@@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="page-body">
|
||||
<view class="wrapper">
|
||||
<view class="grid grid-cols-8" @tap="format">
|
||||
<view :class="formats.bold ? 'ql-active' : ''" class="icon" data-name="bold">
|
||||
B
|
||||
</view>
|
||||
<view
|
||||
:class="formats.italic ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitixieti"
|
||||
data-name="italic"
|
||||
>
|
||||
</view>
|
||||
<view
|
||||
:class="formats.italic ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="italic"
|
||||
>
|
||||
I
|
||||
</view>
|
||||
<view
|
||||
:class="formats.underline ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="underline"
|
||||
>
|
||||
U
|
||||
</view>
|
||||
<view
|
||||
:class="formats.strike ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="strike"
|
||||
>
|
||||
S
|
||||
</view>
|
||||
|
||||
<view
|
||||
:class="formats.align === 'center' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="align"
|
||||
data-value="center"
|
||||
>
|
||||
⬚
|
||||
</view>
|
||||
<view
|
||||
:class="formats.align === 'right' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="align"
|
||||
data-value="right"
|
||||
>
|
||||
➔
|
||||
</view>
|
||||
<view
|
||||
:class="formats.align === 'justify' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="align"
|
||||
data-value="justify"
|
||||
>
|
||||
⬚⬚
|
||||
</view>
|
||||
|
||||
<view class="icon" @tap="removeFormat">⤫</view>
|
||||
|
||||
<view
|
||||
:class="formats.color === '#0000ff' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="color"
|
||||
data-value="#0000ff"
|
||||
>
|
||||
🎨
|
||||
</view>
|
||||
<view
|
||||
:class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="backgroundColor"
|
||||
data-value="#00ff00"
|
||||
>
|
||||
🖌️
|
||||
</view>
|
||||
<view class="icon" @tap="insertDate">📅</view>
|
||||
<view class="icon" data-name="list" data-value="check"> ☑️ </view>
|
||||
<view
|
||||
:class="formats.list === 'ordered' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="list"
|
||||
data-value="ordered"
|
||||
>
|
||||
1.
|
||||
</view>
|
||||
<view
|
||||
:class="formats.list === 'bullet' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="list"
|
||||
data-value="bullet"
|
||||
>
|
||||
•
|
||||
</view>
|
||||
|
||||
<view class="icon" @tap="undo">↶</view>
|
||||
<view class="icon" @tap="redo">↷</view>
|
||||
|
||||
<view class="icon" data-name="indent" data-value="-1">⇤</view>
|
||||
<view class="icon" data-name="indent" data-value="+1">⇥</view>
|
||||
<view class="icon" @tap="insertDivider">─</view>
|
||||
<view class="icon" @tap="insertImage">🖼️</view>
|
||||
<view
|
||||
:class="formats.header === 1 ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="header"
|
||||
:data-value="1"
|
||||
>
|
||||
H1
|
||||
</view>
|
||||
<view
|
||||
:class="formats.script === 'sub' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="script"
|
||||
data-value="sub"
|
||||
>
|
||||
X₂
|
||||
</view>
|
||||
<view
|
||||
:class="formats.script === 'super' ? 'ql-active' : ''"
|
||||
class="icon"
|
||||
data-name="script"
|
||||
data-value="super"
|
||||
>
|
||||
Xⁿ
|
||||
</view>
|
||||
|
||||
<view class="icon" @tap="clear">🗑️</view>
|
||||
<view
|
||||
:class="formats.direction === 'rtl' ? 'ql-active' : ''"
|
||||
class="iconfont icon-direction-rtl"
|
||||
data-name="direction"
|
||||
data-value="rtl"
|
||||
></view>
|
||||
</view>
|
||||
|
||||
<view class="editor-wrapper">
|
||||
<editor
|
||||
id="editor"
|
||||
class="ql-container"
|
||||
:placeholder="placeholder"
|
||||
show-img-size
|
||||
show-img-toolbar
|
||||
show-img-resize
|
||||
@statuschange="onStatusChange"
|
||||
:read-only="readOnly"
|
||||
@blur="onEditorInput"
|
||||
@ready="onEditorReady"
|
||||
>
|
||||
</editor>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入内容...'
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formats: {},
|
||||
cursorPosition: { start: 0, end: 0 }, // 保存光标的起始位置
|
||||
first: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.first) {
|
||||
this.editorCtx.setContents({ html: this.modelValue })
|
||||
this.first = false
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onEditorReady() {
|
||||
// #ifdef APP-PLUS || MP-WEIXIN || H5
|
||||
uni.createSelectorQuery()
|
||||
.select('#editor')
|
||||
.context((res) => {
|
||||
this.editorCtx = res.context
|
||||
this.editorCtx.setContents({ html: this.modelValue })
|
||||
console.log(this.modelValue)
|
||||
})
|
||||
.exec()
|
||||
// #endif
|
||||
},
|
||||
onEditorInput(e) {
|
||||
const htmlContent = e.detail.html
|
||||
// 在更新 modelValue 之前,保存当前光标位置
|
||||
// 在更新 modelValue 之前,保存当前光标位置
|
||||
|
||||
this.$emit('update:modelValue', htmlContent)
|
||||
// // 获取并存储当前光标位置
|
||||
// this.editorCtx.getSelectionText({
|
||||
// success: (res) => {
|
||||
// this.cursorPosition = res.start
|
||||
// }
|
||||
// })
|
||||
},
|
||||
undo() {
|
||||
this.editorCtx.undo()
|
||||
},
|
||||
redo() {
|
||||
this.editorCtx.redo()
|
||||
},
|
||||
format(e) {
|
||||
const { name, value } = e.target.dataset
|
||||
if (!name) return
|
||||
// console.log('format', name, value)
|
||||
this.editorCtx.format(name, value)
|
||||
},
|
||||
onStatusChange(e) {
|
||||
const formats = e.detail
|
||||
this.formats = formats
|
||||
},
|
||||
insertDivider() {
|
||||
this.editorCtx.insertDivider({
|
||||
success: function () {
|
||||
console.log('insert divider success')
|
||||
}
|
||||
})
|
||||
},
|
||||
clear() {
|
||||
uni.showModal({
|
||||
title: '清空编辑器',
|
||||
content: '确定清空编辑器全部内容?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.editorCtx.clear({
|
||||
success: function (res) {
|
||||
console.log('clear success')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
removeFormat() {
|
||||
this.editorCtx.removeFormat()
|
||||
},
|
||||
insertDate() {
|
||||
const date = new Date()
|
||||
const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
|
||||
this.editorCtx.insertText({
|
||||
text: formatDate
|
||||
})
|
||||
},
|
||||
insertImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
const imagePath = res.tempFilePaths[0]
|
||||
|
||||
// 在插入图片前,保存光标位置
|
||||
this.editorCtx.getSelectionText({
|
||||
success: (res) => {
|
||||
// 保存光标的起始位置
|
||||
this.cursorPosition = { start: res.start, end: res.start }
|
||||
|
||||
// 插入图片
|
||||
this.editorCtx.insertImage({
|
||||
src: imagePath,
|
||||
alt: '图片',
|
||||
success: () => {
|
||||
console.log('insert image success')
|
||||
}
|
||||
})
|
||||
|
||||
// 更新 modelValue 后恢复光标位置
|
||||
this.$emit('update:modelValue', this.editorCtx.getContents().html)
|
||||
|
||||
// 恢复光标位置
|
||||
if (this.cursorPosition.start !== null) {
|
||||
this.editorCtx.setSelection({
|
||||
start: this.cursorPosition.start,
|
||||
end: this.cursorPosition.start
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.editor-wrapper {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
display: inline-block;
|
||||
padding: 8px 8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
box-sizing: border-box;
|
||||
border-bottom: 0;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
box-sizing: border-box;
|
||||
padding: 12px 15px;
|
||||
width: 100%;
|
||||
min-height: 30vh;
|
||||
height: 100%;
|
||||
margin-top: 20px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ql-active {
|
||||
color: #06c;
|
||||
}
|
||||
</style>
|
||||
87
business_uniapp/src/components/evaluate-card/index.vue
Normal file
87
business_uniapp/src/components/evaluate-card/index.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<view class="item">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex items-center">
|
||||
<u-image
|
||||
:src="data.avatar"
|
||||
width="80"
|
||||
height="80"
|
||||
borderRadius="50%"
|
||||
></u-image>
|
||||
<view class="flex flex-col justify-between ml-2">
|
||||
<view class="text-base font-medium">
|
||||
{{ data.nickname }}
|
||||
</view>
|
||||
<view class="mt-1 text-muted text-xs">
|
||||
{{ data.create_time }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-rate
|
||||
:count="5"
|
||||
v-model="data.service_comment"
|
||||
:disabled="true"
|
||||
class="ml-auto"
|
||||
inactive-color="#eaeaeb"
|
||||
inactiveIcon="star-fill"
|
||||
active-color="#d86930"
|
||||
></u-rate>
|
||||
</view>
|
||||
<view class="mt-3 break-words text-content comment"> {{ data.comment }}</view>
|
||||
<view class="mt-3 grid gap-2 grid-cols-4">
|
||||
<view
|
||||
v-for="(commentImage, index) in data.goods_comment_image"
|
||||
:key="index"
|
||||
class="mt-[10rpx]"
|
||||
:class="{ 'mr-[10rpx]': (index + 1) % 4 != 0 }"
|
||||
@click.stop="previewImage(data.goods_comment_image, index)"
|
||||
>
|
||||
<u-image
|
||||
:src="commentImage.uri"
|
||||
width="150"
|
||||
height="150"
|
||||
border-radius="14rpx"
|
||||
></u-image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="reply mt-3" v-if="data.reply">
|
||||
<span class="text-sm">商家回复:</span>
|
||||
<span class="text-sm text-content">{{ data.reply }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
data: any
|
||||
}>()
|
||||
|
||||
const previewImage = (url: any, index: number | string) => {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: url.map((item: any) => item.uri)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// .item {
|
||||
// border-bottom: 2rpx solid #f5f5f5;
|
||||
// }
|
||||
|
||||
.comment {
|
||||
line-height: 42rpx;
|
||||
letter-spacing: 3rpx;
|
||||
}
|
||||
|
||||
.reply {
|
||||
line-height: 40rpx;
|
||||
letter-spacing: 3rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 4rpx;
|
||||
background: #F3F3F3;
|
||||
}
|
||||
|
||||
</style>
|
||||
131
business_uniapp/src/components/file-upload/file-upload.vue
Normal file
131
business_uniapp/src/components/file-upload/file-upload.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<u-upload
|
||||
ref="uUploadRef"
|
||||
:file-list="fileList"
|
||||
:action="action"
|
||||
v-bind="props"
|
||||
:header="headers"
|
||||
@on-success="onSuccess"
|
||||
@on-uploaded="onUploaded"
|
||||
@on-progress="onProgress"
|
||||
@on-remove="onRemove"
|
||||
>
|
||||
<slot name="addBtn" />
|
||||
</u-upload>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import config from '@/config'
|
||||
import { RequestCodeEnum } from '@/enums/requestEnums'
|
||||
import { computed, ref, shallowRef, watch } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string | string[]
|
||||
maxCount?: number
|
||||
width?: number | string
|
||||
height?: number | string
|
||||
customBtn?: boolean
|
||||
disabled?: boolean
|
||||
header?: Record<string, any>
|
||||
formData?: Record<string, any>
|
||||
name?: string
|
||||
multiple?: boolean
|
||||
deletable?: boolean
|
||||
withName: boolean
|
||||
}>(),
|
||||
{
|
||||
maxCount: 1,
|
||||
width: 180,
|
||||
height: 180,
|
||||
customBtn: false,
|
||||
disabled: false,
|
||||
header: () => ({}),
|
||||
formData: () => ({}),
|
||||
name: 'file',
|
||||
multiple: true,
|
||||
deletable: true,
|
||||
withName: false
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', modelValue: string | string[]): void
|
||||
}>()
|
||||
|
||||
const uUploadRef = shallowRef()
|
||||
const userStore = useUserStore()
|
||||
const action = `${config.baseUrl}${config.urlPrefix}/upload/image`
|
||||
const fileList = ref<any[]>([])
|
||||
const headers = computed(() => ({
|
||||
token: userStore.token || userStore.temToken,
|
||||
version: config.version,
|
||||
...props.header
|
||||
}))
|
||||
const onSuccess = (data: any, index: number, list: any[]) => {
|
||||
if (data.code !== RequestCodeEnum.SUCCESS) {
|
||||
list[index].progress = 0
|
||||
list[index].error = true
|
||||
uni.$u.toast(data.msg)
|
||||
} else {
|
||||
const url = data.data?.uri
|
||||
let result = url
|
||||
if (!props.withName) {
|
||||
if (props.maxCount <= 1) {
|
||||
result = url
|
||||
} else if (Array.isArray(props.modelValue)) {
|
||||
result = [...props.modelValue, url]
|
||||
} else {
|
||||
result = [url]
|
||||
}
|
||||
} else {
|
||||
result = [...props.modelValue, { url, name: data.data?.name }]
|
||||
}
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
}
|
||||
const isUploading = ref(false)
|
||||
const onProgress = () => {
|
||||
isUploading.value = true
|
||||
}
|
||||
const onUploaded = () => {
|
||||
isUploading.value = false
|
||||
}
|
||||
const onRemove = (index: number) => {
|
||||
if (props.maxCount <= 1) {
|
||||
emit('update:modelValue', '')
|
||||
} else {
|
||||
if (Array.isArray(props.modelValue)) {
|
||||
const newValue = props.modelValue
|
||||
newValue.splice(index, 1)
|
||||
emit('update:modelValue', newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
if (!value || isUploading.value) return
|
||||
uUploadRef.value?.clear()
|
||||
if (!props.withName) {
|
||||
if (Array.isArray(value)) {
|
||||
fileList.value = value.map((url) => ({ url }))
|
||||
} else if (typeof value === 'string') {
|
||||
fileList.value = [{ url: value }]
|
||||
}
|
||||
} else {
|
||||
console.log(value)
|
||||
fileList.value = (value as []).map((item: any) => {
|
||||
return { url: item.url }
|
||||
})
|
||||
// fileList.value = value.map((item) => {
|
||||
// item.url
|
||||
// })
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
97
business_uniapp/src/components/l-swiper/l-swiper.vue
Normal file
97
business_uniapp/src/components/l-swiper/l-swiper.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<u-swiper
|
||||
v-if="lists.length"
|
||||
:list="lists"
|
||||
:mode="mode"
|
||||
:height="height"
|
||||
:effect3d="effect3d"
|
||||
:indicator-pos="indicatorPos"
|
||||
:autoplay="autoplay"
|
||||
:interval="interval"
|
||||
:duration="duration"
|
||||
:circular="circular"
|
||||
:borderRadius="borderRadius"
|
||||
:current="current"
|
||||
:name="name"
|
||||
:bg-color="bgColor"
|
||||
@click="handleClick"
|
||||
@change="handleChange"
|
||||
>
|
||||
</u-swiper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useAppStore} from "@/stores/app";
|
||||
import {navigateTo, navigateToMiniProgram, LinkTypeEnum} from "@/utils/util";
|
||||
import {watchEffect, computed} from "vue";
|
||||
import {useRouter} from "uniapp-router-next";
|
||||
|
||||
const emit = defineEmits(["change"]);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
content?: any; // 轮播图数据
|
||||
mode?: string; // 指示器模式 rect / dot / number / none
|
||||
height?: string; // 轮播图组件高度
|
||||
indicatorPos?: string; // 指示器的位置 topLeft / topCenter / topRight / bottomLeft / bottomRight
|
||||
effect3d?: boolean; // 是否开启3D效果
|
||||
autoplay?: boolean; // 是否自动播放
|
||||
interval?: number | string; // 自动轮播时间间隔,单位ms
|
||||
duration?: number | string; // 切换一张轮播图所需的时间,单位ms
|
||||
circular?: boolean; // 是否衔接播放
|
||||
current?: number; // 默认显示第几项
|
||||
name?: string; // 显示的属性
|
||||
borderRadius?: string; //轮播图圆角值,单位rpx
|
||||
bgColor?: string; // 背景颜色
|
||||
}>(),
|
||||
{
|
||||
content: {
|
||||
data: [],
|
||||
},
|
||||
mode: "round",
|
||||
indicatorPos: "bottomCenter",
|
||||
height: "340",
|
||||
effect3d: false,
|
||||
autoplay: true,
|
||||
interval: "2500",
|
||||
duration: 300,
|
||||
circular: true,
|
||||
current: 0,
|
||||
name: "image",
|
||||
borderRadius: "0",
|
||||
bgColor: "#f3f4f6",
|
||||
}
|
||||
);
|
||||
|
||||
const {getImageUrl} = useAppStore();
|
||||
|
||||
watchEffect(() => {
|
||||
try {
|
||||
const content = props?.content;
|
||||
const len = content?.data?.length;
|
||||
if (!len) return;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const item = content.data[i];
|
||||
item.image = getImageUrl(item.image);
|
||||
}
|
||||
emit("change", 0);
|
||||
} catch (error) {
|
||||
//TODO handle the exception
|
||||
console.log("轮播图数据错误", error);
|
||||
}
|
||||
});
|
||||
|
||||
const lists = computed(() => props.content.data || []);
|
||||
const router = useRouter();
|
||||
|
||||
const handleClick = (index: number) => {
|
||||
const link = props.content.data[index]?.link;
|
||||
if (!link) return
|
||||
navigateTo(link);
|
||||
};
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
emit("change", index);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
51
business_uniapp/src/components/master-card/master-card.vue
Normal file
51
business_uniapp/src/components/master-card/master-card.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<view class="rounded-[20rpx] bg-white p-[20rpx] flex mb-[20rpx]" @click="$emit('toDetail', data.id)">
|
||||
<u-image :src="data?.work_photo" :height="200" :width="200" :border-radius="20"></u-image>
|
||||
<view class="flex flex-col justify-between flex-1 ml-2">
|
||||
<view class="font-bold text-lg mb-1">{{ data?.name }}</view>
|
||||
<view class="text-sm flex justify-between mb-1">
|
||||
<view>
|
||||
<text class="text-info">已服务</text>
|
||||
<text>{{ data?.order_num }}</text>
|
||||
<text class="text-info">人</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="text-info">好评率</text>
|
||||
<text>{{ data?.good_comment }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex mb-1" v-if="data?.skill_name">
|
||||
<text class="mr-1 text-sm text-info tag">{{ data?.skill_name }}</text>
|
||||
</view>
|
||||
|
||||
<slot>
|
||||
<view class="flex justify-end">
|
||||
<view class="bg-[#0b66ef15] text-[#0B66EF] py-[10rpx] px-[30rpx] rounded-full">
|
||||
查看详情
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: (): void => {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['toDetail'])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tag {
|
||||
padding: 6rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
background: var(--, #F6F7F8);
|
||||
}
|
||||
</style>
|
||||
131
business_uniapp/src/components/modal-popup/modal-popup.vue
Normal file
131
business_uniapp/src/components/modal-popup/modal-popup.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<u-popup
|
||||
v-model="showPopup"
|
||||
mode="center"
|
||||
:mask-close-able="false"
|
||||
:customStyle="{
|
||||
'background': `none`
|
||||
}"
|
||||
:closeable="closeable"
|
||||
@close="emits('close')"
|
||||
:border-radius="20"
|
||||
>
|
||||
<view
|
||||
style="width: 600rpx;border-radius: 20rpx;"
|
||||
class="modal-popup p-[40rpx] text-center"
|
||||
>
|
||||
<view class="py-2 font-medium text-3xl text-black relative z-10">
|
||||
{{ title }}
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="py-[16px] text-base text-content relative z-10"
|
||||
style="width: 500rpx; margin: 0 auto;"
|
||||
>
|
||||
<slot name="content">{{ content }}</slot>
|
||||
</view>
|
||||
|
||||
<slot name="footer">
|
||||
<view
|
||||
class="flex gap-[20rpx] mt-[40rpx]"
|
||||
>
|
||||
<view class="flex-1">
|
||||
<u-button
|
||||
@click="cancel"
|
||||
>
|
||||
取消
|
||||
</u-button>
|
||||
</view>
|
||||
|
||||
<view class="flex-1">
|
||||
<u-button
|
||||
type="primary"
|
||||
@click="confirm"
|
||||
>
|
||||
确定
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
show: boolean
|
||||
title: string
|
||||
content: string
|
||||
closeable: boolean
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(event: 'update:show', show: boolean): void
|
||||
(event: 'update', value: any): void
|
||||
(event: 'refresh'): void
|
||||
(event: 'close'): void
|
||||
(event: 'cancel'): void
|
||||
(event: 'confirm'): void
|
||||
}>()
|
||||
|
||||
const showPopup = computed({
|
||||
get() {
|
||||
return props.show
|
||||
},
|
||||
set(val) {
|
||||
emits('update:show', val)
|
||||
}
|
||||
})
|
||||
|
||||
const cancel = () => {
|
||||
showPopup.value = false
|
||||
emits('cancel')
|
||||
}
|
||||
|
||||
const confirm = async () => {
|
||||
showPopup.value = false
|
||||
emits('confirm')
|
||||
|
||||
// 检测定位权限, 如果是就打开设置
|
||||
if (props.title === '定位权限未授权') {
|
||||
const settings = await uni.openSetting()
|
||||
if (settings.authSetting['scope.userLocation']) {
|
||||
emits('refresh') // 重新获取定位
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.modal-popup {
|
||||
position: relative;
|
||||
background: linear-gradient(to bottom, var(--color-primary-light-9) 0%, #fff 50%, #fff 100%);
|
||||
z-index: 2; // 确保内容的z-index高于伪元素
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
opacity: 0.3;
|
||||
border-radius: 50%;
|
||||
z-index: 1; // 确保伪元素的z-index低于内容
|
||||
}
|
||||
|
||||
&::before {
|
||||
left: -50px;
|
||||
top: -90px;
|
||||
background: linear-gradient(200deg, var(--color-primary-light-3) 0%, #fff 100%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: -50px;
|
||||
top: -90px;
|
||||
background: linear-gradient(150deg, var(--color-primary-light-3) 0%, #fff 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,93 @@
|
||||
<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-bold">{{ 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"></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 { computed, ref } from 'vue'
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean
|
||||
},
|
||||
logo: {
|
||||
type: String
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
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 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>
|
||||
65
business_uniapp/src/components/news-card/news-card.vue
Normal file
65
business_uniapp/src/components/news-card/news-card.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<navigator :url="`/pages/news_detail/news_detail?id=${newsId}`">
|
||||
<view class="news-card flex bg-white px-[20rpx] py-[32rpx]">
|
||||
<view class="mr-[20rpx]" v-if="item.image">
|
||||
<u-image :src="item.image" width="240" height="180"></u-image>
|
||||
</view>
|
||||
<view class="news-card-content flex flex-col justify-between flex-1">
|
||||
<view class="news-card-content-title text-base">{{ item.title }}</view>
|
||||
<view class="news-card-content-intro text-gray-400 text-sm mt-[16rpx]">
|
||||
{{ item.desc }}
|
||||
</view>
|
||||
|
||||
<view class="text-muted text-xs w-full flex justify-between mt-[12rpx]">
|
||||
<view>{{ item.create_time }}</view>
|
||||
<view class="flex items-center">
|
||||
<image
|
||||
src="/static/images/icon/icon_visit.png"
|
||||
class="w-[30rpx] h-[30rpx]"
|
||||
></image>
|
||||
<view class="ml-[10rpx]">{{ item.click }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item: any
|
||||
newsId: number
|
||||
}>(),
|
||||
{
|
||||
item: {},
|
||||
newsId: ''
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-card {
|
||||
border-bottom: 1px solid #f8f8f8;
|
||||
&-content {
|
||||
&-title {
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
&-intro {
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
16
business_uniapp/src/components/no-login/no-login.vue
Normal file
16
business_uniapp/src/components/no-login/no-login.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<view class="flex flex-col items-center justify-center">
|
||||
<u-image width="400" height="400" :src="empty"></u-image>
|
||||
<view class="text-info text-sm mt-2">您当前未登录,登录账号可查看信息</view>
|
||||
<u-button @click="toLogin" class="mt-2 w-[300rpx]" type="primary">去登录</u-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import empty from '@/static/images/empty.png'
|
||||
import { navigateTo } from '@/utils/util'
|
||||
|
||||
const toLogin = () => {
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
}
|
||||
</script>
|
||||
118
business_uniapp/src/components/orderCard/index.vue
Normal file
118
business_uniapp/src/components/orderCard/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<view @click="toDetail" class="bg-white rounded-lg px-[24rpx] py-[20rpx] mb-2">
|
||||
<!-- 订单头部 -->
|
||||
<view
|
||||
class="u-flex justify-between"
|
||||
:class="{
|
||||
'gray-effect': data.order_status === 7
|
||||
}"
|
||||
>
|
||||
<view
|
||||
style="padding: 3rpx 8rpx; border-radius: 8rpx"
|
||||
class="u-flex bg-primary-light-9 text-primary"
|
||||
>
|
||||
<u-icon name="calendar" size="26"></u-icon>
|
||||
<text class="ml-1 text-xs">
|
||||
上门时间:{{ data.appoint_date }} {{ data.appoint_time }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
class="text-primary text-xs"
|
||||
:class="{
|
||||
'text-[#3DA0FD]': data.order_status === 1,
|
||||
'text-[#FD463D]': data.order_status === 2,
|
||||
'text-[#3DA0FD]': data.order_status === 3,
|
||||
'text-[#333333]': data.order_status === 4,
|
||||
'text-[#E86016]': data.order_status === 5,
|
||||
'text-[#E86016]': data.order_status === 6,
|
||||
'text-[#333333]': data.order_status === 7
|
||||
}"
|
||||
>
|
||||
{{ data.order_status_desc }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-for="item in data.order_goods" :key="item.order_id" class="flex items-center mt-3">
|
||||
<view class="flex-none">
|
||||
<u-image
|
||||
width="140"
|
||||
height="140"
|
||||
border-radius="20"
|
||||
:src="item.goods_image"
|
||||
></u-image>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="data?.order_goods?.length > 0"
|
||||
class="flex-1 min-w-0 flex ml-2 flex-col justify-between"
|
||||
>
|
||||
<view class="text-base text-main font-bold line-clamp-1">
|
||||
{{ item.goods_name || '' }}
|
||||
</view>
|
||||
<view class="text-xs text-muted font-medium my-1"
|
||||
>服务时间:{{ item.duration || '' }}分钟</view
|
||||
>
|
||||
<price
|
||||
font-weight="600"
|
||||
:content="item.goods_price || 0"
|
||||
main-size="32rpx"
|
||||
minor-size="20rpx"
|
||||
color="#E86016"
|
||||
></price>
|
||||
</view>
|
||||
<view class="ml-auto text-xs text-muted">x {{ item.goods_num }}</view>
|
||||
</view>
|
||||
<view class="bg-[#F6F7F8] p-[12rpx] mt-2 text-muted text-xs font-medium">
|
||||
<view>
|
||||
<text class="font-text-content">服务地址:</text>
|
||||
<text
|
||||
>{{ data.address_snap.province }} {{ data.address_snap.city }}
|
||||
{{ data.address_snap.district }} {{ data.address_snap.address }}
|
||||
{{ data.address_snap?.house_number }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="mt-1 line-clamp-2">
|
||||
<text class="font-text-content">用户备注:</text>
|
||||
<text>{{ data.user_remark || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex justify-between items-center mt-4">
|
||||
<view class="text-main">
|
||||
<text class="text-xs">合计</text>
|
||||
|
||||
<price
|
||||
font-weight="600"
|
||||
:content="data.total_order_amount"
|
||||
main-size="40rpx"
|
||||
minor-size="24rpx"
|
||||
color="#333333"
|
||||
></price>
|
||||
</view>
|
||||
|
||||
<view @click.stop>
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const prop = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
//跳转至详情页
|
||||
const toDetail = () => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/order_detail/order_detail?id=${prop.data.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.gray-effect {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
</style>
|
||||
62
business_uniapp/src/components/page-status/page-status.vue
Normal file
62
business_uniapp/src/components/page-status/page-status.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<view
|
||||
class="page-status"
|
||||
v-if="status !== PageStatusEnum['NORMAL']"
|
||||
:class="{ 'page-status--fixed': fixed }"
|
||||
>
|
||||
<!-- Loading -->
|
||||
<template v-if="status === PageStatusEnum['LOADING']">
|
||||
<slot name="loading">
|
||||
<u-loading :size="60" mode="flower" />
|
||||
</slot>
|
||||
</template>
|
||||
<!-- Error -->
|
||||
<template v-if="status === PageStatusEnum['ERROR']">
|
||||
<slot name="error"></slot>
|
||||
</template>
|
||||
<!-- Empty -->
|
||||
<template v-if="status === PageStatusEnum['EMPTY']">
|
||||
<slot name="empty"></slot>
|
||||
</template>
|
||||
</view>
|
||||
<template v-else>
|
||||
<slot> </slot>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PageStatusEnum } from '@/enums/appEnums'
|
||||
|
||||
const props = defineProps({
|
||||
status: {
|
||||
type: String,
|
||||
default: PageStatusEnum['LOADING']
|
||||
},
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-status {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
&--fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 900;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
322
business_uniapp/src/components/payment/payment.vue
Normal file
322
business_uniapp/src/components/payment/payment.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<u-popup
|
||||
v-model="showPay"
|
||||
mode="bottom"
|
||||
safe-area-inset-bottom
|
||||
:mask-close-able="false"
|
||||
border-radius="14"
|
||||
closeable
|
||||
@close="handleClose"
|
||||
>
|
||||
<view class="h-[900rpx]">
|
||||
<page-status :status="popupStatus" :fixed="false">
|
||||
<template #error>
|
||||
<u-empty text="订单信息错误,无法查询到订单信息" mode="order"></u-empty>
|
||||
</template>
|
||||
<template #default>
|
||||
<view class="payment h-full w-full flex flex-col">
|
||||
<view class="header py-[50rpx] flex flex-col items-center">
|
||||
<price
|
||||
:content="payData.order_amount"
|
||||
mainSize="44rpx"
|
||||
minorSize="40rpx"
|
||||
fontWeight="500"
|
||||
color="#333"
|
||||
></price>
|
||||
</view>
|
||||
<view class="main flex-1 mx-[20rpx]">
|
||||
<view>
|
||||
<view class="payway-lists">
|
||||
<u-radio-group v-model="payWay" class="w-full">
|
||||
<view
|
||||
class="p-[20rpx] flex items-center w-full payway-item"
|
||||
v-for="(item, index) in payData.lists"
|
||||
:key="index"
|
||||
@click="selectPayWay(item.pay_way)"
|
||||
>
|
||||
<u-icon
|
||||
class="flex-none"
|
||||
:size="48"
|
||||
:name="item.icon"
|
||||
></u-icon>
|
||||
<view class="mx-[16rpx] flex-1">
|
||||
<view class="payway-item--name flex-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<view class="text-muted text-xs">{{
|
||||
item.extra
|
||||
}}</view>
|
||||
</view>
|
||||
|
||||
<u-radio activeColor="var(--color-primary)" class="mr-[-20rpx]" :name="item.pay_way">
|
||||
</u-radio>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="submit-btn p-[20rpx]">
|
||||
<u-button
|
||||
@click="handlePay"
|
||||
shape="circle"
|
||||
type="primary"
|
||||
:loading="isLock"
|
||||
>
|
||||
立即支付
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</page-status>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-popup
|
||||
class="pay-popup"
|
||||
v-model="showCheckPay"
|
||||
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="queryPayResult(false)"
|
||||
>
|
||||
重新支付
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<u-button
|
||||
shape="circle"
|
||||
type="primary"
|
||||
size="medium"
|
||||
hover-class="none"
|
||||
:customStyle="{ width: '100%' }"
|
||||
@click="queryPayResult()"
|
||||
>
|
||||
已完成支付
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// import { pay, PayWayEnum } from '@/utils/pay'
|
||||
import { getPayWay, prepay, getPayResult } from '@/api/pay'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useLockFn } from '@/hooks/useLockFn'
|
||||
import { series } from '@/utils/util'
|
||||
import { ClientEnum, PageStatusEnum, PayStatusEnum } from '@/enums/appEnums'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { client } from '@/utils/client'
|
||||
/*
|
||||
页面参数 orderId:订单id,from:订单来源
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
showCheck: {
|
||||
type: Boolean
|
||||
},
|
||||
// 订单id
|
||||
orderId: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
//订单来源
|
||||
from: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
//h5微信支付回跳路径,一般为拉起支付的页面路径
|
||||
redirect: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:showCheck', 'update:show', 'close', 'success', 'fail'])
|
||||
|
||||
const payWay = ref()
|
||||
const popupStatus = ref(PageStatusEnum.LOADING)
|
||||
const payData = ref<any>({
|
||||
order_amount: '',
|
||||
lists: []
|
||||
})
|
||||
|
||||
const showCheckPay = computed({
|
||||
get() {
|
||||
return props.showCheck
|
||||
},
|
||||
set(value) {
|
||||
emit('update:showCheck', value)
|
||||
}
|
||||
})
|
||||
|
||||
const showPay = computed({
|
||||
get() {
|
||||
return props.show
|
||||
},
|
||||
set(value) {
|
||||
emit('update:show', value)
|
||||
}
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
showPay.value = false
|
||||
emit('close')
|
||||
}
|
||||
const getPayData = async () => {
|
||||
popupStatus.value = PageStatusEnum.LOADING
|
||||
try {
|
||||
payData.value = await getPayWay({
|
||||
order_id: props.orderId,
|
||||
from: props.from
|
||||
})
|
||||
popupStatus.value = PageStatusEnum.NORMAL
|
||||
const checkPay =
|
||||
payData.value.lists.find((item: any) => item.is_default) || payData.value.lists[0]
|
||||
payWay.value = checkPay?.pay_way
|
||||
} catch (error) {
|
||||
popupStatus.value = PageStatusEnum.ERROR
|
||||
}
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
const selectPayWay = (pay: number) => {
|
||||
payWay.value = pay
|
||||
}
|
||||
const payment = (() => {
|
||||
// 查询是否绑定微信
|
||||
const checkIsBindWx = async () => {
|
||||
if (
|
||||
userStore.userInfo.is_auth == 0 &&
|
||||
[ClientEnum.OA_WEIXIN, ClientEnum.MP_WEIXIN].includes(client) &&
|
||||
payWay.value == PayWayEnum.WECHAT
|
||||
) {
|
||||
const res: any = await uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '当前账号未绑定微信,无法完成支付',
|
||||
confirmText: '去绑定'
|
||||
})
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user_set/user_set'
|
||||
})
|
||||
}
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
|
||||
// 调用预支付
|
||||
const prepayTask = async () => {
|
||||
uni.showLoading({
|
||||
title: '正在支付中'
|
||||
})
|
||||
const data = await prepay({
|
||||
order_id: props.orderId,
|
||||
from: props.from,
|
||||
pay_way: payWay.value,
|
||||
redirect: props.redirect
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
//拉起支付
|
||||
const payTask = async (data: any) => {
|
||||
try {
|
||||
const res = await pay.payment(data.pay_way, data.config)
|
||||
return res
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
return series(checkIsBindWx, prepayTask, payTask)
|
||||
})()
|
||||
const { isLock, lockFn: handlePay } = useLockFn(async () => {
|
||||
try {
|
||||
const res: PayStatusEnum = await payment()
|
||||
handlePayResult(res)
|
||||
uni.hideLoading()
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
|
||||
const handlePayResult = (status: PayStatusEnum) => {
|
||||
switch (status) {
|
||||
case PayStatusEnum.SUCCESS:
|
||||
emit('success')
|
||||
break
|
||||
case PayStatusEnum.FAIL:
|
||||
emit('fail')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const queryPayResult = async (confirm = true) => {
|
||||
const res = await getPayResult({
|
||||
order_id: props.orderId,
|
||||
from: props.from
|
||||
})
|
||||
|
||||
if (res.pay_status === 0) {
|
||||
if (confirm == true) {
|
||||
uni.$u.toast('您的订单还未支付,请重新支付')
|
||||
}
|
||||
showPay.value = true
|
||||
handlePayResult(PayStatusEnum.FAIL)
|
||||
} else {
|
||||
if (confirm == false) {
|
||||
uni.$u.toast('您的订单已经支付,请勿重新支付')
|
||||
}
|
||||
handlePayResult(PayStatusEnum.SUCCESS)
|
||||
}
|
||||
showCheckPay.value = false
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(value) => {
|
||||
if (value) {
|
||||
if (!props.orderId) {
|
||||
popupStatus.value = PageStatusEnum.ERROR
|
||||
return
|
||||
}
|
||||
getPayData()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.payway-lists {
|
||||
.payway-item {
|
||||
border-bottom: 1px solid;
|
||||
@apply border-page;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
126
business_uniapp/src/components/price/price.vue
Normal file
126
business_uniapp/src/components/price/price.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<view class="price-container">
|
||||
<view
|
||||
:class="['price-wrap', { 'price-wrap--disabled': lineThrough }]"
|
||||
:style="{ color: color }"
|
||||
>
|
||||
<!-- Prefix -->
|
||||
<view class="fix-pre" :style="{ fontSize: minorSize }">
|
||||
<slot name="prefix">{{ prefix }}</slot>
|
||||
</view>
|
||||
|
||||
<!-- Content -->
|
||||
<view :style="{ 'font-weight': fontWeight }">
|
||||
<!-- Integer -->
|
||||
<text :style="{ fontSize: mainSize }">{{ integer }}</text>
|
||||
<!-- Decimals -->
|
||||
<text :style="{ fontSize: minorSize }">{{ decimals }}</text>
|
||||
</view>
|
||||
|
||||
<!-- Suffix -->
|
||||
<view class="fix-suf" :style="{ fontSize: minorSize }">
|
||||
<slot name="suffix">{{ suffix }}</slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* @description 价格展示,适用于有前后缀,小数样式不一
|
||||
* @property {String|Number} content 价格 (必填项)
|
||||
* @property {Number} prec 小数位 (默认: 2)
|
||||
* @property {Boolean} autoPrec 自动小数位【注:以prec为最大小数位】 (默认: true)
|
||||
* @property {String} color 颜色 (默认: 'unset')
|
||||
* @property {String} mainSize 主要内容字体大小 (默认: 46rpx)
|
||||
* @property {String} minorSize 主要内容字体大小 (默认: 32rpx)
|
||||
* @property {Boolean} lineThrough 贯穿线 (默认: false)
|
||||
* @property {String|Number} fontWeight 字重 (默认: normal)
|
||||
* @property {String} prefix 前缀 (默认: ¥)
|
||||
* @property {String} suffix 后缀
|
||||
* @example <price content="100" suffix="\/元" />
|
||||
*/
|
||||
import { computed } from 'vue'
|
||||
import { formatPrice } from '@/utils/util'
|
||||
|
||||
/** Props Start **/
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
content: string | number // 标题
|
||||
prec?: number // 小数数量
|
||||
autoPrec?: boolean // 动态小数
|
||||
color?: string // 颜色
|
||||
mainSize?: string // 主要内容字体大小
|
||||
minorSize?: string // 次要内容字体大小
|
||||
lineThrough?: boolean // 贯穿线
|
||||
fontWeight?: string // 字重
|
||||
prefix?: string // 前缀
|
||||
suffix?: string // 后缀
|
||||
}>(),
|
||||
{
|
||||
content: '',
|
||||
prec: 2,
|
||||
autoPrec: true,
|
||||
color: '#FA8919',
|
||||
mainSize: '36rpx',
|
||||
minorSize: '28rpx',
|
||||
lineThrough: false,
|
||||
fontWeight: 'normal',
|
||||
prefix: '¥',
|
||||
suffix: ''
|
||||
}
|
||||
)
|
||||
/** Props End **/
|
||||
|
||||
/** Computed Start **/
|
||||
/**
|
||||
* @description 金额主体部分
|
||||
*/
|
||||
const integer = computed(() => {
|
||||
return formatPrice({
|
||||
price: props.content,
|
||||
take: 'int'
|
||||
})
|
||||
})
|
||||
/**
|
||||
* @description 金额小数部分
|
||||
*/
|
||||
const decimals = computed(() => {
|
||||
let decimals = formatPrice({
|
||||
price: props.content,
|
||||
take: 'dec',
|
||||
prec: props.prec
|
||||
})
|
||||
// 小数余十不能是 .10||.20||.30以此类推,
|
||||
decimals = decimals % 10 == 0 ? decimals.substr(0, decimals.length - 1) : decimals
|
||||
return props.autoPrec ? (decimals * 1 ? '.' + decimals : '') : props.prec ? '.' + decimals : ''
|
||||
})
|
||||
/** Computed End **/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.price-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.price-wrap {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
&--disabled {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translateY(-50%);
|
||||
display: block;
|
||||
content: '';
|
||||
height: 0.05em;
|
||||
background-color: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
53
business_uniapp/src/components/projectCard/index.vue
Normal file
53
business_uniapp/src/components/projectCard/index.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<view class="bg-white rounded-[24rpx] p-2 flex mb-2" @click="toDetail">
|
||||
<u-image
|
||||
class="flex-none"
|
||||
border-radius="20rpx"
|
||||
height="160rpx"
|
||||
width="160rpx"
|
||||
:src="data.image"
|
||||
/>
|
||||
<view class="ml-2 h-full flex-1 min-w-0">
|
||||
<view class="font-bold text-base line-clamp-2">{{ data.name }}</view>
|
||||
<view class="mt-2">
|
||||
<price fontWeight="900" :content="data.price"></price>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="showCheckbox" class="ml-auto pl-4 flex items-center" @tap.stop="clickCheck">
|
||||
<checkboxMark :select="isSelect"/>
|
||||
</view>
|
||||
<view v-else class="ml-auto flex items-center ml-2 text-muted">
|
||||
<u-icon name="arrow-right" class="flex-none" :size="30"/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import price from '../price/price.vue'
|
||||
import checkboxMark from '@/components/checkbox-mark/index.vue'
|
||||
|
||||
const props = defineProps({
|
||||
data: {} as any,
|
||||
isSelect: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['checkChange'])
|
||||
|
||||
const clickCheck = () => {
|
||||
// isSelect.value = !isSelect.value
|
||||
emit('checkChange', props.data.id)
|
||||
}
|
||||
|
||||
const toDetail = () => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/project_detail/project_detail?id=${props.data.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
84
business_uniapp/src/components/tab/tab.vue
Normal file
84
business_uniapp/src/components/tab/tab.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<view
|
||||
:class="{ active, inactive: !active, tab: true }"
|
||||
:style="shouldShow ? '' : 'display: none;'"
|
||||
>
|
||||
<slot v-if="shouldRender"></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, provide, inject, watch, computed, onMounted, getCurrentInstance } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
dot?: boolean | string
|
||||
name?: boolean | string
|
||||
info?: any
|
||||
}>(),
|
||||
{
|
||||
dot: false,
|
||||
name: ''
|
||||
}
|
||||
)
|
||||
|
||||
const active = ref<boolean>(false)
|
||||
const shouldShow = ref<boolean>(false)
|
||||
const shouldRender = ref<boolean>(false)
|
||||
const inited = ref(undefined)
|
||||
|
||||
const updateTabs: any = inject('updateTabs')
|
||||
const handleChange: any = inject('handleChange')
|
||||
|
||||
const updateRender = (value) => {
|
||||
inited.value = inited.value || value
|
||||
active.value = value
|
||||
shouldRender.value = inited.value!
|
||||
shouldShow.value = value
|
||||
}
|
||||
const update = () => {
|
||||
if (updateTabs) {
|
||||
updateTabs()
|
||||
}
|
||||
}
|
||||
|
||||
const instance = getCurrentInstance()
|
||||
console.log(instance)
|
||||
handleChange(instance?.props, updateRender)
|
||||
|
||||
onMounted(() => {
|
||||
update()
|
||||
})
|
||||
|
||||
const changeData = computed(() => {
|
||||
const { dot, info } = props
|
||||
return {
|
||||
dot,
|
||||
info
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => changeData.value,
|
||||
() => {
|
||||
update()
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.name,
|
||||
(val) => {
|
||||
update()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tab.active {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tab.inactive {
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
60
business_uniapp/src/components/tabbar/tabbar.vue
Normal file
60
business_uniapp/src/components/tabbar/tabbar.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<u-tabbar
|
||||
v-model="current"
|
||||
v-bind="tabbarStyle"
|
||||
:list="tabbarList"
|
||||
:hide-tab-bar="true"
|
||||
@change="handleChange"
|
||||
></u-tabbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {useAppStore} from '@/stores/app'
|
||||
import {navigateTo} from '@/utils/util'
|
||||
import {computed, ref, onMounted} from 'vue'
|
||||
import {getDecorateTabbar} from "@/api/decorate";
|
||||
|
||||
const current = ref()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const tabbarStyle = ref({
|
||||
activeColor: '#007AFF',
|
||||
inactiveColor: '#999999'
|
||||
})
|
||||
const tabbarList = ref<any>([])
|
||||
|
||||
const getTabbarList = async () => {
|
||||
const res = await getDecorateTabbar()
|
||||
const data = JSON.parse(res.data)
|
||||
tabbarStyle.value = {
|
||||
activeColor: data.style.selected_color,
|
||||
inactiveColor: data.style.default_color
|
||||
}
|
||||
tabbarList.value = data.list?.filter((item: any) => item.is_show == 1)
|
||||
.map((item: any) => {
|
||||
return {
|
||||
iconPath: item.unselected,
|
||||
selectedIconPath: item.selected,
|
||||
text: item.name,
|
||||
link: item.link,
|
||||
pagePath: item.link.path
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const nativeTabbar = [
|
||||
'/pages/index/index',
|
||||
'/pages/order/order',
|
||||
'/pages/income/income',
|
||||
'/pages/user/user'
|
||||
]
|
||||
const handleChange = (index: number) => {
|
||||
const selectTab = tabbarList.value[index]
|
||||
const navigateType = nativeTabbar.includes(selectTab.link.path) ? 'switchTab' : 'reLaunch'
|
||||
navigateTo(selectTab.link, navigateType)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTabbarList()
|
||||
})
|
||||
</script>
|
||||
72
business_uniapp/src/components/tabs/hooks/useTouch.ts
Normal file
72
business_uniapp/src/components/tabs/hooks/useTouch.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
/**
|
||||
* @description 触碰屏幕钩子函数
|
||||
* @return { Function } 暴露钩子
|
||||
*/
|
||||
export function useTouch() {
|
||||
// 最小移动距离
|
||||
const MIN_DISTANCE = 10
|
||||
|
||||
const touch = reactive<any>({
|
||||
direction: '',
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
offsetX: 0,
|
||||
offsetY: 0
|
||||
})
|
||||
|
||||
/**
|
||||
* @description 计算距离
|
||||
* @return { string } 空字符串
|
||||
*/
|
||||
const getDirection = (x: number, y: number) => {
|
||||
if (x > y && x > MIN_DISTANCE) {
|
||||
return 'horizontal'
|
||||
}
|
||||
if (y > x && y > MIN_DISTANCE) {
|
||||
return 'vertical'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 重置参数
|
||||
*/
|
||||
const resetTouchStatus = () => {
|
||||
touch.direction = ''
|
||||
touch.deltaX = 0
|
||||
touch.deltaY = 0
|
||||
touch.offsetX = 0
|
||||
touch.offsetY = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 触发
|
||||
*/
|
||||
const touchStart = (event: any) => {
|
||||
resetTouchStatus()
|
||||
const events = event.touches[0]
|
||||
touch.startX = events.clientX
|
||||
touch.startY = events.clientY
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 移动
|
||||
*/
|
||||
const touchMove = (event: any) => {
|
||||
const events = event.touches[0]
|
||||
touch.deltaX = events.clientX - touch.startX
|
||||
touch.deltaY = events.clientY - touch.startY
|
||||
touch.offsetX = Math.abs(touch.deltaX)
|
||||
touch.offsetY = Math.abs(touch.deltaY)
|
||||
touch.direction = touch.direction || getDirection(touch.offsetX, touch.offsetY)
|
||||
}
|
||||
|
||||
return {
|
||||
touch,
|
||||
resetTouchStatus,
|
||||
touchStart,
|
||||
touchMove
|
||||
}
|
||||
}
|
||||
437
business_uniapp/src/components/tabs/tabs.vue
Normal file
437
business_uniapp/src/components/tabs/tabs.vue
Normal file
@@ -0,0 +1,437 @@
|
||||
<template>
|
||||
<view class="tabs">
|
||||
<u-sticky :enable="isFixed" :bg-color="stickyBgColor" :offset-top="top" :h5-nav-height="0">
|
||||
<view
|
||||
:id="id"
|
||||
:style="{
|
||||
background: bgColor
|
||||
}"
|
||||
>
|
||||
<scroll-view
|
||||
:style="{ height: height + 'rpx' }"
|
||||
scroll-x
|
||||
class="scroll-view"
|
||||
:scroll-left="scrollLeft"
|
||||
scroll-with-animation
|
||||
>
|
||||
<view class="scroll-box" :class="{ 'tabs-scorll-flex': !isScroll }">
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
class="tab-item line1"
|
||||
:id="'tab-item-' + index"
|
||||
:key="index"
|
||||
@tap="clickTab(index)"
|
||||
:style="[tabItemStyle(index)]"
|
||||
>
|
||||
<u-badge
|
||||
:count="item[count] || item['dot'] || 0"
|
||||
:offset="offset"
|
||||
size="mini"
|
||||
></u-badge>
|
||||
{{ item[name] || item['name'] }}
|
||||
</view>
|
||||
<view v-if="showBar" class="tab-bar" :style="[tabBarStyle]"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view
|
||||
class="tab-content"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchcancel="onTouchEnd"
|
||||
@touchend="onTouchEnd"
|
||||
>
|
||||
<!-- <view class="tab-track" :class="{'tab-animated': animated}" :style="[trackStyle]"> -->
|
||||
<view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
<!-- </view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getRect } from '@/utils/util'
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
computed,
|
||||
watch,
|
||||
provide,
|
||||
nextTick,
|
||||
onMounted,
|
||||
getCurrentInstance
|
||||
} from 'vue'
|
||||
import { useTouch } from './hooks/useTouch'
|
||||
|
||||
// Touch 钩子
|
||||
const { touch, resetTouchStatus, touchStart, touchMove } = useTouch()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'change', value: number): void
|
||||
}>()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
isScroll?: boolean // 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度
|
||||
current?: number | string // 当前活动tab的索引
|
||||
height?: number | string // 导航栏的高度和行高
|
||||
fontSize?: number | string // 字体大小
|
||||
duration?: number | string // 过渡动画时长, 单位ms
|
||||
activeColor?: number | string // 选中项的主题颜色
|
||||
inactiveColor?: number | string // 未选中项的颜色
|
||||
barWidth?: number | string // 菜单底部移动的bar的宽度,单位rpx
|
||||
barHeight?: number // 移动bar的高度
|
||||
gutter?: number | string // 单个tab的左或有内边距(左右相同)
|
||||
bgColor?: number | string // 导航栏的背景颜色
|
||||
name?: string // 读取传入的数组对象的属性(tab名称)
|
||||
count?: string // 读取传入的数组对象的属性(徽标数)
|
||||
offset?: number[] // 徽标数位置偏移
|
||||
bold?: boolean // 活动tab字体是否加粗
|
||||
activeItemStyle?: any // 当前活动tab item的样式
|
||||
showBar?: boolean // 是否显示底部的滑块
|
||||
barStyle?: any // 底部滑块的自定义样式
|
||||
itemWidth?: string // 标签的宽度
|
||||
isFixed?: boolean // 吸顶是否固定
|
||||
top?: number | string // 吸顶顶部距离
|
||||
stickyBgColor?: string // 吸顶颜色
|
||||
|
||||
swipeable?: boolean // 是否允许滑动切换
|
||||
// animated: boolean // 切换动画
|
||||
}>(),
|
||||
{
|
||||
isScroll: true,
|
||||
current: 0,
|
||||
height: 80,
|
||||
fontSize: 28,
|
||||
duration: 0.3,
|
||||
activeColor: 'var(--color-primary)',
|
||||
inactiveColor: '#333',
|
||||
barWidth: 40,
|
||||
barHeight: 4,
|
||||
gutter: 30,
|
||||
bgColor: '#FFFFFF',
|
||||
name: 'name',
|
||||
count: 'count',
|
||||
offset: [5, 20],
|
||||
bold: true,
|
||||
activeItemStyle: {},
|
||||
showBar: true,
|
||||
barStyle: {},
|
||||
itemWidth: 'auto',
|
||||
isFixed: false,
|
||||
top: 0,
|
||||
stickyBgColor: '#FFFFFF',
|
||||
|
||||
swipeable: true
|
||||
// animated: true
|
||||
}
|
||||
)
|
||||
|
||||
const list = ref<any>([])
|
||||
const childrens = ref<any>([])
|
||||
const scrollLeft = ref<number>(0) // 滚动scroll-view的左边滚动距离
|
||||
const tabQueryInfo = ref<any>([]) // 存放对tab菜单查询后的节点信息
|
||||
const componentWidth = ref<number>(0) // 屏幕宽度,单位为px
|
||||
const scrollBarLeft = ref<number>(0) // 移动bar需要通过translateX()移动的距离
|
||||
const parentLeft = ref<number>(0) // 父元素(tabs组件)到屏幕左边的距离
|
||||
const id = ref<string>('cu-tab') // id值
|
||||
const currentIndex = ref<any>(props.current)
|
||||
const barFirstTimeMove = ref<boolean>(true) // 滑块第一次移动时(页面刚生成时),无需动画,否则给人怪异的感觉
|
||||
const swiping = ref<boolean>(false)
|
||||
|
||||
//@ts-ignore
|
||||
const ctx = getCurrentInstance()
|
||||
|
||||
// 监听tab的变化,重新计算tab菜单的布局信息,因为实际使用中菜单可能是通过
|
||||
// 后台获取的(如新闻app顶部的菜单),获取返回需要一定时间,所以list变化时,重新获取布局信息
|
||||
watch(
|
||||
() => list.value,
|
||||
async (n, o) => {
|
||||
// list变动时,重制内部索引,否则可能导致超出数组边界的情况
|
||||
if (!barFirstTimeMove.value && n.length !== o.length) {
|
||||
currentIndex.value = 0
|
||||
}
|
||||
// 用$nextTick等待视图更新完毕后再计算tab的局部信息,否则可能因为tab还没生成就获取,就会有问题
|
||||
await nextTick()
|
||||
init()
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.current,
|
||||
(nVal, oVal) => {
|
||||
// 视图更新后再执行移动操作、
|
||||
nextTick(() => {
|
||||
currentIndex.value = nVal
|
||||
scrollByIndex()
|
||||
})
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 移动bar的样式
|
||||
const tabBarStyle = computed(() => {
|
||||
const style = {
|
||||
width: props.barWidth + 'rpx',
|
||||
transform: `translate(${scrollBarLeft.value}px, -100%)`,
|
||||
// 滑块在页面渲染后第一次滑动时,无需动画效果
|
||||
'transition-duration': `${barFirstTimeMove.value ? 0 : props.duration}s`,
|
||||
'background-color': props.activeColor,
|
||||
height: props.barHeight + 'rpx',
|
||||
opacity: barFirstTimeMove.value ? 0 : 1,
|
||||
// 设置一个很大的值,它会自动取能用的最大值,不用高度的一半,是因为高度可能是单数,会有小数出现
|
||||
'border-radius': `${props.barHeight / 2}px`
|
||||
}
|
||||
Object.assign(style, props.barStyle)
|
||||
return style
|
||||
})
|
||||
// tab的样式
|
||||
const tabItemStyle = computed(() => {
|
||||
return (index) => {
|
||||
let style: any = {
|
||||
height: props.height + 'rpx',
|
||||
'line-height': props.height + 'rpx',
|
||||
'font-size': props.fontSize + 'rpx',
|
||||
padding: props.isScroll ? `0 ${props.gutter}rpx` : '',
|
||||
flex: props.isScroll ? 'auto' : '1',
|
||||
width: `${props.itemWidth}rpx`
|
||||
}
|
||||
// 字体加粗
|
||||
if (index == currentIndex.value && props.bold) style.fontWeight = 'bold'
|
||||
if (index == currentIndex.value) {
|
||||
style.color = props.activeColor
|
||||
// 给选中的tab item添加外部自定义的样式
|
||||
style = Object.assign(style, props.activeItemStyle)
|
||||
} else {
|
||||
style.color = props.inactiveColor
|
||||
}
|
||||
return style
|
||||
}
|
||||
})
|
||||
|
||||
// const trackStyle = computed(() => {
|
||||
// if (!props.animated) return ''
|
||||
// return {
|
||||
// left: -100 * currentIndex.value + '%',
|
||||
// 'transition-duration': props.duration + 's',
|
||||
// '-webkit-transition-duration': props.duration + 's',
|
||||
// }
|
||||
// })
|
||||
|
||||
const updateTabs = () => {
|
||||
list.value = childrens.value.map((item) => {
|
||||
const { name, dot, active, inited } = item.event
|
||||
const { updateRender } = item
|
||||
return {
|
||||
name,
|
||||
dot,
|
||||
active,
|
||||
inited,
|
||||
updateRender
|
||||
}
|
||||
})
|
||||
// nextTick(() => {
|
||||
// init()
|
||||
// })
|
||||
}
|
||||
|
||||
// 设置一个init方法,方便多处调用
|
||||
const init = async () => {
|
||||
// 获取tabs组件的尺寸信息
|
||||
const tabRect = await getRect('#' + id.value, false, ctx)
|
||||
// tabs组件距离屏幕左边的宽度
|
||||
parentLeft.value = tabRect.left
|
||||
// tabs组件的宽度
|
||||
componentWidth.value = tabRect.width
|
||||
getTabRect()
|
||||
}
|
||||
|
||||
// 点击某一个tab菜单
|
||||
const clickTab = (index) => {
|
||||
// 点击当前活动tab,不触发事件
|
||||
if (index == currentIndex.value) return
|
||||
nextTick(() => {
|
||||
currentIndex.value = index
|
||||
scrollByIndex()
|
||||
})
|
||||
// 发送事件给父组件
|
||||
emit('change', index)
|
||||
}
|
||||
|
||||
// 查询tab的布局信息
|
||||
const getTabRect = () => {
|
||||
// 创建节点查询
|
||||
const query: any = uni.createSelectorQuery().in(ctx)
|
||||
// 历遍所有tab,这里是执行了查询,最终使用exec()会一次性返回查询的数组结果
|
||||
for (let i = 0; i < list.value.length; i++) {
|
||||
// 只要size和rect两个参数
|
||||
query.select(`#tab-item-${i}`).fields({
|
||||
size: true,
|
||||
rect: true
|
||||
})
|
||||
}
|
||||
// 执行查询,一次性获取多个结果
|
||||
query.exec((res) => {
|
||||
tabQueryInfo.value = res
|
||||
// 初始化滚动条和移动bar的位置
|
||||
scrollByIndex()
|
||||
})
|
||||
}
|
||||
|
||||
// 滚动scroll-view,让活动的tab处于屏幕的中间位置
|
||||
const scrollByIndex = () => {
|
||||
// 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
|
||||
const tabInfo = tabQueryInfo.value[currentIndex.value]
|
||||
if (!tabInfo) return
|
||||
// 活动tab的宽度
|
||||
const tabWidth = tabInfo.width
|
||||
// 活动item的左边到tabs组件左边的距离,用item的left减去tabs的left
|
||||
const offsetLeft = tabInfo.left - parentLeft.value
|
||||
// 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动
|
||||
const scrollLefts = offsetLeft - (componentWidth.value - tabWidth) / 2
|
||||
scrollLeft.value = scrollLefts < 0 ? 0 : scrollLefts
|
||||
// 当前活动item的中点点到左边的距离减去滑块宽度的一半,即可得到滑块所需的移动距离
|
||||
const left = tabInfo.left + tabInfo.width / 2 - parentLeft.value
|
||||
// 计算当前活跃item到组件左边的距离
|
||||
scrollBarLeft.value = left - uni.upx2px(props.barWidth) / 2
|
||||
// 第一次移动滑块的时候,barFirstTimeMove为true,放到延时中将其设置false
|
||||
// 延时是因为scrollBarLeft作用于computed计算时,需要一个过程需,否则导致出错
|
||||
if (barFirstTimeMove.value == true) {
|
||||
setTimeout(() => {
|
||||
barFirstTimeMove.value = false
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 更新子组件的显示
|
||||
childrens.value.forEach((item, ind) => {
|
||||
const active = ind === currentIndex.value
|
||||
if (active !== item.event.active || !item.event.inited) {
|
||||
item.updateRender(active)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 子组件调用此函数而产生的事件通信
|
||||
const handleChange = (event, updateRender) => {
|
||||
childrens.value.push({ event: event, updateRender })
|
||||
}
|
||||
// 手指触摸
|
||||
const onTouchStart = (event) => {
|
||||
if (!props.swipeable) return
|
||||
swiping.value = true
|
||||
touchStart(event)
|
||||
}
|
||||
// 手指滑动
|
||||
const onTouchMove = (event) => {
|
||||
if (!props.swipeable || !swiping.value) return
|
||||
touchMove(event)
|
||||
}
|
||||
// 手指滑动结束
|
||||
const onTouchEnd = () => {
|
||||
if (!props.swipeable || !swiping.value) return
|
||||
const minSwipeDistance = 50
|
||||
if (touch.direction === 'horizontal' && touch.offsetX >= minSwipeDistance) {
|
||||
let index,
|
||||
len = list.value.length,
|
||||
curIndex = currentIndex.value
|
||||
if (touch.deltaX <= 0) {
|
||||
curIndex >= len - 1 ? (index = 0) : (index = curIndex + 1)
|
||||
} else {
|
||||
curIndex <= 0 ? (index = len - 1) : (index = curIndex - 1)
|
||||
}
|
||||
nextTick(() => {
|
||||
currentIndex.value = index
|
||||
scrollByIndex()
|
||||
})
|
||||
// 发送事件给父组件
|
||||
emit('change', index)
|
||||
}
|
||||
swiping.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateTabs()
|
||||
})
|
||||
|
||||
provide('handleChange', handleChange)
|
||||
provide('updateTabs', updateTabs)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* #ifndef APP-NVUE */
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.scroll-box {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
/* #ifdef MP-TOUTIAO */
|
||||
white-space: nowrap;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.tab-fixed {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条
|
||||
scroll-view ::v-deep ::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.scroll-view {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: inline-block;
|
||||
/* #endif */
|
||||
text-align: center;
|
||||
transition-property: background-color, color;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
position: absolute;
|
||||
bottom: 6rpx;
|
||||
}
|
||||
|
||||
.tabs-scorll-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// .tab-content {
|
||||
// overflow: hidden;
|
||||
// .tab-track {
|
||||
// position: relative;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
// }
|
||||
// .tab-animated {
|
||||
// display: flex;
|
||||
// transition-property: left;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
43
business_uniapp/src/components/widgets/banner/banner.vue
Normal file
43
business_uniapp/src/components/widgets/banner/banner.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<view
|
||||
class="banner translate-y-0"
|
||||
:class="{ 'px-[20rpx]': !isLargeScreen }"
|
||||
v-if="content.data.length && content.enabled"
|
||||
>
|
||||
<LSwiper
|
||||
:content="content"
|
||||
:height="isLargeScreen ? '1100' : '321'"
|
||||
:circular="true"
|
||||
:effect3d="false"
|
||||
:border-radius="isLargeScreen ? '0' : '14'"
|
||||
interval="7000"
|
||||
bgColor="transparent"
|
||||
@change="handleChange"
|
||||
></LSwiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LSwiper from '@/components/l-swiper/l-swiper.vue'
|
||||
import {useAppStore} from "@/stores/app";
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLargeScreen: {
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
const {getImageUrl} = useAppStore();
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
emit('change', getImageUrl(props['content'].data[index].bg))
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<view class="bg-white p-[30rpx] flex text-[#101010] font-medium text-lg">
|
||||
联系我们
|
||||
</view>
|
||||
<view
|
||||
class="customer-service bg-white flex flex-col justify-center items-center mx-[36rpx] mt-[30rpx] rounded-[20rpx] px-[20rpx] pb-[100rpx]"
|
||||
>
|
||||
<view
|
||||
class="w-full border-solid border-0 border-b border-[#f5f5f5] p-[30rpx] text-center text-[#101010] text-base font-medium">
|
||||
{{ content.title }}
|
||||
</view>
|
||||
|
||||
<view class="mt-[60rpx]">
|
||||
<!-- 这样渲染是为了能在小程序中长按识别二维码 -->
|
||||
<u-parse :html="richTxt"></u-parse>
|
||||
<!-- <u-image width="200" height="200" border-radius="10rpx" :src="getImageUrl(content.qrcode)"/>-->
|
||||
</view>
|
||||
<view v-if="content.remark" class="text-sm mt-[40rpx] font-medium">{{ content.remark }}</view>
|
||||
<view v-if="content.mobile" class="text-sm mt-[24rpx] flex flex-wrap">
|
||||
<!-- #ifdef H5 -->
|
||||
<a class="ml-[10rpx] phone text-primary underline" :href="'tel:' + content.mobile">
|
||||
{{ content.mobile }}
|
||||
</a>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 -->
|
||||
<view class="ml-[10rpx] phone text-primary underline" @click="handleCall">
|
||||
{{ content.mobile }}
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view v-if="content.time" class="text-muted text-sm mt-[30rpx]">
|
||||
服务时间:{{ content.time }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {useAppStore} from '@/stores/app'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const {getImageUrl} = useAppStore()
|
||||
|
||||
const richTxt = computed(() => {
|
||||
const src = getImageUrl(props.content.qrcode)
|
||||
return `<img src="${src}" style="width: 100px;height: 100px; border-radius: 8px" />`
|
||||
})
|
||||
|
||||
const handleCall = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: String(props.content.mobile)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<view
|
||||
class="banner h-[200rpx] mx-[20rpx] mt-[20rpx] translate-y-0"
|
||||
v-if="showList.length && content.enabled"
|
||||
>
|
||||
<swiper
|
||||
class="swiper h-full"
|
||||
:indicator-dots="showList.length > 1"
|
||||
indicator-active-color="#4173ff"
|
||||
:autoplay="true"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="(item, index) in showList"
|
||||
:key="index"
|
||||
@click="handleClick(item.link)"
|
||||
>
|
||||
<u-image
|
||||
mode="widthFix"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="getImageUrl(item.image)"
|
||||
:border-radius="14"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { navigateTo } from '@/utils/util'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const handleClick = (link: any) => {
|
||||
navigateTo(link)
|
||||
}
|
||||
const { getImageUrl } = useAppStore()
|
||||
|
||||
const showList = computed(() => {
|
||||
return props.content.data?.filter((item: any) => item.is_show == '1') || []
|
||||
})
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
75
business_uniapp/src/components/widgets/nav/nav.vue
Normal file
75
business_uniapp/src/components/widgets/nav/nav.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="relative mx-[20rpx] mt-[20rpx]">
|
||||
<swiper
|
||||
class="py-[20rpx] bg-white rounded-lg"
|
||||
:style="{
|
||||
height: navList[0].length > content.per_line ? '288rpx' : '132rpx'
|
||||
}"
|
||||
:autoplay="false"
|
||||
:indicator-dots="false"
|
||||
@change="swiperChange"
|
||||
>
|
||||
<swiper-item v-for="(sItem, sIndex) in navList" :key="sIndex">
|
||||
<view class="nav" v-if="navList.length && content.enabled">
|
||||
<view
|
||||
class="grid grid-rows-auto gap-y-3 w-full"
|
||||
:style="{ 'grid-template-columns': `repeat(${content.per_line}, 1fr)` }"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in sItem"
|
||||
:key="index"
|
||||
class="flex flex-col items-center"
|
||||
@click="handleClick(item.link)"
|
||||
>
|
||||
<u-image width="82" height="82" :src="getImageUrl(item.image)" alt=""/>
|
||||
<view class="mt-[14rpx] text-xs">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, watch, computed} from 'vue'
|
||||
import {useAppStore} from '@/stores/app'
|
||||
import {navigateTo, sliceArray} from '@/utils/util'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const {getImageUrl} = useAppStore()
|
||||
const swiperCurrent = ref<number>(0)
|
||||
const navList = ref<Record<string, any>>([])
|
||||
|
||||
const pagesNum = computed<number>(() => {
|
||||
return props.content.per_line * props.content.show_line
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.content.data,
|
||||
(val) => {
|
||||
const num = props.content.style === 1 ? val.length : pagesNum.value
|
||||
navList.value = sliceArray(val, num)
|
||||
console.log(navList.value)
|
||||
},
|
||||
{deep: true, immediate: true}
|
||||
)
|
||||
|
||||
const handleClick = (link: any) => {
|
||||
navigateTo(link)
|
||||
}
|
||||
|
||||
const swiperChange = (e: any) => {
|
||||
swiperCurrent.value = e.detail.current
|
||||
}
|
||||
</script>
|
||||
93
business_uniapp/src/components/widgets/search/search.vue
Normal file
93
business_uniapp/src/components/widgets/search/search.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<!-- #ifndef H5 -->
|
||||
<u-sticky h5-nav-height="0" bg-color="transparent">
|
||||
<u-navbar
|
||||
:class="{ 'fixed top-0 z-10': isLargeScreen }"
|
||||
:is-back="false"
|
||||
:is-fixed="true"
|
||||
:title="metaData.title"
|
||||
:custom-title="metaData.title_type == 2"
|
||||
:border-bottom="false"
|
||||
:title-bold="true"
|
||||
:background="{ background: 'rgba(256,256, 256, 0)' }"
|
||||
:title-color="percent > 0.5 ? '#000' : metaData.text_color == 1 ? '#fff' : '#000'"
|
||||
>
|
||||
<template #default>
|
||||
<view class="px-[24px] py-[30px] flex items-center">
|
||||
<u-image src="" height="50" width="50" border-radius="999"></u-image>
|
||||
<text class="ml-1 text-white">肩颈管家·精油按摩(雅乐店)</text>
|
||||
</view>
|
||||
<!-- <navigator
|
||||
url="/pages/search/search"
|
||||
class="mini-search"
|
||||
hover-class="none"
|
||||
:style="{ opacity: isLargeScreen ? 1 : percent }"
|
||||
>
|
||||
<u-icon name="search"></u-icon>
|
||||
</navigator> -->
|
||||
</template>
|
||||
<!-- <template #title>
|
||||
<image class="!h-[54rpx]" :src="metaData.title_img" mode="widthFix"></image>
|
||||
</template> -->
|
||||
</u-navbar>
|
||||
</u-sticky>
|
||||
<!-- #endif -->
|
||||
<!-- <navigator
|
||||
v-if="!isLargeScreen"
|
||||
url="/pages/search/search"
|
||||
class="px-[24rpx] mt-[24rpx] mb-[30rpx]"
|
||||
:style="{ opacity: 1 - percent }"
|
||||
hover-class="none"
|
||||
>
|
||||
<u-search
|
||||
placeholder="请输入关键词搜索"
|
||||
:height="72"
|
||||
:disabled="true"
|
||||
:show-action="false"
|
||||
bgColor="#ffffff"
|
||||
></u-search>
|
||||
</navigator> -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
pageMeta: {
|
||||
type: Object,
|
||||
default: () => []
|
||||
},
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLargeScreen: {
|
||||
type: Boolean
|
||||
},
|
||||
percent: {
|
||||
type: Number
|
||||
}
|
||||
})
|
||||
|
||||
const metaData: any = computed(() => {
|
||||
return props.pageMeta[0].content
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mini-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-left: 20rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<view
|
||||
class="banner h-[200rpx] mx-[20rpx] mt-[20rpx] translate-y-0"
|
||||
v-if="content.data.length && content.enabled"
|
||||
>
|
||||
<swiper
|
||||
class="swiper h-full"
|
||||
:indicator-dots="content.data.length > 1"
|
||||
indicator-active-color="#4173ff"
|
||||
:autoplay="true"
|
||||
>
|
||||
<swiper-item
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
@click="handleClick(item.limk)"
|
||||
>
|
||||
<u-image
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="getImageUrl(item.image)"
|
||||
:border-radius="14"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { navigateTo } from '@/utils/util'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const handleClick = (link: any) => {
|
||||
navigateTo(link)
|
||||
}
|
||||
const { getImageUrl } = useAppStore()
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
170
business_uniapp/src/components/widgets/user-info/user-info.vue
Normal file
170
business_uniapp/src/components/widgets/user-info/user-info.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<view class="user-info" v-if="content.enabled">
|
||||
<view class="flex items-center justify-between px-[30rpx] pb-[30rpx] pt-[20rpx]">
|
||||
<view
|
||||
v-if="isLogin"
|
||||
class="flex items-center w-full"
|
||||
@click="router.navigate('/pages/user_set/user_set')"
|
||||
>
|
||||
<u-avatar :src="userInfo.logo" :size="108"></u-avatar>
|
||||
<view class="ml-[28rpx] text-black">
|
||||
<view class="text-xl text-main font-medium">{{ userInfo.name }}</view>
|
||||
<view
|
||||
v-if="content.user_info == 2"
|
||||
class="text-base mt-1 text-content"
|
||||
@click.stop="copy(userInfo.mobile)"
|
||||
>
|
||||
手机号:{{ userInfo.mobile || '-' }}
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="text-base mt-1 text-content"
|
||||
@click.stop="copy(userInfo.sn)"
|
||||
>
|
||||
工号: {{ userInfo.sn || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<navigator
|
||||
v-else
|
||||
class="flex items-center text-black"
|
||||
hover-class="none"
|
||||
url="/pages/login/login"
|
||||
>
|
||||
<u-avatar src="/static/images/user/default_avatar.png" :size="108"></u-avatar>
|
||||
<view class="ml-[28rpx] text-black">
|
||||
<view class="text-xl text-main font-bold">立即登录/注册</view>
|
||||
<view class="text-base text-content mt-1">
|
||||
登录后查看更多
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
<navigator v-if="isLogin" hover-class="none" url="/pages/user_set/user_set">
|
||||
<u-icon name="arrow-right" color="#999" :size="28"></u-icon>
|
||||
</navigator>
|
||||
<navigator v-else hover-class="none" url="/pages/login/login">
|
||||
<u-icon name="arrow-right" color="#999" :size="28"></u-icon>
|
||||
</navigator>
|
||||
</view>
|
||||
|
||||
<view class="bg-white mt-4 rounded-xl p-[10px]">
|
||||
<view class="flex justify-around py-2">
|
||||
<view
|
||||
class="text-center"
|
||||
v-if="content.content?.includes('value1')"
|
||||
@click="router.navigateTo('/packages/pages/my_master/my_master')"
|
||||
>
|
||||
<view class="text-[20px] font-bold">
|
||||
{{ userInfo?.coach_count || 0 }}</view>
|
||||
<view class="text-muted">我的技师</view>
|
||||
</view>
|
||||
<view
|
||||
class="text-center"
|
||||
v-if="content.content?.includes('value2')"
|
||||
@click="router.navigateTo('/packages/pages/platform_project/platform_project')"
|
||||
>
|
||||
<view class="text-[20px] font-bold">
|
||||
{{ userInfo?.goods_count || 0 }}
|
||||
</view>
|
||||
<view class="text-muted">平台项目</view>
|
||||
</view>
|
||||
<view
|
||||
class="text-center"
|
||||
v-if="content.content?.includes('value3')"
|
||||
@click="router.navigateTo('/packages/pages/my_project/my_project')"
|
||||
>
|
||||
<view class="text-[20px] font-bold">{{ userInfo?.shop_goods_count || 0 }}</view>
|
||||
<view class="text-muted">店铺项目</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex gap-x-[10px] mt-2">
|
||||
<view
|
||||
class="comment flex flex-1 text-[#4A65A3]"
|
||||
@click="router.navigateTo('/packages/pages/bond/bond')"
|
||||
>
|
||||
<view class="w-full">
|
||||
<view class="text-xl font-medium">
|
||||
{{ userInfo?.deposit || 0 }}
|
||||
</view>
|
||||
<view class="mt-1 flex items-center">
|
||||
<view class="text-xs mr-[4rpx]">
|
||||
我的保证金
|
||||
</view>
|
||||
<u-icon name="arrow-right" color="#4A65A3" :size="20">
|
||||
</u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="wallet flex flex-1 text-[#2DA9BC]"
|
||||
@click="router.navigateTo('/packages/pages/balance/balance')"
|
||||
>
|
||||
<view class="w-full">
|
||||
<view class="text-xl font-medium">
|
||||
{{ userInfo?.money || 0 }}
|
||||
</view>
|
||||
<view class="mt-1 flex items-center">
|
||||
<view class="text-xs mr-[4rpx]">
|
||||
我的账户余额
|
||||
</view>
|
||||
<u-icon name="arrow-right" color="#2DA9BC" :size="20">
|
||||
</u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useCopy } from '@/hooks/useCopy'
|
||||
import { useRouter } from 'uniapp-router-next'
|
||||
|
||||
const { copy } = useCopy()
|
||||
const router = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
userInfo: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
isLogin: {
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
const navigateTo = (url: string) => {
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-info {
|
||||
padding: 24px 12px 0 12px;
|
||||
|
||||
.comment {
|
||||
background-color: #e0ebfd;
|
||||
background-image: url('@/static/images/user/bg_wdbzj.png');
|
||||
background-size: 100% auto;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.wallet {
|
||||
background-color: #c7effb;
|
||||
background-image: url('@/static/images/user/bg_wdzhye.png');
|
||||
background-size: 100% auto;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div
|
||||
class="user-service bg-white mx-[30rpx] mt-[30rpx] rounded-lg p-[30rpx]"
|
||||
v-if="content.enabled"
|
||||
>
|
||||
<div
|
||||
v-if="content.title"
|
||||
class="title text-content text-base"
|
||||
>
|
||||
<div>{{ content.title }}</div>
|
||||
</div>
|
||||
<!-- 横排 -->
|
||||
<div v-if="content.style == 1" class="grid grid-cols-4 gap-x-6">
|
||||
<div
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
class="flex flex-col items-center pt-[40rpx]"
|
||||
@click="handleClick(item.link)"
|
||||
v-show="Number(item.is_show)"
|
||||
>
|
||||
<u-image width="68" height="68" :src="getImageUrl(item.image)" alt="" />
|
||||
<div class="mt-2 text-xs">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 竖排 -->
|
||||
<div v-if="content.style == 2">
|
||||
<div
|
||||
v-for="(item, index) in content.data"
|
||||
:key="index"
|
||||
class="flex items-center h-[100rpx] px-[24rpx]"
|
||||
@click="handleClick(item.link)"
|
||||
v-show="Number(item.is_show)"
|
||||
>
|
||||
<u-image width="52" height="52" :src="getImageUrl(item.image)" alt="" />
|
||||
<div class="ml-[20rpx] flex-1">{{ item.name }}</div>
|
||||
<div class="text-muted">
|
||||
<u-icon name="arrow-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { navigateTo } from '@/utils/util'
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const { getImageUrl } = useAppStore()
|
||||
const handleClick = (link: any) => {
|
||||
navigateTo(link)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
23
business_uniapp/src/config/index.ts
Normal file
23
business_uniapp/src/config/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { isDevMode } from '@/utils/env'
|
||||
const envBaseUrl = import.meta.env.VITE_APP_BASE_URL || ''
|
||||
|
||||
let baseUrl = `${envBaseUrl}/`
|
||||
|
||||
/*
|
||||
* 微信小程序在`VITE_APP_BASE_URL`存在或`dev`模式下
|
||||
* 使用`VITE_APP_BASE_URL`的值
|
||||
* 其他情况使用`[baseUrl]`,方便服务端替换
|
||||
*/
|
||||
|
||||
//#ifdef MP-WEIXIN
|
||||
baseUrl = isDevMode() || envBaseUrl ? baseUrl : '[baseUrl]'
|
||||
//#endif
|
||||
|
||||
const config = {
|
||||
version: '1.3.9', //版本号
|
||||
baseUrl, //请求接口域名
|
||||
urlPrefix: 'shopapi', //请求默认前缀
|
||||
timeout: 60 * 1000 //请求超时时长
|
||||
}
|
||||
|
||||
export default config
|
||||
5
business_uniapp/src/enums/agreementEnums.ts
Normal file
5
business_uniapp/src/enums/agreementEnums.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
//菜单主题类型
|
||||
export enum AgreementEnum {
|
||||
PRIVACY = 'privacy',
|
||||
SERVICE = 'service'
|
||||
}
|
||||
54
business_uniapp/src/enums/appEnums.ts
Normal file
54
business_uniapp/src/enums/appEnums.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
//菜单主题类型
|
||||
export enum ThemeEnum {
|
||||
LIGHT = 'light',
|
||||
DARK = 'dark'
|
||||
}
|
||||
|
||||
// 客户端
|
||||
export enum ClientEnum {
|
||||
MP_WEIXIN = 1, // 微信-小程序
|
||||
OA_WEIXIN = 2, // 微信-公众号
|
||||
H5 = 3, // H5
|
||||
IOS = 5, //苹果
|
||||
ANDROID = 6 //安卓
|
||||
}
|
||||
export const serviceEnum = {
|
||||
'1': 'mnp',
|
||||
'2': 'oa',
|
||||
'3': 'h5'
|
||||
}
|
||||
export enum SMSEnum {
|
||||
LOGIN = 'YZMDLSHOP',
|
||||
BIND_MOBILE = 'BDSJHMSHOP',
|
||||
CHANGE_MOBILE = 'BGSJHMSHOP',
|
||||
FIND_PASSWORD = 'CSDLMMSHOP',
|
||||
REGISTER = 'ZCYZMSHOP'
|
||||
}
|
||||
|
||||
export enum SearchTypeEnum {
|
||||
HISTORY = 'history'
|
||||
}
|
||||
|
||||
// 用户资料
|
||||
export enum FieldType {
|
||||
NONE = '',
|
||||
AVATAR = 'avatar',
|
||||
USERNAME = 'account',
|
||||
NICKNAME = 'nickname',
|
||||
SEX = 'sex'
|
||||
}
|
||||
|
||||
// 支付结果
|
||||
export enum PayStatusEnum {
|
||||
SUCCESS = 'success',
|
||||
FAIL = 'fail',
|
||||
PENDING = 'pending'
|
||||
}
|
||||
|
||||
// 页面状态
|
||||
export enum PageStatusEnum {
|
||||
LOADING = 'loading', // 加载中
|
||||
NORMAL = 'normal', // 正常
|
||||
ERROR = 'error', // 异常
|
||||
EMPTY = 'empty' // 为空
|
||||
}
|
||||
7
business_uniapp/src/enums/auditEnum.ts
Normal file
7
business_uniapp/src/enums/auditEnum.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// 商家审核状态枚举
|
||||
export enum AuditStatusEnum {
|
||||
NO = '', // 未审核
|
||||
WAIT = 0, // 审核中
|
||||
PASS = 1, // 审核通过
|
||||
REJECT = 2, // 审核拒绝
|
||||
}
|
||||
9
business_uniapp/src/enums/cacheEnums.ts
Normal file
9
business_uniapp/src/enums/cacheEnums.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// 本地缓冲key
|
||||
|
||||
//token
|
||||
export const TOKEN_KEY = 'token'
|
||||
|
||||
// 搜索历史记录
|
||||
export const HISTORY = 'history'
|
||||
|
||||
export const BACK_URL = 'back_url'
|
||||
11
business_uniapp/src/enums/constantEnums.ts
Normal file
11
business_uniapp/src/enums/constantEnums.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// 本地缓冲key
|
||||
|
||||
//token
|
||||
export const TOKEN_KEY = 'business_token'
|
||||
|
||||
// 搜索历史记录
|
||||
export const HISTORY = 'business_history'
|
||||
|
||||
export const BACK_URL = 'business_back_url'
|
||||
|
||||
export const PAY_STATUS_EVENT = 'event:payStatus'
|
||||
22
business_uniapp/src/enums/requestEnums.ts
Normal file
22
business_uniapp/src/enums/requestEnums.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export enum ContentTypeEnum {
|
||||
// json
|
||||
JSON = 'application/json;charset=UTF-8',
|
||||
// form-data 上传资源(图片,视频)
|
||||
FORM_DATA = 'multipart/form-data;charset=UTF-8'
|
||||
}
|
||||
|
||||
export enum RequestMethodsEnum {
|
||||
GET = 'GET',
|
||||
POST = 'POST'
|
||||
}
|
||||
|
||||
export enum RequestCodeEnum {
|
||||
SUCCESS = 1, //成功
|
||||
FAILED = 0, // 失败
|
||||
TOKEN_INVALID = -1 // TOKEN参数无效
|
||||
}
|
||||
|
||||
export enum RequestErrMsgEnum {
|
||||
ABORT = 'request:fail abort',
|
||||
TIMEOUT = 'request:fail timeout'
|
||||
}
|
||||
5
business_uniapp/src/enums/withdraw.ts
Normal file
5
business_uniapp/src/enums/withdraw.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
//菜单主题类型
|
||||
export enum WithdrawEnum {
|
||||
COMMISSION = 1, // 1-佣金提现;
|
||||
EARNEST= 2 // 2-保证金提现;
|
||||
}
|
||||
112
business_uniapp/src/hooks/payment.ts
Normal file
112
business_uniapp/src/hooks/payment.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { getPayWay, prepay } from '@/api/pay'
|
||||
import { wxpay,alipay } from '@/utils/pay'
|
||||
// import { toast } from '@/utils/util'
|
||||
import { getClient } from '@/utils/client'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
/**
|
||||
* @description 支付函数钩子
|
||||
* @param { emit } 是否刷新页面
|
||||
* @return { Function } 暴露钩子
|
||||
*/
|
||||
export function usePay() {
|
||||
/**
|
||||
* @description 1. 初始化获取支付方式
|
||||
* @param { params } 支付订单数据
|
||||
*/
|
||||
const initPayWay = async (params: any): Promise<void> => {
|
||||
try {
|
||||
const res = await getPayWay({
|
||||
//支付方式
|
||||
from: params.from || 'order',
|
||||
scene: getClient()
|
||||
})
|
||||
return res
|
||||
} catch (err) {
|
||||
console.log('获取支付方式', err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 2. 预支付
|
||||
* @param { params } 支付订单数据
|
||||
*/
|
||||
const handlePayPrepay = async (params: any): Promise<void> => {
|
||||
try {
|
||||
const res = await prepay({
|
||||
from: params.from || 'order',
|
||||
pay_way: params.pay_way,
|
||||
order_id: params.order_id
|
||||
})
|
||||
|
||||
const param = JSON.stringify({
|
||||
order_id: params.order_id,
|
||||
from: params.from
|
||||
})
|
||||
|
||||
// 支付方式:1-微信支付;2-支付宝支付;3-余额支付;
|
||||
if (params.pay_way !== 3) {
|
||||
handlePay(res, params)
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: `/bundle/pages/payment_result/payment_result?param=${param}`
|
||||
})
|
||||
uni.$u.toast('支付成功')
|
||||
}
|
||||
} catch (err) {
|
||||
uni.$u.toast(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 3. 吊起支付并处理支付结构
|
||||
* @param { res } 支付订单数据
|
||||
*/
|
||||
const handlePay = async (res: any, params: any): Promise<void> => {
|
||||
try {
|
||||
// // #ifdef MP
|
||||
// await wxpay(res.config)
|
||||
// uni.$u.toast('支付成功')
|
||||
// // #endif
|
||||
|
||||
// // #ifdef H5
|
||||
// await wxpay(res.config)
|
||||
// // #endif
|
||||
//支付方式:1-微信支付;2-支付宝支付;3-余额支付;
|
||||
switch (params.pay_way) {
|
||||
case 1:
|
||||
await wxpay(res.config)
|
||||
break;
|
||||
case 2:
|
||||
await alipay(res.config)
|
||||
break;
|
||||
default:
|
||||
uni.$u.toast('支付异常')
|
||||
}
|
||||
|
||||
const param = JSON.stringify({
|
||||
order_id: params.order_id,
|
||||
from: params.from
|
||||
})
|
||||
|
||||
if (params.from === 'deposit') {
|
||||
return uni.reLaunch({
|
||||
url: '/bundle/pages/deposit/index?isPay=true'
|
||||
})
|
||||
}
|
||||
uni.reLaunch({ url: `/bundle/pages/payment_result/payment_result?param=${param}` })
|
||||
} catch (err) {
|
||||
if (params.from === 'order') uni.reLaunch({ url: `/pages/order/index` })
|
||||
|
||||
if (Number(params.order_amount) == 0) {
|
||||
return uni.$u.toast('下单成功')
|
||||
}
|
||||
uni.$u.toast('支付取消')
|
||||
}
|
||||
}
|
||||
return {
|
||||
initPayWay,
|
||||
handlePayPrepay,
|
||||
handlePay
|
||||
}
|
||||
}
|
||||
10
business_uniapp/src/hooks/useCopy.ts
Normal file
10
business_uniapp/src/hooks/useCopy.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export function useCopy() {
|
||||
const copy = (text: string) => {
|
||||
uni.setClipboardData({
|
||||
data: String(text)
|
||||
})
|
||||
}
|
||||
return {
|
||||
copy
|
||||
}
|
||||
}
|
||||
122
business_uniapp/src/hooks/useLocation.ts
Normal file
122
business_uniapp/src/hooks/useLocation.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { isWeixinClient } from '@/utils/client'
|
||||
import { getGeocoderCoordinate } from '@/api/app'
|
||||
// #ifdef H5
|
||||
import { getLocation as getWeChatLocation } from '@/hooks/wechat'
|
||||
// #endif
|
||||
// import {editLocation} from "@/api/user";
|
||||
|
||||
export interface LocationType {
|
||||
latitude: string | number
|
||||
longitude: string | number
|
||||
name?: string
|
||||
city_id?: number | null
|
||||
id?: string
|
||||
}
|
||||
|
||||
export const location: LocationType = reactive({
|
||||
latitude: '',
|
||||
longitude: '',
|
||||
name: '',
|
||||
city_id: null,
|
||||
id: ''
|
||||
})
|
||||
|
||||
export function useLocation() {
|
||||
const errorTitle = ref<string | null>(null)
|
||||
const errorContent = ref<string | null>(null)
|
||||
const showLocationModal = ref<boolean>(false)
|
||||
|
||||
// 获取微信或浏览器位置信息
|
||||
const getLocationData = async () => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (isWeixinClient()) {
|
||||
// 微信客户端获取定位
|
||||
try {
|
||||
const wechatLocation: any = await getWeChatLocation()
|
||||
location.latitude = wechatLocation.latitude
|
||||
location.longitude = wechatLocation.longitude
|
||||
// editLocation({latitude: location.latitude, longitude: location.longitude})
|
||||
await reverseGeocode(
|
||||
String(wechatLocation.latitude),
|
||||
String(wechatLocation.longitude)
|
||||
)
|
||||
resolve(location)
|
||||
} catch (err) {
|
||||
errorTitle.value = '微信定位获取失败'
|
||||
errorContent.value = '微信定位失败:' + (err?.errMsg || err)
|
||||
console.error('微信定位失败:', err)
|
||||
showLocationModal.value = true
|
||||
reject(err)
|
||||
}
|
||||
} else {
|
||||
// H5或其他客户端获取定位
|
||||
uni.getLocation({
|
||||
type: 'gcj02', // 常用坐标系
|
||||
async success(res) {
|
||||
location.latitude = res.latitude
|
||||
location.longitude = res.longitude
|
||||
await reverseGeocode(String(res.latitude), String(res.longitude))
|
||||
// editLocation({latitude: res.latitude, longitude: res.longitude})
|
||||
resolve(location)
|
||||
},
|
||||
fail(err) {
|
||||
handleLocationFailure(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
errorTitle.value = '定位获取失败'
|
||||
errorContent.value = '定位失败原因:' + (err?.errMsg || err)
|
||||
console.error('定位失败原因:', err)
|
||||
showLocationModal.value = true
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 定位失败处理
|
||||
const handleLocationFailure = (err: any) => {
|
||||
if (!uni.getSystemInfoSync().locationEnabled) {
|
||||
showLocationModal.value = true
|
||||
errorTitle.value = '定位服务未开启'
|
||||
errorContent.value = '请开启定位服务后重新进入应用'
|
||||
} else {
|
||||
showLocationModal.value = true
|
||||
errorTitle.value = '定位权限未授权'
|
||||
errorContent.value = '请在设置中打开授权,以便我们能够更好的提供服务。'
|
||||
}
|
||||
console.error('获取位置失败:', err)
|
||||
}
|
||||
|
||||
const reverseGeocode = async (latitude: string, longitude: string) => {
|
||||
try {
|
||||
const data = await getGeocoderCoordinate({
|
||||
location: `${latitude},${longitude}`
|
||||
})
|
||||
console.log(data)
|
||||
setLocationData(data.result)
|
||||
} catch (error) {
|
||||
console.error('Error reverse geocoding location:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const setLocationData = (data: LocationType | any) => {
|
||||
// @ts-ignore
|
||||
// Reflect.ownKeys(data).map(key => location[key] = data[key])
|
||||
location.latitude = data.location.lat
|
||||
location.longitude = data.location.lng
|
||||
location.name = data.formatted_addresses.recommend
|
||||
location.city_id = data.ad_info.city_code
|
||||
}
|
||||
|
||||
return {
|
||||
location,
|
||||
errorTitle,
|
||||
errorContent,
|
||||
showLocationModal,
|
||||
getLocationData,
|
||||
setLocationData
|
||||
}
|
||||
}
|
||||
21
business_uniapp/src/hooks/useLockFn.ts
Normal file
21
business_uniapp/src/hooks/useLockFn.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
|
||||
const isLock = ref(false)
|
||||
const lockFn = async (...args: any[]) => {
|
||||
if (isLock.value) return
|
||||
isLock.value = true
|
||||
try {
|
||||
const res = await fn(...args)
|
||||
isLock.value = false
|
||||
return res
|
||||
} catch (e) {
|
||||
isLock.value = false
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return {
|
||||
isLock,
|
||||
lockFn
|
||||
}
|
||||
}
|
||||
156
business_uniapp/src/hooks/wechat.ts
Normal file
156
business_uniapp/src/hooks/wechat.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
// #ifdef H5
|
||||
import weixin from 'weixin-js-sdk'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { apiJsConfig, apiCodeUrlGet, apiOALogin } from '@/api/app'
|
||||
|
||||
//判断是否为安卓环境
|
||||
function _isAndroid() {
|
||||
const u = navigator.userAgent
|
||||
return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
|
||||
}
|
||||
|
||||
export function getSignLink() {
|
||||
if (typeof window.signLink === 'undefined' || window.signLink === '') {
|
||||
window.signLink = location.href.split('#')[0]
|
||||
}
|
||||
return _isAndroid() ? location.href.split('#')[0] : window.signLink
|
||||
}
|
||||
|
||||
export function wxOaConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiJsConfig().then((res) => {
|
||||
console.log('res:', res) //微信配置
|
||||
weixin.config({
|
||||
debug: false, // 开启调试模式
|
||||
appId: res.appId, // 必填,公众号的唯一标识
|
||||
timestamp: res.timestamp, // 必填,生成签名的时间戳
|
||||
nonceStr: res.nonceStr, // 必填,生成签名的随机串
|
||||
signature: res.signature, // 必填,签名
|
||||
jsApiList: res.jsApiList, // 必填,需要使用的JS接口列表
|
||||
success: () => {
|
||||
resolve('success')
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject('weixin config is fail')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//获取微信登录url
|
||||
export function getWxUrl() {
|
||||
apiCodeUrlGet().then((res) => {
|
||||
location.href = res.url
|
||||
})
|
||||
}
|
||||
|
||||
//微信授权
|
||||
export function authLogin(code) {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiOALogin({
|
||||
code
|
||||
}).then((res) => {
|
||||
const { userStore } = useUserStore()
|
||||
userStore.setToken(res.token)
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function wxOaReady() {
|
||||
return new Promise((resolve, reject) => {
|
||||
weixin.ready(() => {
|
||||
resolve('success')
|
||||
console.log('111222')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function wxOaShare(options: Record<any, any>) {
|
||||
wxOaReady().then(() => {
|
||||
const { shareTitle, shareLink, shareImage, shareDesc } = options
|
||||
weixin.updateTimelineShareData({
|
||||
title: shareTitle, // 分享标题
|
||||
link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
|
||||
imgUrl: shareImage // 分享图标
|
||||
})
|
||||
// 发送给好友
|
||||
weixin.updateAppMessageShareData({
|
||||
title: shareTitle, // 分享标题
|
||||
link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
|
||||
imgUrl: shareImage, // 分享图标
|
||||
desc: shareDesc
|
||||
})
|
||||
// 发送到tx微博
|
||||
weixin.onMenuShareWeibo({
|
||||
title: shareTitle, // 分享标题
|
||||
link: shareLink, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
|
||||
imgUrl: shareImage, // 分享图标
|
||||
desc: shareDesc
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function wxOaPay(options: Record<any, any>) {
|
||||
console.log('options:', options)
|
||||
|
||||
return new Promise((reslove, reject) => {
|
||||
wxOaReady()
|
||||
.then(() => {
|
||||
console.log('微信支付', options)
|
||||
weixin.chooseWXPay({
|
||||
timestamp: options.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写
|
||||
nonceStr: options.nonceStr, // 支付签名随机串,不长于 32 位
|
||||
package: options.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
|
||||
signType: options.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
|
||||
paySign: options.paySign, // 支付签名
|
||||
success: (res: any) => {
|
||||
reslove(res)
|
||||
},
|
||||
cancel: (res: any) => {
|
||||
reject(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function wxOaAddress() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wxOaReady().then(() => {
|
||||
weixin.openAddress({
|
||||
success: (res: any) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function getLocation() {
|
||||
return new Promise((reslove, reject) => {
|
||||
wxOaReady().then(() => {
|
||||
weixin.getLocation({
|
||||
type: 'gcj02',
|
||||
success: (res: any) => {
|
||||
reslove(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// #endif
|
||||
22
business_uniapp/src/main.ts
Normal file
22
business_uniapp/src/main.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createSSRApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import plugins from './plugins'
|
||||
import router from './router'
|
||||
import './styles/index.scss'
|
||||
import { setupMixin } from './mixins'
|
||||
import share from '@/utils/share'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
setupMixin(app)
|
||||
app.use(plugins)
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
app.mixin(share)
|
||||
// #endif
|
||||
|
||||
app.use(router)
|
||||
return {
|
||||
app
|
||||
}
|
||||
}
|
||||
103
business_uniapp/src/manifest.json
Normal file
103
business_uniapp/src/manifest.json
Normal file
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"name" : "",
|
||||
"appid" : "",
|
||||
"description" : "",
|
||||
"versionName" : "1.3.9",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules" : {},
|
||||
/* 应用发布信息 */
|
||||
"distribute" : {
|
||||
/* android打包配置 */
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios" : {},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "wx386a75e518b38935",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : true,
|
||||
"minified" : true
|
||||
},
|
||||
"__usePrivacyCheck__" : true,
|
||||
"usingComponents" : true,
|
||||
"permission" : {
|
||||
"scope.userLocation" : {
|
||||
"desc" : "需要获取定位"
|
||||
}
|
||||
},
|
||||
"requiredPrivateInfos" : [
|
||||
"getLocation",
|
||||
"chooseAddress",
|
||||
"chooseLocation",
|
||||
"choosePoi",
|
||||
"onLocationChange",
|
||||
"startLocationUpdate",
|
||||
"startLocationUpdateBackground"
|
||||
]
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
"vueVersion" : "3",
|
||||
"h5" : {
|
||||
"router" : {
|
||||
"mode" : "history",
|
||||
"base" : "/shop/"
|
||||
},
|
||||
"title" : "加载中",
|
||||
"sdkConfigs" : {
|
||||
"maps" : {
|
||||
"qqmap" : {
|
||||
"key" : "E62BZ-C7PKH-CS4DX-WZVKZ-LNERJ-NFB4I"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
business_uniapp/src/mixins/index.ts
Normal file
5
business_uniapp/src/mixins/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { App } from 'vue'
|
||||
import theme from './theme'
|
||||
export function setupMixin(app: App) {
|
||||
app.mixin(theme)
|
||||
}
|
||||
18
business_uniapp/src/mixins/theme.ts
Normal file
18
business_uniapp/src/mixins/theme.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useThemeStore } from '@/stores/theme'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
$theme() {
|
||||
const themeStore = useThemeStore()
|
||||
const appStore = useAppStore()
|
||||
return {
|
||||
primaryColor: themeStore.primaryColor,
|
||||
pageStyle: themeStore.vars,
|
||||
navColor: themeStore.navColor,
|
||||
navBgColor: themeStore.navBgColor,
|
||||
title: appStore.getWebsiteConfig.shop_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
business_uniapp/src/packages/pages/404/404.vue
Normal file
24
business_uniapp/src/packages/pages/404/404.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar
|
||||
:front-color="$theme.navColor"
|
||||
:background-color="$theme.navBgColor"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view class="h-screen flex flex-col justify-center items-center">
|
||||
<view>
|
||||
<u-empty text="对不起,您访问的页面不存在" mode="data"></u-empty>
|
||||
</view>
|
||||
<view class="w-full px-[100rpx] mt-[40rpx]">
|
||||
<router-navigate
|
||||
class="bg-primary rounded-full text-btn-text leading-[80rpx] text-center"
|
||||
to="/"
|
||||
nav-type="reLaunch"
|
||||
>
|
||||
返回首页
|
||||
</router-navigate>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
76
business_uniapp/src/packages/pages/account_detail/index.vue
Normal file
76
business_uniapp/src/packages/pages/account_detail/index.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view>
|
||||
<view class="list">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<view class="card u-flex justify-between" v-for="(item, index) in dataList" :key="index">
|
||||
<view>
|
||||
<view class="font-bold text-1xl">{{ item.change_type_desc }}</view>
|
||||
<view class="mt-[16rpx] text-[24rpx] text-muted">{{ item.create_time }}</view>
|
||||
</view>
|
||||
|
||||
<view class="font-bold text-1xl">
|
||||
<view class="text-[#2189ff]" v-if="item.action == 2">{{ item.change_amount }}</view>
|
||||
<view class="text-[#ff2c3c]" v-if="item.action == 1">{{ item.change_amount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, computed } from 'vue'
|
||||
import { apiFinanceLists } from '@/api/app'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const dataList = ref<Array<any>>([])
|
||||
const paging = shallowRef<any>(null)
|
||||
const type = ref(1)
|
||||
|
||||
//获取佣金明细
|
||||
const queryList = async (page_no: number, page_size: number) => {
|
||||
try {
|
||||
const { lists } = await apiFinanceLists({
|
||||
type: type.value,
|
||||
page_no,
|
||||
page_size
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options:any) => {
|
||||
type.value = options.type as number
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list {
|
||||
padding: 30rpx 24rpx;
|
||||
height: 100vh;
|
||||
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
padding: 30rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
173
business_uniapp/src/packages/pages/apply/apply.vue
Normal file
173
business_uniapp/src/packages/pages/apply/apply.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<view class="page flex flex-col">
|
||||
<view class="pt-[30rpx] pb-[80rpx] px-[20rpx] text-white topBg">
|
||||
<view class="flex">
|
||||
<u-image width="120rpx" height="120rpx" :src="examine"></u-image>
|
||||
<view class="flex flex-col justify-between ml-2">
|
||||
<view class="text-5xl">填写入驻信息</view>
|
||||
<view>快来获得入驻资格吧!</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
style="background-color: rgba(255, 255, 255, 0.1)"
|
||||
class="flex justify-around mt-4 p-[20rpx] rounded-lg"
|
||||
>
|
||||
<view class="flex flex-col items-center progressItem">
|
||||
<view
|
||||
style="background-color: rgba(255, 255, 255, 0.1)"
|
||||
class="rounded-full w-[44rpx] h-[44rpx] flex items-center justify-center"
|
||||
>
|
||||
<view v-if="current == 0">1</view>
|
||||
<view
|
||||
v-if="current != 0"
|
||||
class="w-[22rpx] h-[22rpx] bg-white rounded-full"
|
||||
></view>
|
||||
</view>
|
||||
<view class="mt-2">商家认证</view>
|
||||
</view>
|
||||
<view class="flex flex-col items-center progressItem">
|
||||
<view
|
||||
style="background-color: rgba(255, 255, 255, 0.1)"
|
||||
class="rounded-full w-[44rpx] h-[44rpx] flex items-center justify-center"
|
||||
>
|
||||
<view v-if="current == 1">2</view>
|
||||
<view
|
||||
v-if="current != 1"
|
||||
class="w-[22rpx] h-[22rpx] bg-white rounded-full"
|
||||
></view>
|
||||
</view>
|
||||
<view class="mt-2">店铺资料</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rounded-t-2xl flex-1 min-h-0 p-[40rpx] mt-[-30rpx] bg-white">
|
||||
<Base v-model="formData" v-if="current == 0"></Base>
|
||||
<PersonalData v-model="formData" v-if="current == 1"></PersonalData>
|
||||
</view>
|
||||
<view class="px-[20rpx] py-[20rpx] flex footer bg-white">
|
||||
<u-button @click="toPer" v-if="current != 0" class="w-full">上一步</u-button>
|
||||
<u-button @click="toNext" v-if="current != 1" class="w-full ml-2" type="primary">下一步</u-button>
|
||||
<u-button @click="submit" v-if="current == 1" class="w-full ml-2" type="primary">提交</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Base from './components/base.vue'
|
||||
import PersonalData from './components/personal-data.vue'
|
||||
import examine from '@/static/images/examine.png'
|
||||
import {apply} from '@/api/app'
|
||||
import {ref} from 'vue'
|
||||
import {onLoad, onShow, onUnload} from '@dcloudio/uni-app'
|
||||
import cache from '@/utils/cache'
|
||||
import {useUserStore} from '@/stores/user'
|
||||
import {getShopDetail} from '@/api/user'
|
||||
import {AuditStatusEnum} from '@/enums/auditEnum'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const formData = ref({
|
||||
name: '',
|
||||
business_start_time: '',
|
||||
business_end_time: '',
|
||||
short_name: '',
|
||||
mobile: '',
|
||||
type: 1,
|
||||
social_credit_ode: '',
|
||||
legal_person: '',
|
||||
legal_id_card: '',
|
||||
shop_address_detail: '',
|
||||
longitude: '',
|
||||
latitude: '',
|
||||
category_ids: [],
|
||||
goods_ids: [],
|
||||
id_card_front: '',
|
||||
id_card_back: '',
|
||||
portrait_shooting: '',
|
||||
logo: '',
|
||||
business_license: '',
|
||||
work_status: '1',
|
||||
server_status: '1',
|
||||
shop_image: '',
|
||||
city_id: '',
|
||||
province_id: '',
|
||||
synopsis: '',
|
||||
province_name: '',
|
||||
city_name: '',
|
||||
region_name: '',
|
||||
})
|
||||
|
||||
const current = ref(0)
|
||||
|
||||
const toPer = () => {
|
||||
current.value != 0 && current.value--
|
||||
}
|
||||
const toNext = () => {
|
||||
current.value != 2 && current.value++
|
||||
}
|
||||
|
||||
//提交
|
||||
const submit = async () => {
|
||||
console.log(formData.value)
|
||||
await apply(formData.value, {token: userStore.temToken})
|
||||
userStore.logout()
|
||||
// userStore.getUser()
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 获取提交信息-审核拒绝
|
||||
const getInfo = async () => {
|
||||
const res = await getShopDetail({token: userStore.temToken})
|
||||
res.goods_ids = res.goods_lists?.map((item: { id: number }) => item.id)
|
||||
formData.value = res
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
const auditStatus = options.auditStatus || ''
|
||||
|
||||
// 审核拒绝,获取上次的申请信息
|
||||
if (auditStatus == AuditStatusEnum.REJECT)
|
||||
getInfo()
|
||||
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
userStore.logout()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 100rpx);
|
||||
|
||||
.footer {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -8rpx 96rpx 0 #141a231f;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
}
|
||||
|
||||
.progressItem {
|
||||
position: relative;
|
||||
|
||||
&:not(:last-child) {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 190rpx;
|
||||
height: 6rpx;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
top: 20rpx;
|
||||
left: 125rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.topBg {
|
||||
background: linear-gradient(180deg, #0963ea 0%, #0b66ef 32.51%, #6aa6ff 68.91%, #f6f7f8 97.06%);
|
||||
}
|
||||
</style>
|
||||
108
business_uniapp/src/packages/pages/apply/components/base.vue
Normal file
108
business_uniapp/src/packages/pages/apply/components/base.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<view>
|
||||
<formItem title="店铺名称">
|
||||
<view class="">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg">
|
||||
<u-input v-model="data.name" placeholder="请输入您的姓名"></u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="店铺类型">
|
||||
<view class="flex items-center">
|
||||
<view class="ml-4">
|
||||
<u-radio-group v-model="data.type">
|
||||
<u-radio :name="1">企业</u-radio>
|
||||
<u-radio :name="2">个体工商户</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="统一社会信用代码">
|
||||
<view class="">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg">
|
||||
<u-input
|
||||
v-model="data.social_credit_ode"
|
||||
placeholder="请输入统一社会信用代码"
|
||||
></u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="营业执照">
|
||||
<view class="">
|
||||
<fileUpload v-model="data.business_license" :max-count="1"></fileUpload>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="法人姓名">
|
||||
<view class="">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg">
|
||||
<u-input v-model="data.legal_person" placeholder="请输入法人的姓名"></u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="法人身份证">
|
||||
<view class="">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg">
|
||||
<u-input v-model="data.legal_id_card" placeholder="请输入法人的身份证"></u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<!-- <formItem title="法人手机号">-->
|
||||
<!-- <view class="">-->
|
||||
<!-- <view class="bg-[#F8F9F9] p-2 rounded-lg">-->
|
||||
<!-- <u-input v-model="data.mobile" placeholder="请输入法人的手机号"></u-input>-->
|
||||
<!-- </view>-->
|
||||
<!-- </view>-->
|
||||
<!-- </formItem>-->
|
||||
<formItem title="身份证照片(人像面)">
|
||||
<view class="">
|
||||
<fileUpload v-model="data.id_card_back" :max-count="1"></fileUpload>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="身份证照片(国徽面)">
|
||||
<view class="">
|
||||
<fileUpload v-model="data.id_card_front" :max-count="1"></fileUpload>
|
||||
</view>
|
||||
</formItem>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import fileUpload from '@/components/file-upload/file-upload.vue'
|
||||
import formItem from './form-item.vue'
|
||||
// import { getCityList } from '@/api/app'
|
||||
import { ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: any
|
||||
}>(),
|
||||
{
|
||||
modelValue: {}
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const data: any = computed({
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
},
|
||||
get() {
|
||||
return props.modelValue
|
||||
}
|
||||
})
|
||||
|
||||
const regionStr = ref('')
|
||||
const regionList = ref([])
|
||||
|
||||
const confirm = (value: any) => {
|
||||
data.value.province_id = value[0].value
|
||||
data.value.city_id = value[1].value
|
||||
regionStr.value = value[0].label + ' ' + value[1].label
|
||||
}
|
||||
|
||||
const show = ref(false)
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<view class="mb-4">
|
||||
<view>{{ title }}</view>
|
||||
<view class="mt-3">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const prop = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<view>
|
||||
<formItem title="店铺简称">
|
||||
<view class="">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg">
|
||||
<u-input
|
||||
v-model="data.short_name"
|
||||
placeholder="长度控制在16个字符以内"
|
||||
></u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="店铺营业时间">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg mr-1">
|
||||
<u-input v-model="data.business_start_time" placeholder="开始时间"></u-input>
|
||||
</view>
|
||||
-
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg ml-1">
|
||||
<u-input v-model="data.business_end_time" placeholder="结束时间"></u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="店铺简介">
|
||||
<view class="">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg">
|
||||
<u-input v-model="data.synopsis" type="textarea" :height="344" placeholder="长度控制在10-100个字符以内" :maxlength="100">
|
||||
</u-input>
|
||||
</view>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="店铺logo">
|
||||
<view class="">
|
||||
<fileUpload v-model="data.logo" :max-count="1"></fileUpload>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="店铺地址">
|
||||
<view
|
||||
@click="getLocation"
|
||||
class="bg-[#F8F9F9] p-3 rounded-lg text-info flex items-center justify-between"
|
||||
>
|
||||
<view>{{ regionStr || '请选择详细地址' }}</view>
|
||||
<u-icon name="arrow-right" :size="28" color="#999"></u-icon>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="详细地址">
|
||||
<view class="bg-[#F8F9F9] p-2 rounded-lg text-info h-[152rpx]">
|
||||
<u-input
|
||||
v-model="data.shop_address_detail"
|
||||
type="textarea"
|
||||
placeholder="请输入详细地址"
|
||||
></u-input>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="店铺经营范围(可多选)">
|
||||
<view class="grid grid-cols-3 gap-2">
|
||||
<view
|
||||
@click="selectCategory(item)"
|
||||
v-for="(item, index) in categoryList"
|
||||
:key="index"
|
||||
class="px-[20rpx] py-[18rpx] bg-[#F8F9F9] text-center rounded-lg"
|
||||
:class="{ 'bg-primary': data.category_ids.includes(item.id), 'text-white': data.category_ids.includes(item.id)}"
|
||||
>{{ item.name }}</view
|
||||
>
|
||||
</view>
|
||||
</formItem>
|
||||
<formItem title="服务项目">
|
||||
<view
|
||||
@click="toSelectProject"
|
||||
class="bg-[#F8F9F9] p-3 rounded-lg text-info flex items-center justify-between"
|
||||
>
|
||||
<view>{{
|
||||
data.goods_ids.length != 0
|
||||
? `已选${data.goods_ids.length}项`
|
||||
: `请选择你的服务项目`
|
||||
}}</view>
|
||||
<u-icon name="arrow-right" :size="28" color="#999"></u-icon>
|
||||
</view>
|
||||
</formItem>
|
||||
<!-- <u-select
|
||||
value-name="id"
|
||||
label-name="name"
|
||||
child-name="sons"
|
||||
v-model="show"
|
||||
mode="mutil-column-auto"
|
||||
:list="regionList"
|
||||
@confirm="confirm"
|
||||
></u-select> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import fileUpload from '@/components/file-upload/file-upload.vue'
|
||||
import formItem from './form-item.vue'
|
||||
import { ref, watch, watchEffect } from 'vue'
|
||||
import { computed, onUnmounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import cache from '@/utils/cache'
|
||||
import { getCategory } from '@/api/app'
|
||||
import { getGeocoderCoordinate } from '@/api/app'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import {useRouter} from "uniapp-router-next";
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: any
|
||||
}>(),
|
||||
{
|
||||
modelValue: {}
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const data: any = computed({
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
},
|
||||
get() {
|
||||
return props.modelValue
|
||||
}
|
||||
})
|
||||
|
||||
const toSelectProject = () => {
|
||||
if (!data.value.category_ids.length) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请选择技能'
|
||||
})
|
||||
return
|
||||
}
|
||||
router.navigate({
|
||||
path: '/packages/pages/select_project/select_project',
|
||||
query: {
|
||||
id: data.value.category_ids,
|
||||
ids: data?.value.goods_ids.join(',')
|
||||
}
|
||||
})
|
||||
}
|
||||
const regionStr = ref('')
|
||||
|
||||
watchEffect(() => {
|
||||
regionStr.value = `${props.modelValue.province_name}${props.modelValue.city_name}${props.modelValue.region_name}`
|
||||
}
|
||||
)
|
||||
|
||||
const getLocation = () => {
|
||||
uni.navigateTo({
|
||||
url: '/packages/pages/location/index'
|
||||
})
|
||||
}
|
||||
|
||||
//获取分类数据
|
||||
const categoryList = ref([])
|
||||
|
||||
const getCategoryList = async () => {
|
||||
// 使用临时token
|
||||
categoryList.value = await getCategory({ token: userStore.temToken })
|
||||
}
|
||||
|
||||
const selectCategory = (item: any) => {
|
||||
data.value.goods_ids = []
|
||||
if (data.value.category_ids.includes(item.id)) {
|
||||
data.value.category_ids = data.value.category_ids.filter((id: number) => id != item.id)
|
||||
return
|
||||
}
|
||||
data.value.category_ids.push(item.id)
|
||||
}
|
||||
|
||||
getCategoryList()
|
||||
|
||||
uni.$on('selectProject', (val) => {
|
||||
console.log('选择了哦', val)
|
||||
data.value.goods_ids = val
|
||||
})
|
||||
|
||||
// 监听选择的地址
|
||||
uni.$on('choiceAddress', async(event) => {
|
||||
data.value.longitude = event.longitude
|
||||
data.value.latitude = event.latitude
|
||||
// console.log('监听地址选择 event ===>', event)
|
||||
|
||||
try {
|
||||
const addressInfo = await getGeocoderCoordinate({
|
||||
location: `${data.value.latitude},${data.value.longitude}`
|
||||
}, { token: userStore.temToken })
|
||||
|
||||
if (addressInfo.status == 0) {
|
||||
let city_id = addressInfo.result.ad_info.city_code.substr(3, 6)
|
||||
if (city_id == 110000 || city_id == 310000 || city_id == 210000 || city_id == 410000) {
|
||||
city_id = city_id * 1
|
||||
city_id += 100
|
||||
}
|
||||
data.value.city_id = city_id + ''
|
||||
data.value.province_id = data.value.city_id.substr(0, 3) + '000'
|
||||
data.value.region_id = addressInfo.result.ad_info.adcode
|
||||
data.value.shop_address_detail = addressInfo.result.address_component.street_number?.length > 0 ? addressInfo.result.address_component.street_number : addressInfo.result.address_component.street
|
||||
// regionStr.value = `${addressInfo.result.ad_info.province} ${addressInfo.result.ad_info.city} ${addressInfo.result.ad_info.district}`
|
||||
data.value.province_name = addressInfo.result.ad_info.province
|
||||
data.value.city_name = addressInfo.result.ad_info.city
|
||||
data.value.region_name = addressInfo.result.ad_info.district
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: addressInfo.message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('逆解析地址错误:', error)
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off(['choiceAddress', 'selectProject'])
|
||||
})
|
||||
</script>
|
||||
63
business_uniapp/src/packages/pages/balance/balance.vue
Normal file
63
business_uniapp/src/packages/pages/balance/balance.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view class="relative user-balance">
|
||||
<image class="absolute w-full" :src="balanceBG"></image>
|
||||
<Nav :percent="percent"></Nav>
|
||||
<view class="px-[30rpx] mt-[40rpx] w-full pb-[40rpx]">
|
||||
<card></card>
|
||||
<statement :scrollTop="scrollTop" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Nav from './components/nav.vue'
|
||||
import statement from './components/statement.vue'
|
||||
import balanceBG from '../../static/images/balanceBG.png'
|
||||
import { onPageScroll } from '@dcloudio/uni-app'
|
||||
import card from './components/card.vue'
|
||||
|
||||
const scrollTop = ref<number>(0)
|
||||
const percent = ref<number>(0)
|
||||
|
||||
onPageScroll((event: any) => {
|
||||
scrollTop.value = event.scrollTop
|
||||
const top = uni.upx2px(100)
|
||||
percent.value = event.scrollTop / top > 1 ? 1 : event.scrollTop / top
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-balance {
|
||||
// &::before {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// width: 100%;
|
||||
// height: 400rpx;
|
||||
// z-index: 2;
|
||||
// background-size: contain;
|
||||
// background-repeat: no-repeat;
|
||||
// background-position: top right;
|
||||
// background-image: url('@/packages/static/images/topBg.png');
|
||||
// }
|
||||
// &::after {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// top: -770px;
|
||||
// left: 50%;
|
||||
// transform: translateX(-50%);
|
||||
// width: 1000px;
|
||||
// height: 1000px;
|
||||
// z-index: 1;
|
||||
// border-radius: 1000px;
|
||||
// background: linear-gradient(292deg,var(--color-primary) 29.15%, var(--color-primary-light-5) 69.66%);
|
||||
// }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<view class="w-full relative mt-[70rpx] z-10">
|
||||
<view class="wrapper backdrop-blur-sm">
|
||||
<view class="flex items-center">
|
||||
<u-image
|
||||
:src="userStore.userInfo.logo"
|
||||
width="60"
|
||||
height="60"
|
||||
border-radius="50%"
|
||||
></u-image>
|
||||
<view class="text-base text-white ml-2">
|
||||
{{ userStore.userInfo.name }}
|
||||
</view>
|
||||
|
||||
<view class="ml-auto mr-3 text-white" @click="toCashOutRecord">提现记录</view>
|
||||
</view>
|
||||
<view
|
||||
class="flex justify-between"
|
||||
style="margin-top: 32rpx"
|
||||
>
|
||||
<view>
|
||||
<price
|
||||
:content="userStore.userInfo.money"
|
||||
main-size="72rpx"
|
||||
minor-size="48rpx"
|
||||
font-weight="900"
|
||||
color="#ffffff"
|
||||
></price>
|
||||
<view class="mt-1 text-base text-white font-medium">
|
||||
当前余额(元)
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="text-primary text-center text-xs bg-white inline"
|
||||
style="height: 72rpx; margin-top: 30px; padding: 18rpx 28rpx; border-radius: 30px 0 0 30px;"
|
||||
@click="toCashOut()"
|
||||
>
|
||||
<text>我要提现</text>
|
||||
<u-icon name="arrow-right" size="24"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {useUserStore} from '@/stores/user'
|
||||
import {useRouter} from "uniapp-router-next";
|
||||
import { WithdrawEnum } from '@/enums/withdraw'
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const toCashOutRecord = () => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/cash_out_record/cash_out_record?apply_type=${WithdrawEnum.COMMISSION}`
|
||||
})
|
||||
}
|
||||
|
||||
//提现
|
||||
const toCashOut = () => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/cash_out/cash_out?apply_type=${WithdrawEnum.COMMISSION}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wrapper {
|
||||
height: 316rpx;
|
||||
padding: 32rpx 0 52rpx 32rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 32rpx;
|
||||
border: 2rpx solid #ffffffcc;
|
||||
background: #ffffff4d;
|
||||
box-shadow: 0 6rpx 72rpx 0 #10653333;
|
||||
backdrop-filter: blur(48rpx);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<u-navbar
|
||||
:is-back="true"
|
||||
title="平台余额"
|
||||
:border-bottom="false"
|
||||
:title-bold="true"
|
||||
:fixed="false"
|
||||
title-color="#fff"
|
||||
back-icon-color="#fff"
|
||||
:background="{
|
||||
background: percent == 0 ? 'rgba(256,256, 256, 0)' : 'rgba(53,55, 66, 1)'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
percent: {
|
||||
type: Number,
|
||||
defualt: 0
|
||||
}
|
||||
})
|
||||
|
||||
watch((value: any) => {
|
||||
console.log(value)
|
||||
}, props.percent)
|
||||
</script>
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<view>
|
||||
<z-paging :use-page-scroll="true" ref="paging" v-model="listData" @query="queryList">
|
||||
<view class="flex items-center pt-[40rpx] pb-[28rpx]">
|
||||
<view class="block"></view>
|
||||
<view class="text-xl font-medium ml-2">账户流水</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<view
|
||||
v-for="(item, index) in listData"
|
||||
:key="index"
|
||||
class="bg-white px-[20rpx] py-[40rpx] mb-3 rounded-lg flex items-center"
|
||||
>
|
||||
<u-image :src="cashOutIcon" width="80rpx" height="80rpx"></u-image>
|
||||
<view class="ml-3 flex flex-col justify-between">
|
||||
<view class="font-medium text-xl">{{ item.change_type_desc }}</view>
|
||||
<view class="text-xs text-muted mt-2">{{ item.create_time }}</view>
|
||||
</view>
|
||||
<view class="font-bold text-2xl ml-auto">
|
||||
<text :class="item.action == 1 ? 'text-primary' : 'text-[#E86016]'">
|
||||
{{ item.action == 1 ? '+' : '-' }}{{ item.change_amount }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { userMoneyList } from '@/api/account'
|
||||
import { ref, shallowRef, watch } from 'vue'
|
||||
import cashOutIcon from '@/packages/static/images/cashOutIcon.png'
|
||||
import { onReachBottom } from '@dcloudio/uni-app'
|
||||
import { WithdrawEnum } from '@/enums/withdraw'
|
||||
|
||||
const props = defineProps<{
|
||||
scrollTop: number
|
||||
}>()
|
||||
|
||||
const paging = shallowRef()
|
||||
const listData = ref<any>([])
|
||||
|
||||
const queryList = async (page_no: number, page_size: number) => {
|
||||
try {
|
||||
const res = await userMoneyList({
|
||||
page_no,
|
||||
page_size,
|
||||
type: WithdrawEnum.COMMISSION
|
||||
})
|
||||
console.log(res.lists)
|
||||
|
||||
paging.value.complete(res.lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.scrollTop, (v) => {
|
||||
paging.value?.updatePageScrollTop(v)
|
||||
})
|
||||
|
||||
// 底部刷新
|
||||
onReachBottom(() => {
|
||||
paging.value?.doLoadMore()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.block {
|
||||
width: 8rpx;
|
||||
height: 32rpx;
|
||||
background-color: #0b66ef;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<view class="p-4">
|
||||
<view
|
||||
v-for="(item, index) in config.way_list"
|
||||
:key="index"
|
||||
class="mb-2 bg-white rounded-lg px-[30rpx] py-[30rpx] flex items-center"
|
||||
@click="goPage('/packages/pages/bind_edit_cash_out/index?type='+item.type)"
|
||||
>
|
||||
<u-image
|
||||
v-if="item.type == 1"
|
||||
border-radius="16rpx"
|
||||
width="50rpx"
|
||||
height="50rpx"
|
||||
:src="wechatIcon"
|
||||
></u-image>
|
||||
<u-image
|
||||
v-if="item.type == 2"
|
||||
border-radius="16rpx"
|
||||
width="50rpx"
|
||||
height="50rpx"
|
||||
:src="aliIcon"
|
||||
></u-image>
|
||||
<u-image
|
||||
v-if="item.type == 3"
|
||||
border-radius="16rpx"
|
||||
width="50rpx"
|
||||
height="50rpx"
|
||||
:src="bankIcon"
|
||||
></u-image>
|
||||
<view class="ml-2 flex flex-col justify-between">
|
||||
<view class="font-bold text-lg"
|
||||
>提现至{{
|
||||
item.type == 1 ? '微信' : item == 2 ? '支付宝' : '银行卡'
|
||||
}}</view
|
||||
>
|
||||
<view class="text-sm text-info">
|
||||
{{ item.config.name?.length ? item.config.name + ' ' + (item.type == 1 ? item.config.mobile : item.type == 2 ? item.config.account : item.type == 3 ? item.config.bank_card : '') : '请设置提现账户' }}
|
||||
</view>
|
||||
</view>
|
||||
<u-icon class="ml-auto" name="arrow-right"></u-icon>
|
||||
</view>
|
||||
<EditPop :type="editType" ref="popRef" @confirm="getConfig"></EditPop>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import EditPop from './components/editPop.vue'
|
||||
import { shallowRef } from 'vue'
|
||||
import wechatIcon from '@/packages/static/images/wechat.png'
|
||||
import aliIcon from '@/packages/static/images/ali.png'
|
||||
import bankIcon from '@/packages/static/images/bank.png'
|
||||
import { getCashOutConfig } from '@/api/cashOut'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
|
||||
//提现配置
|
||||
const config = ref<any>({})
|
||||
//获取配置
|
||||
const getConfig = async () => {
|
||||
config.value = await getCashOutConfig()
|
||||
}
|
||||
const popRef = shallowRef()
|
||||
const editType = ref(0)
|
||||
|
||||
const openEditPop = (type: number) => {
|
||||
editType.value = type
|
||||
popRef.value.open()
|
||||
}
|
||||
|
||||
// 跳转页面
|
||||
const goPage = (url: any) => {
|
||||
uni.navigateTo({ url: url })
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
getConfig()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<view>
|
||||
<u-popup v-model="show" mode="bottom" height="50%" border-radius="14">
|
||||
<view class="text-center py-4 font-bold text-lg">{{ typeText }}提现账户</view>
|
||||
<view class="mt-2 px-4" v-if="type == 1">
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center">
|
||||
<text class="w-[100rpx]">姓名</text>
|
||||
<u-input v-model="config.name" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center mt-4">
|
||||
<text class="w-[100rpx]">手机号</text>
|
||||
<u-input v-model="config.mobile" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<u-button @click="submit" class="mt-4" type="primary">确定</u-button>
|
||||
</view>
|
||||
<view class="mt-2 px-4" v-if="type == 2">
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center">
|
||||
<text class="w-[100rpx]">姓名</text>
|
||||
<u-input v-model="config.name" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center mt-4">
|
||||
<text class="w-[100rpx]">账号</text>
|
||||
<u-input v-model="config.account" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<u-button @click="submit" class="mt-4" type="primary">确定</u-button>
|
||||
</view>
|
||||
<view class="mt-2 px-4" v-if="type == 3">
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center">
|
||||
<text class="w-[120rpx]">姓名</text>
|
||||
<u-input v-model="config.name" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center mt-4">
|
||||
<text class="w-[120rpx]">开户行</text>
|
||||
<u-input v-model="config.bank" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<view class="bg-[#F6F7F8] rounded-md p-2 flex items-center mt-4">
|
||||
<text class="w-[120rpx]">银行卡号</text>
|
||||
<u-input v-model="config.bank_card" class="flex-1 ml-2" />
|
||||
</view>
|
||||
<u-button @click="submit" class="mt-4" type="primary">确定</u-button>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { setWithdrawConfig, withdrawConfig } from '@/api/app'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const config = ref({
|
||||
name: '',
|
||||
bank: '',
|
||||
bank_card: '',
|
||||
mobile: '',
|
||||
account: ''
|
||||
})
|
||||
|
||||
const typeText = computed(() => {
|
||||
return props.type == 1 ? '微信' : props.type == 2 ? '支付宝' : '银行卡'
|
||||
})
|
||||
|
||||
const submit = async () => {
|
||||
await setWithdrawConfig({ type: props.type, config: config.value })
|
||||
show.value = false
|
||||
}
|
||||
|
||||
const getConfig = async () => {
|
||||
await nextTick()
|
||||
const res = await withdrawConfig({ type: props.type })
|
||||
Object.keys(res.config).map((item) => {
|
||||
//@ts-ignore
|
||||
config.value[item] = res.config[item]
|
||||
})
|
||||
}
|
||||
|
||||
const show = ref(false)
|
||||
|
||||
const open = () => {
|
||||
show.value = true
|
||||
getConfig()
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
135
business_uniapp/src/packages/pages/bind_edit_cash_out/index.vue
Normal file
135
business_uniapp/src/packages/pages/bind_edit_cash_out/index.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<view class="bind">
|
||||
<view v-if="type == 1">
|
||||
<view>
|
||||
<view class="font-bold text-2xl mb-[20rpx]">姓名</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.name" :clearable="false" placeholder="输入微信姓名" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx]">
|
||||
<view class="font-bold text-2xl mb-[20rpx]">手机号</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.mobile" :clearable="false" placeholder="输入微信绑定的手机号" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="type == 2">
|
||||
<view>
|
||||
<view class="font-bold text-2xl mb-[20rpx]">姓名</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.name" :clearable="false" placeholder="输入支付宝姓名" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx]">
|
||||
<view class="font-bold text-2xl mb-[20rpx]">账号</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.account" :clearable="false" placeholder="输入支付宝账号" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="type == 3">
|
||||
<view>
|
||||
<view class="font-bold text-2xl mb-[20rpx]">姓名</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.name" :clearable="false" placeholder="输入开户人姓名" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx]">
|
||||
<view class="font-bold text-2xl mb-[20rpx]">开户行</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.bank" :clearable="false" placeholder="输入开户行名称" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx]">
|
||||
<view class="font-bold text-2xl mb-[20rpx]">银行卡号</view>
|
||||
<view>
|
||||
<u-input class="w-full m-0" :custom-style="{'padding':'14rpx 20rpx','border-radius':'12rpx','background-color':'#f5f7f9'}" v-model="config.bank_card" :clearable="false" placeholder="输入银行卡号" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<button class="bg-primary text-lg text-white" @click="confirm">
|
||||
确定
|
||||
</button>
|
||||
<!-- <u-button type="primary" size="medium" @click="confirm">确定</u-button> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { setWithdrawConfig, withdrawConfig } from '@/api/app'
|
||||
|
||||
const type = ref<number>(1)
|
||||
const config = ref({
|
||||
name: '',
|
||||
bank: '',
|
||||
bank_card: '',
|
||||
mobile: '',
|
||||
account: ''
|
||||
})
|
||||
|
||||
|
||||
const getConfig = async () => {
|
||||
await nextTick()
|
||||
const res = await withdrawConfig({ type: type.value })
|
||||
Object.keys(res.config).map((item) => {
|
||||
//@ts-ignore
|
||||
config.value[item] = res.config[item]
|
||||
})
|
||||
}
|
||||
|
||||
//提交
|
||||
const confirm = async() => {
|
||||
await setWithdrawConfig({ type: type.value, config: config.value })
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
onLoad((option) => {
|
||||
type.value = option?.type || 1
|
||||
let titleName = '微信'
|
||||
if(type.value == 2) {
|
||||
titleName = '支付宝'
|
||||
}
|
||||
if(type.value == 3) {
|
||||
titleName = '银行卡'
|
||||
}
|
||||
uni.setNavigationBarTitle({
|
||||
title: titleName
|
||||
})
|
||||
getConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bind {
|
||||
height: 100vh;
|
||||
background-color: #ffffff;
|
||||
padding: 40rpx 24rpx;
|
||||
|
||||
// ::v-deep .input .u-input {
|
||||
// background-color: #f5f7f9 !important;
|
||||
// padding: 14rpx 20rpx !important;
|
||||
// border-radius: 12rpx !important;
|
||||
// }
|
||||
}
|
||||
|
||||
// 底部按钮
|
||||
.footer {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 11;
|
||||
padding: 20rpx 30rpx 50rpx;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 2rpx 2rpx 22rpx rgba($color: #000000, $alpha: 0.2);
|
||||
|
||||
::v-deep button {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
174
business_uniapp/src/packages/pages/bond/bond.vue
Normal file
174
business_uniapp/src/packages/pages/bond/bond.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view class="relative">
|
||||
<image class="absolute w-full" :src="bondBG"></image>
|
||||
<Nav :percent="percent"></Nav>
|
||||
<view class="px-[30rpx] mt-[40rpx] w-full pb-[400rpx]">
|
||||
<card :bond-data="depositData.deposit"></card>
|
||||
<bond_main :list-data="depositData.package_list" @change="handleFormData"></bond_main>
|
||||
</view>
|
||||
<bottom @click="recharge"></bottom>
|
||||
</view>
|
||||
|
||||
<!-- 支付宝支付弹窗 -->
|
||||
<u-popup
|
||||
v-model="alipayShow"
|
||||
mode="bottom"
|
||||
height="600rpx"
|
||||
safe-area-inset-bottom
|
||||
border-radius="20"
|
||||
closeable
|
||||
@close="handleclose"
|
||||
>
|
||||
<view style="padding: 60rpx 30rpx;display: flex;justify-content: center;align-items: center;flex-direction: column;">
|
||||
<view style="font-size: 50rpx;margin: 10rpx 0 20rpx;">¥{{ formData.money }}</view>
|
||||
<view class="flex row-between m-t-50" style="width: 100%;justify-content: space-between;font-weight: bold;">
|
||||
<text class="bold">支付方式</text>
|
||||
<text class="bold">支付宝</text>
|
||||
</view>
|
||||
<view class="p-20 m-t-50 m-b-50" style="width: 100%;background-color: #9e9e9e40;padding: 15rpx 15rpx;margin: 50rpx 0;">请复制链接,粘贴至浏览器并支付</view>
|
||||
<button @click="copyAlipayLink()" style="border-radius: 12rpx;width: 100%;height: 80rpx;line-height: 80rpx;font-size: 28rpx;color: white;background-color: #F36161;">复制链接</button>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Nav from './components/nav.vue'
|
||||
import bondBG from '../../static/images/bondBG.png'
|
||||
import card from './components/card.vue'
|
||||
import bond_main from './components/bond_main.vue'
|
||||
import bottom from './components/bottom.vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { onPageScroll } from '@dcloudio/uni-app'
|
||||
import { getDepositList,apiRechargeDeposit } from '@/api/user'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { usePay } from '@/hooks/payment'
|
||||
import { apiJumpPayPrepay, apiJumpPayResult } from '@/api/pay'
|
||||
|
||||
const { initPayWay, handlePayPrepay } = usePay()
|
||||
const scrollTop = ref<number>(0)
|
||||
const percent = ref<number>(0)
|
||||
const formData = reactive({
|
||||
pay_way: '' as any,
|
||||
money: '' as any
|
||||
})
|
||||
const depositData = ref<any>({})
|
||||
const orderId = ref()
|
||||
const alipayLink = ref('')
|
||||
const alipayShow = ref(false)
|
||||
const token = getToken()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const getPackageList = async () => {
|
||||
depositData.value = await getDepositList()
|
||||
}
|
||||
const handleFormData = (e:any) => {
|
||||
formData.pay_way = e.pay_way
|
||||
formData.money = e.money
|
||||
}
|
||||
|
||||
|
||||
//充值
|
||||
const recharge = async() => {
|
||||
if (!formData.money) return uni.$u.toast('请输入充值金额')
|
||||
if (!formData.pay_way) return uni.$u.toast('请选择支付方式')
|
||||
uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '是否确认下单支付?',
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
handlePlaceOrder()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 下单处理
|
||||
const handlePlaceOrder = async (): Promise<void> => {
|
||||
uni.showLoading({
|
||||
title: '订单提交中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await apiRechargeDeposit(formData)
|
||||
orderId.value = res.id
|
||||
|
||||
|
||||
//支付宝支付
|
||||
if(formData.pay_way == 2) {
|
||||
uni.hideLoading();
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
alipayLink.value = `${import.meta.env.VITE_APP_BASE_URL || ''}shop/packages/pages/toAlipay/toAlipay?order_id=${orderId.value}&from=${res.type}&pay_way=${formData.pay_way}&key=${token}`
|
||||
alipayShow.value = true
|
||||
return
|
||||
// #endif
|
||||
|
||||
//#ifdef H5
|
||||
// let ua = navigator.userAgent.toLowerCase()
|
||||
// if (ua.match(/MicroMessenger/i)) {
|
||||
// //微信浏览器环境
|
||||
// uni.reLaunch({
|
||||
// url: `/packages/pages/toAlipay/toAlipay?order_id=${orderId.value}&from=${res.type}&pay_way=${formData.pay_way}&key=${token}`
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
// 支付宝支付,这里只要提交表单
|
||||
const result = await apiJumpPayPrepay({
|
||||
from: res.type,
|
||||
pay_way: formData.pay_way,
|
||||
order_id: orderId.value
|
||||
},token)
|
||||
// 创建一个新的div元素
|
||||
const docdiv = document.createElement('div');
|
||||
// 渲染这个div中的表单(res.data.pay_img就是后端返回的表单)
|
||||
docdiv.innerHTML = result.config;
|
||||
// 进行元素追加渲染
|
||||
document.body.appendChild(docdiv);
|
||||
// 提交表单(alipaysubmit这个名字是和表单上的name一致的,要注意别搞错了)
|
||||
document.forms['alipaysubmit'].submit();
|
||||
return
|
||||
//#endif
|
||||
}
|
||||
|
||||
//其他支付
|
||||
handlePayPrepay({
|
||||
from: res.type,
|
||||
pay_way: formData.pay_way,
|
||||
order_id: orderId.value
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('下单', err)
|
||||
}
|
||||
}
|
||||
//支付宝弹窗关闭
|
||||
function handleclose() {
|
||||
const param = JSON.stringify({
|
||||
order_id: orderId.value,
|
||||
from: 'deposit'
|
||||
})
|
||||
uni.reLaunch({
|
||||
url: `/pages/payment_result/payment_result?id=${orderId.value}&from=deposit`
|
||||
})
|
||||
}
|
||||
//复制支付宝支付链接
|
||||
function copyAlipayLink() {
|
||||
uni.setClipboardData({
|
||||
data: alipayLink.value
|
||||
});
|
||||
}
|
||||
|
||||
getPackageList()
|
||||
|
||||
onPageScroll((event: any) => {
|
||||
scrollTop.value = event.scrollTop
|
||||
const top = uni.upx2px(100)
|
||||
percent.value = event.scrollTop / top > 1 ? 1 : event.scrollTop / top
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="bg-white rounded-lg flex items-center mt-4 py-[20rpx] px-[32rpx]">
|
||||
<text class="text-[40rpx] font-bold">¥</text>
|
||||
<u-input placeholder="请输入自定义金额" class="ml-2" v-model="money"></u-input>
|
||||
</view>
|
||||
<view class="mt-[50rpx]">
|
||||
<view class="flex items-center">
|
||||
<view class="block"></view>
|
||||
<view class="text-lg font-medium ml-2">支付方式</view>
|
||||
</view>
|
||||
<view class="mt-[28rpx]" v-for="item in payWayList" :key="item.pay_way" @click="choosePayWay = item.pay_way">
|
||||
<view class="bg-white rounded-lg py-[26rpx] px-[32rpx] flex items-center">
|
||||
<u-image width="50" height="50" :src="item.image"></u-image>
|
||||
<view class="ml-2 font-semibold">{{ item.extra }}</view>
|
||||
<checkboxMark background="#7E5008" class="ml-auto" :select="choosePayWay == item.pay_way"></checkboxMark>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-[50rpx]">
|
||||
<view class="flex items-center">
|
||||
<view class="block"></view>
|
||||
<view class="text-lg font-medium ml-2">保证金套餐</view>
|
||||
</view>
|
||||
<view class="grid grid-cols-3 gap-2 mt-[28rpx]">
|
||||
<view
|
||||
v-for="(item, index) in listData"
|
||||
:key="index"
|
||||
class="relative bg-white p-[50rpx] flex flex-col items-center rounded-lg"
|
||||
style="overflow: hidden;"
|
||||
@click="money = item.money"
|
||||
>
|
||||
<view class="text-[28rpx] mb-[6rpx]">{{ item.name }}</view>
|
||||
<view class="mb-[40rpx] text-center">
|
||||
<price
|
||||
fontWeight="900"
|
||||
:content="item.money"
|
||||
color="#7E5008"
|
||||
main-size="48rpx"
|
||||
minor-size="28rpx"
|
||||
></price>
|
||||
</view>
|
||||
<view class="absolute bottom-0 bg-[#F9F1E6] w-full text-center text-[#7E5008] text-[24rpx] py-[6rpx]">
|
||||
日限{{ item.order_limit }}单
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import checkboxMark from '@/components/checkbox-mark/index.vue'
|
||||
import { getPayWay } from '@/api/pay'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const payWayList = ref<any>([])
|
||||
const choosePayWay = ref(0)
|
||||
const money = ref()
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const getPayWayList = async () => {
|
||||
const data = await getPayWay()
|
||||
choosePayWay.value = data.lists.find(element => element.is_default == 1)?.pay_way || 0
|
||||
payWayList.value = data.lists
|
||||
}
|
||||
|
||||
const prop = defineProps({
|
||||
listData: {
|
||||
type: Array,
|
||||
default: () => [] as any
|
||||
}
|
||||
})
|
||||
|
||||
getPayWayList()
|
||||
|
||||
watch(
|
||||
() => [money.value,choosePayWay.value],
|
||||
([value1,value2]) => {
|
||||
emit('change',{pay_way:choosePayWay.value,money:money.value})
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.block {
|
||||
width: 4px;
|
||||
height: 36rpx;
|
||||
background-color: #7e5008;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<view class="bottom fixed z-50 bottom-0 bg-white w-full pt-[20rpx] px-[30rpx] pb-[60rpx]">
|
||||
<u-button v-if="true" :custom-style="btnClass" @click="beClick">缴纳保证金</u-button>
|
||||
<view class="mt-[24rpx] text-[32rpx] font-bold text-center" @click="toCashOut">提现</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { WithdrawEnum } from '@/enums/withdraw'
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const btnClass = ref({
|
||||
background: '#424755',
|
||||
color: '#fff'
|
||||
})
|
||||
|
||||
//点击
|
||||
const beClick = () => {
|
||||
console.log(uni.$u.color['error'])
|
||||
emit('click')
|
||||
}
|
||||
|
||||
//提现
|
||||
const toCashOut = () => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/cash_out/cash_out?apply_type=${WithdrawEnum.EARNEST}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bottom {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 40rpx);
|
||||
}
|
||||
</style>
|
||||
75
business_uniapp/src/packages/pages/bond/components/card.vue
Normal file
75
business_uniapp/src/packages/pages/bond/components/card.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<view class="w-full relative mt-[100rpx]">
|
||||
<image
|
||||
class="w-[100%] h-[316rpx]"
|
||||
:src="bondCard"
|
||||
style="border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 30rpx"
|
||||
></image>
|
||||
<view class="absolute w-full z-30 top-0 p-4 text-white">
|
||||
<view class="flex items-center">
|
||||
<u-image
|
||||
:src="userStore.userInfo.logo"
|
||||
width="60"
|
||||
height="60"
|
||||
border-radius="50%"
|
||||
></u-image>
|
||||
<view class="text-base text-white ml-2">
|
||||
{{ userStore.userInfo.name }}
|
||||
</view>
|
||||
|
||||
<view class="ml-auto text-white" @click="toCashOutRecord">提现记录</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<price
|
||||
color="#fff"
|
||||
minor-size="40rpx"
|
||||
main-size="60rpx"
|
||||
:content="bondData"
|
||||
fontWeight="900"
|
||||
></price>
|
||||
</view>
|
||||
<view class="text-lg mt-2">当前保证金(元)</view>
|
||||
<view class="absolute text-[#7E5008] right-[0rpx] bottom-[50rpx] bg-liu-shui"
|
||||
@click="goPage('/packages/pages/account_detail/index?type=2')"
|
||||
>
|
||||
<span>账户流水</span>
|
||||
<u-icon name="arrow-right" color="#7E5008" :size="24"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import bondCard from '../../../static/images/bondCard.png'
|
||||
import price from '@/components/price/price.vue'
|
||||
import { WithdrawEnum } from '@/enums/withdraw'
|
||||
|
||||
const prop = defineProps({
|
||||
bondData: {
|
||||
type: [Number, String],
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const toCashOutRecord = () => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/cash_out_record/cash_out_record?apply_type=${WithdrawEnum.EARNEST}`
|
||||
})
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
//页面跳转
|
||||
const goPage = (url: string) => {
|
||||
uni.navigateTo({ url: url })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bg-liu-shui {
|
||||
padding: 18rpx 16rpx 18rpx 28rpx;
|
||||
background-color: #FFF;
|
||||
border-radius: 30px 0 0 30px;
|
||||
}
|
||||
</style>
|
||||
29
business_uniapp/src/packages/pages/bond/components/nav.vue
Normal file
29
business_uniapp/src/packages/pages/bond/components/nav.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<u-navbar
|
||||
:is-back="true"
|
||||
title="我的保证金"
|
||||
:border-bottom="false"
|
||||
:title-bold="true"
|
||||
:fixed="false"
|
||||
title-color="#fff"
|
||||
back-icon-color="#fff"
|
||||
:background="{
|
||||
background: percent == 0 ? 'rgba(256,256, 256, 0)' : 'rgba(53,55, 66, 1)'
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
percent: {
|
||||
type: Number,
|
||||
defualt: 0
|
||||
}
|
||||
})
|
||||
|
||||
watch((value: any) => {
|
||||
console.log(value)
|
||||
}, props.percent)
|
||||
</script>
|
||||
@@ -0,0 +1,131 @@
|
||||
<!-- 营业时间 -->
|
||||
<template>
|
||||
<view class="m-[30rpx] p-[20rpx] bg-white rounded-[20rpx]">
|
||||
|
||||
<view class="">
|
||||
<view class="mb-[20rpx] text-xl">
|
||||
营业日期
|
||||
</view>
|
||||
<u-checkbox v-model="formData.monday">周一</u-checkbox>
|
||||
<u-checkbox v-model="formData.tuesday">周二</u-checkbox>
|
||||
<u-checkbox v-model="formData.wednesday">周三</u-checkbox>
|
||||
<u-checkbox v-model="formData.thursday">周四</u-checkbox>
|
||||
<u-checkbox v-model="formData.friday">周五</u-checkbox>
|
||||
<u-checkbox v-model="formData.saturday">周六</u-checkbox>
|
||||
<u-checkbox v-model="formData.sunday">周日</u-checkbox>
|
||||
</view>
|
||||
|
||||
<view class="mt-[40rpx]">
|
||||
<view class="mb-[20rpx] text-xl">
|
||||
营业时间
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view class="bg-[#f5f5f5] rounded-[12rpx] input-item flex-1" @click="openTimePicker('business_start_time')">
|
||||
<!-- <u-input input-align="center" placeholder="请输入开始时间" v-model="formData.business_start_time"></u-input> -->
|
||||
{{ formData.business_start_time || '请选择开始时间' }}
|
||||
</view>
|
||||
<view class="flex-none division-line"></view>
|
||||
<view class="bg-[#f5f5f5] rounded-[12rpx] input-item flex-1" @click="openTimePicker('business_end_time')">
|
||||
<!-- <u-input input-align="center" placeholder="请输入结束时间" v-model="formData.business_end_time"></u-input> -->
|
||||
{{ formData.business_end_time || '请选择结束时间' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bg-white my-btn">
|
||||
<u-button type="primary" @click="submit">确定</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 时间选择器 -->
|
||||
<u-picker
|
||||
@confirm="timePickerConfirm"
|
||||
:params="timePickerParams"
|
||||
v-model="timePickerShow"
|
||||
mode="time"
|
||||
:showTimeTag="false"
|
||||
></u-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getBusiness, setBusiness } from '@/api/user'
|
||||
|
||||
const formData = ref({
|
||||
monday: false,
|
||||
tuesday: false,
|
||||
wednesday: false,
|
||||
thursday: false,
|
||||
friday: false,
|
||||
saturday: false,
|
||||
sunday: false,
|
||||
business_start_time: '',
|
||||
business_end_time: '',
|
||||
})
|
||||
|
||||
//时间选择模块
|
||||
const timePickerShow = ref(false)
|
||||
const timePickerParams = ref({
|
||||
year: false,
|
||||
month: false,
|
||||
day: false,
|
||||
hour: true,
|
||||
minute: true,
|
||||
second: false
|
||||
})
|
||||
const timePickerOpenType = ref('')
|
||||
//打开时间选择弹框
|
||||
const openTimePicker = (value: string) => {
|
||||
timePickerShow.value = true
|
||||
timePickerOpenType.value = value
|
||||
}
|
||||
|
||||
const timePickerConfirm = (time: any) => {
|
||||
formData.value[timePickerOpenType.value] = `${time.hour}:${time.minute}`
|
||||
}
|
||||
|
||||
|
||||
const submit = async () => {
|
||||
await setBusiness({...formData.value})
|
||||
getDetail()
|
||||
}
|
||||
|
||||
const getDetail = async () => {
|
||||
formData.value = await getBusiness()
|
||||
console.log('formData.value =>', formData.value)
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
getDetail()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-item {
|
||||
padding: 20rpx 30rpx;
|
||||
height: 80rpx;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.division-line {
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 2rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.my-btn {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 24px 0 #141a231f;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
</style>
|
||||
144
business_uniapp/src/packages/pages/cash_out/cash_out.vue
Normal file
144
business_uniapp/src/packages/pages/cash_out/cash_out.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<!-- 余额、保证金提现 -->
|
||||
<view class="px-2 mt-4">
|
||||
<view class="bg-white rounded-lg py-4 px-2">
|
||||
<view>提现金额</view>
|
||||
<view class="bg-page rounded-lg p-4 flex items-center mt-2">
|
||||
<text class="text-4xl font-bold">¥</text>
|
||||
<u-input
|
||||
v-model="formData.money"
|
||||
placeholder="请输入自定义金额"
|
||||
class="ml-2"
|
||||
></u-input>
|
||||
</view>
|
||||
<view class="mt-4 text-info flex items-center">
|
||||
<text>可提现{{ formData.apply_type == WithdrawEnum.COMMISSION ? '余额':'保证金' }}:</text>
|
||||
<text v-if="formData.apply_type == WithdrawEnum.COMMISSION">¥{{ config.money }}</text>
|
||||
<text v-if="formData.apply_type == WithdrawEnum.EARNEST">¥{{ config.deposit }}</text>
|
||||
<text class="ml-2 text-primary" @click="allCashOut">全部提现</text>
|
||||
<text class="ml-auto text-sm" v-if="formData.apply_type == WithdrawEnum.COMMISSION">提现手续费{{ config.service_charge }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<view class="flex items-center">
|
||||
<view class="block"></view>
|
||||
<view class="text-lg font-medium ml-2">选择提现账户</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<view
|
||||
@click="selectType(item.type)"
|
||||
v-for="(item, index) in config.way_list"
|
||||
:key="index"
|
||||
class="bg-white p-4 rounded-lg flex items-center mb-2"
|
||||
>
|
||||
<u-image
|
||||
v-if="item.type == 1"
|
||||
border-radius="16rpx"
|
||||
width="50rpx"
|
||||
height="50rpx"
|
||||
:src="wechatIcon"
|
||||
></u-image>
|
||||
<u-image
|
||||
v-if="item.type == 2"
|
||||
border-radius="16rpx"
|
||||
width="50rpx"
|
||||
height="50rpx"
|
||||
:src="aliIcon"
|
||||
></u-image>
|
||||
<u-image
|
||||
v-if="item.type == 3"
|
||||
border-radius="16rpx"
|
||||
width="50rpx"
|
||||
height="50rpx"
|
||||
:src="bankIcon"
|
||||
></u-image>
|
||||
<view class="flex flex-col justify-between ml-2">
|
||||
<view class="font-bold text-lg"
|
||||
>提现至{{
|
||||
item.type == 1 ? '微信' : item.type == 2 ? '支付宝' : '银行卡'
|
||||
}}</view
|
||||
>
|
||||
<view class="text-sm text-info" @click.stop="goPage('/packages/pages/bind_edit_cash_out/index?type='+item.type)">
|
||||
<text>{{ item.config.name?.length ? item.config.name + ' ' + (item.type == 1 ? item.config.mobile : item.type == 2 ? item.config.account : item.type == 3 ? item.config.bank_card : '') : '请设置提现账户' }}</text>
|
||||
<u-icon name="arrow-right" color="#909399" size="22rpx"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<checkbox-mark
|
||||
:select="item.type == formData.type"
|
||||
class="ml-auto"
|
||||
></checkbox-mark>
|
||||
</view>
|
||||
<view class="text-muted text-sm">温馨提示:{{ config.tips }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<bottom @click="cashOut"></bottom>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import bottom from './components/bottom.vue'
|
||||
import { getCashOutConfig, toCashOut } from '@/api/cashOut'
|
||||
import checkboxMark from '@/components/checkbox-mark/index.vue'
|
||||
import wechatIcon from '@/packages/static/images/wechat.png'
|
||||
import aliIcon from '@/packages/static/images/ali.png'
|
||||
import bankIcon from '@/packages/static/images/bank.png'
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||||
import { WithdrawEnum } from '@/enums/withdraw'
|
||||
|
||||
//提现配置
|
||||
const config = ref<any>({})
|
||||
//获取配置
|
||||
const getConfig = async () => {
|
||||
config.value = await getCashOutConfig()
|
||||
}
|
||||
|
||||
//提现表单
|
||||
const formData = ref<any>({
|
||||
money: '',
|
||||
type: '',
|
||||
apply_type: 0, //1-佣金提现;2-保证金提现;
|
||||
})
|
||||
|
||||
//提现
|
||||
const cashOut = async () => {
|
||||
await toCashOut(formData.value)
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/cash_out_record/cash_out_record?apply_type=${formData.value.apply_type}`
|
||||
})
|
||||
}
|
||||
|
||||
//选择提现方式
|
||||
const selectType = (type: number | string) => {
|
||||
formData.value.type = type
|
||||
}
|
||||
|
||||
const allCashOut = () => {
|
||||
if(formData.value.apply_type == WithdrawEnum.COMMISSION) {
|
||||
formData.value.money = config.value.money
|
||||
}else {
|
||||
formData.value.money = config.value.deposit
|
||||
}
|
||||
}
|
||||
|
||||
//页面跳转
|
||||
const goPage = (url: string) => {
|
||||
uni.navigateTo({ url: url })
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
getConfig()
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
formData.value.apply_type = options.apply_type || 0
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.block {
|
||||
width: 5px;
|
||||
height: 50rpx;
|
||||
background-color: var(--color-primary, #0b66ef);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<view class="bottom fixed z-50 bottom-0 bg-white w-full pt-[20rpx] px-[30rpx]">
|
||||
<u-button type="primary" @click="beClick">提现</u-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const btnClass = ref({
|
||||
background: '#424755',
|
||||
color: '#fff'
|
||||
})
|
||||
|
||||
//点击
|
||||
const beClick = () => {
|
||||
console.log(uni.$u.color['error'])
|
||||
emit('click')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bottom {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<view class="withdraw-detail" :style="$theme.pageStyle">
|
||||
<view class="withdraw-detail-item">
|
||||
|
||||
<view class="py-[80rpx] bg-white rounded-t-[20rpx]">
|
||||
<view class="text-[52rpx] font-black text-center">
|
||||
¥{{ formData.money }}
|
||||
</view>
|
||||
<view v-if="formData.status == 1" class="mt-[16rpx] text-muted text-center">{{ formData.status_desc }}</view>
|
||||
<view v-if="formData.status == 2 || formData.status == 4 || formData.status == 5" class="mt-[16rpx] text-primary text-center ">{{ formData.status_desc }}</view>
|
||||
<view v-if="formData.status == 3 || formData.status == 6" class="mt-[16rpx] text-error text-center ">{{ formData.status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<view class="pt-[20rpx] px-[30rpx] bg-white rounded-b-[20rpx]">
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">提现方式</view>
|
||||
<view class="content">{{ formData.type_desc }}</view>
|
||||
</view>
|
||||
<!-- 微信收款码提现 -->
|
||||
<view class="" v-if="formData.type == 1">
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">姓名</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">手机号码</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.mobile }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付宝收款码提现 -->
|
||||
<view v-if="formData.type == 2">
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">姓名</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">账号</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.account }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 银行卡提现 -->
|
||||
<view v-if="formData.type == 3">
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">开户人</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">开户行</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.bank }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">银行卡号</view>
|
||||
<view class="content">{{ formData.withdraw_config_snap.bank_card }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">提现时间</view>
|
||||
<view class="content">{{ formData.create_time }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content" v-if="withdrawType == WithdrawEnum.COMMISSION">
|
||||
<view class="label">服务费</view>
|
||||
<view class="content">{{ formData.service_charge }}%</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content" v-if="withdrawType == WithdrawEnum.COMMISSION">
|
||||
<view class="label">实际到账</view>
|
||||
<view class="text-error">¥{{ formData.left_money }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转账凭证 -->
|
||||
<view
|
||||
class="px-[30rpx] pt-[20rpx] mt-[20rpx] bg-white rounded-[20rpx]"
|
||||
v-if="formData.status !== 1"
|
||||
>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view class="label">审核时间</view>
|
||||
<view class="content">{{ formData.update_time || '-' }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content" v-if="formData.status == 5">
|
||||
<view class="label">转账凭证</view>
|
||||
<!-- <u-image
|
||||
height="160"
|
||||
width="160"
|
||||
:src="formData.transfer_proof"
|
||||
v-if="formData.transfer_proof"
|
||||
@click="showImage([formData.transfer_proof])"
|
||||
>
|
||||
</u-image> -->
|
||||
<view class="text-primary" @click="showImage([formData.transfer_proof])">查看</view>
|
||||
</view>
|
||||
|
||||
<view class="flex justify-between withdrawal-content" v-if="formData.status == 3 || formData.status == 6">
|
||||
<view class="label">失败原因</view>
|
||||
<view class="content">{{ formData.verify_remark || '-' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <view class="check-withdrawal-record">
|
||||
|
||||
<view class="mt-[20rpx]">
|
||||
<button
|
||||
class="plain-primary h-[88rpx] leading-[88rpx] rounded-[30px] tohome"
|
||||
@click="toHome"
|
||||
>
|
||||
返回首页
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="review-success-tips">* 审核成功后约72小时内到账,请留意账户明细</view> -->
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, reactive, ref, shallowRef, unref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { userWithdrawDetail } from '@/api/cashOut'
|
||||
import PageStatus from '@/components/page-status/page-status.vue'
|
||||
import { useRouter } from 'uniapp-router-next'
|
||||
import { WithdrawEnum } from '@/enums/withdraw'
|
||||
|
||||
const router = useRouter()
|
||||
const pageRef = shallowRef()
|
||||
const withdrawId = ref<number>(0)
|
||||
|
||||
const withdrawType = ref(0)
|
||||
|
||||
const formData = ref<any>({
|
||||
type: '', // 提现方式:1-微信;2-支付宝;3-银行卡
|
||||
typeMsg: '', // 提现类型描述
|
||||
status: '', // 提现状态:1-待审核;2-审核成功;3-审核失败;4-提现中;5-转账成功;6-转账失败
|
||||
statusMSg: '', // 提现状态描述
|
||||
money: '', // 是 提现金额
|
||||
bank: '', // 否 type=3时需要 银行
|
||||
bank_account: '', // 否 type=3时需要 账号
|
||||
bank_account_name: '', // 否 type=3时需要 持卡人姓名
|
||||
wechat_account: '', // 否 type=1时需要 微信账号
|
||||
wechat_account_name: '', // 否 type=1时需要 微信真实姓名
|
||||
alipay_account: '', // 否 type=2时需要 支付宝账号
|
||||
alipay_account_name: '', // 否 type=2时需要 支付宝真实姓名
|
||||
remark: '' // 否 提现备注
|
||||
})
|
||||
|
||||
// 获取提现申请详情
|
||||
const getWithdrawDetail = async () => {
|
||||
try {
|
||||
const data = await userWithdrawDetail({
|
||||
id: withdrawId.value
|
||||
})
|
||||
Reflect.ownKeys(data).map((item: any) => {
|
||||
formData[item] = data[item]
|
||||
})
|
||||
formData.value = data
|
||||
// unref(pageRef).close()
|
||||
} catch (error) {
|
||||
// unref(pageRef).show({
|
||||
// text: error,
|
||||
// mode: 'order'
|
||||
// })
|
||||
console.log('获取提现详情', error)
|
||||
}
|
||||
}
|
||||
|
||||
const toRecord = () => {
|
||||
router.redirectTo('/packageA/pages/withdraw_record/withdraw_record')
|
||||
}
|
||||
|
||||
const toHome = () => {
|
||||
router.reLaunch('/pages/index/index')
|
||||
}
|
||||
|
||||
const showImage = (list: any) => {
|
||||
uni.previewImage({
|
||||
urls: list,
|
||||
current: 1
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
|
||||
console.log('cash_out_detail')
|
||||
console.log('options ====>>>', options)
|
||||
|
||||
withdrawType.value = options?.withdrawType || 0
|
||||
withdrawId.value = options?.id || ''
|
||||
|
||||
nextTick(() => {
|
||||
getWithdrawDetail()
|
||||
})
|
||||
// await nextTick()
|
||||
// try {
|
||||
// if (!options?.id) {
|
||||
// throw new Error('请传入详情ID')
|
||||
// }
|
||||
// withdrawId.value = options?.id || ''
|
||||
// await getWithdrawDetail()
|
||||
// } catch (error) {
|
||||
// // unref(pageRef).show({
|
||||
// // text: error,
|
||||
// // mode: 'order'
|
||||
// // })
|
||||
// // console.log('商品详情报错', error)
|
||||
// }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.withdraw-detail {
|
||||
padding: 20rpx 30rpx;
|
||||
// padding-bottom: 100px;
|
||||
|
||||
.label {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.content {
|
||||
color: #666
|
||||
}
|
||||
|
||||
.withdraw-detail-item {
|
||||
.withdrawal-content {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.check-withdrawal-record {
|
||||
padding: 40rpx 0rpx 40rpx 0;
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.review-success-tips {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
button::after {
|
||||
border: none;
|
||||
}
|
||||
.tohome {
|
||||
background-color: white !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<view class="">
|
||||
<tabs
|
||||
:isScroll="true"
|
||||
: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="orderList pt-[20rpx] px-2">
|
||||
<list :cid="item.id" :i="i" :index="current" :withdrawType="withdrawType"></list>
|
||||
</view>
|
||||
</tab>
|
||||
</tabs>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { onLoad, onShow, onReady } from '@dcloudio/uni-app'
|
||||
import list from './components/list.vue'
|
||||
import noLogin from '@/components/no-login/no-login.vue'
|
||||
import { getArticleCate } from '@/api/news'
|
||||
|
||||
// 提现类型
|
||||
const withdrawType = ref(0)
|
||||
|
||||
const tabList = ref<any>([
|
||||
{
|
||||
name: '全部'
|
||||
},
|
||||
{
|
||||
name: '待审核'
|
||||
},
|
||||
{
|
||||
name: '提现中'
|
||||
},
|
||||
{
|
||||
name: '提现成功'
|
||||
},
|
||||
{
|
||||
name: '提现失败'
|
||||
}
|
||||
])
|
||||
const current = ref<number>(0)
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
console.log(index)
|
||||
current.value = Number(index)
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
withdrawType.value = options.apply_type || 0
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.orderList {
|
||||
height: calc(100vh - 80rpx - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<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">
|
||||
<view @click="toDetail(item.id)" class="px-[30rpx] py-[30rpx] bg-white mb-2 rounded-lg flex">
|
||||
<u-image :src="cashOutIcon" width="80rpx" height="80rpx"></u-image>
|
||||
<view class="flex flex-col justify-between ml-2">
|
||||
<view class="font-bold text-base">{{ item.desc }}</view>
|
||||
<view class="text-info text-xs">{{ item.create_time }}</view>
|
||||
</view>
|
||||
<view class="flex flex-col justify-between items-end ml-auto">
|
||||
<view v-if="3 == item.status || 6 == item.status" class="text-error text-xs">{{ item.status_desc }}</view>
|
||||
<view v-else class="text-info text-xs">{{ item.status_desc }}</view>
|
||||
<view class="font-black text-base">{{ item.money }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, shallowRef } from 'vue'
|
||||
import cashOutIcon from '../../../static/images/cashOutIcon.png'
|
||||
import { listData } from '@/api/cashOut'
|
||||
import orderCard from '@/components/orderCard/index.vue'
|
||||
import { getArticleList } from '@/api/news'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
cid: number
|
||||
i: number
|
||||
index: number
|
||||
withdrawType: number | string
|
||||
}>(),
|
||||
{
|
||||
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 toDetail = (id: number) => {
|
||||
console.log('cash_out_record')
|
||||
console.log('id => ', id)
|
||||
console.log('props.withdrawType => ', props.withdrawType)
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/cash_out_detail/cash_out_detail?id=${id}&withdrawType=${props.withdrawType}`,
|
||||
})
|
||||
}
|
||||
|
||||
const queryList = async (page_no: any, page_size: any) => {
|
||||
try {
|
||||
const { lists } = await listData({
|
||||
status: props.index,
|
||||
type: props.withdrawType,
|
||||
page_no,
|
||||
page_size
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
// paging.value.complete([1])
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
96
business_uniapp/src/packages/pages/city/index.vue
Normal file
96
business_uniapp/src/packages/pages/city/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view class="container">
|
||||
<view class="text-3xl font-bold mb-[30rpx]">选择城市</view>
|
||||
<!-- 搜索 -->
|
||||
<u-search
|
||||
placeholder="搜索城市名"
|
||||
v-model="keyword"
|
||||
shape="round"
|
||||
:clearabled="true"
|
||||
:animation="true"
|
||||
:height="70"
|
||||
bg-color="#f2f2f2"
|
||||
@search="getCityData"
|
||||
@custom="getCityData"
|
||||
></u-search>
|
||||
|
||||
<!-- 定位城市 -->
|
||||
<view class="text-[24rpx] text-[#c8c9cc] mt-[30rpx] mb-[30rpx]">已开通以下城市</view>
|
||||
<view>
|
||||
<view>
|
||||
<!-- 城市列表 -->
|
||||
<view
|
||||
v-for="(cityItem, cityIndex) in cityList"
|
||||
:key="cityIndex"
|
||||
class="anchor flex mb-[10rpx]"
|
||||
>
|
||||
<view class="font-bold text-[32rpx]">{{ cityIndex }}</view>
|
||||
<view class="ml-[20rpx]">
|
||||
<block v-for="(cityItem2, cityIndex2) in cityItem" :key="cityIndex2">
|
||||
<view
|
||||
@click="chooseCity(cityItem2)"
|
||||
class="pb-[40rpx] pt-[4rpx] w-[500rpx]"
|
||||
>
|
||||
<view v-if="userStore.userInfo?.city_id != cityItem2.id">
|
||||
{{ cityItem2.name }}
|
||||
</view>
|
||||
<view v-else class="u-flex justify-between">
|
||||
<view class="text-[#fa3534]">{{ cityItem2.name }}</view>
|
||||
<u-icon name="checkmark" color="#fa3534"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { apiRegionCity } from '@/api/app'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
// 搜索关键字
|
||||
const keyword = ref<string | number>('')
|
||||
// 城市列表
|
||||
const cityList = ref<any>([])
|
||||
|
||||
// 选择城市
|
||||
const chooseCity = (param: any) => {
|
||||
const info = {
|
||||
city_id: param.id,
|
||||
latitude: param.db09_lat,
|
||||
longitude: param.db09_lng,
|
||||
cityName: param.name,
|
||||
}
|
||||
uni.$emit('chooseCity', info)
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 获取城市数据
|
||||
const getCityData = async (): Promise<void> => {
|
||||
const data = await apiRegionCity({keyword:keyword.value}, { token: userStore.temToken })
|
||||
cityList.value = data
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
getCityData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
background-color: #ffffff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
383
business_uniapp/src/packages/pages/coach_details/index.vue
Normal file
383
business_uniapp/src/packages/pages/coach_details/index.vue
Normal file
@@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<!-- 技师详情 -->
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view class="coach-detail">
|
||||
<u-sticky h5-nav-height="0" bg-color="transparent">
|
||||
<u-navbar
|
||||
:is-back="true"
|
||||
:is-fixed="true"
|
||||
title="详情"
|
||||
:immersive="true"
|
||||
:border-bottom="false"
|
||||
:title-bold="false"
|
||||
:background="{ background: `rgba(256,256, 256, ${percent})` }"
|
||||
:title-color="percent > 0.5 ? '#000' : !isEmpty ? '#000' : '#fff'"
|
||||
:back-icon-color="percent > 0.5 ? '#000' : !isEmpty ? '#000' : '#fff'"
|
||||
>
|
||||
</u-navbar>
|
||||
</u-sticky>
|
||||
|
||||
<view v-if="isEmpty">
|
||||
<!-- 轮播图 -->
|
||||
<l-swiper
|
||||
:content="{
|
||||
data: coachData.life_photo
|
||||
}"
|
||||
name="url"
|
||||
height="750"
|
||||
indicatorPos="bottomRight"
|
||||
mode="number"
|
||||
borderRadius="0"
|
||||
/>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<view class="coach-detail-info">
|
||||
<view class="flex items-center bg-[#FEF4EB] px-[30rpx] py-[12rpx] rounded-lg">
|
||||
<u-image mode="aspectFit" height="36" width="120" :src="anxingouLogo"></u-image>
|
||||
<view class="text-[#CB9F5D] text-sm ml-4 tips">
|
||||
<text>实名认证</text>
|
||||
<text>资质认证</text>
|
||||
<text>平台担保</text>
|
||||
</view>
|
||||
<u-icon color="#CB9F5D" class="ml-auto" size="28" name="arrow-right"></u-icon>
|
||||
</view>
|
||||
<view class="p-[24rpx] rounded-lg bg-white">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="text-xl font-bold">{{ coachData.name }}</view>
|
||||
<view class="text-[#C38925]">最早可约{{ coachData.first_appoint }}</view>
|
||||
</view>
|
||||
<view class="text-info mt-4 introduct">
|
||||
<text>{{ coachData.gender == 1 ? '男' : '女' }}</text>
|
||||
<text>{{ coachData.age }}岁</text>
|
||||
<text>{{ coachData.education }}</text>
|
||||
<text>{{ coachData.nation }}</text>
|
||||
<text>{{ coachData.shop_name }}</text>
|
||||
</view>
|
||||
<view class="text-xs mt-4 flex justify-between">
|
||||
<view class="flex items-center">
|
||||
<u-image height="24" width="24" :src="user"></u-image>
|
||||
<view class="ml-1">
|
||||
<text class="text-info">已服务</text>
|
||||
<text class="text-primary">{{ coachData.order_num }}人</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<u-image height="24" width="24" :src="favor"></u-image>
|
||||
<view class="ml-1">
|
||||
<text class="text-info">好评率</text>
|
||||
<text class="text-primary">{{ coachData.good_comment }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<u-image height="24" width="24" :src="distance"></u-image>
|
||||
<view class="ml-1">
|
||||
<text class="text-info">距离</text>
|
||||
<text class="text-primary">{{ coachData.distance_desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="coachData.introduction.length"
|
||||
class="bg-white rounded-[20rpx] p-[24rpx] mx-[30rpx]"
|
||||
>
|
||||
<view class="text-lg font-medium">自我介绍</view>
|
||||
<view class="mt-2 font-medium text-content">
|
||||
{{ coachData.introduction }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rounded-[20rpx] bg-white p-[24rpx] m-[30rpx]">
|
||||
<tabs
|
||||
:isScroll="false"
|
||||
height="70"
|
||||
bar-width="60"
|
||||
font-size="32rpx"
|
||||
:bold="true"
|
||||
inactiveColor="#333"
|
||||
:activeColor="$theme.primaryColor"
|
||||
>
|
||||
<tab name="服务项目">
|
||||
<view class="mt-4" v-for="item in coachData.goods_lists" :key="item.id">
|
||||
<project :data="item" :coach_id="coachId" />
|
||||
</view>
|
||||
</tab>
|
||||
<tab name="用户评价">
|
||||
<evaluate
|
||||
v-for="item in coachData.comment_lists"
|
||||
:index="item.id"
|
||||
:data="item"
|
||||
/>
|
||||
</tab>
|
||||
</tabs>
|
||||
</view>
|
||||
|
||||
<view class="footer u-flex fixed">
|
||||
<view class="flex-1">
|
||||
<u-button type="primary" @click="handleCollection(coachData.is_collect)">
|
||||
<u-icon
|
||||
:name="coachData.is_collect ? 'star-fill' : 'star'"
|
||||
color="#ffffff"
|
||||
size="30"
|
||||
class="mb-[1rpx]"
|
||||
/>
|
||||
<text class="text-base font-medium ml-1">加收藏</text>
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="empty">
|
||||
<u-empty
|
||||
text="抱歉,该技师不存在~"
|
||||
:src="'/static/images/empty/collection.png'"
|
||||
:icon-size="300"
|
||||
color="#888888"
|
||||
>
|
||||
<template #bottom>
|
||||
<view class="empty-bottom">
|
||||
<button
|
||||
class="bg-primary text-lg text-white leading-[80rpx] h-[80rpx]"
|
||||
@click="router.reLaunch('/pages/index/index')"
|
||||
>
|
||||
去看看其它
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
</u-empty>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 返回顶部按钮 -->
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
options: {
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, reactive, shallowRef, unref } from 'vue'
|
||||
import { onLoad, onPageScroll, onShow } from '@dcloudio/uni-app'
|
||||
import { apiCoachDetail } from '@/api/coach'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useRouter } from 'uniapp-router-next'
|
||||
import { location } from '@/hooks/useLocation'
|
||||
import anxingouLogo from '@/bundle/static/images/anxingouLogo.png'
|
||||
import user from '@/bundle/static/icon/user.png'
|
||||
import favor from '@/bundle/static/icon/favor.png'
|
||||
import distance from '@/bundle/static/icon/distance.png'
|
||||
|
||||
import project from '@/components/project-card/index.vue'
|
||||
import evaluate from '@/components/evaluate-card/index.vue'
|
||||
import { putCollect } from '@/api/store'
|
||||
|
||||
type CoachType = {
|
||||
age: number
|
||||
comment_num: number
|
||||
distance: number
|
||||
distance_desc: string
|
||||
education: string
|
||||
first_appoint: string
|
||||
gender: number
|
||||
good_comment: string
|
||||
id: number
|
||||
introduction: string
|
||||
life_photo: string[]
|
||||
is_collect: number
|
||||
name: string
|
||||
nation: string
|
||||
order_num: number
|
||||
shop_name: string
|
||||
skill_id: number
|
||||
skill_name: string
|
||||
work_photo: string
|
||||
work_status: number
|
||||
goods_lists: any
|
||||
comment_lists: any
|
||||
[index: string]: string | number | any
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
const appStore = useAppStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const coachData = reactive<CoachType>({
|
||||
age: -1,
|
||||
comment_num: -1,
|
||||
distance: -1,
|
||||
distance_desc: '',
|
||||
education: '',
|
||||
first_appoint: '',
|
||||
gender: -1,
|
||||
good_comment: '',
|
||||
id: -1,
|
||||
introduction: '',
|
||||
is_collect: 0,
|
||||
life_photo: [],
|
||||
name: '',
|
||||
nation: '',
|
||||
order_num: -1,
|
||||
shop_name: '',
|
||||
skill_id: -1,
|
||||
skill_name: '',
|
||||
work_photo: '',
|
||||
work_status: -1,
|
||||
goods_lists: [],
|
||||
comment_lists: []
|
||||
})
|
||||
|
||||
const isLogin = computed(() => userStore.token)
|
||||
const coachId = ref<number | string>('')
|
||||
const scrollTop = ref<number>(0)
|
||||
const percent = ref<number>(0)
|
||||
const isEmpty = ref<boolean>(true)
|
||||
|
||||
/**
|
||||
* @description 获取商品详情
|
||||
*/
|
||||
const initializeCoachDetails = async (): Promise<void> => {
|
||||
try {
|
||||
const res: CoachType = await apiCoachDetail({
|
||||
id: coachId.value,
|
||||
longitude: location.longitude,
|
||||
latitude: location.latitude
|
||||
})
|
||||
res.life_photo = res.life_photo.map((item: string) => {
|
||||
return { url: item }
|
||||
})
|
||||
// @ts-ignore
|
||||
Reflect.ownKeys(coachData).map((key: string) => (coachData[key] = res[key]))
|
||||
console.log(coachData)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
isEmpty.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 收藏
|
||||
const handleCollection = async (is_collect: number): Promise<void> => {
|
||||
if (!isLogin.value) {
|
||||
router.navigateTo('/pages/login/login')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await putCollect({
|
||||
id: unref(coachId) as number,
|
||||
type: 1
|
||||
})
|
||||
if (is_collect) {
|
||||
uni.$u.toast('取消收藏成功')
|
||||
} else {
|
||||
uni.$u.toast('收藏成功')
|
||||
}
|
||||
await initializeCoachDetails()
|
||||
} catch (error) {
|
||||
//TODO handle the exception
|
||||
console.log('收藏请求错误', error)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
coachId.value = options?.id || 0
|
||||
initializeCoachDetails()
|
||||
})
|
||||
|
||||
onPageScroll((event: any) => {
|
||||
scrollTop.value = event.scrollTop
|
||||
const top = uni.upx2px(100)
|
||||
percent.value = event.scrollTop / top > 1 ? 1 : event.scrollTop / top
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coach-detail {
|
||||
padding-bottom: 200rpx;
|
||||
|
||||
:deep(.u-swiper-indicator) {
|
||||
bottom: 100rpx !important;
|
||||
}
|
||||
|
||||
.coach-detail-info {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
margin: -70rpx 30rpx 0 30rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
.introduct {
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 14rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
background-color: #f6f7f8;
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
text {
|
||||
position: relative;
|
||||
margin-right: 20rpx;
|
||||
&:not(:first-child) {
|
||||
::before {
|
||||
content: '·';
|
||||
position: absolute;
|
||||
left: -15rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收藏图标
|
||||
.collection {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
// 底部按钮
|
||||
.footer {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 -4px 48px 0 #141a231f;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
|
||||
// 服务下架或不存在时
|
||||
.empty {
|
||||
padding-top: 200rpx;
|
||||
|
||||
.empty-bottom {
|
||||
width: 90vw;
|
||||
margin-top: 130rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<u-popup mode="bottom" border-radius="14" height="60%" v-model="show" :closeable="true">
|
||||
<view class="bg-page h-full px-2">
|
||||
<view class="text-center py-[40rpx] text-lg font-[900]">待服务订单</view>
|
||||
<z-paging ref="paging" v-model="list" @query="queryList" :fixed="false" height="100%">
|
||||
<view v-for="item in list" :key="item.id">
|
||||
<view @click="toDetail(item.id)" class="bg-white rounded-lg px-[20rpx] py-[30rpx] mb-[20rpx]">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="bg-[#E0EBFD] text-xs text-primary p-[10rpx] rounded-md flex items-center">
|
||||
<u-image src="@/static/images/order/icon_order.png" :width="32" :height="32"></u-image>
|
||||
<view class="ml-[10rpx]">
|
||||
上门时间:{{ item.appoint_date }} {{ item.appoint_time }}
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="item.order_status == 1" class="text-success text-xs">{{ item.order_status_desc }}</view>
|
||||
<view v-else-if="item.order_status == 2" class="text-primary text-xs">{{ item.order_status_desc }}</view>
|
||||
<view v-else class="text-xs">{{ item.order_status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<view v-if="item?.order_goods.length" class="mt-[16rpx] flex items-center">
|
||||
<u-image
|
||||
border-radius="10"
|
||||
width="136rpx"
|
||||
height="136rpx"
|
||||
:src="item?.order_goods[0].goods_image"
|
||||
class="flex-none"
|
||||
></u-image>
|
||||
<view class="ml-[20rpx] flex flex-col justify-between">
|
||||
<view class="font-black">{{ item?.order_goods[0].goods_name }}</view>
|
||||
<view class="text-xs text-muted"
|
||||
>服务时间:{{ item?.order_goods[0].duration }}分钟</view
|
||||
>
|
||||
<view class="font-black text-[#E86016]">
|
||||
<price :content="item?.order_goods[0].goods_price" fontWeight="500" mainSize="32rpx" minorSize="32rpx"></price>
|
||||
</view>
|
||||
</view>
|
||||
<view class="ml-auto text-muted text-xs"> x{{ item?.order_goods[0].goods_num }} </view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import orderCard from '@/components/orderCard/index.vue'
|
||||
import { getDispatchList } from '@/api/order'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
|
||||
const show = ref(false)
|
||||
|
||||
const paging = shallowRef()
|
||||
|
||||
const list = ref<any>([])
|
||||
const coach_id = ref()
|
||||
const open = async (id: number) => {
|
||||
show.value = true
|
||||
coach_id.value = id
|
||||
// list.value = await getDispatchList({ coach_id })
|
||||
paging.value.reload()
|
||||
}
|
||||
|
||||
const queryList = async (page_no: number, page_size: number) => {
|
||||
try {
|
||||
const { lists } = await getDispatchList({
|
||||
coach_id: coach_id.value,
|
||||
page_no,
|
||||
page_size
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
//跳转至详情页
|
||||
const toDetail = (id: number) => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/order_detail/order_detail?id=${id}`
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<!-- 分配订单 -->
|
||||
<page-meta :page-style="$theme.pageStyle">
|
||||
<!-- #ifndef H5 -->
|
||||
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
|
||||
<!-- #endif -->
|
||||
</page-meta>
|
||||
<view class="distribute-order">
|
||||
<!-- 搜索框 -->
|
||||
<view class="px-[24rpx] py-[14rpx] bg-white search-coach">
|
||||
<u-search
|
||||
v-model="keyword"
|
||||
placeholder="请输入技师名"
|
||||
height="72"
|
||||
@search="handleSearch"
|
||||
@custom="handleSearch"
|
||||
></u-search>
|
||||
</view>
|
||||
|
||||
<!-- 搜索 -->
|
||||
<view class="search-content mt-[100rpx]">
|
||||
<!-- -->
|
||||
<view class="search-content-s pt-[30rpx] px-[30rpx]">
|
||||
<block v-for="item in lists" :key="item.id">
|
||||
<view class="rounded-[28rpx] bg-white mb-[20rpx] p-[12rpx] flex noSelect" @click="toSelect(item.id)" :class="{isSelect: item.id == formData.coach_id,}">
|
||||
<u-image :src="item?.work_photo" height="200" width="200" :border-radius="20"></u-image>
|
||||
<view class="flex flex-col justify-between flex-1 ml-2">
|
||||
<view class="font-bold text-lg">{{ item?.name }}</view>
|
||||
<view class="text-sm flex justify-between">
|
||||
<view>
|
||||
<text class="text-info">已服务</text>
|
||||
<text>{{ item?.order_num }}</text>
|
||||
<text class="text-info">人</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="text-info">好评率</text>
|
||||
<text>{{ item?.good_comment }}%</text>
|
||||
</view>
|
||||
<view class="text-info">{{ item?.distance_desc }}</view>
|
||||
</view>
|
||||
<view class="text-info">
|
||||
<u-icon name="map"></u-icon>
|
||||
<text v-if="item?.location?.province">{{ item?.location?.province}}{{ item?.location?.city}}{{ item?.location?.district}}</text>
|
||||
<text v-else>暂无定位信息</text>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<view
|
||||
@click.stop="showPop(item.id)"
|
||||
class="bg-primary-light-9 text-primary py-[6rpx] px-[18rpx] rounded-full text-sm"
|
||||
>
|
||||
<text>待服务订单 {{ item?.wait_serve_num || 0 }}</text>
|
||||
<u-icon name="arrow-down" class="ml-[8rpx]"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-[20rpx] py-[20rpx] footer bg-white">
|
||||
<u-button type="primary" @click="confirm">确定</u-button>
|
||||
</view>
|
||||
<orderPop ref="popRef"></orderPop>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, shallowRef, nextTick } from 'vue'
|
||||
import orderPop from './components/orderPop.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { changeService, getOrderCoachLists } from '@/api/order'
|
||||
|
||||
|
||||
const popRef = shallowRef()
|
||||
|
||||
const keyword = ref<string>('')
|
||||
const paging = shallowRef()
|
||||
|
||||
// 技师列表数据
|
||||
const lists = ref({})
|
||||
|
||||
const formData = ref({
|
||||
id: '',
|
||||
coach_id: ''
|
||||
})
|
||||
|
||||
// 搜索
|
||||
const handleSearch = (value: string) => {
|
||||
keyword.value = value
|
||||
queryList()
|
||||
}
|
||||
|
||||
// 获取列表数据
|
||||
const queryList = async () => {
|
||||
try {
|
||||
lists.value = await getOrderCoachLists({
|
||||
id: formData.value.id,
|
||||
keyword: keyword.value,
|
||||
})
|
||||
console.log('lists=》',lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
}
|
||||
}
|
||||
|
||||
//跳转至详情
|
||||
const showPop = (id: any) => {
|
||||
popRef.value.open(id)
|
||||
}
|
||||
|
||||
// 选择技师
|
||||
const toSelect = (id) => {
|
||||
formData.value.coach_id = id
|
||||
}
|
||||
|
||||
//确定
|
||||
const confirm = async () => {
|
||||
await changeService({ ...formData.value })
|
||||
uni.navigateBack()
|
||||
console.log(formData.value)
|
||||
}
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
formData.value.id = options.id
|
||||
await nextTick()
|
||||
queryList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.distribute-order{
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 120rpx);
|
||||
|
||||
.search-coach {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// .search-content {
|
||||
// height: calc(100vh - 126px - 120rpx - env(safe-area-inset-bottom));
|
||||
// &-s {
|
||||
// height: 100%;
|
||||
// }
|
||||
// }
|
||||
|
||||
.footer {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -8rpx 96rpx 0 #141a231f;
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
}
|
||||
|
||||
.noSelect {
|
||||
border: 4rpx solid transparent;
|
||||
}
|
||||
.isSelect {
|
||||
border: 4rpx solid #007aff;
|
||||
background-image: url("@/packages/static/images/master/bg_select_jishi.png");
|
||||
background-size: cover
|
||||
}
|
||||
</style>
|
||||
132
business_uniapp/src/packages/pages/entry_info/entry_info.vue
Normal file
132
business_uniapp/src/packages/pages/entry_info/entry_info.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<!-- 入驻信息 -->
|
||||
<template>
|
||||
<view class="px-[30rpx] py-[20rpx] entry-info">
|
||||
|
||||
<view class="bg-white rounded-lg">
|
||||
<view class="item flex justify-between px-[20rpx] py-[30rpx] ">
|
||||
<view class="text-content">
|
||||
商户名称
|
||||
</view>
|
||||
<view class="">
|
||||
{{ entryInfo?.name || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="item flex justify-between px-[20rpx] py-[30rpx] ">
|
||||
<view class="text-content">
|
||||
商户类型
|
||||
</view>
|
||||
<view class="">
|
||||
{{ handleShopType(entryInfo?.type)}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="item flex justify-between px-[20rpx] py-[30rpx] ">
|
||||
<view class="text-content">
|
||||
负责人
|
||||
</view>
|
||||
<view class="">
|
||||
{{ entryInfo?.legal_person || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="item flex justify-between px-[20rpx] py-[30rpx] ">
|
||||
<view class="text-content">
|
||||
手机号
|
||||
</view>
|
||||
<view class="">
|
||||
{{ entryInfo?.mobile || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="item flex justify-between px-[20rpx] py-[30rpx] ">
|
||||
<view class="text-content">
|
||||
统一社会信用代码
|
||||
</view>
|
||||
<view class="">
|
||||
{{ entryInfo?.social_credit_ode || '-' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 营业执照 -->
|
||||
<view class="bg-white rounded-lg px-[20rpx] py-[30rpx] mt-[20rpx]">
|
||||
<view class="title mb-2 flex items-center text-center">
|
||||
<view class="block"></view>
|
||||
<view class="ml-2">营业执照</view>
|
||||
</view>
|
||||
<u-image :src="entryInfo?.business_license" width="650" height="340" mode="aspectFit"></u-image>
|
||||
</view>
|
||||
|
||||
<!-- 身份证人像面 -->
|
||||
<view class="bg-white rounded-lg px-[20rpx] py-[30rpx] mt-[20rpx]">
|
||||
<view class="title mb-2 flex items-center text-center">
|
||||
<view class="block"></view>
|
||||
<view class="ml-2">身份证人像面</view>
|
||||
</view>
|
||||
<u-image :src="entryInfo?.id_card_back" width="650" height="340" mode="aspectFit"></u-image>
|
||||
</view>
|
||||
|
||||
<!-- 身份证国徽面 -->
|
||||
<view class="bg-white rounded-lg px-[20rpx] py-[30rpx] mt-[20rpx]">
|
||||
<view class="title mb-2 flex items-center text-center">
|
||||
<view class="block"></view>
|
||||
<view class="ml-2">身份证国徽面</view>
|
||||
</view>
|
||||
<u-image :src="entryInfo?.id_card_front" width="650" height="340" mode="aspectFit"></u-image>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
// import { useUserStore } from '@/stores/user'
|
||||
import { getShopDetail } from '@/api/user'
|
||||
|
||||
// const { userInfo } = useUserStore()
|
||||
|
||||
const entryInfo = ref({
|
||||
type: 1,
|
||||
})
|
||||
|
||||
// 获取入驻信息
|
||||
const getInfo = async () => {
|
||||
let data = await getShopDetail()
|
||||
entryInfo.value = data
|
||||
}
|
||||
|
||||
// 商户类型 1-企业,2-个体工商户
|
||||
|
||||
const handleShopType = (type:number):string => {
|
||||
if( type == 1 )
|
||||
return '企业'
|
||||
else if ( type == 2 )
|
||||
return '个体工商户'
|
||||
else
|
||||
return '-'
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
getInfo()
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.entry-info {
|
||||
.item {
|
||||
border-bottom: 2rpx solid #f8f8f8;
|
||||
}
|
||||
|
||||
.title {
|
||||
.block {
|
||||
width: 4rpx;
|
||||
height: 28rpx;
|
||||
background-color: #0B66EF;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<view class="bg-white px-[20rpx] py-[30rpx] rounded-lg">
|
||||
<view class="text-sm flex justify-between">
|
||||
<text class="text-info">订单编号:1231231231123</text>
|
||||
<text class="text-[#E86016]">待结算</text>
|
||||
</view>
|
||||
<view class="mt-2 flex items-center">
|
||||
<u-image src="" width="130rpx" height="130rpx" border-radius="16rpx"></u-image>
|
||||
<view class="flex flex-col justify-between ml-2">
|
||||
<view class="font-bold text-lg">精油推背-40分钟</view>
|
||||
<view class="text-info text-sm">服务时间:60分钟</view>
|
||||
<price font-weight="700" content="128"></price>
|
||||
</view>
|
||||
<view class="ml-auto text-info">x1</view>
|
||||
</view>
|
||||
<view class="mt-2 text-info text-xs">服务完成时间:2024-04-29 12:56</view>
|
||||
<view class="mt-2 flex items-end justify-end">
|
||||
<view>总金额:¥</view>
|
||||
<view class="font-bold text-3xl ml-1">218</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import price from '@/components/price/price.vue'
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<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">
|
||||
<card></card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</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 { getArticleList } from '@/api/news'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
cid: number
|
||||
i: number
|
||||
index: number
|
||||
}>(),
|
||||
{
|
||||
cid: 0
|
||||
}
|
||||
)
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref([1])
|
||||
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 toDetail = () => {
|
||||
uni.navigateTo({
|
||||
url: '/packages/pages/cash_out_detail/cash_out_detail'
|
||||
})
|
||||
}
|
||||
|
||||
const queryList = async (page_no, page_size) => {
|
||||
try {
|
||||
// const { lists } = await getArticleList({
|
||||
// cid: props.cid,
|
||||
// page_no,
|
||||
// page_size
|
||||
// })
|
||||
const lists: any = [1]
|
||||
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="flex justify-between">
|
||||
<view class="flex items-center" @click="showDataPicker">
|
||||
<text>2024/02/26-2024/03/24</text>
|
||||
<u-icon class="ml-2" name="arrow-down-fill" size="18"></u-icon>
|
||||
</view>
|
||||
<view>
|
||||
<text>总结算金额:</text>
|
||||
<text class="font-bold text-lg">150.00</text>
|
||||
<text>元</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-calendar v-model="show" mode="range" @change="change"></u-calendar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const show = ref(false)
|
||||
|
||||
//打开日期选择
|
||||
const showDataPicker = () => {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const change = (value: any) => {
|
||||
console.log(value)
|
||||
}
|
||||
</script>
|
||||
61
business_uniapp/src/packages/pages/income/income.vue
Normal file
61
business_uniapp/src/packages/pages/income/income.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<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 class="pt-[20rpx] px-2">
|
||||
<optionCom></optionCom>
|
||||
<view class="orderList pt-2">
|
||||
<list :cid="item.id" :i="i" :index="current"></list>
|
||||
</view>
|
||||
</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 list from './components/list.vue'
|
||||
import optionCom from './components/option.vue'
|
||||
import noLogin from '@/components/no-login/no-login.vue'
|
||||
import { getArticleCate } from '@/api/news'
|
||||
|
||||
const tabList = ref<any>([
|
||||
{
|
||||
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 - 80rpx - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
||||
347
business_uniapp/src/packages/pages/location/index.vue
Normal file
347
business_uniapp/src/packages/pages/location/index.vue
Normal file
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 搜索框 -->
|
||||
<view class="flex search">
|
||||
<!-- 左侧城市 -->
|
||||
<view class="flex search--city" @click="goPage('/packages/pages/city/index')">
|
||||
<text class="mr-[15rpx]">{{ cityName || '请选择' }}</text>
|
||||
<u-icon name="arrow-down" size="20"></u-icon>
|
||||
</view>
|
||||
<!-- 右侧搜索城市 -->
|
||||
<input
|
||||
type="text"
|
||||
class="flex-1 search--search"
|
||||
placeholder="搜索地点"
|
||||
v-model="keyword"
|
||||
@confirm="search"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 地图组件 -->
|
||||
<view class="mt-[30rpx] mb-[30rpx] map">
|
||||
<map
|
||||
id="myMap"
|
||||
theme="normal"
|
||||
style="width: 100%; height: 400rpx; border-radius: 20rpx; overflow: hidden"
|
||||
show-location
|
||||
:enable-overlooking="false"
|
||||
:scale="13"
|
||||
@regionchange="regionchange"
|
||||
:latitude="latitude"
|
||||
:longitude="longitude"
|
||||
:markers="markersList"
|
||||
>
|
||||
<cover-view class="iconImg">
|
||||
<cover-image
|
||||
class="img"
|
||||
src="https://hellouniapp.dcloud.net.cn/static/location.png"
|
||||
></cover-image>
|
||||
</cover-view>
|
||||
|
||||
<!-- #ifdef MP -->
|
||||
<cover-view class="origin" @click="originLocation">
|
||||
<cover-image
|
||||
class="origin_icon"
|
||||
src="@/packages/static/images/map/origin.png"
|
||||
></cover-image>
|
||||
<cover-view class="text-xs mt-[15rpx]">定位</cover-view>
|
||||
</cover-view>
|
||||
<!-- #endif -->
|
||||
</map>
|
||||
</view>
|
||||
|
||||
<view class="address">
|
||||
<!-- 地址列表 -->
|
||||
<view v-if="addressList?.length">
|
||||
<scroll-view scroll-y="true" class="scroll-Y" :show-scrollbar="false">
|
||||
<view
|
||||
class="address-item"
|
||||
v-for="(item, index) in addressList"
|
||||
:key="index"
|
||||
@click="choiceAddress(item)"
|
||||
>
|
||||
<view class="u-flex justify-between">
|
||||
<view class="u-flex">
|
||||
<u-icon name="map"></u-icon>
|
||||
<view class="font-bold ml-[10rpx] w-[500rpx] truncate">{{
|
||||
item.title
|
||||
}}</view>
|
||||
</view>
|
||||
<view class="text-[#909399] text-[24rpx]">{{
|
||||
computeDistance(item._distance)
|
||||
}}</view>
|
||||
</view>
|
||||
<view
|
||||
class="text-[#909399] text-[24rpx] ml-[40rpx] mt-[10rpx] w-[500rpx] truncate"
|
||||
>{{ item.address }}</view
|
||||
>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 无地址 -->
|
||||
<!-- <view class="flex flex-col items-center pb-3" v-else>
|
||||
<view class="mt-[40rpx]">
|
||||
<u-image src="@/packages/static/images/map/good.png" width="290" height="200" />
|
||||
</view>
|
||||
<view class="my-[30rpx]">
|
||||
<text>没有数据哦</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { getNearbyLocation } from '@/api/app'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { isWeixinClient } from '@/utils/client'
|
||||
// import { editLocation } from '@/api/user'
|
||||
import { getGeocoderCoordinate } from '@/api/app'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const appStore = useAppStore()
|
||||
//经纬度
|
||||
const latitude = ref(userStore.userInfo?.location?.latitude)
|
||||
const longitude = ref(userStore.userInfo?.location?.longitude)
|
||||
const cityName = ref(userStore.userInfo?.location?.city)
|
||||
//搜索关键词
|
||||
const keyword = ref('')
|
||||
|
||||
// 创建map的上下文对象, 从而操控map组件
|
||||
const mapCtx = uni.createMapContext('myMap')
|
||||
|
||||
// 地图标记点
|
||||
const markersList = ref<any>([])
|
||||
|
||||
// 获取地址列表
|
||||
const addressList = ref<any>([])
|
||||
const getAddressList = async () => {
|
||||
if (!latitude.value || !longitude.value) {
|
||||
uni.showLoading({ title: '定位中...' })
|
||||
await getLocation()
|
||||
uni.hideLoading()
|
||||
}
|
||||
const { data } = await getNearbyLocation(
|
||||
{
|
||||
keyword: keyword.value,
|
||||
latitude: latitude.value,
|
||||
longitude: longitude.value
|
||||
},
|
||||
{ token: userStore.temToken }
|
||||
)
|
||||
addressList.value = data
|
||||
renderPointToMap(addressList.value)
|
||||
}
|
||||
//获取当前位置
|
||||
const getLocation = () => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// if (isWeixinClient()) {
|
||||
// try {
|
||||
// // 获取微信给的地址
|
||||
// const res:any = await getLocation()
|
||||
// latitude.value = res.latitude
|
||||
// longitude.value = res.longitude
|
||||
// const addressInfo = await getGeocoderCoordinate({
|
||||
// location: `${res.latitude},${res.longitude}`
|
||||
// }, { token: userStore.temToken })
|
||||
// cityName.value = addressInfo.result.ad_info.city || ''
|
||||
// // editLocation({ latitude:res.latitude, longitude:res.longitude })
|
||||
// resolve('success')
|
||||
// } catch (e) {
|
||||
// console.log('获取位置失败:', e)
|
||||
// reject('fail')
|
||||
// }
|
||||
// } else {
|
||||
uni.getLocation({
|
||||
// #ifndef APP
|
||||
type: 'gcj02',
|
||||
// #endif
|
||||
async success(res) {
|
||||
latitude.value = res.latitude
|
||||
longitude.value = res.longitude
|
||||
const addressInfo = await getGeocoderCoordinate(
|
||||
{
|
||||
location: `${res.latitude},${res.longitude}`
|
||||
},
|
||||
{ token: userStore.temToken }
|
||||
)
|
||||
cityName.value = addressInfo.result.ad_info.city || ''
|
||||
// editLocation({ latitude:res.latitude, longitude:res.longitude })
|
||||
resolve('success')
|
||||
},
|
||||
fail(result) {
|
||||
console.log('获取位置失败:', result)
|
||||
reject('fail')
|
||||
}
|
||||
})
|
||||
// }
|
||||
})
|
||||
}
|
||||
getAddressList()
|
||||
|
||||
//计算地址距离
|
||||
const computeDistance = (distance: number) => {
|
||||
let res = distance + 'm'
|
||||
if (distance > 1000) {
|
||||
res = (distance / 1000).toFixed(3) + 'km'
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 渲染所有地址
|
||||
const renderPointToMap = (arr: any[]) => {
|
||||
if (!arr.length) return
|
||||
markersList.value = []
|
||||
arr.forEach((item) => {
|
||||
if (!item.status) return
|
||||
markersList.value.push({
|
||||
id: item.id,
|
||||
latitude: +item.latitude,
|
||||
longitude: +item.longitude,
|
||||
iconPath: '/packages/static/images/map/end_point_icon.png',
|
||||
height: 30,
|
||||
width: 20
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 滑动地图更新当前定位
|
||||
const regionchange = (e: any) => {
|
||||
// #ifdef WEB
|
||||
if (e.detail.type == 'end' && (e.detail.causedBy == 'update' || e.detail.causedBy == 'drag')) {
|
||||
mapCtx.getCenterLocation({
|
||||
success: async (res: any) => {
|
||||
latitude.value = res.latitude
|
||||
longitude.value = res.longitude
|
||||
getAddressList()
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
if (e.type == 'end' && (e.causedBy == 'update' || e.causedBy == 'drag')) {
|
||||
mapCtx.getCenterLocation({
|
||||
success: async (res: any) => {
|
||||
latitude.value = res.latitude
|
||||
longitude.value = res.longitude
|
||||
getAddressList()
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 回到我的位置
|
||||
// const originLocation = async () => {
|
||||
// mapCtx.moveToLocation({latitude:latitude.value,longitude:longitude.value})
|
||||
// await appStore.getLocationFunc()
|
||||
// getAddressList()
|
||||
// }
|
||||
|
||||
//搜索
|
||||
const search = async () => {
|
||||
uni.showLoading({
|
||||
title: '搜索中...',
|
||||
mask: true
|
||||
})
|
||||
await getAddressList()
|
||||
uni.hideLoading()
|
||||
}
|
||||
|
||||
//选择地址
|
||||
const choiceAddress = (res: any) => {
|
||||
const latitude = res.location.lat
|
||||
const longitude = res.location.lng
|
||||
// console.log('res.ad_info ==>>', res.ad_info)
|
||||
const cityName = res.ad_info.province + res.ad_info.city + res.ad_info.district
|
||||
const title = res.title
|
||||
uni.$emit('choiceAddress', { latitude: latitude, longitude: longitude, cityName, title })
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const goPage = (url: string) => {
|
||||
uni.navigateTo({
|
||||
url: url
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
// 监听选择的地址
|
||||
uni.$on('chooseCity', async (event) => {
|
||||
cityName.value = event.cityName
|
||||
longitude.value = event.longitude
|
||||
latitude.value = event.latitude
|
||||
getAddressList()
|
||||
})
|
||||
})
|
||||
onUnload(() => {
|
||||
uni.$off(['chooseCity'])
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
|
||||
.search {
|
||||
height: 100%;
|
||||
padding: 15rpx 30rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
&--city {
|
||||
color: #222;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
&--search {
|
||||
margin-left: 30rpx;
|
||||
padding-left: 30rpx;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
}
|
||||
.map {
|
||||
position: relative;
|
||||
.iconImg {
|
||||
position: absolute;
|
||||
top: 46%;
|
||||
left: 47%;
|
||||
.img {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
}
|
||||
}
|
||||
.origin {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
right: 10rpx;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
text-align: center;
|
||||
border: 1rpx solid rgba(187, 187, 187, 1);
|
||||
.origin_icon {
|
||||
position: relative;
|
||||
top: 15%;
|
||||
left: 30%;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.address {
|
||||
background-color: #ffffff;
|
||||
padding: 30rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
.address-item {
|
||||
padding-bottom: 30rpx;
|
||||
margin: 20rpx 0;
|
||||
border-bottom: 1px solid #f4f4f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,96 @@
|
||||
<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%"
|
||||
>
|
||||
<view class="px-[30rpx]">
|
||||
<block v-for="(item, index) in dataList" :key="index">
|
||||
<masterCard :data="item" @to-detail="toDetail">
|
||||
<view class="flex justify-end">
|
||||
<u-button
|
||||
:custom-style="{
|
||||
margin: 0
|
||||
}"
|
||||
type="primary"
|
||||
plain
|
||||
size="mini"
|
||||
shape="circle"
|
||||
@click="toDetail(item.id)"
|
||||
>
|
||||
查看详情
|
||||
</u-button>
|
||||
</view>
|
||||
</masterCard>
|
||||
</block>
|
||||
</view>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, shallowRef } from 'vue'
|
||||
import masterCard from '@/components/master-card/master-card.vue'
|
||||
import { getMasterApplyList } from '@/api/master'
|
||||
import { onShow } from '@dcloudio/uni-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 getMasterApplyList({
|
||||
page_no,
|
||||
page_size,
|
||||
type: props.index + 1
|
||||
})
|
||||
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
//跳转至技师申请详情
|
||||
const toDetail = (id: any) => {
|
||||
uni.navigateTo({
|
||||
url: `/packages/pages/master_apply_detail/master_apply_detail?id=${id}&type=${props.i}`
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
paging.value?.reload()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<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 class="List bg-page pt-[20rpx]">
|
||||
<MasterList :cid="item.id" :i="i" :index="current"></MasterList>
|
||||
</view>
|
||||
</tab>
|
||||
</tabs>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
import MasterList from './components/list.vue'
|
||||
|
||||
const tabList = ref<any>([
|
||||
{
|
||||
name: '加入申请'
|
||||
},
|
||||
{
|
||||
name: '退出申请'
|
||||
}
|
||||
])
|
||||
const current = ref<number>(0)
|
||||
//已选择list
|
||||
const isSelectList = ref([])
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
current.value = Number(index)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.List {
|
||||
height: calc(100vh - 200rpx - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<view class="bottom fixed flex z-50 bottom-0 bg-white w-full pt-[20rpx] px-[30rpx]">
|
||||
<u-button class="flex-1" plain @click="toReject">拒绝</u-button>
|
||||
<u-button class="flex-1 ml-4" v-if="applyType == 0" type="primary" @click="$emit('pass')">通过加入申请</u-button>
|
||||
<u-button class="flex-1 ml-4" v-if="applyType == 1" type="primary" @click="$emit('pass')">通过退出申请</u-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
applyType: number
|
||||
}>(),
|
||||
{
|
||||
applyType: 0
|
||||
}
|
||||
)
|
||||
const emit = defineEmits(['reject', 'pass'])
|
||||
|
||||
//点击
|
||||
const toReject = () => {
|
||||
// console.log(uni.$u.color['error'])
|
||||
emit('reject')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bottom {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<view class="py-[30rpx] px-[24rpx] bg-white rounded-lg mt-4">
|
||||
<view class="text-lg font-normal font-bold">自我介绍</view>
|
||||
<view v-if="!data?.introduction" class="text-info mt-4">暂无介绍~</view>
|
||||
<u-parse v-else :html="data?.introduction"></u-parse>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<!-- <u-sticky h5-nav-height="0" bg-color="transparent"> -->
|
||||
<!-- <u-navbar
|
||||
:is-back="false"
|
||||
title=""
|
||||
:border-bottom="false"
|
||||
:title-bold="true"
|
||||
:fixed="false"
|
||||
:background="{ background: 'rgba(256,256, 256, 1)' }"
|
||||
> -->
|
||||
<view>
|
||||
<u-navbar
|
||||
:is-back="true"
|
||||
title=""
|
||||
:border-bottom="false"
|
||||
:title-bold="true"
|
||||
:fixed="false"
|
||||
title-color="#000"
|
||||
back-icon-color="#000"
|
||||
:background="{
|
||||
background: percent == 0 ? 'rgba(256,256, 256, 0)' : 'rgba(256,256, 256, 1)'
|
||||
}"
|
||||
/>
|
||||
<!-- <view class="absolute w-full z-[999] p-2">
|
||||
<tips></tips>
|
||||
</view> -->
|
||||
</view>
|
||||
<!-- </u-sticky> -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue'
|
||||
// import tips from '../tips/index.vue'
|
||||
|
||||
const props = defineProps({
|
||||
percent: {
|
||||
type: Number,
|
||||
defualt: 0
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<u-popup v-model="show" mode="bottom" length="42%" border-radius="14" :closeable="true">
|
||||
<view class="h-full flex flex-col reject-pop">
|
||||
<view class="py-[40rpx] text-center font-bold text-lg"> 审核拒绝 </view>
|
||||
<view class="px-4 flex-1 flex flex-col justify-between w-full">
|
||||
<u-input
|
||||
v-model="value"
|
||||
type="textarea"
|
||||
:border="true"
|
||||
:height="312"
|
||||
:auto-height="true"
|
||||
placeholder="请输入您的拒绝原因"
|
||||
:maxlength="60"
|
||||
class="bg-[#F6F7F8]"
|
||||
/>
|
||||
<u-button type="primary" @click="$emit('reject', value)" class="mt-2 w-full">提交</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const emits = defineEmits(['reject'])
|
||||
|
||||
const show = ref(false)
|
||||
const value = ref('')
|
||||
|
||||
const open = () => {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.reject-pop {
|
||||
padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="flex flex-1 items-center bg-[#FEF4EB] text-[#CB9F5D] text-xs px-[30rpx] py-[24rpx] rounded-lg">
|
||||
<u-image src="/static/images/apply/an_xin_gou.png" :width="136" :height="36"></u-image>
|
||||
<view class="tag-line mx-[20rpx]">
|
||||
</view>
|
||||
<view class="flex flex-1">
|
||||
实名认证·资质认证·平台担保
|
||||
</view>
|
||||
<u-icon name="arrow-right" :size="20"></u-icon>
|
||||
</view>
|
||||
<view class="p-[24rpx] rounded-lg bg-white">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="text-xl font-bold">{{ data?.name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="text-info introduct">
|
||||
<text>{{ data?.skill_name || '-' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col items-center justify-center" @click="toCall">
|
||||
<u-icon class="bg-page p-1 rounded-full" name="phone-fill"></u-icon>
|
||||
<view class="text-info text-xs mt-1">电话</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-xs mt-4 flex flex-1 justify-between">
|
||||
<view class="flex items-center ">
|
||||
<u-image class="mb-[4rpx]" height="24" width="24" :src="user"></u-image>
|
||||
<view class="ml-1">
|
||||
<text class="text-info">已服务</text>
|
||||
<text class="text-primary">{{ data?.order_num }}人</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<u-image class="mb-[4rpx]" height="24" width="24" :src="favor"></u-image>
|
||||
<view class="ml-1">
|
||||
<text class="text-info">好评率</text>
|
||||
<text class="text-primary">{{ data?.good_comment }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="flex items-center">
|
||||
<u-image height="24" width="24" :src="distance"></u-image>
|
||||
<view class="ml-1">
|
||||
<text class="text-info">距离</text>
|
||||
<text class="text-primary">{{ 0 }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import user from '@/packages/static/icon/user.png'
|
||||
import favor from '@/packages/static/icon/favor.png'
|
||||
import distance from '@/packages/static/icon/distance.png'
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
//打电话
|
||||
const toCall = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: props.data.mobile
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.tag-line {
|
||||
display: block;
|
||||
height: 28rpx;
|
||||
width: 2rpx;
|
||||
background: #CB9F5D;
|
||||
}
|
||||
.introduct {
|
||||
text {
|
||||
position: relative;
|
||||
margin-right: 20rpx;
|
||||
&:not(:first-child) {
|
||||
::before {
|
||||
content: '|';
|
||||
position: absolute;
|
||||
left: -12rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
text {
|
||||
position: relative;
|
||||
margin-right: 20rpx;
|
||||
&:not(:first-child) {
|
||||
::before {
|
||||
content: '·';
|
||||
position: absolute;
|
||||
left: -15rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user