【小程序】 新增# 项目初始化
commit
549435671a
.vscode
scripts
src
bundle
pages
account_detail
components
appoint_time
category_goods_coupon
category_goods_list
city
collection_list
contact_service
coupon_order
dist_order
distributor_binding_user
components
distributor_order
components
evaluate_list
evaluate_submit
master_worker_detail
master_worker_list
my_coupon
components
my_distributor
receive_coupon
recharge_record
search
component
service_explain
service_order
components
service_order_detail
share_qrcode
user_address
user_address_edit
user_recharge
user_wallet
user_withdraw
withdraw_money
withdraw_record
withdrawal_details
components
app
avatar-upload
cheap-popup
goods-card
goods-desc
la-swiper
mplogin-popup
news-card
order-footer
page-status
payment
component
tab
|
@ -0,0 +1,39 @@
|
|||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
ignorePatterns: ['src/uni_modules/'],
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript/recommended',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
rules: {
|
||||
'prettier/prettier': [
|
||||
'warn',
|
||||
{
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
printWidth: 100,
|
||||
proseWrap: 'preserve',
|
||||
bracketSameLine: false,
|
||||
endOfLine: 'lf',
|
||||
tabWidth: 4,
|
||||
useTabs: false,
|
||||
trailingComma: 'none'
|
||||
}
|
||||
],
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'no-undef': 'off',
|
||||
'vue/prefer-import-from-vue': 'off',
|
||||
'no-prototype-builtins': 'off',
|
||||
'prefer-spread': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off'
|
||||
},
|
||||
globals: {}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
dist
|
||||
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.hbuilderx
|
||||
# .env
|
||||
.env.development
|
||||
.env.production
|
||||
yarn.lock
|
||||
package-lock.json
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"editor.detectIndentation": false,
|
||||
"editor.tabSize": 4,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"css.validate": false,
|
||||
"less.validate": false,
|
||||
"scss.validate": false,
|
||||
"prettier.printWidth": 100, // 超过最大值换行
|
||||
"prettier.tabWidth": 4, // 缩进字节数
|
||||
"prettier.useTabs": false, // 缩进不使用tab,使用空格
|
||||
"prettier.semi": false, // 句尾添加分号
|
||||
"prettier.singleQuote": true, // 使用单引号代替双引号
|
||||
"prettier.proseWrap": "preserve", // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
|
||||
"prettier.arrowParens": "avoid", // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
|
||||
"prettier.bracketSpacing": true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }"
|
||||
|
||||
"prettier.endOfLine": "auto", // 结尾是 \n \r \n\r auto
|
||||
"prettier.htmlWhitespaceSensitivity": "ignore",
|
||||
"prettier.ignorePath": ".prettierignore", // 不使用prettier格式化的文件填写在项目的.prettierignore文件中
|
||||
"prettier.jsxSingleQuote": false, // 在jsx中使用单引号代替双引号
|
||||
"prettier.requireConfig": false, // Require a 'prettierconfig' to format prettier
|
||||
"prettier.trailingComma": "none" // 函数最后不需要逗号
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"name": "uni-preset-vue",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev:app": "uni -p app",
|
||||
"dev:custom": "uni -p",
|
||||
"dev:h5": "uni",
|
||||
"dev:h5:ssr": "uni --ssr",
|
||||
"dev:mp-alipay": "uni -p mp-alipay",
|
||||
"dev:mp-baidu": "uni -p mp-baidu",
|
||||
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||
"dev:mp-lark": "uni -p mp-lark",
|
||||
"dev:mp-qq": "uni -p mp-qq",
|
||||
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||
"build:app": "uni build -p app",
|
||||
"build:custom": "uni build -p",
|
||||
"build:h5": "node scripts/build.h5.mjs",
|
||||
"build:h5:ssr": "uni build --ssr",
|
||||
"build:mp-alipay": "uni build -p mp-alipay",
|
||||
"build:mp-baidu": "uni build -p mp-baidu",
|
||||
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||
"build:mp-lark": "uni build -p mp-lark",
|
||||
"build:mp-qq": "uni build -p mp-qq",
|
||||
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||
"build:mp-weixin": "uni build -p mp-weixin",
|
||||
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-app-plus": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-components": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-h5": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-alipay": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-baidu": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-kuaishou": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-lark": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-qq": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-toutiao": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-mp-weixin": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-quickapp-webview": "^3.0.0-alpha-3050520220824001",
|
||||
"enumtor": "^1.0.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pinia": "^2.0.20",
|
||||
"vconsole": "^3.14.6",
|
||||
"vue": "^3.2.37",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"weixin-js-sdk": "^1.6.0",
|
||||
"z-paging": "^2.3.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "^3.0.13",
|
||||
"@dcloudio/uni-automator": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-cli-shared": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/uni-stacktracey": "^3.0.0-alpha-3050520220824001",
|
||||
"@dcloudio/vite-plugin-uni": "^3.0.0-alpha-3050520220824001",
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/lodash-es": "^4.17.6",
|
||||
"@types/node": "^18.7.16",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.4.0",
|
||||
"execa": "^6.1.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"postcss": "^8.4.16",
|
||||
"postcss-rem-to-responsive-pixel": "^5.1.3",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.54.5",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^2.9.14",
|
||||
"weapp-tailwindcss-webpack-plugin": "^1.7.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"compileType": "miniprogram",
|
||||
"setting": {
|
||||
"coverView": true,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"enhance": true,
|
||||
"showShadowRootInWxmlPanel": true,
|
||||
"packNpmRelationList": [],
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
},
|
||||
"ignoreUploadUnusedFiles": true
|
||||
},
|
||||
"condition": {},
|
||||
"editorSetting": {
|
||||
"tabIndent": "insertSpaces",
|
||||
"tabSize": 2
|
||||
},
|
||||
"libVersion": "3.0.0",
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"appid": "wxf712ba4cbc2df1e9"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
|
||||
"projectname": "housekeeping-uniapp",
|
||||
"setting": {
|
||||
"compileHotReLoad": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { execaCommand } from 'execa'
|
||||
import path from 'path'
|
||||
import fsExtra from 'fs-extra'
|
||||
const { existsSync, remove, copy } = fsExtra
|
||||
const cwd = process.cwd()
|
||||
//打包发布路径,谨慎改动
|
||||
const releaseRelativePath = '../public/mobile'
|
||||
const distPath = path.resolve(cwd, 'dist/build/h5')
|
||||
const releasePath = path.resolve(cwd, releaseRelativePath)
|
||||
|
||||
async function build() {
|
||||
await execaCommand('uni build', { stdio: 'inherit', encoding: 'utf-8', cwd })
|
||||
if (existsSync(releasePath)) {
|
||||
await remove(releasePath)
|
||||
}
|
||||
console.log(`文件正在复制 ==> ${releaseRelativePath}`)
|
||||
try {
|
||||
await copyFile(distPath, releasePath)
|
||||
} catch (error) {
|
||||
console.log(`\n ${error}`)
|
||||
}
|
||||
console.log(`文件已复制 ==> ${releaseRelativePath}`)
|
||||
}
|
||||
|
||||
function copyFile(sourceDir, targetDir) {
|
||||
return new Promise((resolve, reject) => {
|
||||
copy(sourceDir, targetDir, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
build()
|
|
@ -0,0 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
import { onLaunch } from '@dcloudio/uni-app'
|
||||
import { useAppStore } from './stores/app'
|
||||
import { useUserStore } from './stores/user'
|
||||
import { apiIndexVisit } from '@/api/app'
|
||||
import { getClient } from '@/utils/client'
|
||||
const appStore = useAppStore()
|
||||
const { getUser } = useUserStore()
|
||||
|
||||
onLaunch(async () => {
|
||||
await appStore.getConfig()
|
||||
appStore.getLocationFunc()
|
||||
appStore.getSystemInfoFn()
|
||||
// #ifdef H5
|
||||
const { status, close, url } = appStore.getH5Config
|
||||
if (status == 0) {
|
||||
if (close == 1) return (location.href = url)
|
||||
uni.reLaunch({ url: '/pages/empty/empty' })
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
await appStore.checkForUpdate()
|
||||
// #endif
|
||||
await getUser()
|
||||
await apiIndexVisit({
|
||||
terminal: getClient()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
//
|
||||
</style>
|
|
@ -0,0 +1,26 @@
|
|||
import { client } from '@/utils/client'
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录
|
||||
export function login(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/check', data: { ...data, client } })
|
||||
}
|
||||
|
||||
//注册
|
||||
export function register(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/register', data: { ...data, client } })
|
||||
}
|
||||
|
||||
//忘记密码
|
||||
export function forgotPassword(data: Record<string, any>) {
|
||||
return request.post({ url: '/login/forgotPassword', data })
|
||||
}
|
||||
|
||||
//向微信请求code的链接
|
||||
export function getWxCodeUrl() {
|
||||
return request.get({ url: '/login/codeUrl', data: { url: location.href } })
|
||||
}
|
||||
|
||||
export function OALogin(data: Record<string, any>) {
|
||||
return request.get({ url: '/login/oaLogin', data })
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import request from '@/utils/request'
|
||||
// #ifdef H5
|
||||
import { getSignLink } from '@/hooks/wechat'
|
||||
// #endif
|
||||
|
||||
//发送短信
|
||||
export function smsSend(data: any) {
|
||||
return request.post({ url: '/sms/send', data: data })
|
||||
}
|
||||
|
||||
// 获取公共配置
|
||||
export function getConfig() {
|
||||
return request.get({ url: '/config' })
|
||||
}
|
||||
|
||||
// 访问量
|
||||
export const apiIndexVisit = (params: any) => request.get({ url: '/index/visit', data: params })
|
||||
|
||||
// 获取政策协议
|
||||
export function getPolicy(data: any) {
|
||||
return request.get({ url: '/policy', data: data })
|
||||
}
|
||||
|
||||
// 获取客服信息
|
||||
export function apiContactService(data: any) {
|
||||
return request.get({ url: '/decorate/pages/service', data: data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 公众号登录
|
||||
export const apiOALogin = (params: any) => request.post({ url: '/login/oaLogin', data: params })
|
||||
|
||||
// 公众号-获取授权url
|
||||
export const apiCodeUrlGet = () =>
|
||||
request.get({
|
||||
url: '/login/codeUrl',
|
||||
data: {
|
||||
url: location.href,
|
||||
headers: { 1: 1 }
|
||||
}
|
||||
})
|
||||
|
||||
// 获取城市信息-字母分组
|
||||
export const apiCityInfo = (params: any) => request.get({ url: '/region/city', data: params })
|
||||
|
||||
// 微信sdk配置
|
||||
export const apiJsConfig = (data: any) =>
|
||||
request.get({ url: '/wechat/jsConfig', data: { url: getSignLink() } })
|
||||
|
||||
/**
|
||||
* @param { Object } params { address: 广州市番禺区xxx }
|
||||
* @return { Promise }
|
||||
* @description 地址逆解析
|
||||
*/
|
||||
export const getGeocoder = (params: any) =>
|
||||
request.get({ url: '/region/keyWordAddress', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params { location: xxx,xxx }
|
||||
* @return { Promise }
|
||||
* @description 地址逆解析
|
||||
*/
|
||||
export const getGeocoderCoordinate = (params: any) =>
|
||||
request.post({ url: '/region/genCoder', data: params })
|
||||
|
||||
// 支付方式
|
||||
export const apiPayPayWay = (params: any) =>
|
||||
request.get({ url: '/order/pay/way/list', data: params })
|
||||
|
||||
// 预支付
|
||||
export const apiPayPrepay = (params: any) => request.post({ url: '/pay/wx/prePay', data: params })
|
||||
|
||||
// h5预支付
|
||||
export const apiH5PayPrepay = (params: any) => request.post({ url: '/pay/h5/prePay', data: params })
|
||||
// DY 预支付
|
||||
export const apiBytePayPrepay = (params: any) =>
|
||||
request.post({ url: '/pay/byte/createOrder', data: params })
|
||||
// DY 更新订单状态
|
||||
export const apiByteQueryOrder = (params: any) =>
|
||||
request.post({ url: '/pay/byte/queryOrder', data: params })
|
||||
|
||||
// 充值支付接口
|
||||
export const apiRechargeOrder = (params: any) =>
|
||||
request.post({ url: '/pay/wx/prePay', data: params })
|
||||
|
||||
// 查询订单
|
||||
export const apiQueryOrder = (params: any) =>
|
||||
request.get({ url: '/order/pay/status', data: params })
|
||||
|
||||
// 查询充值记录
|
||||
export const apiQueryRechargeList = (params: any) =>
|
||||
request.get({ url: '/recharge/record', data: params })
|
||||
|
||||
// 查询充值状态
|
||||
export const apiQueryRecharge = (params: any) =>
|
||||
request.get({ url: '/recharge/payStatus', data: params })
|
||||
|
||||
// 绑定分销员id
|
||||
export const apiBindingDistId = (params: any) =>
|
||||
request.post({ url: '/user/bindDistributor', data: params })
|
|
@ -0,0 +1,28 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 获取我的优惠券
|
||||
export const apiMyCouponList = (params) => request.get({ url: '/coupon/myCoupon', data: params })
|
||||
|
||||
// 领券中心
|
||||
export const apiCouponCenterList = (params) => request.get({ url: '/coupon/list', data: params })
|
||||
|
||||
// 优惠券详情
|
||||
export const apiCouponDetail = (params) => request.get({ url: '/coupon/detail', data: params })
|
||||
|
||||
// 领取优惠券
|
||||
export const apiCouponManualGet = (params) => request.post({ url: '/coupon/getCoupon', data: params })
|
||||
|
||||
// 品类券对应商品
|
||||
export const apiCouponCategoryGoods = (params) => request.get({ url: '/coupon/deductionGoodsCategory', data: params })
|
||||
|
||||
// 商品券对应商品
|
||||
export const apiCouponGoods = (params) => request.get({ url: '/coupon/deductionGoods', data: params })
|
||||
|
||||
// 下单获取最大优惠
|
||||
export const apiCouponMaxMoney = (params) => request.get({ url: '/coupon/maxDiscounts', data: params })
|
||||
|
||||
// 可用优惠券+不可用优惠券
|
||||
export const apiCouponByUse = (params) => request.get({ url: '/coupon/selectCoupon', data: params })
|
||||
|
||||
// 可用优惠券+不可用优惠券数量
|
||||
export const apiCouponCount = (params) => request.get({ url: '/coupon/selectCouponCount', data: params })
|
|
@ -0,0 +1,34 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
/**获取分销商数据 */
|
||||
export function getDistributorCenter(params) {
|
||||
return request.get({ url: '/distributor/myDistributor', data: params })
|
||||
}
|
||||
/**获取月份账单 */
|
||||
export function getDistributorOrder(params) {
|
||||
return request.get({ url: '/distributor/distributorOrder', data: params })
|
||||
}
|
||||
/**获取月份账单详情 */
|
||||
export function getDistributorOrderDetail(params) {
|
||||
return request.get({ url: '/distributor/distributorOrderDetail', data: params })
|
||||
}
|
||||
/**获取提现记录 */
|
||||
export function getDistributorWithDrawList(params) {
|
||||
return request.get({ url: '/distributor/withdrawalRecord', data: params })
|
||||
}
|
||||
/**分享太阳码 */
|
||||
export function getDistributorQrcode(params) {
|
||||
return request.get({ url: '/distributor/distributorQrCode', data: params })
|
||||
}
|
||||
/**获取分销用户 */
|
||||
export function getDistributorBindingUser(params) {
|
||||
return request.get({ url: '/distributor/getDistributorUser', data: params })
|
||||
}
|
||||
/**获取分销订单-月份总金额 */
|
||||
export function getDistributorOrderByMonth(params) {
|
||||
return request.get({ url: '/distributor/distributorOrderByMonth', data: params })
|
||||
}
|
||||
/**获取分销用户-月份分销总用户 */
|
||||
export function getDistributorUserByMonth(params) {
|
||||
return request.get({ url: '/distributor/distributorUserByMonth', data: params })
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-08-14 15:38:40
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-12-20 20:08:02
|
||||
* @FilePath: \housekeeping-uniapp\src\api\goods.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取商品详情
|
||||
export const apiGoodsDetail = params => request.get({ url: '/goods/detail', data: params })
|
||||
|
||||
// 收藏列表
|
||||
export const apiCollectLists = () => request.get({ url: '/collect/list' })
|
||||
|
||||
// 商品收藏
|
||||
export const apiGoodsCollection = params => request.post({ url: '/collect/add', data: params })
|
||||
|
||||
// 取消收藏
|
||||
export const apiGoodsCollectionCancel = params => request.get({ url: '/collect/del', data: params })
|
||||
|
||||
// 获取预约时间列表
|
||||
export const apiAppointTime = (params = {}) => request.get({ url: '/time/getTime', data: params })
|
||||
|
||||
// 添加评价
|
||||
export const apiAddEvaluateGoods = (params = '') =>
|
||||
request.post({ url: '/comment/add', data: params })
|
||||
|
||||
// 获取商品评价列表
|
||||
export const apiEvaluateGoodsLists = (params: any) =>
|
||||
request.post({ url: '/comment/list', data: params })
|
||||
|
||||
// 待评价列表
|
||||
export const apiWaitEvaluateGoodsLists = (params: any) =>
|
||||
request.get({ url: '/order/wait/comment/list', data: params }, {})
|
||||
|
||||
// 获取商品评价统计数据-已评价、待评价
|
||||
export const apiEvaluateStatis = (params: any) =>
|
||||
request.get({ url: '/comment/statistics/user', data: params })
|
||||
|
||||
// 获取商品评价统计数据-好评、差评...
|
||||
export const apiEvaluateGoodsStatis = (params: any) =>
|
||||
request.get({ url: '/comment/statistics/goods', data: params })
|
||||
|
||||
// 退单规则
|
||||
export const apiRuleList = (params: any) =>
|
||||
request.get({ url: '/goods/getCancelOrderGoodsConfig', data: params })
|
||||
|
||||
// 获取活动专区详情
|
||||
export const apiActivityDetail = (params: any) =>
|
||||
request.get({ url: '/activity/detail', data: params })
|
||||
|
||||
// 获取活动专区列表
|
||||
export const apiActivityList = (params: any) => request.get({ url: '/activity/list', data: params })
|
|
@ -0,0 +1,56 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 订单初始化
|
||||
export const apiInitOrder = (params: any) => request.get({ url: '/order/init', data: params })
|
||||
|
||||
// 订单下单
|
||||
export const apiPlaceOrder = (params: any) => request.post({ url: '/order/add', data: params })
|
||||
|
||||
// 订单列表
|
||||
export const apiOrderLists = (params: any) =>
|
||||
request.get({ url: '/order/list', data: params }, { ignoreCancel: true })
|
||||
|
||||
// 订单详情
|
||||
export const apiOrderDetail = (params: any) => request.get({ url: '/order/detail', data: params })
|
||||
|
||||
// 取消订单
|
||||
export const apiOrderCancel = (params: any) => request.post({ url: '/order/cancel', data: params })
|
||||
|
||||
// 删除订单
|
||||
export const apiOrderDel = (params: any) => request.get({ url: '/order/del', data: params })
|
||||
|
||||
// 关闭订单(取消支付)
|
||||
export const apiOrderClose = (params: any) =>
|
||||
request.get({ url: '/pay/wx/closeOrder', data: params })
|
||||
|
||||
/** 师傅订单服务 Start **/
|
||||
|
||||
// 师傅服务列表
|
||||
export const apiStaffOrderLists = (params: any) =>
|
||||
request.get({ url: '/order/staff/list', data: params }, { ignoreCancel: true })
|
||||
|
||||
// 师傅服务详情
|
||||
export const apiStaffOrderDetail = (params: any) =>
|
||||
request.get({ url: '/order/detail', data: params })
|
||||
|
||||
// 确认服务
|
||||
export const apiStaffOrderConfirmService = (params: any) =>
|
||||
request.get({ url: '/order/staff/confirm', data: params })
|
||||
|
||||
// 核销订单
|
||||
export const apiStaffOrderVerification = (params: any) =>
|
||||
request.get({ url: '/order/staff/verification', data: params })
|
||||
|
||||
// 用户核销订单
|
||||
export const apiUserOrderVerification = (params: any) =>
|
||||
request.get({ url: '/order/user/verification', data: params })
|
||||
|
||||
/** 师傅订单服务 Start **/
|
||||
|
||||
// DY 取消更新状态
|
||||
export const apiByteQueryRefund = (params: any) =>
|
||||
request.post({ url: '/pay/byte/queryRefund', data: params })
|
||||
|
||||
// 取消订单扣分规则
|
||||
export const apiCancelOrderRule = (params: any) =>
|
||||
request.get({ url: '/order/getCancelOrderConfigByOrder', data: params })
|
|
@ -0,0 +1,25 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
//首页数据
|
||||
export function getIndex(data: any) {
|
||||
return request.get({ url: '/index/index', data })
|
||||
}
|
||||
|
||||
// 装修页面
|
||||
export function getDecorate(data: any) {
|
||||
return request.get({ url: '/decorate', data })
|
||||
}
|
||||
|
||||
// 热门搜索
|
||||
export function getHotSearch() {
|
||||
return request.get({ url: '/hotSearch' })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 搜索
|
||||
* @param { string } keyword 关键词
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getSearch(data: { keyword: string; pageNo: number; pageSize: number }) {
|
||||
return request.get({ url: '/search', data })
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-08-14 15:38:40
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-12-25 20:10:21
|
||||
* @FilePath: \housekeeping-uniapp\src\api\store.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 轮博图
|
||||
*/
|
||||
export const apiAdLists = (params: any) => request.get({ url: '/ad/lists', data: params })
|
||||
|
||||
// 服务分类-分页列表
|
||||
export const apiCategoryLists = () => request.get({ url: '/category/list' })
|
||||
|
||||
// 商品服务分类-父级
|
||||
export const apiGoodsCategoryLists = (params: any) =>
|
||||
request.get({ url: '/category/commonList', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 服务列表
|
||||
*/
|
||||
export const apiGoodsLists = (params: any) =>
|
||||
request.get({ url: '/goods/list', data: params }, { ignoreCancel: true })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 服务师傅列表
|
||||
*/
|
||||
export const apiStaffLists = (params: any) => request.post({ url: '/staff/list', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 师傅详情
|
||||
*/
|
||||
export const apiStaffDetail = (params: any) => request.get({ url: '/staff/detail', data: params })
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 获取地级市列表
|
||||
*/
|
||||
export const apiRegionCity = () => request.get({ url: '/region/char/city' })
|
||||
|
||||
/**
|
||||
* @description 热门搜索
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getHotSearch() {
|
||||
return request.get({ url: '/hotSearch' })
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 搜索
|
||||
* @param { string } keyword 关键词
|
||||
* @return { Promise }
|
||||
*/
|
||||
export function getSearch(data: { keyword: string; pageNo: number; pageSize: number }) {
|
||||
return request.get({ url: '/search', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { Object } params
|
||||
* @return { Promise }
|
||||
* @description 服务详情底部商品推荐
|
||||
*/
|
||||
export const apiRecommendService = (params: any) =>
|
||||
request.get({ url: '/goods/recommendationList', data: params })
|
|
@ -0,0 +1,54 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getUserCenter(header?: any) {
|
||||
return request.get({ url: '/user/center', header })
|
||||
}
|
||||
|
||||
// 个人信息
|
||||
export function getUserInfo() {
|
||||
return request.get({ url: '/user/info' }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 个人编辑
|
||||
export function userEdit(data: any) {
|
||||
return request.post({ url: '/user/edit', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 绑定手机
|
||||
export function userBindMobile(data: any, header?: any) {
|
||||
return request.post({ url: '/user/bindMobile', data, header }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 微信电话
|
||||
export function userMnpMobile(data: any) {
|
||||
return request.post({ url: '/user/mnpMobile', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 更改密码
|
||||
export function userChangePwd(data: any) {
|
||||
return request.post({ url: '/user/changePwd', data }, { isAuth: true })
|
||||
}
|
||||
|
||||
// 地址列表
|
||||
export const apiAddressLists = () => request.get({ url: '/address/list' })
|
||||
|
||||
// 获取地址详情
|
||||
export const apiAddressDetail = (params: any) =>
|
||||
request.get({ url: '/address/detail', data: params })
|
||||
|
||||
// 编辑地址
|
||||
export const apiAddressEdit = (params: any) => request.post({ url: '/address/edit', data: params })
|
||||
|
||||
// 新增地址
|
||||
export const apiAddressAdd = (params: any) => request.post({ url: '/address/add', data: params })
|
||||
|
||||
// 删除地址
|
||||
export const apiAddressDel = (params: any) => request.get({ url: '/address/del', data: params })
|
||||
|
||||
// 提交订单
|
||||
export const apiEvaluateAdd = (params: any) => request.post({ url: '/comment/add', data: params })
|
||||
|
||||
//更新微信小程序头像昵称
|
||||
export function updateUser(data: Record<string, any>, header: any) {
|
||||
return request.post({ url: '/user/updateUser', data, header })
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-08-14 15:38:40
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2024-01-28 16:20:42
|
||||
* @FilePath: \housekeeping-uniapp\src\api\wallet.ts
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
*/
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 钱包信息
|
||||
export const apiUserWallet = (params: any) => request.get({ url: '/recharge/config', data: params })
|
||||
|
||||
// 账户明细列表
|
||||
export const apiAccountLogLists = (params: any) =>
|
||||
request.get({ url: '/account/log/list', data: params }, { ignoreCancel: true })
|
||||
|
||||
// 充值
|
||||
export const apiRecharge = (params: any) =>
|
||||
request.post({ url: '/recharge/placeOrder', data: params })
|
||||
|
||||
// 充值记录列表
|
||||
export const apiRechargeLogLists = (params: any) =>
|
||||
request.get({ url: '/recharge/record', data: params })
|
||||
|
||||
// 获取提现配置
|
||||
export const apiGetWithdrawConfig = (params: any) =>
|
||||
request.get({ url: '/withdraw/getConfig', data: params })
|
||||
|
||||
// 提现申请
|
||||
export const apiWithdrawApply = (params: any) =>
|
||||
request.post({ url: '/withdraw/apply', data: params })
|
||||
|
||||
// 提现申请列表
|
||||
export const apiWithdrawLists = (params: any) =>
|
||||
request.get({ url: '/withdraw/lists', data: params })
|
||||
|
||||
// 提现申请详情
|
||||
export const apiWithdrawDetail = (params: any) =>
|
||||
request.get({ url: '/withdraw/detail', data: params })
|
||||
|
||||
// 提现申请-不需要审核
|
||||
export const apiWithdrawCommission = (params: any) =>
|
||||
request.post({ url: '/distributor/withdrawCommission', data: params })
|
||||
|
||||
// 提现申请-新-需要审核
|
||||
export const applyForWithdraw = (params: any) =>
|
||||
request.post({ url: '/distributor/applyForAdd', data: params })
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<view class="main">
|
||||
<tabs
|
||||
:current="current"
|
||||
height="80"
|
||||
bar-width="60"
|
||||
:auth="true"
|
||||
bgColor="#fff"
|
||||
activeColor="#F36161"
|
||||
inactiveColor="#666"
|
||||
:itemWidth="150"
|
||||
:is-scroll="false"
|
||||
@change="onChange"
|
||||
>
|
||||
<tab v-for="(item, i) in tabList" :key="i" :name="item.name" />
|
||||
</tabs>
|
||||
<view class="list pt-[20rpx]">
|
||||
<list :status="status" :index="current" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import list from './components/list.vue'
|
||||
import tab from '@/components/tab/tab.vue'
|
||||
import tabs from '@/components/tabs/tabs.vue'
|
||||
|
||||
const tabList = ref<any>([
|
||||
{
|
||||
name: '全部',
|
||||
status: ''
|
||||
},
|
||||
{
|
||||
name: '收入',
|
||||
status: 1
|
||||
},
|
||||
{
|
||||
name: '支出',
|
||||
status: 0
|
||||
}
|
||||
])
|
||||
const current = ref<number>(0)
|
||||
const status = ref<number | string>()
|
||||
const onChange = (index: number) => {
|
||||
current.value = index
|
||||
status.value = tabList.value[index].status
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list {
|
||||
height: calc(100vh - 44px - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<z-paging ref="paging" v-model="balance.lists" @query="queryList" :fixed="false" height="100%">
|
||||
<!-- 余额明细列表 -->
|
||||
<view class="balance-details">
|
||||
<view v-for="(item, index) in balance.lists" :key="index">
|
||||
<view class="balance-details-item">
|
||||
<view class="flex">
|
||||
<view class="flex-1 balance-details-item-text">{{
|
||||
item?.changeTypeDesc
|
||||
}}</view>
|
||||
<view
|
||||
class="balance-details-item-amount"
|
||||
:class="{ 'text-error': item.action === 0 }"
|
||||
>
|
||||
{{ item.actionDesc + item.changeAmount }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="balance-details-item-time">{{ item.remark }}</view>
|
||||
<view class="balance-details-item-time">{{ item.createTime }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, watch, shallowRef } from 'vue'
|
||||
import { apiAccountLogLists } from '@/api/wallet'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
status: string | number
|
||||
index: number
|
||||
}>(),
|
||||
{
|
||||
status: ''
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.index,
|
||||
async () => {
|
||||
paging.value.reload()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const balance = reactive({
|
||||
lists: [] as any,
|
||||
change_type: 1
|
||||
})
|
||||
// 下拉组件的Ref
|
||||
const paging = shallowRef()
|
||||
const queryList = async (pageNo = 1, pageSize = 10) => {
|
||||
try {
|
||||
const { lists } = await apiAccountLogLists({
|
||||
action: props.status,
|
||||
pageNo,
|
||||
pageSize
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
balance.change_type = options.changeObject
|
||||
|
||||
if (balance.change_type == 2) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: '佣金明细'
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.balance-details {
|
||||
.balance-details-item {
|
||||
background-color: #fff;
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1rpx solid #ebebeb;
|
||||
|
||||
.balance-details-item-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.balance-details-item-amount-add {
|
||||
font-size: 34rpx;
|
||||
font-weight: 400;
|
||||
color: #ff0017;
|
||||
}
|
||||
|
||||
.balance-details-item-amount {
|
||||
font-size: 34rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.balance-details-item-time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,266 @@
|
|||
<template>
|
||||
<!-- Header Start -->
|
||||
<scroll-view class="scroll-view-box" scroll-x="true">
|
||||
<block v-for="(item, index) in date" :key="index">
|
||||
<view
|
||||
class="day text-base"
|
||||
:class="{ active: dayIndex == index }"
|
||||
@click="dayIndex = index"
|
||||
>
|
||||
<view>{{ item.week }}</view>
|
||||
<view class="mt-[10rpx]">{{ item.date }}</view>
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
<!-- Header End -->
|
||||
|
||||
<!-- Main Start -->
|
||||
<view class="time-box">
|
||||
<block v-for="(item2, index2) in timeSlot" :key="index2">
|
||||
<view
|
||||
class="time-item"
|
||||
:class="{
|
||||
select: selectIndex == index2,
|
||||
disabled: dayIndex === 0 && item2.disabled
|
||||
}"
|
||||
@click="selectAppoint(index2, dayIndex === 0 && item2.disabled)"
|
||||
>
|
||||
{{ item2.startTime }} - {{ item2.endTime }}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- Main End -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { apiAppointTime } from '@/api/goods'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
|
||||
interface DateObj {
|
||||
year: number
|
||||
week: string | undefined
|
||||
date: string
|
||||
}
|
||||
|
||||
interface TimeSlotObj {
|
||||
startTime: string
|
||||
endTime: string
|
||||
disabled: boolean
|
||||
}
|
||||
|
||||
// 日期
|
||||
const date = ref<DateObj[]>([])
|
||||
// 当天日期索引
|
||||
const dayIndex = ref<number>(0)
|
||||
// 时间段选择索引
|
||||
const selectIndex = ref<number>(0)
|
||||
// 预约时间段
|
||||
const timeSlot = ref<TimeSlotObj[]>([])
|
||||
// 商品数据
|
||||
const goodsForm = ref<any | null>({
|
||||
goods_num: '' as string,
|
||||
id: '' as string
|
||||
})
|
||||
|
||||
/**
|
||||
* @param { number } index
|
||||
* @param { boolean } disabled
|
||||
* @return { void }
|
||||
* @description 选择上门时间段
|
||||
*/
|
||||
const selectAppoint = (index: number, disabled: boolean) => {
|
||||
if (disabled) return uni.$u.toast('服务时间段已过!请选择其他时间段')
|
||||
selectIndex.value = index
|
||||
// 缓存用户所选的预约时间
|
||||
uni.setStorage({
|
||||
key: 'selectDate',
|
||||
data: [dayIndex.value, selectIndex.value]
|
||||
})
|
||||
const dateTime = date.value[dayIndex.value]
|
||||
const timeSlotItem = timeSlot.value[index]
|
||||
const appointTime = {
|
||||
year: dateTime.year,
|
||||
date: dateTime.date,
|
||||
start_time: timeSlotItem.startTime,
|
||||
end_time: timeSlotItem.endTime
|
||||
}
|
||||
if (goodsForm.value.id != '') {
|
||||
const params = {
|
||||
goodsData: goodsForm.value,
|
||||
appointData: appointTime
|
||||
}
|
||||
goPage(`/pages/order_buy/index?type=1¶ms=${JSON.stringify(params)}`)
|
||||
return
|
||||
}
|
||||
uni.$emit('appointTime', appointTime)
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return { Promise } void
|
||||
* @description 请求获取预约时间信息
|
||||
*/
|
||||
const getAppointTime = async (id: number): Promise<void> => {
|
||||
// 获取可预约时间段
|
||||
const res = await apiAppointTime({ goodsId: id })
|
||||
console.log('data', res)
|
||||
|
||||
timeSlot.value = handleTimeSlot(res.orderTimeListVoList)
|
||||
date.value = handleAppointDay(res.time)
|
||||
|
||||
// // 获取用户所选的预约时间
|
||||
uni.getStorage({
|
||||
key: 'selectDate',
|
||||
success: res => {
|
||||
dayIndex.value = res.data[0]
|
||||
selectIndex.value = res.data[1]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { TimeSlotObj[] } timeSlot
|
||||
* @return { TimeSlotObj[] } 时间段
|
||||
* @description 计算已过期时间
|
||||
*/
|
||||
const handleTimeSlot = (timeSlot: TimeSlotObj[]) => {
|
||||
const time = new Date() // 时间戳转换成标准日期时间
|
||||
const min = time.getMinutes()
|
||||
const startTime = time.getHours() + '' + (min <= 9 ? '0' + min : min) // 获取时分并组合成标准格式(时分)比如 10:30 --> 1030
|
||||
timeSlot.forEach(item => {
|
||||
const end = item.endTime.replace(':', '')
|
||||
// 只可预约三个小时后的时间段
|
||||
item.disabled = Number(startTime) + 300 >= Number(end)
|
||||
// console.log("startTime: " + startTime);
|
||||
// console.log("end: " + end);
|
||||
})
|
||||
return timeSlot
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number } days
|
||||
* @return { string } 日期
|
||||
* @description 根据返回预约天数计算出日期
|
||||
*/
|
||||
const handleAppointDay = (days: number | string | undefined) => {
|
||||
const timeArr = []
|
||||
// 现在时间戳
|
||||
const newTime = new Date().getTime()
|
||||
for (let i = 0; i <= days; i++) {
|
||||
// 获取天数时间戳
|
||||
const millisecond = newTime + i * 24 * 60 * 60 * 1000
|
||||
// 年
|
||||
const year = new Date(millisecond).getFullYear()
|
||||
// 月
|
||||
const month = new Date(millisecond).getMonth() + 1
|
||||
// 星期
|
||||
const week = new Date(millisecond).getDay()
|
||||
// 日
|
||||
const day = new Date(millisecond).getDate()
|
||||
// 未来天数的数组
|
||||
timeArr.push({
|
||||
year: year,
|
||||
week: handleWeek(week, i),
|
||||
date: (month <= 9 ? '0' + month : month) + '-' + (day <= 9 ? '0' + day : day)
|
||||
})
|
||||
}
|
||||
return timeArr
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number } week
|
||||
* @param { number } i
|
||||
* @return { string } 日期
|
||||
* @description 转换日期
|
||||
*/
|
||||
const handleWeek = (week: number | string | undefined, i: number) => {
|
||||
if (i === 0) return '今天'
|
||||
else if (i === 1) return '明天'
|
||||
else if (week === 0) return '周日'
|
||||
else if (week === 1) return '周一'
|
||||
else if (week === 2) return '周二'
|
||||
else if (week === 3) return '周三'
|
||||
else if (week === 4) return '周四'
|
||||
else if (week === 5) return '周五'
|
||||
else if (week === 6) return '周六'
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { string } url
|
||||
* @return { void }
|
||||
* @description 跳转页面方法
|
||||
*/
|
||||
const goPage = (url: string) => {
|
||||
uni.redirectTo({ url: url })
|
||||
}
|
||||
|
||||
onLoad(options => {
|
||||
console.log('options', options)
|
||||
|
||||
if (options.params) {
|
||||
const goodsData = JSON.parse(options.params)
|
||||
goodsForm.value = goodsData
|
||||
}
|
||||
if (options.goodsId) getAppointTime(options.goodsId)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.scroll-view-box {
|
||||
height: 140rpx;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
background-color: #ffffff;
|
||||
padding: 30rpx 20rpx 0 20rpx;
|
||||
|
||||
.day {
|
||||
width: 105rpx;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
color: #222222;
|
||||
margin: 0 20rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: $blue5;
|
||||
border-bottom: 4px solid $blue5;
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.time-box {
|
||||
padding: 24rpx;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 48%);
|
||||
grid-column-gap: 4%;
|
||||
grid-row-gap: 20rpx;
|
||||
|
||||
.time-item {
|
||||
// width: 220rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
background-color: #ffffff;
|
||||
// margin: 0 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.time-item:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.select {
|
||||
background-color: rgba(255, 244, 244, 1);
|
||||
color: $blue5;
|
||||
// border: 1px solid rgba(243, 97, 97, 1);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: rgba(153, 153, 153, 1);
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view class="goods-item" @click="handleGoodItem(attributes!)">
|
||||
<view class="info-img">
|
||||
<!-- <u-image :src="attributes.image" class="goods-image" width="300" height="300" border-radius="15"></u-image> -->
|
||||
<image :src="attributes!.image" class="goods-image"></image>
|
||||
</view>
|
||||
|
||||
<view class="info-column">
|
||||
<view style="text-align: left">
|
||||
<view class="goods-title overflow-ellipsis-line ellipsis-line1">
|
||||
{{ attributes!.name }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="price-wrap mt-[20rpx]">
|
||||
¥
|
||||
<span class="price">{{ attributes!.price }}</span>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
|
||||
interface GoodProp {
|
||||
id: Number
|
||||
name: string
|
||||
remarks: string
|
||||
category: string
|
||||
unit: string
|
||||
image: string
|
||||
price: number
|
||||
scribingPrice: number
|
||||
status: number
|
||||
statusDesc: string
|
||||
}
|
||||
const props = defineProps({
|
||||
attributes: Object as PropType<GoodProp>
|
||||
})
|
||||
const emit = defineEmits(['handleGoodItem'])
|
||||
|
||||
const handleGoodItem = (row: GoodProp) => {
|
||||
// emit('handleGoodItem', row.id)
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?id=${row.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.goods-item {
|
||||
// display: inline-block; //让每个子项可以方便换行
|
||||
// padding: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
// background-color: pink;
|
||||
// height: 200px;
|
||||
// width: 47%; //这样可以每行最多两个goods item
|
||||
|
||||
.info-img {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.goods-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.info-column {
|
||||
text-align: left;
|
||||
padding-top: 20rpx;
|
||||
padding-left: 5rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.goods-scribingPrice {
|
||||
font-size: 0.775rem;
|
||||
color: #808695;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.price-wrap {
|
||||
color: red;
|
||||
|
||||
.price {
|
||||
font-size: 1.1rem; //¥
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<view class="goods__container">
|
||||
<z-paging ref="paging" v-model="goodsList" @query="transformQueryList" :fixed="false" height="100%"
|
||||
auto-show-back-to-top>
|
||||
<view class="goods">
|
||||
<block v-for="good in goodsList" :key="good.id">
|
||||
<goodItem :attributes="good" />
|
||||
</block>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { ref, unref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { useZPaging, useCoupon } from '@/hooks/useCoupon'
|
||||
import goodItem from './components/goodItem.vue'
|
||||
import { apiCouponGoods } from '@/api/coupon'
|
||||
|
||||
interface GoodProp {
|
||||
id: number
|
||||
name: string
|
||||
remarks: string
|
||||
category: string
|
||||
unit: string
|
||||
image: string
|
||||
price: number
|
||||
scribingPrice: number
|
||||
status: number
|
||||
statusDesc: string
|
||||
}
|
||||
const goodsList = ref<GoodProp[]>([])
|
||||
|
||||
const { couponId, setCouponId } = useCoupon()
|
||||
const { paging, queryList } = useZPaging({}, apiCouponGoods)
|
||||
|
||||
onLoad((options) => {
|
||||
const { id } = options as any
|
||||
setCouponId(id)
|
||||
})
|
||||
|
||||
function transformQueryList(page_no: number, page_size: number) {
|
||||
const config = {
|
||||
couponId: unref(couponId),
|
||||
}
|
||||
return queryList(page_no, page_size, config)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods__container {
|
||||
height: 100vh;
|
||||
|
||||
.goods {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,151 @@
|
|||
<template>
|
||||
<tabs
|
||||
class="tabs"
|
||||
:current="current"
|
||||
v-bind="tabStyle"
|
||||
@change="handleChange"
|
||||
:swipeable="false"
|
||||
>
|
||||
<tab v-for="(item, i) in categoryLists" :key="i" :name="item.name"></tab>
|
||||
|
||||
<view class="core">
|
||||
<z-paging
|
||||
class="z-paging"
|
||||
ref="paging"
|
||||
v-model="categoryLists"
|
||||
@query="transformQueryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
auto-show-back-to-top
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<view class="cate-container mt-[20rpx]">
|
||||
<view
|
||||
v-for="(category, i) in categoryLists"
|
||||
:key="i"
|
||||
:id="'category-item' + i"
|
||||
class="category-item"
|
||||
>
|
||||
<view class="text-center mt-[40rpx] mb-[20rpx] text-content text-sm">
|
||||
{{ category.name }}
|
||||
</view>
|
||||
<view class="goods">
|
||||
<block v-for="(item, idx) in category.sonGoodsList" :key="item.id">
|
||||
<GoodItem :attributes="item" />
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</tabs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, getCurrentInstance, unref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GoodItem from './components/goodItem.vue'
|
||||
import { useCoupon, useZPaging } from '@/hooks/useCoupon'
|
||||
import { apiCouponCategoryGoods } from '@/api/coupon'
|
||||
|
||||
let TAB_HEIGHT = 50
|
||||
|
||||
const instance = getCurrentInstance() // 获取组件实例
|
||||
|
||||
const distances = ref<number[]>([])
|
||||
const categoryLists = ref<any[]>([])
|
||||
const current = ref(0)
|
||||
const isTabChange = ref(false)
|
||||
|
||||
const tabStyle = computed(() => {
|
||||
return {
|
||||
height: 100,
|
||||
barWidth: 60,
|
||||
fontSize: 32,
|
||||
barStyle: { bottom: 0 },
|
||||
activeColor: '#1296DB'
|
||||
}
|
||||
})
|
||||
const { couponId, setCouponId } = useCoupon()
|
||||
|
||||
const { paging, queryList } = useZPaging({}, apiCouponCategoryGoods)
|
||||
|
||||
onMounted(async () => {
|
||||
setTabHeight()
|
||||
setDisatnces()
|
||||
})
|
||||
|
||||
onLoad(options => {
|
||||
const { id } = options as any
|
||||
setCouponId(id)
|
||||
})
|
||||
|
||||
// 锚点定位(start)
|
||||
const handleScroll = event => {
|
||||
if (unref(isTabChange)) return
|
||||
const { scrollTop } = event.detail
|
||||
const index = distances.value.findIndex(distance => distance - TAB_HEIGHT - scrollTop > 0)
|
||||
current.value = index > 0 ? index - 1 : distances.value.length - 1
|
||||
}
|
||||
const handleChange = async (index: number) => {
|
||||
// current.value = index
|
||||
isTabChange.value = true
|
||||
const id = `#category-item${index}`
|
||||
uni.createSelectorQuery()
|
||||
.select(id)
|
||||
.boundingClientRect(data => {
|
||||
uni.createSelectorQuery()
|
||||
.select('.cate-container')
|
||||
.boundingClientRect(res => {
|
||||
const scrollTop = data.top - res.top
|
||||
paging.value.scrollToY(scrollTop)
|
||||
isTabChange.value = false
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
function setDisatnces() {
|
||||
setTimeout(() => {
|
||||
const query = uni.createSelectorQuery().in(instance)
|
||||
query
|
||||
.selectAll('.category-item')
|
||||
.boundingClientRect(data => {
|
||||
distances.value = data.map(v => v.top)
|
||||
})
|
||||
.exec()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
function setTabHeight() {
|
||||
const query = uni.createSelectorQuery().in(instance)
|
||||
query
|
||||
.select('.tabs >>> #cu-tab')
|
||||
.boundingClientRect(res => {
|
||||
TAB_HEIGHT = (res.height / (uni.upx2px(100) / 100)) as number
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
// 锚点定位(end)
|
||||
|
||||
function transformQueryList(page_no: number, page_size: number) {
|
||||
const config = {
|
||||
couponId: unref(couponId)
|
||||
}
|
||||
queryList(page_no, page_size, config)
|
||||
return paging.value.complete()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.core {
|
||||
height: calc(100vh - 100rpx);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,156 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<scroll-view
|
||||
class="menu"
|
||||
scroll-x="true"
|
||||
:scroll-left="160 * idx"
|
||||
v-if="menuData?.sons?.length"
|
||||
>
|
||||
<view
|
||||
class="menu--item"
|
||||
:class="{ active: categoryId === menuData.id }"
|
||||
@click="changeTabs(menuData)"
|
||||
>
|
||||
<view class="flex justify-center">
|
||||
<u-image src="@/bundle/static/images/mb-like.svg" width="56" height="56" />
|
||||
</view>
|
||||
<view class="black font-medium mt-[14rpx]">{{ '全部服务' }}</view>
|
||||
</view>
|
||||
<block v-for="item in menuData.sons" :key="item.id">
|
||||
<view
|
||||
class="menu--item"
|
||||
:class="{ active: categoryId === item.id }"
|
||||
@click="changeTabs(item)"
|
||||
>
|
||||
<view class="flex justify-center">
|
||||
<u-image :src="item.image" width="56" height="56" />
|
||||
</view>
|
||||
<view class="black font-medium mt-[14rpx]">{{ ellipsisText(item.name) }}</view>
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
|
||||
<view class="main">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="goodsData"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<view class="px-[24rpx]">
|
||||
<GoodsDesc :lists="goodsData"></GoodsDesc>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, shallowRef } from 'vue'
|
||||
import { onLoad, onShareAppMessage } from '@dcloudio/uni-app'
|
||||
import { apiGoodsCategoryLists, apiCategoryLists, apiGoodsLists } from '@/api/store'
|
||||
// import GoodsCard from '@/components/goods-card/index.vue'
|
||||
import GoodsDesc from '@/components/goods-desc/index.vue'
|
||||
|
||||
const categoryId = ref<number>()
|
||||
const menuData = ref<any>({})
|
||||
const goodsData = ref<any>([])
|
||||
const paging = shallowRef()
|
||||
// 记录当前选中商品下标
|
||||
const idx = ref(0)
|
||||
const ellipsisText = computed(() => name => name?.length > 4 ? `${name.slice(0, 4)}...` : name)
|
||||
|
||||
onLoad((options: any) => {
|
||||
console.log('options:', options)
|
||||
|
||||
categoryId.value = options?.id * 1 || 0
|
||||
getCategoryList()
|
||||
})
|
||||
|
||||
// 切换菜单
|
||||
const changeTabs = (event: any) => {
|
||||
console.log('event:', event)
|
||||
|
||||
// 如果点击同一个2次的话那就是取消当前选择然后选择一级分类的商品
|
||||
categoryId.value = event.id === categoryId.value ? menuData.value.id : event.id
|
||||
paging.value.reload()
|
||||
}
|
||||
|
||||
// 获取分类列表
|
||||
const getCategoryList = async (): Promise<void> => {
|
||||
try {
|
||||
const res = await apiGoodsCategoryLists({ pid: categoryId.value })
|
||||
uni.setNavigationBarTitle({
|
||||
title: res[0].name
|
||||
})
|
||||
menuData.value = res[0]
|
||||
if (menuData.value.sons?.length) {
|
||||
idx.value = menuData.value.sons.findIndex((item: any) => item.id === categoryId.value)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取商品列表
|
||||
const queryList = async (pageNo: number, pageSize: number) => {
|
||||
try {
|
||||
const { lists } = await apiGoodsLists({
|
||||
pageNo: pageNo,
|
||||
pageSize: pageSize,
|
||||
categoryId: categoryId.value
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: `粤好生活`,
|
||||
imageUrl: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
.menu {
|
||||
height: 188rpx;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx 0 20rpx 24rpx;
|
||||
|
||||
&--item {
|
||||
width: 160rpx;
|
||||
height: 148rpx;
|
||||
padding: 20rpx 0;
|
||||
margin-right: 20rpx;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
font-size: 26rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #f36161;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,456 @@
|
|||
<template>
|
||||
<!-- 搜索 -->
|
||||
<view class="search flex m-[24rpx]">
|
||||
<!-- 左侧图标 -->
|
||||
<u-icon name="search" size="34" color="#888888"></u-icon>
|
||||
<!-- 输入 -->
|
||||
<input type="text" class="search--input" @input="handleSearch" placeholder="请输入城市名称" @focus="showClear = true"
|
||||
@blur="showClear = false" v-model="keyword" />
|
||||
<!-- 清空图标 -->
|
||||
<image src="../../../static/images/icon_clear.png" class="clear" v-show="showClear" @click="handleReSearch"></image>
|
||||
</view>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<view class="city-container" v-show="searchStatus">
|
||||
<view class="letter-head">搜索结果</view>
|
||||
<view class="bg-white city-box-head rounded-lg">
|
||||
<block v-for="(searchItem, searchIndex) in searchResult" :key="searchIndex">
|
||||
<view class="text-base city-item" @click="chooseCity(searchItem)">
|
||||
{{ searchItem.name }}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 定位城市 -->
|
||||
<view v-show="!searchStatus">
|
||||
<view class="city-container">
|
||||
<!-- 当前定位城市 -->
|
||||
<view class="letter-head">当前定位城市</view>
|
||||
<view class="city-box-head flex justify-between bg-white p-[24rpx]">
|
||||
<view class="flex text-base normal" @click="goBack">
|
||||
<image src="../../../static/images/icon_city_address.png"></image>
|
||||
<text class="ml-[18rpx]">{{ cityInfo.name }}</text>
|
||||
</view>
|
||||
<view class="flex text-base text-blue" @click="reChooseLocation">重新定位</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门城市列表 -->
|
||||
<view>
|
||||
<view class="letter-head">热门城市</view>
|
||||
<view class="city-box-head">
|
||||
<block v-for="(hotItem, hotIndex) in hotList" :key="hotIndex">
|
||||
<view class="text-base city-item" @click="chooseCity(hotItem)">
|
||||
{{ hotItem.name }}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 城市列表 -->
|
||||
<view v-for="(cityItem, cityIndex) in cityList" :key="cityIndex" class="anchor">
|
||||
<view class="letter">{{ cityItem.firstChar }}</view>
|
||||
<view class="bg-white city-box">
|
||||
<block v-for="(cityItem2, cityIndex2) in cityItem.list" :key="cityIndex2">
|
||||
<view class="text-base truncate city-item" @click="chooseCity(cityItem2)">
|
||||
{{ cityItem2.name }}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 侧边导航条 -->
|
||||
<view class="bar__sidebar" @touchstart.stop.prevent="onTouchMove" @touchmove.stop.prevent="onTouchMove"
|
||||
@touchend.stop.prevent="onTouchStop" @touchcancel.stop.prevent="onTouchStop">
|
||||
<view v-for="(barItem, barIndex) in labelList" :key="barIndex" class="bar__index"
|
||||
:class="'title ' + (barIndex == touchmoveIndex ? 'active' : '')">
|
||||
{{ barItem.firstChar }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 侧边弹窗 -->
|
||||
<view class="list-alert" v-if="touchmove && labelList[touchmoveIndex + '']">
|
||||
<text>{{ labelList[touchmoveIndex]?.firstChar }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref, nextTick } from 'vue'
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app'
|
||||
import { apiRegionCity } from '@/api/store'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { getGeocoderCoordinate } from '@/api/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
// 页面状态
|
||||
const searchStatus = ref<boolean>(false)
|
||||
// 搜索关键字
|
||||
const keyword = ref<string | number>('')
|
||||
// 清空输入框
|
||||
const showClear = ref<boolean>(false)
|
||||
// 搜索结果
|
||||
const searchResult = ref<any>([])
|
||||
// 城市列表
|
||||
const cityList = ref<any>([])
|
||||
// 侧边列表
|
||||
const labelList = ref<any>([])
|
||||
// 热门城市列表
|
||||
const hotList = [
|
||||
{
|
||||
name: '北京市',
|
||||
gcj02_lat: '39.929986',
|
||||
gcj02_lng: '116.395645',
|
||||
id: 110100
|
||||
},
|
||||
{
|
||||
name: '上海市',
|
||||
gcj02_lat: '31.249162',
|
||||
gcj02_lng: '121.487899',
|
||||
id: 310100
|
||||
},
|
||||
{
|
||||
name: '广州市',
|
||||
gcj02_lat: '23.120049',
|
||||
gcj02_lng: '113.30765',
|
||||
id: 440100
|
||||
},
|
||||
{
|
||||
name: '深圳市',
|
||||
gcj02_lat: '22.546054',
|
||||
gcj02_lng: '114.025974',
|
||||
id: 440300
|
||||
},
|
||||
{
|
||||
name: '重庆市',
|
||||
gcj02_lat: '29.544606',
|
||||
gcj02_lng: '106.530635',
|
||||
id: 110100
|
||||
},
|
||||
{
|
||||
name: '成都市',
|
||||
gcj02_lat: '30.679943',
|
||||
gcj02_lng: '104.067923',
|
||||
id: 510100
|
||||
},
|
||||
{
|
||||
name: '杭州市',
|
||||
gcj02_lat: '30.259244',
|
||||
gcj02_lng: '120.219375',
|
||||
id: 110100
|
||||
},
|
||||
{
|
||||
name: '苏州市',
|
||||
gcj02_lat: '31.317987',
|
||||
gcj02_lng: '120.619907',
|
||||
id: 320500
|
||||
},
|
||||
{
|
||||
name: '武汉市',
|
||||
gcj02_lat: '30.581084',
|
||||
gcj02_lng: '114.3162',
|
||||
id: 420100
|
||||
},
|
||||
{
|
||||
name: '沈阳市',
|
||||
gcj02_lat: '41.808645',
|
||||
gcj02_lng: '123.432791',
|
||||
id: 210100
|
||||
}
|
||||
]
|
||||
//
|
||||
const touchmove = ref(false)
|
||||
const touchmoveIndex = ref(0)
|
||||
const anchor = ref<any>([])
|
||||
const sidebar = ref<any>({})
|
||||
|
||||
const cityInfo = computed(() => appStore.cityInfo)
|
||||
|
||||
onLoad(() => {
|
||||
uni.getSetting({
|
||||
success: (res) => {
|
||||
if (!res.authSetting['scope.userLocation']) {
|
||||
uni.authorize({
|
||||
scope: 'scope.userLocation',
|
||||
success: () => {
|
||||
// uni.getLocation({
|
||||
// // #ifndef APP
|
||||
// type: 'gcj02',
|
||||
// // #endif
|
||||
// async success(res) {
|
||||
// const res1 = await getGeocoderCoordinate({
|
||||
// lat: res.latitude,
|
||||
// lng: res.longitude
|
||||
// })
|
||||
// const obj = {
|
||||
// name: res1.amapGenCoderResponse.amapAddressComponent.city, // 市
|
||||
// city_id: res1.cityId,
|
||||
// latitude: res.latitude,
|
||||
// longitude: res.longitude
|
||||
// }
|
||||
// console.log("当前城市:", obj)
|
||||
// appStore.setCityInfo(obj)
|
||||
// }
|
||||
// })
|
||||
appStore.getLocation()
|
||||
},
|
||||
fail: () => {
|
||||
appStore.getAuthorizeFunc()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log(res);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
initCityData()
|
||||
})
|
||||
|
||||
onPageScroll(({ scrollTop }) => {
|
||||
const index = anchor.value.findIndex((item: number | string) => {
|
||||
return item >= scrollTop
|
||||
})
|
||||
const isLessTop = index !== -1
|
||||
if (isLessTop && !unref(touchmove)) touchmoveIndex.value = index
|
||||
})
|
||||
|
||||
// 搜索城市
|
||||
const handleSearch = (event: any) => {
|
||||
// keyword.value = event?.detail?.value.trim()
|
||||
if (!keyword.value) {
|
||||
searchStatus.value = false
|
||||
return
|
||||
}
|
||||
searchStatus.value = true
|
||||
searchResult.value = []
|
||||
for (const key in cityList.value) {
|
||||
const len = cityList.value[key].list.length
|
||||
const item = cityList.value[key].list
|
||||
for (let i = 0; i < len; i++) {
|
||||
const reg = new RegExp(keyword.value)
|
||||
if (reg.test(item[i].name)) {
|
||||
searchResult.value.push(item[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 重置搜索
|
||||
const handleReSearch = () => {
|
||||
keyword.value = ''
|
||||
showClear.value = false
|
||||
searchStatus.value = false
|
||||
searchResult.value = []
|
||||
}
|
||||
|
||||
// 重新定位
|
||||
const reChooseLocation = () => {
|
||||
uni.chooseLocation({
|
||||
success(res: any) {
|
||||
appStore.initCityFunc(res.latitude, res.longitude)
|
||||
},
|
||||
fail(err) {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理侧边栏移动函数
|
||||
const onTouchMove = (event: any): void => {
|
||||
const y = parseInt(event.changedTouches[0].clientY)
|
||||
const len = unref(labelList).length
|
||||
const itemHeight = Number(unref(sidebar).height / len)
|
||||
let index = Math.floor((y - unref(sidebar).top) / itemHeight)
|
||||
if (index < 0) {
|
||||
index = 0
|
||||
} else if (index > len - 1) {
|
||||
index = len - 1
|
||||
}
|
||||
if (unref(touchmoveIndex) != index) {
|
||||
touchmove.value = true
|
||||
touchmoveIndex.value = index
|
||||
uni.pageScrollTo({
|
||||
duration: 0,
|
||||
scrollTop: unref(anchor)[index - 1] ? unref(anchor)[index - 1] : 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const onTouchStop = () => {
|
||||
touchmove.value = false
|
||||
}
|
||||
|
||||
// 选择城市
|
||||
const chooseCity = (param: any) => {
|
||||
const obj = {
|
||||
name: param.name,
|
||||
city_id: param.id,
|
||||
latitude: param.db09_lat,
|
||||
longitude: param.db09_lng
|
||||
}
|
||||
appStore.setCityInfo(obj)
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 初始化城市数据
|
||||
const initCityData = async (): Promise<void> => {
|
||||
const data = await apiRegionCity()
|
||||
cityList.value = data
|
||||
labelList.value = data
|
||||
setRect()
|
||||
}
|
||||
|
||||
// 获取元素数据
|
||||
const setRect = async () => {
|
||||
await nextTick()
|
||||
const title = uni.createSelectorQuery().selectAll('.bar__sidebar')
|
||||
title
|
||||
.boundingClientRect((res: any) => {
|
||||
sidebar.value = {
|
||||
height: res[0].height,
|
||||
top: res[0].top
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
const letter = uni.createSelectorQuery().selectAll('.anchor')
|
||||
letter
|
||||
.boundingClientRect((res: any) => {
|
||||
res.top = Number(res.bottom)
|
||||
anchor.value = res.map((item: { bottom: number }) => Number(item.bottom))
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
/**
|
||||
* @return { void }
|
||||
* @description 返回上一页
|
||||
*/
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 头部搜索
|
||||
.search {
|
||||
height: 70rpx;
|
||||
padding: 15rpx 30rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: white;
|
||||
|
||||
// 输入框
|
||||
&--input {
|
||||
width: 84%;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
// 清空
|
||||
.clear {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.city-container {
|
||||
padding: 0 60rpx 0 24rpx;
|
||||
|
||||
// 首字母样式
|
||||
.letter,
|
||||
.letter-head {
|
||||
height: 70rpx;
|
||||
line-height: 50rpx;
|
||||
padding: 10rpx 4rpx;
|
||||
margin-top: 10rpx;
|
||||
color: #888888;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
// 城市样式
|
||||
.city-box,
|
||||
.city-box-head {
|
||||
border-radius: 4rpx;
|
||||
|
||||
.city-item {
|
||||
padding: 0 24rpx;
|
||||
width: 210rpx;
|
||||
height: 88rpx;
|
||||
display: inline-block;
|
||||
margin-right: 18rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
border-radius: 4rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
// line-1
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.city-item:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.city-item:hover {
|
||||
color: white;
|
||||
background: rgba(#1296db, 0.5);
|
||||
}
|
||||
|
||||
image {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar__sidebar {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
right: 8rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
transform: translateY(-50%);
|
||||
user-select: none;
|
||||
z-index: 99;
|
||||
|
||||
.active {
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
background-color: #1296db;
|
||||
}
|
||||
}
|
||||
|
||||
.bar__index {
|
||||
font-weight: 500;
|
||||
padding: 8rpx 8rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.list-alert {
|
||||
position: fixed;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
right: 90rpx;
|
||||
top: 50%;
|
||||
margin-top: -60rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 50rpx;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, 0.65);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
z-index: 9999999;
|
||||
|
||||
text {
|
||||
line-height: 50rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,110 @@
|
|||
<!--
|
||||
* @Author: 王翠碧 1254597151@qq.com
|
||||
* @Date: 2023-05-22 22:41:29
|
||||
* @LastEditors: 王翠碧 1254597151@qq.com
|
||||
* @LastEditTime: 2023-05-26 23:12:10
|
||||
* @FilePath: \uniapp\src\bundle\pages\collection_list\index.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<!-- Main Start -->
|
||||
<uni-transition
|
||||
mode-class="zoom-in"
|
||||
needLayout="true"
|
||||
:show="collectData.length"
|
||||
:duration="500"
|
||||
>
|
||||
<u-swipe-action>
|
||||
<u-swipe-action-item
|
||||
v-for="item in collectData"
|
||||
:key="item.id"
|
||||
:options="{ text: '取消收藏' }"
|
||||
>
|
||||
<!-- <block> -->
|
||||
<view class="collect flex justify-between" @click="toGoodsDetail(item.goodsId)">
|
||||
<view class="flex">
|
||||
<u-image :src="item.image" width="140" height="140"></u-image>
|
||||
<view class="ml-[20rpx]">
|
||||
<view class="normal font-medium text-xl">{{ item.name }}</view>
|
||||
<view class="mt-[24rpx]">
|
||||
<price :price="item.price" :desc="item.unit"></price>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pt-[30rpx]">
|
||||
<u-button
|
||||
size="mini"
|
||||
:plain="true"
|
||||
type="primary"
|
||||
shape="circle"
|
||||
@click="toGoodsDetail(item.goodsId)"
|
||||
>
|
||||
去下单
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- </block> -->
|
||||
</u-swipe-action-item>
|
||||
</u-swipe-action>
|
||||
</uni-transition>
|
||||
<!-- Main End -->
|
||||
<!-- empty Start -->
|
||||
<view class="empty" v-show="!collectData.length">
|
||||
<u-empty
|
||||
text="暂无收藏数据~"
|
||||
:src="'/static/images/empty/collection.png'"
|
||||
:icon-size="300"
|
||||
color="#888888"
|
||||
></u-empty>
|
||||
</view>
|
||||
<!-- empty Start -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { apiCollectLists } from '@/api/goods'
|
||||
import Price from '@/components/price/index.vue'
|
||||
|
||||
/** Data Start **/
|
||||
const collectData = ref<any>([])
|
||||
/** Data End **/
|
||||
|
||||
/** Methods Start **/
|
||||
// 初始化收藏
|
||||
const initCollect = async (): Promise<void> => {
|
||||
const { lists } = await apiCollectLists()
|
||||
collectData.value = lists
|
||||
}
|
||||
// 去商品详情
|
||||
const toGoodsDetail = (id: number | null) => {
|
||||
console.log('id', id)
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?id=${id}`
|
||||
})
|
||||
}
|
||||
/** Methods End **/
|
||||
|
||||
/** Life Cycle Start **/
|
||||
onShow(() => {
|
||||
initCollect()
|
||||
})
|
||||
/** Life Cycle End **/
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.collect {
|
||||
border-radius: 14rpx;
|
||||
background-color: white;
|
||||
margin: 20rpx 20rpx 0 20rpx;
|
||||
padding: 30rpx;
|
||||
image {
|
||||
border-radius: 16rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding-top: 300rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<uni-transition
|
||||
mode-class="zoom-in"
|
||||
needLayout="true"
|
||||
:show="serviceData.qrcode.length"
|
||||
:duration="500"
|
||||
>
|
||||
<view class="box">
|
||||
<view class="flex justify-center">
|
||||
<u-image :src="getImageUrl(serviceData.qrcode)" width="300" height="300"></u-image>
|
||||
</view>
|
||||
<view class="text-center mt-[20rpx] normal text-base">
|
||||
{{ serviceData.title }}
|
||||
</view>
|
||||
<view class="text-center mt-[20rpx] text-muted text-sm" v-if="serviceData.time">
|
||||
服务时间:{{ serviceData.time }}
|
||||
</view>
|
||||
<view class="text-center mt-[20rpx] text-muted text-sm" v-if="serviceData.mobile">
|
||||
服务电话:{{ serviceData.mobile }}
|
||||
</view>
|
||||
<view class="mt-[40rpx]">
|
||||
<!-- #ifdef H5 -->
|
||||
<button
|
||||
class="custom-button-bgColor text-lg text-white rounded-full"
|
||||
@click="toast('长按二维码保存')"
|
||||
>
|
||||
长按二维码保存
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef H5 -->
|
||||
<button
|
||||
class="custom-button-bgColor text-lg text-white leading-[80rpx] h-[80rpx] rounded-full"
|
||||
@click="saveImageQr"
|
||||
>
|
||||
保存二维码
|
||||
</button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</uni-transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 联系客服
|
||||
import { ref } from 'vue'
|
||||
import { toast } from '@/utils/util'
|
||||
import { apiContactService } from '@/api/app'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const { getImageUrl } = useAppStore()
|
||||
|
||||
/** Interface Start **/
|
||||
interface serviceDataObj {
|
||||
qrcode: string // 客服二维码
|
||||
title: string // 客服微信
|
||||
mobile: string // 客服电话
|
||||
time: string // 服务时间
|
||||
}
|
||||
/** Interface End **/
|
||||
|
||||
/** Data Start **/
|
||||
|
||||
const serviceData = ref<serviceDataObj>({
|
||||
qrcode: '',
|
||||
title: '',
|
||||
mobile: '',
|
||||
time: ''
|
||||
})
|
||||
/** Data End **/
|
||||
|
||||
/** Methods Start **/
|
||||
// 获取客服信息
|
||||
const getContactService = async (): Promise<void> => {
|
||||
const data = await apiContactService('')
|
||||
const res = JSON.parse(data.pageData)
|
||||
serviceData.value = res[0].content
|
||||
}
|
||||
|
||||
// 保存二维码
|
||||
const saveImageQr = async (): Promise<void> => {
|
||||
try {
|
||||
const res = await uni.getImageInfo({ src: getImageUrl(serviceData.value.qrcode) })
|
||||
|
||||
try {
|
||||
await uni.saveImageToPhotosAlbum({ filePath: res.path })
|
||||
uni.$u.toast('保存成功')
|
||||
} catch (e) {
|
||||
const modelRes = await uni.showModal({
|
||||
title: '图片保存失败',
|
||||
content: '请确认是否已开启授权'
|
||||
})
|
||||
if (modelRes.confirm) uni.openSetting()
|
||||
}
|
||||
} catch (err) {
|
||||
uni.$u.toast('请在小程序后台配置downloadFile')
|
||||
}
|
||||
}
|
||||
/** Methods End **/
|
||||
|
||||
/** Life Cycle Start **/
|
||||
getContactService()
|
||||
/** Life Cycle End **/
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 64rpx 120rpx;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(#f36161, 0.1),
|
||||
rgba(#f36161, 0) 164rpx,
|
||||
transparent 0
|
||||
);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,275 @@
|
|||
<template>
|
||||
<view class="coupon">
|
||||
<view class="coupon__detail">
|
||||
<view class="coupon__detail__header">
|
||||
<view class="left">
|
||||
<view class="title">{{ couponDetail.name }}</view>
|
||||
<view class="date">{{ couponDetail.useTimeEnd }}到期</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="money">
|
||||
<text>¥</text>
|
||||
<text class="strong">{{ couponDetail.money }}</text>
|
||||
</view>
|
||||
<text class="condition">{{ condition(couponDetail) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon__detail__rule">
|
||||
<text class="sub_title">使用规则</text>
|
||||
<view class="content">
|
||||
<view class="flex">
|
||||
<text class="label">优惠券类型:</text>
|
||||
<text class="flex-1">{{ parseUseGoodsType(couponDetail) }}</text>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<text class="label">领取说明:</text>
|
||||
<text>每人限领{{ couponDetail.getNum }}张</text>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<text class="label">有效时间:</text>
|
||||
<text>{{ parseTime(couponDetail) }}</text>
|
||||
</view>
|
||||
<view class="flex" v-show="couponDetail.useGoodsType !== 1">
|
||||
<text class="label">使用限制:</text>
|
||||
<text>{{ parseLimitContent(couponDetail) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon__btn">
|
||||
<u-button class="manual" type="error" shape="circle" @click="handleManualCoupon"
|
||||
:disabled="isDisabled">立即领取</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { apiCouponDetail } from '@/api/coupon';
|
||||
import { onBackPress, onLoad, onUnload } from '@dcloudio/uni-app';
|
||||
import { computed, reactive, ref, unref, toRaw } from 'vue';
|
||||
|
||||
interface CouponProp {
|
||||
name: string
|
||||
money: number
|
||||
getType: number //发放方式
|
||||
channelType: number //分销员/指定用户
|
||||
useGoodsType: number,//优惠券类型
|
||||
conditionType: number,//使用门槛
|
||||
sendTimeStart: number, //发放开始时间
|
||||
sendTimeEnd: number,//发放结束时间
|
||||
useTimeStart: string,//使用开始时间
|
||||
useTimeEnd: string,//使用结束时间
|
||||
sendTotal: number,
|
||||
sendTotalType: number//发放数量
|
||||
getNum: number, //领取数量
|
||||
status: number,//发放状态
|
||||
conditionMoney: number | string,//有门槛金额
|
||||
distributors: any[]//渠道选中分销员
|
||||
goodsCategoryDetailVos: any[],//品类券选中值
|
||||
goodsDetailVos: any[]//商品券选中列表
|
||||
isToggle?: boolean //是否展开
|
||||
goods: any[] //商品信息
|
||||
goodsCategories: any[] //品类信息
|
||||
}
|
||||
const couponId = ref()
|
||||
const couponDetail = reactive<Partial<CouponProp>>({})
|
||||
const count = ref(3) //x张优惠券
|
||||
const num = ref(0)
|
||||
const isDisabled = computed(() => couponDetail.getNum === 0)
|
||||
const condition = computed(() => {
|
||||
return (row: Partial<CouponProp>) => {
|
||||
if (!Object.keys(row).length) return
|
||||
const { conditionType, conditionMoney } = row
|
||||
return conditionType === 1 ? '立减券' : `满${conditionMoney}可用`
|
||||
}
|
||||
})
|
||||
const parseTime = computed(() => {
|
||||
return (row: Partial<CouponProp>) => {
|
||||
if (!Object.keys(row).length) return
|
||||
const { useTimeStart, useTimeEnd } = row as CouponProp
|
||||
|
||||
const [useTimeStartDate, useTimeStartHS] = useTimeStart.split(' ')
|
||||
const [useTimeEndDate, useTimeEndHS] = useTimeEnd.split(' ')
|
||||
|
||||
const startText = useTimeStartHS.split(':').slice(0, -1).join(':')
|
||||
const endText = useTimeEndHS.split(':').slice(0, -1).join(':')
|
||||
|
||||
return `${useTimeStartDate} ${startText}至${useTimeEndDate} ${endText}`
|
||||
}
|
||||
})
|
||||
const parseLimitContent = computed(() => {
|
||||
return (row: Partial<CouponProp>) => {
|
||||
if (!Object.keys(row).length) return
|
||||
const { useGoodsType, goodsCategories, goods } = toRaw(couponDetail) as CouponProp
|
||||
if (useGoodsType === 1) return
|
||||
const categoryNames = useGoodsType === 2
|
||||
? transformGoods(goodsCategories, 'name')
|
||||
: transformGoods(goods, 'categoryName')
|
||||
return `仅可购买${categoryNames}商品`
|
||||
}
|
||||
})
|
||||
const useGoodsTypeMap: Record<number, string> = {
|
||||
1: '通用券',
|
||||
2: '品类券',
|
||||
3: '商品券'
|
||||
}
|
||||
const parseUseGoodsType = computed(() => {
|
||||
return (row: Partial<CouponProp>) => {
|
||||
const { useGoodsType } = row
|
||||
return useGoodsTypeMap[useGoodsType!]
|
||||
}
|
||||
})
|
||||
function transformGoods(goods: any[], field: string, separator = '、') {
|
||||
return goods.map(category => category[field]).join(separator)
|
||||
}
|
||||
onLoad((options) => {
|
||||
const { id } = options
|
||||
couponId.value = id
|
||||
fetchCouponDetail()
|
||||
})
|
||||
/**获取优惠券详情 */
|
||||
const fetchCouponDetail = async () => {
|
||||
try {
|
||||
const res = await apiCouponDetail({ couponId: unref(couponId) })
|
||||
for (const key in res) {
|
||||
couponDetail[key] = res[key]
|
||||
}
|
||||
} catch (error) { }
|
||||
}
|
||||
/**领取后
|
||||
*
|
||||
* 1、根据用户可领取张数
|
||||
* 2、领取后重新刷新详情页
|
||||
*/
|
||||
const handleManualCoupon = () => {
|
||||
couponDetail.getNum = couponDetail.getNum - 1
|
||||
uni.$u.toast(`优惠券已放到卡包中${couponDetail.getNum}`)
|
||||
/**
|
||||
* 1、有多张优惠券
|
||||
* 2、只有一张优惠券
|
||||
*/
|
||||
// uni.navigateBack({
|
||||
// delta: 1,
|
||||
// success: () => {
|
||||
// const pages = getCurrentPages()
|
||||
// const prevPage = pages[pages.length - 1]
|
||||
// prevPage.onLoad?.()
|
||||
// }
|
||||
// })
|
||||
}
|
||||
onUnload(() => {
|
||||
uni.$emit('refresh', {})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coupon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
.coupon__detail {
|
||||
height: calc(100% - 60px);
|
||||
overflow: auto;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
margin: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
line-height: 1.8;
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #777;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 0 20rpx;
|
||||
height: 148rpx;
|
||||
border-left: 1px dashed #F1F2F2;
|
||||
|
||||
.money {
|
||||
color: #fc5531;
|
||||
|
||||
.strong {
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.condition {
|
||||
color: #777;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__rule {
|
||||
background-color: #fff;
|
||||
margin: 20rpx;
|
||||
padding: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.sub_title {
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 20rpx;
|
||||
line-height: 60rpx;
|
||||
font-size: 13px;
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 180rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 12rpx;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon__btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 120rpx;
|
||||
padding: 0 40rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.manual {
|
||||
height: 80rpx;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
line-height: 80rpx;
|
||||
color: #fff;
|
||||
border-radius: 999px;
|
||||
background-color: #fc5531;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<view class="coupon__center">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="couponList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<block v-for="item in couponList" :key="item.id">
|
||||
<coupon-card :item="item">
|
||||
<template #manual>
|
||||
<text class="btn__link" @click="handleManualCoupon(item)">立即领取</text>
|
||||
</template>
|
||||
<template #count>
|
||||
<text class="count" v-if="item.alreadyGetCouponCount > 0">
|
||||
已领取{{ item.alreadyGetCouponCount }}张
|
||||
</text>
|
||||
</template>
|
||||
</coupon-card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useZPaging } from '@/hooks/useCoupon'
|
||||
import { apiCouponCenterList, apiCouponManualGet } from '@/api/coupon'
|
||||
import cache from '@/utils/cache'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { DIST_ID } from '@/enums/cacheEnums'
|
||||
const couponList = ref<any>([])
|
||||
|
||||
const config = {
|
||||
initParams: {
|
||||
distributorId: cache.get(DIST_ID) ?? '' //扫码传
|
||||
},
|
||||
requestApi: apiCouponCenterList
|
||||
}
|
||||
const { queryList, paging } = useZPaging(config.initParams, config.requestApi)
|
||||
|
||||
/**立即领取
|
||||
* 1、领取张数只有1张:提示+刷新列表
|
||||
* 2、领取张数有多张:提示 + 文本(xx)+ 刷新列表
|
||||
* 3、领取后刷新列表
|
||||
*/
|
||||
const handleManualCoupon = async (row: any) => {
|
||||
const { id } = row
|
||||
try {
|
||||
await apiCouponManualGet({ id })
|
||||
uni.$u.toast(`优惠券已放到卡包中`)
|
||||
paging.value.reload()
|
||||
} catch (error) {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coupon__center {
|
||||
height: 100vh;
|
||||
|
||||
.btn__link {
|
||||
background-color: $white;
|
||||
color: $blue1;
|
||||
padding: 8rpx 16rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 999px;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,234 @@
|
|||
<template>
|
||||
<view class="coupon__order">
|
||||
<view class="coupon__order__header">
|
||||
<view class="popup__header__tabs">
|
||||
<tabs
|
||||
:current="setTabIndex"
|
||||
:swipeable="false"
|
||||
v-bind="tabStyle"
|
||||
@change="handleChangeTab"
|
||||
>
|
||||
<tab
|
||||
v-for="(item, i) in couponTabs"
|
||||
:key="i"
|
||||
:name="`${item.name}(${item.count})`"
|
||||
></tab>
|
||||
</tabs>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon__order__content">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="couponList"
|
||||
@query="transformQueryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
:to-bottom-loading-more-enabled="false"
|
||||
:loading-more-enabled="false"
|
||||
>
|
||||
<block v-for="(item, index) in couponList" :key="`${index}-${item.id}`">
|
||||
<coupon-card :item="item" :activeTabName="activeTabName">
|
||||
<template v-slot:selection>
|
||||
<u-checkbox-group
|
||||
v-if="tabStatus === CouponStatusEnum.AVALIABLE"
|
||||
class="selection"
|
||||
>
|
||||
<u-checkbox
|
||||
v-model="item.defaultSelect"
|
||||
:name="item.id"
|
||||
@change="detail => handleSelect(detail, item)"
|
||||
shape="circle"
|
||||
active-color="#2979FF"
|
||||
></u-checkbox>
|
||||
</u-checkbox-group>
|
||||
</template>
|
||||
<template #count>
|
||||
<text class="count" v-if="item.alreadyGetCouponCount >= 2">
|
||||
{{ parseText }}使用{{ item.alreadyGetCouponCount }}张
|
||||
</text>
|
||||
</template>
|
||||
<template #expiringSoon>
|
||||
<div class="expiring-soon" v-if="showExpiringSoon(item!)">快失效</div>
|
||||
</template>
|
||||
</coupon-card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</view>
|
||||
<view class="coupon__order__btn" v-if="isVisibleConfirmBtn">
|
||||
<u-button type="primary" shape="circle" @click="handleSelectCoupon">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref, watch, reactive } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { useTabs, useZPaging } from '@/hooks/useCoupon'
|
||||
import { apiCouponByUse, apiCouponCount } from '@/api/coupon'
|
||||
import { Recordable } from '@/config/interface'
|
||||
import { CouponCountMap, showExpiringSoonDays } from '@/enums/appEnums'
|
||||
import { CouponStatusEnum } from '@/enums/appEnums'
|
||||
|
||||
const goodsId = ref()
|
||||
const goodsCategoryId = ref()
|
||||
const couponList = ref<any[]>([])
|
||||
const couponTabs = ref<any[]>([])
|
||||
const availableCount = ref(0) //可用数量
|
||||
|
||||
let selectedCouponInfo = reactive<Recordable<string>>({})
|
||||
|
||||
const { tabStatus, setTabIndex, handleChangeTab } = useTabs()
|
||||
const { paging, queryList, refresh } = useZPaging({}, apiCouponByUse)
|
||||
|
||||
const isVisibleConfirmBtn = computed(
|
||||
() => unref(availableCount) > 0 && unref(tabStatus) === CouponStatusEnum.AVALIABLE
|
||||
)
|
||||
const tabStyle = computed(() => {
|
||||
return {
|
||||
height: 100,
|
||||
barWidth: 90,
|
||||
fontSize: 32,
|
||||
barStyle: { bottom: 0 },
|
||||
activeColor: '#1296DB'
|
||||
}
|
||||
})
|
||||
const parseText = computed(() => (unref(tabStatus) === CouponStatusEnum.AVALIABLE ? '可' : '不可'))
|
||||
const activeTabName = computed(() => couponTabs.value[setTabIndex.value]?.name)
|
||||
const showExpiringSoon = computed(() => {
|
||||
return (row: any) => {
|
||||
const { useTimeEnd, useStatus } = row
|
||||
const currentTime = new Date().getTime()
|
||||
const endTime = Date.parse(useTimeEnd)
|
||||
const daysDifference = Math.abs(endTime - currentTime) / (60 * 60 * 24 * 1000)
|
||||
return daysDifference <= showExpiringSoonDays && activeTabName.value == '可用优惠券'
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => unref(tabStatus),
|
||||
() => {
|
||||
const params = { usableStatus: unref(tabStatus) }
|
||||
refresh(params)
|
||||
}
|
||||
)
|
||||
|
||||
/**选择优惠券 */
|
||||
const handleSelect = (detail, item) => {
|
||||
const { name: id, value } = detail
|
||||
const selectedItem = unref(couponList).find(coupon => coupon.id === id)
|
||||
if (!selectedItem) return
|
||||
unref(couponList).forEach(coupon => {
|
||||
coupon.defaultSelect = false
|
||||
})
|
||||
selectedItem.defaultSelect = !selectedCouponInfo.defaultSelect
|
||||
value ? saveSelectItem(item) : (selectedCouponInfo = {})
|
||||
}
|
||||
/**确定 */
|
||||
const handleSelectCoupon = async () => {
|
||||
if (Object.keys(selectedCouponInfo).length > 0) {
|
||||
paging.value.reload()
|
||||
} else {
|
||||
selectedCouponInfo = unref(couponList).find(i => i.defaultSelect) ?? []
|
||||
}
|
||||
uni.navigateBack()
|
||||
uni.$emit('selectCoupon', {
|
||||
selectedCouponInfo,
|
||||
availableCount: unref(availableCount)
|
||||
})
|
||||
}
|
||||
|
||||
/**获取优惠券数量 */
|
||||
const getCouponCount = async () => {
|
||||
const params = {
|
||||
goodsId: unref(goodsId),
|
||||
goodsCategoryId: unref(goodsCategoryId)
|
||||
}
|
||||
try {
|
||||
const res = await apiCouponCount(params)
|
||||
availableCount.value = res.availableCount
|
||||
couponTabs.value = Object.keys(res).map(key => {
|
||||
return {
|
||||
name: CouponCountMap[key],
|
||||
count: res[key]
|
||||
}
|
||||
})
|
||||
} catch (error) {}
|
||||
}
|
||||
onLoad(async options => {
|
||||
await setPayload(options)
|
||||
getCouponCount()
|
||||
})
|
||||
|
||||
/**保存传递过来的参数 */
|
||||
function setPayload(options: any) {
|
||||
const { goodsId: goodId, goodsCategoryId: categoryId, selectCoupon } = options
|
||||
const couponInfo = JSON.parse(selectCoupon)
|
||||
goodsId.value = goodId
|
||||
goodsCategoryId.value = categoryId
|
||||
if (!Object.keys(couponInfo).length) return
|
||||
saveSelectItem(couponInfo)
|
||||
}
|
||||
|
||||
/**保存选中项内容 */
|
||||
function saveSelectItem(item: any) {
|
||||
for (const key in item) {
|
||||
selectedCouponInfo[key] = item[key]
|
||||
}
|
||||
}
|
||||
function transformQueryList(page_no: number, page_size: number) {
|
||||
const config = {
|
||||
initParams: {
|
||||
goodsId: unref(goodsId),
|
||||
goodsCategoryId: unref(goodsCategoryId),
|
||||
usableStatus: unref(tabStatus),
|
||||
selectCouponId: selectedCouponInfo.id ?? ''
|
||||
}
|
||||
}
|
||||
return queryList(page_no, page_size, config.initParams)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.coupon__order {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
&__header {
|
||||
}
|
||||
|
||||
&__content {
|
||||
height: calc(100% - 100rpx);
|
||||
overflow: auto;
|
||||
|
||||
.selection {
|
||||
position: absolute;
|
||||
right: -20rpx;
|
||||
top: 30rpx;
|
||||
|
||||
:deep(.u-checkbox__icon-wrap) {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__btn {
|
||||
height: 120rpx;
|
||||
background-color: $white;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
}
|
||||
.expiring-soon {
|
||||
position: absolute;
|
||||
background: #d83d3b;
|
||||
width: 162rpx;
|
||||
height: 32rpx;
|
||||
rotate: 26deg;
|
||||
top: -4px;
|
||||
right: -11px;
|
||||
font-size: 19rpx;
|
||||
line-height: 32rpx;
|
||||
padding-left: 53rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<view class="card" v-if="isEmpty">
|
||||
<view class="cell" v-for="cell in orderFields" :key="cell.label">
|
||||
<text class="width-200">{{ cell.label }}</text>
|
||||
<text class="flex-1 overflow-ellipsis-line">{{ setUnit(cell.value, cell.unit) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="loading" v-else-if="!isLoading">
|
||||
<u-loading></u-loading>
|
||||
<view>加载中...</view>
|
||||
</view>
|
||||
<view class="empty" v-else>
|
||||
<u-empty text="" mode="data"></u-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, unref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { useLockFn } from '@/hooks/useLockFn'
|
||||
import { getDistributorOrderDetail } from '@/api/distributor_center'
|
||||
import { setUnit } from '@/utils/util'
|
||||
|
||||
interface OrderProps {
|
||||
[key: string]: string | number
|
||||
id: number
|
||||
commission: number
|
||||
orderNo: number
|
||||
goodsCategoryName: string
|
||||
goodsName: string
|
||||
goodsNum: number
|
||||
orderAmount: number
|
||||
finishTime: string
|
||||
commissionRate: number
|
||||
appointTimeStart: string
|
||||
appointTimeEnd: string
|
||||
}
|
||||
const fieldsMap: Record<string, string | string[]> = {
|
||||
orderNo: '订单编号',
|
||||
goodsCategoryName: '服务类别',
|
||||
goodsName: '服务名称',
|
||||
goodsNum: ['商品数量', '个'],
|
||||
orderAmount: ['实付金额', '元'],
|
||||
address: '上门地址',
|
||||
appointTime: '预约时间',
|
||||
finishTime: '完成时间'
|
||||
// commissionRate: ['最终抽佣比', '%'],
|
||||
// commission: ['抽佣金额', '元']
|
||||
}
|
||||
const orderFields = ref<any[]>([])
|
||||
const orderDetail = reactive<Partial<OrderProps>>({})
|
||||
const isEmpty = computed(() => unref(orderFields).length > 0)
|
||||
const isLoading = computed(() => Object.keys(orderDetail).length > 0)
|
||||
|
||||
onLoad(options => {
|
||||
const { id } = options
|
||||
initOrderDetail(id)
|
||||
})
|
||||
|
||||
/**订单详情 */
|
||||
const { lockFn: initOrderDetail } = useLockFn(async id => {
|
||||
const data = await getDistributorOrderDetail({ id })
|
||||
Object.keys(data).length > 0 && setDetailData(data)
|
||||
})
|
||||
|
||||
/**保存信息 */
|
||||
function setDetailData(data: any) {
|
||||
for (const key in data) {
|
||||
orderDetail[key] = data[key]
|
||||
}
|
||||
orderDetail.appointTime = `${orderDetail.appointTimeStart?.replace(
|
||||
/:\d{2}$/,
|
||||
''
|
||||
)}-${orderDetail.appointTimeEnd?.split(' ')[1].replace(/:\d{2}$/, '')}`
|
||||
|
||||
// 订单未完成,不显示订单完成时间
|
||||
if (orderDetail.orderStatus !== 3 && orderDetail.orderStatus !== 4) {
|
||||
delete fieldsMap.finishTime
|
||||
}
|
||||
orderFields.value = Object.keys(fieldsMap).map(field => {
|
||||
const text = fieldsMap[field]
|
||||
const [label, unit] = Array.isArray(text) ? text : [text, '']
|
||||
return {
|
||||
label: label,
|
||||
value: orderDetail[field],
|
||||
unit
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
background-color: $white;
|
||||
margin: 20rpx 40rpx 0;
|
||||
padding: 30rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
.cell {
|
||||
display: flex;
|
||||
line-height: 1.8;
|
||||
|
||||
.width-200 {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
color: #827e7e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
@include center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.loading {
|
||||
@include center;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,91 @@
|
|||
<template>
|
||||
<view class="card">
|
||||
<view class="card__top top_style title-active">
|
||||
<text>{{ item?.currentDate }}</text>
|
||||
<text>
|
||||
共计:
|
||||
<text class="text-3xl text-[#1296DB]">{{ item?.userVoList.length }}</text>
|
||||
人
|
||||
</text>
|
||||
</view>
|
||||
<view class="card__content">
|
||||
<view class="header">
|
||||
<text>用户账号</text>
|
||||
<text>绑定到期时间</text>
|
||||
</view>
|
||||
<view class="table">
|
||||
<view class="core h-8" v-for="user in item?.userVoList" :key="user.id">
|
||||
<text>{{ user.userNo }}</text>
|
||||
<text>{{ user.unBindTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
|
||||
interface ChildrenProps {
|
||||
id: number
|
||||
userNo: string
|
||||
username: string
|
||||
unBindTime: string
|
||||
}
|
||||
interface UserProps {
|
||||
id: number
|
||||
currentDate: string
|
||||
userVoList: ChildrenProps[]
|
||||
}
|
||||
const props = defineProps({
|
||||
item: Object as PropType<UserProps>
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
background-color: $white;
|
||||
margin: 20rpx 40rpx 0;
|
||||
padding: 20rpx 30rpx;
|
||||
border-radius: 16rpx;
|
||||
@include flex-column;
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.header {
|
||||
@include flex;
|
||||
|
||||
> text {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
@include flex-column;
|
||||
line-height: 1.8;
|
||||
margin-top: 15rpx;
|
||||
|
||||
.core {
|
||||
@include flex;
|
||||
|
||||
> text {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.total {
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
}
|
||||
.top_style {
|
||||
@apply border-0 border-b border-solid border-[#f7f4f4] h-10 flex items-center justify-between;
|
||||
}
|
||||
.title-active::before {
|
||||
left: -30rpx;
|
||||
background-color: #1296db;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,127 @@
|
|||
<template>
|
||||
<u-navbar title="" class="u-navbar">
|
||||
<view class="slot-wrap" @click="handleToggle">
|
||||
<!-- {{ currentDate }}份分销用户 -->
|
||||
<text class="m-[5px]">店铺客户</text>
|
||||
<u-icon name="arrow-down"></u-icon>
|
||||
<u-picker
|
||||
mode="time"
|
||||
v-model="show"
|
||||
:default-time="`${convertChineseMonthToDate(currentDate)}-${day}`"
|
||||
:params="params"
|
||||
@confirm="handleConfirm"
|
||||
></u-picker>
|
||||
</view>
|
||||
</u-navbar>
|
||||
<div class="total-money bg-[#FDF6EC] text-[#ef2c2c] px-4 text-2xl py-2">
|
||||
<span>{{ `${currentDate}总新增用户:` }}</span>
|
||||
<span class="font-bold">{{ `${monthTotalPeople}人` }}</span>
|
||||
</div>
|
||||
<view class="list" :style="`height:calc(100vh - ${navbarHeight}px)`">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<block v-for="(item, index) in dataList" :key="`${index} + 'unique'`">
|
||||
<userCard :item="item" />
|
||||
</block>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import userCard from './components/userCard.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useZPaging } from '@/hooks/useCoupon'
|
||||
import { useToggle } from '@/hooks/useLockFn'
|
||||
import { getDistributorBindingUser, getDistributorUserByMonth } from '@/api/distributor_center'
|
||||
import { convertChineseMonthToDate } from '@/utils/util'
|
||||
|
||||
const date = `${new Date().getFullYear()}年${new Date().getMonth() + 1}月`
|
||||
const today = new Date().getDate()
|
||||
const day = today < 10 ? String(today).padStart(2, '0') : today
|
||||
|
||||
const params = {
|
||||
year: true,
|
||||
month: true,
|
||||
day: false,
|
||||
hour: false,
|
||||
minute: false,
|
||||
second: false
|
||||
}
|
||||
|
||||
const dataList = ref<any>([])
|
||||
const currentDate = ref(date)
|
||||
const monthTotalPeople = ref(0)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
const { show, handleToggle } = useToggle()
|
||||
|
||||
const config = {
|
||||
initParams: {
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
date: convertChineseMonthToDate(unref(currentDate))
|
||||
},
|
||||
requestApi: getDistributorBindingUser
|
||||
}
|
||||
const { paging, queryList, refresh } = useZPaging(config.initParams, config.requestApi)
|
||||
|
||||
/**选择日期 */
|
||||
const handleConfirm = res => {
|
||||
const { year, month } = res
|
||||
currentDate.value = `${year}年${month}月`
|
||||
refresh({ date: convertChineseMonthToDate(unref(currentDate)) })
|
||||
getUserByMonth()
|
||||
}
|
||||
/**每月获取总分销用户 */
|
||||
const getUserByMonth = async () => {
|
||||
try {
|
||||
const params = {
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
date: convertChineseMonthToDate(unref(currentDate))
|
||||
}
|
||||
const data = await getDistributorUserByMonth(params)
|
||||
monthTotalPeople.value = Number(data) ?? 0
|
||||
} catch (error) {}
|
||||
}
|
||||
onMounted(getUserByMonth)
|
||||
|
||||
let navbarHeight = ref(95)
|
||||
onMounted(() => {
|
||||
uni.createSelectorQuery()
|
||||
.select('.u-navbar')
|
||||
.boundingClientRect(data => {
|
||||
navbarHeight.value = (data?.height || 95) as number
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.slot-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
margin-left: 110rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
.total-money {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
height: 86rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list {
|
||||
// height: calc(100vh - 88rpx);
|
||||
padding-top: 86rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,108 @@
|
|||
<template>
|
||||
<view class="card">
|
||||
<view class="card__top top_style title-active">
|
||||
<text>{{ item?.currentDate?.split(' ')[0] }}</text>
|
||||
<!-- <text class="">
|
||||
共计:
|
||||
<text class="text-3xl text-[#1296DB]">{{ item.dayTotalMoney }}</text>
|
||||
元
|
||||
</text> -->
|
||||
</view>
|
||||
<view class="card__content">
|
||||
<view
|
||||
class="core"
|
||||
v-for="(child, index) in item?.distributorOrderListVo"
|
||||
:key="index"
|
||||
@click="handleNavigate(child)"
|
||||
>
|
||||
<view class="title flex-1">
|
||||
<text class="overflow-ellipsis-line mb-2">{{ child.orderNo }}</text>
|
||||
<text class="overflow-ellipsis-line">{{ child.goodsName }}</text>
|
||||
</view>
|
||||
<view class="money">
|
||||
<text>实付金额</text>
|
||||
<text class="totalPrice">{{ joinSuffix(child.orderAmount) }}</text>
|
||||
</view>
|
||||
<!-- <view class="money">
|
||||
<text>分销抽佣</text>
|
||||
<text class="totalPrice">{{ joinSuffix(child.commission) }}</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
import { joinSuffix } from '@/utils/util'
|
||||
|
||||
interface ChildrenProps {
|
||||
createTime: string
|
||||
distributorId: number
|
||||
goodsName: string
|
||||
orderAmount: number
|
||||
orderId: number
|
||||
orderNo: number
|
||||
}
|
||||
interface CardProps {
|
||||
currentDate: string
|
||||
distributorOrderListVo: ChildrenProps[]
|
||||
money: number
|
||||
dayTotalMoney: number
|
||||
}
|
||||
const props = defineProps({
|
||||
item: Object as PropType<CardProps>
|
||||
})
|
||||
const emit = defineEmits(['navigate'])
|
||||
/**跳转地址 */
|
||||
const handleNavigate = (item: ChildrenProps) => {
|
||||
emit('navigate', item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
background-color: $white;
|
||||
margin: 20rpx 40rpx 0;
|
||||
padding: 0rpx 30rpx 20rpx;
|
||||
border-radius: 16rpx;
|
||||
@include flex-column;
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.core {
|
||||
@include flex-justify;
|
||||
margin-top: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1px solid #f7f4f4;
|
||||
|
||||
.totalPrice {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include flex-column;
|
||||
}
|
||||
|
||||
.money {
|
||||
@include flex-column;
|
||||
color: #1296db;
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
.top_style {
|
||||
@apply border-0 border-b border-solid border-[#f7f4f4] h-10 flex items-center justify-between;
|
||||
}
|
||||
.title-active::before {
|
||||
left: -30rpx;
|
||||
background-color: #1296db;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<u-navbar title="" class="u-navbar">
|
||||
<view class="slot-wrap" @click="handleToggle">
|
||||
<!-- {{ currentDate }}份分销账单 -->
|
||||
<text class="m-[5px]">店铺订单</text>
|
||||
<u-icon name="arrow-down"></u-icon>
|
||||
<u-picker
|
||||
mode="time"
|
||||
v-model="show"
|
||||
:default-time="`${convertChineseMonthToDate(currentDate)}-${day}`"
|
||||
:params="params"
|
||||
@confirm="handleConfirm"
|
||||
></u-picker>
|
||||
</view>
|
||||
</u-navbar>
|
||||
<!-- <div
|
||||
class="total-money bg-[#FDF6EC] text-[#ef2c2c] px-4 text-2xl py-2"
|
||||
:style="{ top: navbarHeight + 'px' }"
|
||||
>
|
||||
<span>{{ `${currentDate}总分销金额:` }}</span>
|
||||
<span class="font-bold">{{ `${monthTotalMoney}元` }}</span>
|
||||
</div> -->
|
||||
<view class="list">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<block v-for="(item, index) in dataList" :key="`${index} + 'unique'`">
|
||||
<orderCard :item="item" @navigate="handleNavigate" />
|
||||
</block>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, unref, onMounted } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import orderCard from './components/orderCard.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useZPaging } from '@/hooks/useCoupon'
|
||||
import { useToggle } from '@/hooks/useLockFn'
|
||||
import { getDistributorOrder, getDistributorOrderByMonth } from '@/api/distributor_center'
|
||||
import { convertChineseMonthToDate } from '@/utils/util'
|
||||
import { router } from '@/utils/util'
|
||||
|
||||
const date = `${new Date().getFullYear()}年${new Date().getMonth() + 1}月`
|
||||
const today = new Date().getDate()
|
||||
const day = today < 10 ? String(today).padStart(2, '0') : today
|
||||
|
||||
const params = {
|
||||
year: true,
|
||||
month: true,
|
||||
day: false,
|
||||
hour: false,
|
||||
minute: false,
|
||||
second: false
|
||||
}
|
||||
|
||||
const dataList = ref<any>([])
|
||||
const currentDate = ref(date)
|
||||
const monthTotalMoney = ref(0)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
const { show, handleToggle } = useToggle()
|
||||
|
||||
const config = {
|
||||
initParams: {
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
date: convertChineseMonthToDate(unref(currentDate))
|
||||
},
|
||||
requestApi: getDistributorOrder
|
||||
}
|
||||
const { paging, queryList, refresh } = useZPaging(config.initParams, config.requestApi)
|
||||
|
||||
/**选择日期 */
|
||||
const handleConfirm = res => {
|
||||
const { year, month } = res
|
||||
currentDate.value = `${year}年${month}月`
|
||||
refresh({ date: convertChineseMonthToDate(unref(currentDate)) })
|
||||
getOrderByMonth()
|
||||
}
|
||||
/**跳转地址 */
|
||||
const handleNavigate = (item: any) => {
|
||||
const { orderId } = item
|
||||
router(`/bundle/pages/dist_order/index?id=${orderId}`)
|
||||
}
|
||||
const getOrderByMonth = () => {
|
||||
const params = {
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
date: convertChineseMonthToDate(unref(currentDate))
|
||||
}
|
||||
getDistributorOrderByMonth(params).then(res => {
|
||||
monthTotalMoney.value = res || 0
|
||||
})
|
||||
}
|
||||
getOrderByMonth()
|
||||
|
||||
let navbarHeight = ref(95)
|
||||
onMounted(() => {
|
||||
uni.createSelectorQuery()
|
||||
.select('.u-navbar')
|
||||
.boundingClientRect(data => {
|
||||
navbarHeight.value = (data?.height || 95) as number
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.slot-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
margin-left: 110rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
.total-money {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
height: 86rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list {
|
||||
height: calc(100vh - 95px);
|
||||
// padding-top: 86rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,127 @@
|
|||
<template>
|
||||
<view class="card">
|
||||
<view class="card--header flex justify-between col-start">
|
||||
<view class="flex">
|
||||
<u-image
|
||||
:src="user?.avatar || defaultAvatar"
|
||||
width="80"
|
||||
height="80"
|
||||
border-radius="50%"
|
||||
></u-image>
|
||||
<view class="ml-[20rpx]">
|
||||
<view class="text-base normal font-medium">
|
||||
{{ user?.nickname || FieldType.defalutNickname }}
|
||||
</view>
|
||||
<view class="text-muted text-xs mt-[10rpx]">{{ createTime }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<u-rate
|
||||
:count="5"
|
||||
v-model="serviceComment"
|
||||
size="32"
|
||||
inactive-icon="star-fill"
|
||||
activeColor="#FBC02D"
|
||||
disabled
|
||||
></u-rate>
|
||||
<!-- <view class="ml-[20rpx] lighter text-xs">
|
||||
<text v-if="serviceComment == 5">非常好</text>
|
||||
<text v-if="serviceComment == 4">好</text>
|
||||
<text v-if="serviceComment == 3">一般</text>
|
||||
<text v-if="serviceComment == 2">差</text>
|
||||
<text v-if="serviceComment == 1">非常差</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card--main">
|
||||
<view class="content truncate">
|
||||
{{ comment }}
|
||||
</view>
|
||||
|
||||
<view class="flex flex-wrap">
|
||||
<block v-for="(item3, index) in commentUrlList" :key="index">
|
||||
<view
|
||||
class="mt-[10rpx]"
|
||||
:class="{ 'mr-[10rpx]': (index + 1) % 3 != 0 }"
|
||||
@click.stop="previewImage(index)"
|
||||
>
|
||||
<u-image
|
||||
:src="item3"
|
||||
width="210"
|
||||
height="210"
|
||||
border-radius="15rpx"
|
||||
></u-image>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<view class="reply normal text-base mt-[20rpx] font-medium" v-if="reply">
|
||||
商家回复: {{ reply }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import defaultAvatar from '@/static/images/user/default_avatar.png'
|
||||
import { FieldType } from '@/enums/appEnums'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
goodsId: string | number
|
||||
comment: string | null
|
||||
commentUrlList: string | null
|
||||
reply: string | null
|
||||
createTime: string | null
|
||||
serviceComment: string | number | any
|
||||
user: any
|
||||
}>(),
|
||||
{
|
||||
goodsId: '',
|
||||
comment: '',
|
||||
commentUrlList: '',
|
||||
reply: '',
|
||||
createTime: '',
|
||||
serviceComment: '',
|
||||
user: {
|
||||
avatar: '',
|
||||
nickname: ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 查看评价图片
|
||||
const previewImage = (current: number) => {
|
||||
uni.previewImage({
|
||||
current,
|
||||
urls: props.commentUrlList
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-radius: 14rpx;
|
||||
background-color: white;
|
||||
margin: 0 20rpx 0 20rpx;
|
||||
padding: 30rpx;
|
||||
&--header {
|
||||
width: 100%;
|
||||
}
|
||||
&--main {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
.content {
|
||||
padding: 20rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
}
|
||||
.reply {
|
||||
word-break: break-all;
|
||||
padding: 24rpx 20rpx;
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,77 @@
|
|||
<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">
|
||||
<Card
|
||||
:comment="item.comment"
|
||||
:commentUrlList="item.commentUrlList"
|
||||
:reply="item.reply"
|
||||
:createTime="item.createTime"
|
||||
:goodsId="item.goodsId"
|
||||
:serviceComment="item.serviceComment"
|
||||
:user="item.userInfoVo"
|
||||
></Card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, shallowRef, unref } from 'vue'
|
||||
import Card from './card.vue'
|
||||
import { apiEvaluateGoodsLists } from '@/api/goods'
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref<any>([])
|
||||
const isFirst = ref<boolean>(true)
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
goodsId: number | string
|
||||
i: number
|
||||
index: number
|
||||
cid: number
|
||||
}>(),
|
||||
{
|
||||
goodsId: 0,
|
||||
cid: 0
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.index,
|
||||
async () => {
|
||||
await nextTick()
|
||||
if (props.i == props.index && unref(isFirst)) {
|
||||
isFirst.value = false
|
||||
paging.value?.reload()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const queryList = async (pageNo: number, pageSize = 100) => {
|
||||
try {
|
||||
const { lists } = await apiEvaluateGoodsLists({
|
||||
goodsId: +props.goodsId,
|
||||
commentLevel: props.i,
|
||||
pageNo,
|
||||
pageSize: 100
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,107 @@
|
|||
<!--
|
||||
* @Author: 王翠碧 1254597151@qq.com
|
||||
* @Date: 2023-05-22 22:41:29
|
||||
* @LastEditors: 王翠碧 1254597151@qq.com
|
||||
* @LastEditTime: 2023-05-27 14:44:46
|
||||
* @FilePath: \uniapp\src\bundle\pages\evaluate_goods\index.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<view class="container" v-if="tabList.length">
|
||||
<tabs
|
||||
:current="current"
|
||||
@change="handleChange"
|
||||
height="80"
|
||||
bar-width="60"
|
||||
:barStyle="{ bottom: '0' }"
|
||||
:auth="true"
|
||||
v-bind="tabsColor"
|
||||
>
|
||||
<tab v-for="(item, i) in tabList" :key="i" :name="`${item.name}(${item?.count})`">
|
||||
<view class="List pt-[20rpx]" v-if="isLogin">
|
||||
<List :cid="item.id" :i="i" :index="current" :goodsId="goodsId"></List>
|
||||
</view>
|
||||
</tab>
|
||||
</tabs>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { apiEvaluateGoodsLists } from '@/api/goods'
|
||||
import List from './components/list.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { apiEvaluateGoodsStatis } from '@/api/goods'
|
||||
import { useTabs } from '@/hooks/useLockFn'
|
||||
|
||||
const { tabsColor } = useTabs()
|
||||
const goodsId = ref<number>(8)
|
||||
|
||||
const tabList = ref<any>([
|
||||
{
|
||||
name: '全部',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '好评',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '中评',
|
||||
count: 2
|
||||
},
|
||||
{
|
||||
name: '差评',
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '晒图',
|
||||
count: 0
|
||||
}
|
||||
])
|
||||
const current = ref<number>(0)
|
||||
// 是否登录
|
||||
const userStore = useUserStore()
|
||||
const isLogin = computed(() => userStore.token)
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
current.value = Number(index)
|
||||
}
|
||||
|
||||
// 获取评价统计数据
|
||||
const getEvaluateStatis = async () => {
|
||||
const res = await apiEvaluateGoodsStatis({ goodsId: goodsId.value })
|
||||
tabList.value[0].count = res.totalCount
|
||||
tabList.value[1].count = res.goodCount
|
||||
tabList.value[2].count = res.generalCount
|
||||
tabList.value[3].count = res.badCount
|
||||
tabList.value[4].count = res.pictureCount
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
goodsId.value = options.id || 0
|
||||
getEvaluateStatis()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: scroll;
|
||||
swiper {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.List {
|
||||
height: calc(100vh - 86px - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<!-- @click.stop="toGoodsDetail(goods_id)" -->
|
||||
<view class="card">
|
||||
<!-- Header Start -->
|
||||
<view class="flex justify-between card--header">
|
||||
<view class="text-xs text-muted">{{ create_time }}</view>
|
||||
<view class="flex items-center">
|
||||
<u-rate
|
||||
:count="5"
|
||||
v-model="service_comment"
|
||||
size="28"
|
||||
inactive-icon="star-fill"
|
||||
activeColor="#FBC02D"
|
||||
disabled
|
||||
></u-rate>
|
||||
<view class="ml-[20rpx] lighter text-xs">
|
||||
<text v-if="service_comment == 5">非常好</text>
|
||||
<text v-if="service_comment == 4">好</text>
|
||||
<text v-if="service_comment == 3">一般</text>
|
||||
<text v-if="service_comment == 2">差</text>
|
||||
<text v-if="service_comment == 1">非常差</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Main Start -->
|
||||
<view class="card--main">
|
||||
<view class="content truncate">
|
||||
{{ comment }}
|
||||
</view>
|
||||
|
||||
<view class="flex flex-wrap">
|
||||
<block v-for="(item3, index) in goods_comment_image" :key="index">
|
||||
<view
|
||||
class="mt-[10rpx]"
|
||||
:class="{ 'mr-[10rpx]': (index + 1) % 3 != 0 }"
|
||||
@click.stop="previewImage(index)"
|
||||
>
|
||||
<u-image :src="item3" width="210" height="210"></u-image>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<view class="flex mt-[20rpx] goods">
|
||||
<u-image :src="goods_image" width="140" height="140"></u-image>
|
||||
<view class="ml-[20rpx]">
|
||||
<view class="text-xl font-medium normal">{{ goods_name }}</view>
|
||||
<view class="mt-[24rpx]">
|
||||
<price :price="goods_price" :desc="unit_name"></price>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="reply normal text-base mt-[20rpx] font-medium truncate" v-if="reply">
|
||||
商家回复: {{ reply }}
|
||||
</view> -->
|
||||
<view class="reply normal text-base mt-[20rpx] font-medium" v-if="reply">
|
||||
<u-read-more show-height="300" toggle text-indent="0">
|
||||
<rich-text :nodes="joinReply(reply)"></rich-text>
|
||||
</u-read-more>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Price from '@/components/price/index.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
/** Props Start **/
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
goods_id: string | number
|
||||
comment: string | null
|
||||
goods_comment_image: string | null | string[]
|
||||
reply: string | null
|
||||
create_time: string | null
|
||||
service_comment: any
|
||||
goods_image: string
|
||||
goods_name: string
|
||||
goods_price: string | number
|
||||
unit_name: string
|
||||
}>(),
|
||||
{
|
||||
goods_id: '',
|
||||
comment: '',
|
||||
goods_comment_image: '',
|
||||
reply: '',
|
||||
create_time: '',
|
||||
service_comment: '',
|
||||
goods_image: '',
|
||||
goods_name: '',
|
||||
goods_price: '',
|
||||
unit_name: ''
|
||||
}
|
||||
)
|
||||
/** Props End **/
|
||||
const joinReply = computed(() => (reply: string | null) => `商家回复: ${reply}`)
|
||||
/** Methods Start **/
|
||||
// 去商品详情
|
||||
const toGoodsDetail = (id: number | string) => {
|
||||
uni.redirectTo({
|
||||
url: `/pages/goods/index?id=${id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 查看评价图片
|
||||
const previewImage = (current: number) => {
|
||||
uni.previewImage({
|
||||
current,
|
||||
urls: props.goods_comment_image
|
||||
})
|
||||
}
|
||||
/** Methods End **/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-radius: 14rpx;
|
||||
background-color: white;
|
||||
margin: 0 20rpx 0 20rpx;
|
||||
padding: 30rpx;
|
||||
&--header {
|
||||
width: 100%;
|
||||
}
|
||||
&--main {
|
||||
overflow-x: hidden;
|
||||
.content {
|
||||
padding: 20rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
}
|
||||
.goods {
|
||||
padding-top: 20rpx;
|
||||
border-top: 1px solid #f6f6f6;
|
||||
}
|
||||
.reply {
|
||||
word-break: break-all;
|
||||
padding: 24rpx 20rpx;
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,83 @@
|
|||
<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="(item2, index) in dataList" :key="index">
|
||||
<already
|
||||
:comment="item2.comment"
|
||||
:goods_comment_image="item2.commentUrlList"
|
||||
:reply="item2.reply"
|
||||
:create_time="item2.createTime"
|
||||
:goods_id="item2.goodsId"
|
||||
:service_comment="item2.serviceComment"
|
||||
:goods_image="item2.goodsImageUrl"
|
||||
:goods_name="item2.goodsName"
|
||||
:goods_price="item2.price"
|
||||
:unit_name="item2.unitName"
|
||||
></already>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, shallowRef, unref } from 'vue'
|
||||
import Already from './already.vue'
|
||||
import { apiEvaluateGoodsLists } from '@/api/goods'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type: number
|
||||
count: number
|
||||
i: number
|
||||
index: number
|
||||
}>(),
|
||||
{
|
||||
type: 0,
|
||||
count: 0
|
||||
}
|
||||
)
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref<any>([])
|
||||
const isFirst = ref<boolean>(true)
|
||||
|
||||
watch(
|
||||
() => props.index,
|
||||
async () => {
|
||||
await nextTick()
|
||||
if (props.i == props.index && unref(isFirst)) {
|
||||
isFirst.value = false
|
||||
paging.value?.reload()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const queryList = async (pageNo: number, pageSize: number) => {
|
||||
try {
|
||||
const { lists } = await apiEvaluateGoodsLists({
|
||||
userId: userInfo.value.id,
|
||||
pageNo,
|
||||
pageSize
|
||||
})
|
||||
|
||||
paging.value.complete(lists)
|
||||
console.log('lists', lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,80 @@
|
|||
<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="(item2, index) in dataList" :key="index">
|
||||
<wait
|
||||
:name="item2.goodsName"
|
||||
:image="item2.goodsImage"
|
||||
:unit_name="item2.unitName"
|
||||
:price="item2.goodsPrice"
|
||||
:goodsId="item2.id"
|
||||
:finishTime="item2.finishTime"
|
||||
></wait>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, shallowRef, unref } from 'vue'
|
||||
import Wait from './wait.vue'
|
||||
import { apiWaitEvaluateGoodsLists } from '@/api/goods'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type: number
|
||||
count: number
|
||||
i: number
|
||||
index: number
|
||||
}>(),
|
||||
{
|
||||
type: 0,
|
||||
count: 0
|
||||
}
|
||||
)
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref<any>([])
|
||||
const isFirst = ref<boolean>(true)
|
||||
|
||||
watch(
|
||||
() => props.index,
|
||||
async () => {
|
||||
await nextTick()
|
||||
if (props.i == props.index && unref(isFirst)) {
|
||||
isFirst.value = false
|
||||
paging.value?.reload()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const queryList = async (pageNo = 1, pageSize = 10) => {
|
||||
try {
|
||||
const { lists } = await apiWaitEvaluateGoodsLists({
|
||||
pageNo,
|
||||
pageSize
|
||||
})
|
||||
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
// paging.value?.reload()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,82 @@
|
|||
<!--
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-08-14 15:38:40
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-11-03 11:36:00
|
||||
* @FilePath: \housekeeping-uniapp\src\bundle\pages\evaluate_list\components\wait.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<view class="card flex flex-col justify-between" @click="goPage">
|
||||
<view class="mb-3">
|
||||
<text class="font-medium">完成时间:</text>
|
||||
<text>{{ formatDate(finishTime) }}</text>
|
||||
</view>
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="flex flex-1 mr-1">
|
||||
<u-image :src="image" width="140" height="140" border-radius="16"></u-image>
|
||||
<view class="ml-[20rpx]">
|
||||
<view class="normal font-medium text-xl overflow-ellipsis-line">
|
||||
{{ name }}
|
||||
</view>
|
||||
<view class="mt-[24rpx]">
|
||||
<price :price="price" :desc="unit_name"></price>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<button
|
||||
class="evaluate bg-white text-sm text-muted leading-[60rpx] h-[60rpx] rounded-full"
|
||||
@click="goPage"
|
||||
>
|
||||
去评价
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Price from '@/components/price/index.vue'
|
||||
import { formatDate } from '@/utils/util'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
name: string | null
|
||||
image: string | null
|
||||
unit_name: any
|
||||
price: any
|
||||
goodsId: string | number
|
||||
finishTime: string | number
|
||||
}>(),
|
||||
{
|
||||
name: '',
|
||||
image: '',
|
||||
unit_name: '',
|
||||
price: '',
|
||||
goodsId: '',
|
||||
finishTime: ''
|
||||
}
|
||||
)
|
||||
|
||||
const goPage = () => {
|
||||
uni.redirectTo({
|
||||
url: `/bundle/pages/evaluate_submit/index?id=${props.goodsId}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-radius: 14rpx;
|
||||
background-color: white;
|
||||
margin: 0 20rpx 0 20rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
.evaluate {
|
||||
border: 1px solid $blue5;
|
||||
color: $blue5;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<!-- :auth="true" 是表示需要权限登录的 -->
|
||||
<tabs
|
||||
:current="current"
|
||||
@change="handleChange"
|
||||
height="86"
|
||||
:font-size="32"
|
||||
bar-width="60"
|
||||
:barStyle="{ bottom: '0' }"
|
||||
:auth="true"
|
||||
v-bind="tabsColor"
|
||||
>
|
||||
<tab v-for="(item, i) in tabList" :key="i" :name="`${item.name}(${item.count})`">
|
||||
<view class="List pt-[20rpx]" v-if="isLogin">
|
||||
<List
|
||||
v-if="i === 0"
|
||||
:type="item.type"
|
||||
:count="item.count"
|
||||
:i="i"
|
||||
:index="current"
|
||||
/>
|
||||
<alreadyList
|
||||
v-if="i === 1"
|
||||
:type="item.type"
|
||||
:count="item.count"
|
||||
:i="i"
|
||||
:index="current"
|
||||
/>
|
||||
</view>
|
||||
</tab>
|
||||
</tabs>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import List from './components/list.vue'
|
||||
import alreadyList from './components/alreadyList.vue'
|
||||
import tab from '@/components/tab/tab.vue'
|
||||
import tabs from '@/components/tabs/tabs.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { apiEvaluateStatis } from '@/api/goods'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { useTabs } from '@/hooks/useLockFn'
|
||||
|
||||
const { tabsColor } = useTabs()
|
||||
|
||||
const tabList = ref([
|
||||
{
|
||||
name: '待评价',
|
||||
type: 0,
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
name: '已评价',
|
||||
type: 1,
|
||||
count: 0
|
||||
}
|
||||
])
|
||||
const current = ref<number>(0)
|
||||
const userStore = useUserStore()
|
||||
// 是否登录
|
||||
const isLogin = computed(() => userStore.token)
|
||||
const handleChange = (index: number) => {
|
||||
current.value = Number(index)
|
||||
}
|
||||
|
||||
onLoad(async (options: { type?: any }) => {
|
||||
current.value = options?.type * 1 || 0
|
||||
getEvaluateStatis()
|
||||
})
|
||||
// 获取评价统计数据
|
||||
const getEvaluateStatis = async () => {
|
||||
const res = await apiEvaluateStatis('')
|
||||
tabList.value[0].count = res.waitCommentCount
|
||||
tabList.value[1].count = res.commentCount
|
||||
}
|
||||
// getEvaluateStatis()
|
||||
|
||||
onShow(() => {
|
||||
// getEvaluateStatis()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: scroll;
|
||||
swiper {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.List {
|
||||
height: calc(100vh - 86px - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,164 @@
|
|||
<template>
|
||||
<!-- Header Start -->
|
||||
<view class="card flex">
|
||||
<u-image :src="evaluateInfo?.orderGoods?.imageUrl" width="140" height="140"></u-image>
|
||||
<view class="ml-[20rpx]">
|
||||
<view class="normal font-medium text-xl overflow-ellipsis-line">
|
||||
{{ evaluateInfo?.orderGoods?.goodsName }}
|
||||
</view>
|
||||
<view class="mt-[24rpx]">
|
||||
<price
|
||||
:price="evaluateInfo?.goodsPrice"
|
||||
:desc="evaluateInfo?.orderGoods?.unitName"
|
||||
></price>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- Header End -->
|
||||
|
||||
<!-- Main Start -->
|
||||
<view class="card mt-[20rpx]">
|
||||
<view class="flex">
|
||||
<text class="normal font-medium text-base mr-[20rpx]">服务评分</text>
|
||||
<u-rate
|
||||
:count="5"
|
||||
v-model="formData.serviceComment"
|
||||
:min-count="1"
|
||||
inactive-icon="star-fill"
|
||||
activeColor="#FBC02D"
|
||||
size="34"
|
||||
></u-rate>
|
||||
<view class="ml-[20rpx] lighter text-xs">
|
||||
<text v-if="formData.serviceComment === 5">非常好</text>
|
||||
<text v-if="formData.serviceComment === 4">好</text>
|
||||
<text v-if="formData.serviceComment === 3">一般</text>
|
||||
<text v-if="formData.serviceComment === 2">差</text>
|
||||
<text v-if="formData.serviceComment === 1">非常差</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content mt-[30rpx]">
|
||||
<u-input
|
||||
v-model="formData.comment"
|
||||
type="textarea"
|
||||
placeholder="请输入评价内容"
|
||||
height="200"
|
||||
maxlength="250"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="mt-[30rpx]">
|
||||
<Uploader v-model="formData.commentImages" :maxUpload="6" image-fit="aspectFill" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer mt-[30rpx]">
|
||||
<button
|
||||
class="bg-primary text-lg text-white leading-[80rpx] h-[80rpx] rounded-full"
|
||||
@click="onSubmit"
|
||||
>
|
||||
提交
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { apiEvaluateAdd } from '@/api/user'
|
||||
import Price from '@/components/price/index.vue'
|
||||
import Uploader from '@/components/uploader/index.vue'
|
||||
import { apiOrderDetail } from '@/api/order'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
interface FormDataObj {
|
||||
orderId: number
|
||||
serviceComment: number | null
|
||||
comment: string
|
||||
commentImages: Array<string | null>
|
||||
}
|
||||
|
||||
const evaluateInfo = ref<any>({})
|
||||
const formData = ref<FormDataObj>({
|
||||
orderId: 0,
|
||||
serviceComment: 0,
|
||||
comment: '',
|
||||
commentImages: []
|
||||
})
|
||||
|
||||
const onSubmit = async (): Promise<void> => {
|
||||
try {
|
||||
let commentLevel = 0 // 服务评价等级 1 好评 2中评 3差评
|
||||
switch (formData.value.serviceComment) {
|
||||
case 1:
|
||||
commentLevel = 3
|
||||
break
|
||||
case 2:
|
||||
commentLevel = 3
|
||||
break
|
||||
case 3:
|
||||
commentLevel = 2
|
||||
break
|
||||
case 4:
|
||||
commentLevel = 1
|
||||
break
|
||||
case 5:
|
||||
commentLevel = 1
|
||||
break
|
||||
}
|
||||
// return
|
||||
if (commentLevel === 0) return uni.$u.toast('请选择服务评分')
|
||||
if (commentLevel == 3 && formData.value.comment.length == 0)
|
||||
return uni.$u.toast('请输入10个字以上的评价内容!')
|
||||
await apiEvaluateAdd({
|
||||
...formData.value,
|
||||
commentLevel,
|
||||
goodsId: evaluateInfo.value?.orderGoods?.goodsId,
|
||||
userId: userInfo.value.id
|
||||
})
|
||||
uni.$u.toast('操作成功')
|
||||
uni.redirectTo({ url: '/bundle/pages/evaluate_list/index?type=1' })
|
||||
} catch (error) {
|
||||
console.log('提交评价: ', error)
|
||||
}
|
||||
}
|
||||
|
||||
const initEvaluateGoodsInfo = async (): Promise<void> => {
|
||||
try {
|
||||
const res = await apiOrderDetail({
|
||||
id: formData.value.orderId
|
||||
})
|
||||
evaluateInfo.value = res
|
||||
} catch (error) {
|
||||
console.log('获取评价: ', error)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(options => {
|
||||
formData.value.orderId = options.id || 0
|
||||
initEvaluateGoodsInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.card {
|
||||
border-radius: 14rpx;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx 20rpx 0 20rpx;
|
||||
padding: 30rpx;
|
||||
|
||||
.content {
|
||||
padding: 0 24rpx;
|
||||
border-radius: 14rpx;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<view class="master-detail">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
:auto="true"
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
height="100%"
|
||||
>
|
||||
<view class="header">
|
||||
<view class="flex items-center h-[200rpx] bg-primary px-[24rpx]">
|
||||
<u-image
|
||||
class="mx-[15rpx]"
|
||||
:src="masterData.userVo?.avatar"
|
||||
width="100"
|
||||
height="100"
|
||||
border-radius="50%"
|
||||
>
|
||||
</u-image>
|
||||
<text class="text-2xl text-white font-medium ml-[20rpx]">{{
|
||||
masterData.name
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="bg-white px-[24rpx] py-[30rpx]">
|
||||
<view class="text-xl font-medium pb-[20rpx]">TA的信息</view>
|
||||
<view class="text-sm pb-[20rpx]"
|
||||
>所在地区:{{ masterData.city }} - {{ masterData.district }}</view
|
||||
>
|
||||
<view class="text-sm pb-[20rpx]">加入时间:{{ masterData.createTime }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="p-[24rpx] bg-white mt-[20rpx]">
|
||||
<view class="text-xl font-medium pb-[20rpx] normal"
|
||||
>TA的服务({{ masterData?.goodsList?.length || 0 }})
|
||||
</view>
|
||||
|
||||
<view class="">
|
||||
<goods-card :goodsList="masterData.goodsList"></goods-card>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, shallowRef, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { apiStaffDetail } from '@/api/store'
|
||||
import GoodsCard from '@/components/goods-card/index.vue'
|
||||
|
||||
// 师傅id
|
||||
const masterId = ref<number | string>('')
|
||||
// 师傅列表数据
|
||||
const dataList = ref<any>([])
|
||||
// 下拉组件的Ref
|
||||
const paging = shallowRef<any>(null)
|
||||
|
||||
const masterData = computed(() => unref(dataList)[0] || { goods: [] })
|
||||
|
||||
onLoad((options) => {
|
||||
masterId.value = options?.id || ''
|
||||
})
|
||||
|
||||
const queryList = async () => {
|
||||
try {
|
||||
const data = await apiStaffDetail({
|
||||
id: masterId.value
|
||||
})
|
||||
paging.value.complete([data])
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
|
@ -0,0 +1,148 @@
|
|||
<template>
|
||||
<view class="container">
|
||||
<view class="px-[24rpx] py-[14rpx] bg-white">
|
||||
<u-search
|
||||
v-model="keyword"
|
||||
placeholder="请输入关键词搜索"
|
||||
height="72"
|
||||
@search="search"
|
||||
@custom="search"
|
||||
@clear="queryList"
|
||||
></u-search>
|
||||
</view>
|
||||
|
||||
<view class="main bg-white mt-[20rpx]">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
:auto="true"
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<!-- 订单卡片 -->
|
||||
<navigator
|
||||
v-for="item in dataList"
|
||||
:key="item.id"
|
||||
:url="`/bundle/pages/master_worker_detail/index?id=${item.id}`"
|
||||
>
|
||||
<view class="flex master_worker_item col-start">
|
||||
<u-image
|
||||
:src="item.avatarUrl"
|
||||
width="100"
|
||||
height="100"
|
||||
border-radius="50%"
|
||||
></u-image>
|
||||
<view class="ml-[20rpx]">
|
||||
<view class="text-lg font-medium normal">{{ item.name }}</view>
|
||||
<view class="lighter text-sm mt-[10rpx]"
|
||||
>所在地区:{{ item.city }} - {{ item.district }}</view
|
||||
>
|
||||
<view class="lighter text-sm mt-[10rpx] truncate w-[580rpx]"
|
||||
>服务项目:{{ item.goodsList }}</view
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</z-paging>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { apiStaffLists } from '@/api/store'
|
||||
|
||||
// 页面状态
|
||||
const searchStatus = ref<boolean>(false)
|
||||
// 搜索关键字
|
||||
const keyword = ref<string | number>('')
|
||||
// 师傅列表数据
|
||||
const dataList = ref<any>([])
|
||||
// 下拉组件的Ref
|
||||
const paging = shallowRef<any>(null)
|
||||
|
||||
// 处理搜索
|
||||
const search = (flag: boolean) => {
|
||||
if (flag) {
|
||||
if (keyword.value !== '') searchStatus.value = true
|
||||
} else {
|
||||
keyword.value = ''
|
||||
searchStatus.value = false
|
||||
}
|
||||
paging.value?.reload()
|
||||
}
|
||||
|
||||
const queryList = async (pageNo: number, pageSize: number) => {
|
||||
try {
|
||||
const { lists } = await apiStaffLists({
|
||||
pageNo,
|
||||
pageSize,
|
||||
staffInfo: keyword.value
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 去师傅主页
|
||||
const toDetail = (id: number | string) => {
|
||||
uni.navigateTo({
|
||||
url: `/bundle/pages/master_worker_detail/index?id=${id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
// 头部搜索
|
||||
.search-box {
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
padding: 15rpx 24rpx;
|
||||
|
||||
.search {
|
||||
width: 620rpx;
|
||||
height: 100%;
|
||||
padding: 15rpx 30rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
// 输入框
|
||||
&--input {
|
||||
width: 84%;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
// 清空
|
||||
.clear {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: scroll;
|
||||
|
||||
// 师傅
|
||||
.master_worker_item {
|
||||
padding: 30rpx 24rpx 28rpx 24rpx;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,136 @@
|
|||
<template>
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<block v-for="item in dataList" :key="item.id">
|
||||
<coupon-card :item="item">
|
||||
<template #use="{ row }">
|
||||
<view class="btn__link" v-if="condition">
|
||||
<text @click.stop="handleNavigate(row)">立即使用</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:status>
|
||||
<view class="btn__link" v-if="!condition">
|
||||
<text>{{ parseText }}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template #count>
|
||||
<text class="count" v-if="item.getNum > 1">
|
||||
已领取{{ item.alreadyGetCouponCount }}张
|
||||
</text>
|
||||
</template>
|
||||
<template #expiringSoon>
|
||||
<div class="expiring-soon" v-if="showExpiringSoon(item!)">快失效</div>
|
||||
</template>
|
||||
</coupon-card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject, computed, unref, watch } from 'vue'
|
||||
import { useZPaging } from '@/hooks/useCoupon'
|
||||
import { COUPON_INJECTION_KEY } from '@/config/symbol'
|
||||
import { apiMyCouponList } from '@/api/coupon'
|
||||
import {
|
||||
CouponEnum,
|
||||
CouponStatusMap,
|
||||
NavigateMap,
|
||||
UseGoodsTypeEnum,
|
||||
CouponStatusEnum,
|
||||
showExpiringSoonDays
|
||||
} from '@/enums/appEnums'
|
||||
import { router } from '@/utils/util'
|
||||
|
||||
const parent = inject(COUPON_INJECTION_KEY)
|
||||
|
||||
const dataList = ref<any>([])
|
||||
|
||||
const tabStatus = computed(() => parent?.tabStatus.value) as any
|
||||
const condition = computed(() => unref(tabStatus) === CouponEnum.NO_APPLY)
|
||||
const parseText = computed(() => CouponStatusMap[unref(tabStatus)])
|
||||
const showExpiringSoon = computed(() => {
|
||||
return (row: any) => {
|
||||
const { useTimeEnd, useStatus } = row
|
||||
const currentTime = new Date().getTime()
|
||||
const endTime = Date.parse(useTimeEnd)
|
||||
const daysDifference = Math.abs(endTime - currentTime) / (60 * 60 * 24 * 1000)
|
||||
return daysDifference <= showExpiringSoonDays && useStatus == CouponStatusEnum.AVALIABLE
|
||||
}
|
||||
})
|
||||
|
||||
/**优惠券列表参数 */
|
||||
const config = {
|
||||
initParams: {
|
||||
useStatus: unref(tabStatus)
|
||||
},
|
||||
requestApi: apiMyCouponList
|
||||
}
|
||||
|
||||
const { paging, queryList, refresh } = useZPaging(config.initParams, config.requestApi!)
|
||||
|
||||
/**
|
||||
* 1、通用券:跳转到首页
|
||||
* 2、品类券:对应品类页面
|
||||
* 3、商品券:对应商品页面
|
||||
*/
|
||||
const handleNavigate = (row: any) => {
|
||||
const { useGoodsType } = row
|
||||
const url = NavigateMap[useGoodsType]
|
||||
router(joinUrl(url, row))
|
||||
}
|
||||
|
||||
watch(
|
||||
() => unref(tabStatus),
|
||||
() => {
|
||||
const params = { useStatus: unref(tabStatus) }
|
||||
refresh({ ...params })
|
||||
}
|
||||
)
|
||||
|
||||
function joinUrl(url: string, row: any) {
|
||||
const { useGoodsType, id } = row
|
||||
let params = ''
|
||||
params = useGoodsType === UseGoodsTypeEnum.ALL ? '' : `?id=${id}`
|
||||
return url + params
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.btn__link {
|
||||
background-color: $white;
|
||||
color: $blue1;
|
||||
padding: 6rpx 16rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 999px;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.status {
|
||||
position: absolute;
|
||||
top: 6rpx;
|
||||
right: -78rpx;
|
||||
padding: 10rpx 80rpx;
|
||||
background-color: $gray1;
|
||||
color: $white;
|
||||
transform: rotate(45deg);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.expiring-soon {
|
||||
position: absolute;
|
||||
background: #d83d3b;
|
||||
width: 162rpx;
|
||||
height: 32rpx;
|
||||
rotate: 26deg;
|
||||
top: -4px;
|
||||
right: -11px;
|
||||
font-size: 19rpx;
|
||||
line-height: 32rpx;
|
||||
padding-left: 53rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<tabs
|
||||
:current="setTabIndex"
|
||||
v-bind="tabStyle"
|
||||
:is-scroll="false"
|
||||
:auth="true"
|
||||
@change="handleChangeTab"
|
||||
>
|
||||
<tab v-for="(item, i) in optionMap.couponStatus" :key="i" :name="item.name" />
|
||||
<view class="List">
|
||||
<list />
|
||||
</view>
|
||||
<view class="header-box">
|
||||
<button class="header-btn" @click.stop="handleNavigateCouponCenter">
|
||||
<u-icon name="coupon" size="36" color="text-content" class="mr-2"></u-icon>
|
||||
获取更多好券 >
|
||||
</button>
|
||||
</view>
|
||||
</tabs>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { provide, computed } from 'vue'
|
||||
import list from './components/list.vue'
|
||||
import { useTabs } from '@/hooks/useCoupon'
|
||||
import { COUPON_INJECTION_KEY } from '@/config/symbol'
|
||||
import { optionMap } from '@/config'
|
||||
import { router } from '@/utils/util'
|
||||
|
||||
const { tabStatus, setTabIndex, handleChangeTab } = useTabs()
|
||||
|
||||
const tabStyle = computed(() => {
|
||||
return {
|
||||
height: 80,
|
||||
barWidth: 60,
|
||||
bgColor: '#06a9ff',
|
||||
inactiveColor: '#fff',
|
||||
activeColor: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
/**点击获取更多好券跳转 */
|
||||
const handleNavigateCouponCenter = () => {
|
||||
router('/bundle/pages/coupon/index')
|
||||
}
|
||||
|
||||
provide(COUPON_INJECTION_KEY, {
|
||||
tabStatus
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.List {
|
||||
position: relative;
|
||||
height: calc(100vh - 80rpx - env(safe-area-inset-bottom));
|
||||
padding-bottom: 15rpx;
|
||||
padding-top: 100rpx;
|
||||
}
|
||||
|
||||
.header-box {
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 80rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: $white;
|
||||
padding: 20rpx 20rpx 0;
|
||||
|
||||
.header-btn {
|
||||
@apply bg-[#FAE8E7] text-lg text-[#D83D3B] leading-[82rpx] h-[82rpx] rounded-xl font-bold flex justify-center;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,295 @@
|
|||
<template>
|
||||
<view class="wrapper">
|
||||
<view @click="handleToggle">
|
||||
<u-notice-bar
|
||||
mode="horizontal"
|
||||
:list="noticeList"
|
||||
:duration="1500"
|
||||
v-bind="setNoticeBarBgColor"
|
||||
></u-notice-bar>
|
||||
</view>
|
||||
<view class="card__money">
|
||||
<view class="total__money">
|
||||
<text>已提现收益</text>
|
||||
<text class="total">{{ parseMoney('alreadyWithdraw') }}</text>
|
||||
</view>
|
||||
<view class="btn" @click="handleNavigateWithDraw">去提现</view>
|
||||
<view class="part__money">
|
||||
<view class="freeze">
|
||||
<text>待结算收益</text>
|
||||
<text class="price">{{ parseMoney('toBeSettledMoney') }}</text>
|
||||
</view>
|
||||
<view class="withdraw">
|
||||
<text>可提现收益</text>
|
||||
<text class="price">{{ parseMoney('canWithdrawCommission') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="card__usercount">
|
||||
<view>
|
||||
<text class="title">总分销用户</text>
|
||||
<text>{{ distributorInfo.totalDistributorUser }}人</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="title">今日分销用户</text>
|
||||
<text>{{ distributorInfo.toDayDistributorUser }}人</text>
|
||||
</view>
|
||||
</view> -->
|
||||
<view class="card__cell">
|
||||
<u-cell-group>
|
||||
<u-cell-item
|
||||
class="cell-item"
|
||||
v-for="cell in cellGroup"
|
||||
:key="cell.title"
|
||||
:title="cell.title"
|
||||
@click="cell.event(cell.field)"
|
||||
></u-cell-item>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<view class="share-btn">
|
||||
<button class="share" open-type="share">分享链接</button>
|
||||
</view>
|
||||
</view>
|
||||
<u-modal v-model="show">
|
||||
<view class="slot-content">
|
||||
<view>
|
||||
在有效期内,新用户或公池中的用户扫店铺码或通过店铺链接进入小程序可以和店铺信息相互绑定。
|
||||
</view>
|
||||
<view>
|
||||
有效期截止之后,新用户或公池中的用户扫店铺码或通过店铺链接进入小程序不再和店铺信息绑定。
|
||||
</view>
|
||||
</view>
|
||||
</u-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, unref, onActivated } from 'vue'
|
||||
import { onShareAppMessage, onShow } from '@dcloudio/uni-app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useLockFn, useToggle } from '@/hooks/useLockFn'
|
||||
import { getDistributorCenter } from '@/api/distributor_center'
|
||||
import { formatMoney, navigateTo, router } from '@/utils/util'
|
||||
import { Recordable } from '@/config/interface'
|
||||
|
||||
interface DistributorProps {
|
||||
[key: string]: number | string | undefined
|
||||
alreadyWithdraw: number
|
||||
canWithdrawCommission: number
|
||||
toBeSettledMoney: number
|
||||
toDayDistributorUser: number
|
||||
totalDistributorUser: number
|
||||
distributorEndTime: string
|
||||
}
|
||||
const cellGroup = [
|
||||
{ title: '店铺订单', field: 'order', event: handleNavigate },
|
||||
{ title: '店铺客户', field: 'user', event: handleNavigate },
|
||||
{ title: '收益记录', field: 'record', event: handleNavigate },
|
||||
{ title: '店铺码', field: 'share', event: handleNavigate }
|
||||
]
|
||||
const navigateUrls: Recordable<string> = {
|
||||
order: '/bundle/pages/distributor_order/index',
|
||||
user: '/bundle/pages/distributor_binding_user/index',
|
||||
withdraw: '/bundle/pages/withdraw_money/index',
|
||||
record: '/bundle/pages/withdraw_distributor_record/index',
|
||||
share: '/bundle/pages/share_qrcode/index'
|
||||
}
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
const { show, handleToggle } = useToggle()
|
||||
|
||||
const distributorInfo = reactive<Partial<DistributorProps>>({})
|
||||
|
||||
const compareDistributorTime = computed(
|
||||
() =>
|
||||
Date.now() / 1000 >=
|
||||
new Date(`${distributorInfo.distributorEndTime?.replace(/\-/g, '/')}`).getTime() / 1000
|
||||
)
|
||||
const noticeList = computed(() =>
|
||||
unref(compareDistributorTime)
|
||||
? ['店铺码已过期,如需再次成为店长,请联系客服!!!']
|
||||
: [`店铺码有效期截至时间:${distributorInfo.distributorEndTime}`]
|
||||
)
|
||||
const setNoticeBarBgColor = computed(() => ({
|
||||
bgColor: unref(compareDistributorTime) ? '#F56C6C' : '#fdf6ec',
|
||||
color: unref(compareDistributorTime) ? '#fff' : '#f9ae3d'
|
||||
}))
|
||||
|
||||
const parseMoney = computed(() => {
|
||||
return (field: string) => {
|
||||
return '¥' + formatMoney(distributorInfo[field])
|
||||
}
|
||||
})
|
||||
/**去提现 */
|
||||
const handleNavigateWithDraw = () => {
|
||||
router(`/bundle/pages/withdraw_money/index`)
|
||||
}
|
||||
|
||||
/**跳转地址 */
|
||||
function handleNavigate(type: string) {
|
||||
router(navigateUrls[type])
|
||||
}
|
||||
/**分享给好友 */
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
path: `/pages/index/index?scene=${unref(userInfo).distributorId}`,
|
||||
imageUrl: '../../../static/images/index.jpg'
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
lockFn()
|
||||
})
|
||||
|
||||
const { lockFn } = useLockFn(async () => {
|
||||
const data = await getDistributorCenter({ distributorId: unref(userInfo).distributorId })
|
||||
initDistributorInfo(data)
|
||||
})
|
||||
|
||||
/**设置数据 */
|
||||
function initDistributorInfo(data: DistributorProps) {
|
||||
for (const key in data) {
|
||||
distributorInfo[key] = data[key]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card__money {
|
||||
padding: 40rpx;
|
||||
background-image: linear-gradient(to right, $blue3 0%, $blue4 100%);
|
||||
color: $white;
|
||||
line-height: 1.6;
|
||||
border-radius: 20rpx;
|
||||
margin: 20rpx;
|
||||
position: relative;
|
||||
|
||||
.total__money {
|
||||
@include flex-column;
|
||||
position: relative;
|
||||
|
||||
.total {
|
||||
font-size: 46rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.withdraw {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: -40rpx;
|
||||
top: 0;
|
||||
background-color: pink;
|
||||
color: #2f63fe;
|
||||
padding: 12rpx 30rpx;
|
||||
border-top-left-radius: 999px;
|
||||
border-bottom-left-radius: 999px;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.part__money {
|
||||
@include flex;
|
||||
margin-top: 40rpx;
|
||||
|
||||
> view {
|
||||
flex: 1;
|
||||
@include flex-column;
|
||||
|
||||
.price {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card__usercount {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
margin: 20rpx;
|
||||
|
||||
> view {
|
||||
@include flex-center;
|
||||
padding: 30rpx;
|
||||
background-color: $white;
|
||||
border-radius: 12rpx;
|
||||
line-height: 1.8;
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card__cell {
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.cell-item) {
|
||||
.u-border-bottom {
|
||||
padding: 40rpx 32rpx !important;
|
||||
|
||||
.u-cell_title {
|
||||
font-size: 32rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(60rpx + env(safe-area-inset-bottom));
|
||||
padding: 0 30rpx;
|
||||
|
||||
.share {
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
background-color: $blue1;
|
||||
color: $white;
|
||||
border-radius: 999px;
|
||||
}
|
||||
}
|
||||
|
||||
.share {
|
||||
// position: absolute;
|
||||
// right: 0;
|
||||
// bottom: -140rpx;
|
||||
// background-color: $blue1;
|
||||
// color: $white;
|
||||
// border-top-left-radius: 999px;
|
||||
// border-bottom-left-radius: 999px;
|
||||
// font-size: 32rpx;
|
||||
}
|
||||
|
||||
.slot-content {
|
||||
padding: 0 40rpx;
|
||||
line-height: 1.8;
|
||||
|
||||
> view {
|
||||
margin: 30rpx 0;
|
||||
color: $red;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
right: 0;
|
||||
padding: 12rpx 40rpx;
|
||||
background-color: #457ded;
|
||||
border-radius: 999px 0 0 999px;
|
||||
border: 1px solid #fff;
|
||||
border-right: none;
|
||||
box-sizing: border-box;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar
|
||||
:background="{
|
||||
background: `url(${getImageUrl(img)}) no-repeat center center`,
|
||||
backgroundSize: 'cover'
|
||||
}"
|
||||
height="210"
|
||||
class="navbar"
|
||||
back-icon-color="#fff"
|
||||
/>
|
||||
<view class="List">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="couponLists"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<view class="px-[30rpx]" v-for="item in couponLists" :key="item.id">
|
||||
<coupon-card :item="item" />
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { getDecorate } from '@/api/shop'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
// import { apiCouponList } from '@/api/coupon'
|
||||
|
||||
const { getImageUrl } = useAppStore()
|
||||
|
||||
// 获取装修信息
|
||||
const img = ref('')
|
||||
const getData = async () => {
|
||||
const data = await getDecorate({ id: 6 })
|
||||
const obj = JSON.parse(data.data)[0].content
|
||||
switch (obj.enabled) {
|
||||
case 1:
|
||||
img.value = obj.defaultImg
|
||||
break
|
||||
case 2:
|
||||
img.value = obj.customImg
|
||||
break
|
||||
}
|
||||
}
|
||||
getData()
|
||||
|
||||
// 获取优惠券列表
|
||||
const paging = shallowRef<any>(null)
|
||||
const couponLists = ref<any>([])
|
||||
const queryList = async (page_no = 1, page_size = 10) => {
|
||||
try {
|
||||
const { lists } = await apiCouponList({
|
||||
page_no,
|
||||
page_size
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
:deep(.u-back-wrap) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
.List {
|
||||
height: calc(100vh - 420rpx - env(safe-area-inset-bottom));
|
||||
padding: 30rpx 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<view class="user-recharge">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<template v-for="(item, index) in dataList" :key="index">
|
||||
<view class="wrapper">
|
||||
<view class="left">
|
||||
<view class="title text-lg">余额充值</view>
|
||||
<view class="time">{{ item.createTime }}</view>
|
||||
</view>
|
||||
<view class="right"> +{{ item.orderAmount }} </view>
|
||||
</view>
|
||||
</template>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { apiRechargeLogLists } from '@/api/wallet'
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref<any>([])
|
||||
const queryList = async (page_no: number, page_size: number) => {
|
||||
try {
|
||||
const { lists } = await apiRechargeLogLists({
|
||||
page_no,
|
||||
page_size,
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-recharge {
|
||||
height: calc(100vh - env(safe-area-inset-bottom));
|
||||
padding-top: 15rpx;
|
||||
.wrapper {
|
||||
padding: 20rpx 21rpx;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-top: 1px solid rgba(234, 234, 234, 1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.left {
|
||||
.time {
|
||||
margin-top: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
color: #ff2c3c;
|
||||
font-size: 38rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<view class="bg-white suggest">
|
||||
<!-- 热门搜索 -->
|
||||
<view class="hot" v-if="hot_search.length">
|
||||
<view class="font-medium pl-[24rpx] pt-[26rpx] pb-[6rpx] text-lg">热门搜索</view>
|
||||
|
||||
<view class="w-full px-[24rpx]">
|
||||
<block v-for="(hotItem, index) in hot_search" :key="index">
|
||||
<view class="max-w-full truncate keyword" @click="handleHistoreSearch(hotItem)"
|
||||
>{{ hotItem }}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <view class="mx-[24rpx] my-[40rpx]" v-if="hot_search.length && his_search.length"></view> -->
|
||||
|
||||
<!-- 历史搜索 -->
|
||||
<view class="history" v-if="his_search.length">
|
||||
<view class="flex justify-between px-[24rpx] pb-[6rpx] pt-[26rpx]">
|
||||
<view class="text-lg font-medium">历史搜索</view>
|
||||
<view class="text-xs text-muted" @click="() => emit('clear')">清空</view>
|
||||
</view>
|
||||
|
||||
<view class="w-full px-[24rpx]">
|
||||
<block v-for="(hisItem, index) in his_search" :key="index">
|
||||
<view class="truncate keyword" @click="handleHistoreSearch(hisItem)">{{
|
||||
hisItem
|
||||
}}</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'search', value: string): void
|
||||
(event: 'clear', value: void): void
|
||||
}>()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
hot_search?: any[]
|
||||
his_search?: string[]
|
||||
}>(),
|
||||
{
|
||||
hot_search: [],
|
||||
his_search: []
|
||||
}
|
||||
)
|
||||
|
||||
const handleHistoreSearch = (text: string) => {
|
||||
emit('search', text)
|
||||
}
|
||||
onMounted(() => {
|
||||
console.log('props', props.hot_search)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.suggest {
|
||||
height: 100%;
|
||||
|
||||
.keyword {
|
||||
display: inline-block;
|
||||
margin: 24rpx 16rpx 0 0;
|
||||
padding: 8rpx 24rpx;
|
||||
border-radius: 26rpx;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,141 @@
|
|||
<template>
|
||||
<view class="search">
|
||||
<!-- 搜索框 -->
|
||||
<view class="px-[24rpx] py-[14rpx] bg-white">
|
||||
<u-search
|
||||
v-model="keyword"
|
||||
placeholder="请输入关键词搜索"
|
||||
height="72"
|
||||
@search="handleSearch"
|
||||
@custom="handleSearch"
|
||||
@clear="search.searching = false"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 搜索 -->
|
||||
<view class="search-content">
|
||||
<!-- -->
|
||||
<suggest
|
||||
v-show="!search.searching"
|
||||
@search="handleSearch"
|
||||
@clear="handleClear"
|
||||
:hot_search="search.hot_search"
|
||||
:his_search="search.his_search"
|
||||
/>
|
||||
|
||||
<!-- -->
|
||||
<view class="search-content-s pt-[20rpx]" v-show="search.searching">
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="search.result"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<view class="bg-white px-[24rpx]">
|
||||
<goods-card :goodsList="search.result" type="list" />
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, shallowRef, watch, nextTick } from 'vue'
|
||||
import Suggest from './component/suggest.vue'
|
||||
import { HISTORY } from '@/enums/cacheEnums'
|
||||
import { getHotSearch, apiGoodsLists } from '@/api/store'
|
||||
import cache from '@/utils/cache'
|
||||
import GoodsCard from '@/components/goods-card/index.vue'
|
||||
|
||||
interface Search {
|
||||
hot_search: string[]
|
||||
his_search: string[]
|
||||
result: any
|
||||
searching: boolean
|
||||
}
|
||||
|
||||
const search = reactive<Search>({
|
||||
hot_search: [],
|
||||
his_search: [],
|
||||
result: [],
|
||||
searching: false
|
||||
})
|
||||
const keyword = ref<string>('')
|
||||
const paging = shallowRef()
|
||||
|
||||
watch(
|
||||
() => keyword.value,
|
||||
val => {
|
||||
nextTick(() => {
|
||||
if (!val) {
|
||||
search.searching = false
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
keyword.value = value
|
||||
if (keyword.value) {
|
||||
if (!search.his_search.includes(keyword.value)) {
|
||||
search.his_search.unshift(keyword.value)
|
||||
cache.set(HISTORY, search.his_search)
|
||||
}
|
||||
}
|
||||
paging.value.reload()
|
||||
search.searching = true
|
||||
}
|
||||
|
||||
const getHotSearchFunc = async () => {
|
||||
try {
|
||||
const data = await getHotSearch()
|
||||
search.hot_search = data
|
||||
} catch (e) {
|
||||
//TODO handle the exception
|
||||
console.log('获取热门搜索失败=>', e)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClear = async (): Promise<void> => {
|
||||
const resModel: any = await uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '是否清空历史记录?'
|
||||
})
|
||||
if (resModel.confirm) {
|
||||
cache.set(HISTORY, '')
|
||||
search.his_search = []
|
||||
}
|
||||
}
|
||||
|
||||
const queryList = async (pageNo: number, pageSize: number) => {
|
||||
try {
|
||||
const { lists } = await apiGoodsLists({
|
||||
name: keyword.value,
|
||||
pageNo,
|
||||
pageSize
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
getHotSearchFunc()
|
||||
search.his_search = cache.get(HISTORY) || []
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search {
|
||||
&-content {
|
||||
height: calc(100vh - 46px - env(safe-area-inset-bottom));
|
||||
|
||||
&-s {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,60 @@
|
|||
<!--
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-08-23 17:14:02
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-09-21 21:04:33
|
||||
* @FilePath: \housekeeping-uniapp\src\bundle\pages\service_explain\index.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<view class="main">
|
||||
<view class="my-3 text-3xl font-bold flex justify-center">
|
||||
<view class="title">粤好生活服务说明</view>
|
||||
</view>
|
||||
<u-image
|
||||
src="@/static/images/service/1.jpg"
|
||||
width="100%"
|
||||
height="560"
|
||||
class="image"
|
||||
mode="scaleToFill"
|
||||
></u-image>
|
||||
<u-image
|
||||
src="@/static/images/service/2.jpg"
|
||||
width="100%"
|
||||
height="560"
|
||||
class="image"
|
||||
mode="scaleToFill"
|
||||
></u-image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fefefe;
|
||||
.image {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
background-image: linear-gradient(to right, #1b4655, #675fe9);
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
position: absolute;
|
||||
background: $u-type-primary;
|
||||
bottom: -3px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<view class="card" @click.stop="goPage">
|
||||
<view class="card--header flex justify-between">
|
||||
<view class="order-sn">订单编号:{{ orderInfo.sn }}</view>
|
||||
<view class="status">{{ getStatueName(orderInfo) }}</view>
|
||||
</view>
|
||||
|
||||
<view class="card--main flex">
|
||||
<u-image
|
||||
:src="orderInfo.orderGoodsDetailVo.imageUrl"
|
||||
width="160"
|
||||
height="160"
|
||||
border-radius="16"
|
||||
></u-image>
|
||||
<view class="ml-[20rpx] service-text">
|
||||
<view class="service-text--name truncate">
|
||||
{{ orderInfo.orderGoodsDetailVo.goodsName }}
|
||||
</view>
|
||||
<view class="appointTitle mt-[16rpx]">
|
||||
预约: {{ orderInfo.appointTime }} {{ orderInfo.weekDay }}
|
||||
{{ orderInfo.appointTimeStart }} - {{ orderInfo.appointTimeEnd }}
|
||||
</view>
|
||||
<view class="appointTitle mt-[16rpx]"></view>
|
||||
<view class="mt-[16rpx]">
|
||||
实付金额:
|
||||
<text class="text-[#f36161] text-base pl-1.5">
|
||||
¥{{ orderInfo.orderAmount }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card--footer flex justify-between">
|
||||
<view class="text-primary">
|
||||
<template v-if="orderInfo.cancelTime">
|
||||
<view class="flex" v-if="timeStamp >= 0">
|
||||
<u-count-down
|
||||
:timestamp="timeStamp"
|
||||
format="mm:ss"
|
||||
:font-size="26"
|
||||
:separator-size="26"
|
||||
@end="timeStamp = 0"
|
||||
/>
|
||||
<text class="ml-[10rpx]">后自动取消</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watchEffect } from 'vue'
|
||||
import { apiOrderClose } from '@/api/order'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
orderInfo?: any // 底部
|
||||
}>(),
|
||||
{
|
||||
orderInfo: {}
|
||||
}
|
||||
)
|
||||
|
||||
const timeStamp = ref<number | null>(0)
|
||||
watchEffect(() => {
|
||||
// 获取倒计时段
|
||||
const endTimestamp = props.orderInfo.order_cancel_time
|
||||
const startTimestamp = new Date().getTime() / 1000
|
||||
const time = (endTimestamp - startTimestamp) * 1000
|
||||
timeStamp.value = time
|
||||
if (timeStamp.value === 0) orderClose()
|
||||
})
|
||||
|
||||
// 超时关闭订单
|
||||
const orderClose = async () => {
|
||||
await apiOrderClose({
|
||||
outTradeNo: props.orderInfo.id
|
||||
})
|
||||
}
|
||||
|
||||
const goPage = () => {
|
||||
uni.navigateTo({
|
||||
url: `/bundle/pages/service_order_detail/index?id=${props.orderInfo.id}`
|
||||
})
|
||||
}
|
||||
const getStatueName = orderInfo => {
|
||||
// 1预约中且已派单,师傅这里显示 已接单
|
||||
if (orderInfo.orderStatus == 1 && orderInfo.isDispatch == 1) {
|
||||
return '已接单'
|
||||
}
|
||||
|
||||
return orderInfo.orderStatusName
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-radius: 14rpx;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx 20rpx 0 20rpx;
|
||||
|
||||
&--header {
|
||||
padding: 24rpx 30rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
// border-bottom: 1px solid #e5e5e5;
|
||||
.order-sn {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.status {
|
||||
color: #f36161;
|
||||
}
|
||||
}
|
||||
|
||||
&--main {
|
||||
padding: 30rpx;
|
||||
color: #555555;
|
||||
font-size: 26rpx;
|
||||
|
||||
.service-text {
|
||||
&--name {
|
||||
width: 460rpx;
|
||||
font-weight: 500;
|
||||
color: #222222;
|
||||
font-size: 32rpx;
|
||||
// line-1
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--footer {
|
||||
padding: 20rpx 30rpx;
|
||||
font-size: 26rpx;
|
||||
// border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,101 @@
|
|||
<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">
|
||||
<order-card :orderInfo="item">
|
||||
<order-footer
|
||||
:orderId="item?.id"
|
||||
:mobile="item?.mobile"
|
||||
:staffId="item.staffId"
|
||||
:orderStatus="item.orderStatus"
|
||||
:cancel="item.cancelBtn"
|
||||
:evaluate="item.commentBtn"
|
||||
:contact="item.contactBtn"
|
||||
:pay="item.pay_btn"
|
||||
:confirmService="item.serviceBtn"
|
||||
:verification="item.verificationBtn"
|
||||
:goodsImage="item.orderGoodsDetailVo.imageUrl"
|
||||
:goodsName="item.orderGoodsDetailVo.goodsName"
|
||||
:type="2"
|
||||
:appointTime="item.appointTime"
|
||||
:appointTimeStartStr="item.appointTimeStart"
|
||||
:isDispatch="item.isDispatch"
|
||||
@refresh="queryList"
|
||||
/>
|
||||
</order-card>
|
||||
</block>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, shallowRef, unref } from 'vue'
|
||||
import orderCard from './order-card.vue'
|
||||
import orderFooter from '@/components/order-footer/index.vue'
|
||||
import { apiStaffOrderLists } from '@/api/order'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
orderStatus: number | string
|
||||
i: number
|
||||
index: number
|
||||
refresh: boolean
|
||||
}>(),
|
||||
{
|
||||
orderStatus: '',
|
||||
refresh: false
|
||||
}
|
||||
)
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref<any>([])
|
||||
const isFirst = ref<boolean>(true)
|
||||
|
||||
watch(
|
||||
() => props.index,
|
||||
async () => {
|
||||
await nextTick()
|
||||
if (props.i == props.index && unref(isFirst)) {
|
||||
isFirst.value = false
|
||||
paging.value?.reload()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.refresh,
|
||||
val => {
|
||||
console.log(val, 'val')
|
||||
val && paging.value?.reload()
|
||||
}
|
||||
)
|
||||
|
||||
const queryList = async (pageNo = 1, pageSize = 10) => {
|
||||
try {
|
||||
const { lists } = await apiStaffOrderLists({
|
||||
orderStatus: props.orderStatus,
|
||||
pageNo,
|
||||
pageSize
|
||||
})
|
||||
if (paging.value) {
|
||||
paging.value.complete(lists)
|
||||
} else {
|
||||
queryList()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,111 @@
|
|||
<!--
|
||||
* @Author: 王翠碧 1254597151@qq.com
|
||||
* @Date: 2023-05-22 22:41:29
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-09-07 19:09:50
|
||||
* @FilePath: \uniapp\src\bundle\pages\service_order\index.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<view class="order-list">
|
||||
<!-- :auth="true" 是表示需要权限登录的 -->
|
||||
<tabs
|
||||
:current="current"
|
||||
@change="handleChange"
|
||||
height="80"
|
||||
bar-width="60"
|
||||
:font-size="32"
|
||||
:barStyle="{ bottom: '0' }"
|
||||
:auth="true"
|
||||
activeColor="#1296DB"
|
||||
>
|
||||
<tab v-for="(item, i) in tabList" :key="i" :name="item.name">
|
||||
<view class="orderList pt-[20rpx]" v-if="isLogin">
|
||||
<orderList
|
||||
:orderStatus="item.orderStatus"
|
||||
:i="i"
|
||||
:index="current"
|
||||
:refresh="item.refresh"
|
||||
></orderList>
|
||||
</view>
|
||||
</tab>
|
||||
</tabs>
|
||||
|
||||
<tabbar />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import orderList from './components/order-list.vue'
|
||||
import tab from '@/components/tab/tab.vue'
|
||||
import tabs from '@/components/tabs/tabs.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 是否登录
|
||||
const isLogin = computed(() => userStore.token)
|
||||
|
||||
const tabList = ref<any>([
|
||||
{
|
||||
name: '全部',
|
||||
orderStatus: '',
|
||||
refresh: false
|
||||
},
|
||||
{
|
||||
name: '预约中',
|
||||
orderStatus: 1,
|
||||
refresh: false
|
||||
},
|
||||
{
|
||||
name: '服务中',
|
||||
orderStatus: 2,
|
||||
refresh: false
|
||||
},
|
||||
{
|
||||
name: '已完成',
|
||||
orderStatus: 3,
|
||||
refresh: false
|
||||
},
|
||||
{
|
||||
name: '已取消',
|
||||
orderStatus: 4,
|
||||
refresh: false
|
||||
}
|
||||
])
|
||||
const current = ref<number>(0)
|
||||
const handleChange = (index: number) => {
|
||||
current.value = Number(index)
|
||||
tabList.value[index].refresh = true
|
||||
setTimeout(() => {
|
||||
tabList.value[index].refresh = false
|
||||
}, 200)
|
||||
}
|
||||
|
||||
onLoad(async (options: { type?: any }) => {
|
||||
current.value = options?.type * 1 || 0
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: scroll;
|
||||
swiper {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.orderList {
|
||||
height: calc(100vh - 86px - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,261 @@
|
|||
<template>
|
||||
<uni-transition mode-class="zoom-in" needLayout="true" :show="orderData" :duration="500">
|
||||
<view class="order_detail">
|
||||
<!-- Main Start -->
|
||||
<view class="flex pb-[30rpx]">
|
||||
<image
|
||||
v-if="orderData.orderStatus === 0"
|
||||
class="header-image"
|
||||
src="/static/images/icon_pay.png"
|
||||
/>
|
||||
<image
|
||||
v-if="orderData.orderStatus === 1 || orderData.orderStatus === 2"
|
||||
class="header-image"
|
||||
src="/static/images/icon_wait.png"
|
||||
/>
|
||||
<image
|
||||
v-if="orderData.orderStatus === 3"
|
||||
class="header-image"
|
||||
src="/static/images/icon_success.png"
|
||||
/>
|
||||
<image
|
||||
v-if="orderData.orderStatus === 4"
|
||||
class="header-image"
|
||||
src="@/static/images/icon_close.png"
|
||||
/>
|
||||
<text class="statusDec ml-[15rpx]">{{ orderData.orderStatusName }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 地址卡片 -->
|
||||
<view class="card">
|
||||
<view class="card--header" @click="openLocation">
|
||||
<view class="title">{{ orderData.contact }} {{ orderData.mobile }}</view>
|
||||
</view>
|
||||
<view class="text-sm text-muted">
|
||||
{{ orderData.province }}
|
||||
{{ orderData.city }}
|
||||
{{ orderData.district }}
|
||||
{{ orderData.address }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品卡片 -->
|
||||
<view class="card">
|
||||
<view class="goods-item">
|
||||
<u-image
|
||||
:src="orderData.orderGoods?.imageUrl"
|
||||
width="160"
|
||||
height="160"
|
||||
border-radius="4"
|
||||
/>
|
||||
<view class="ml-[20rpx] mt-[4rpx]">
|
||||
<view class="flex justify-between title">
|
||||
<view class="goods-item--name truncate">
|
||||
{{ orderData.orderGoods?.goodsName }}
|
||||
</view>
|
||||
<text>x{{ orderData.orderGoods?.goodsNum }}</text>
|
||||
</view>
|
||||
<view class="mt-[24rpx]">
|
||||
<price
|
||||
:price="orderData.orderGoods?.goodsPrice"
|
||||
:desc="orderData.orderGoods?.unitName"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 上门时间 -->
|
||||
<view class="card normal text-base flex justify-between">
|
||||
<view>上门时间</view>
|
||||
<view>
|
||||
{{ orderData.appointTime }} {{ orderData.weekDay }}
|
||||
{{ orderData.appointTimeStartStr }}-{{ orderData.appointTimeEndStr }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务金额 -->
|
||||
<view class="card normal text-base">
|
||||
<view class="flex justify-between">
|
||||
<view>服务金额</view>
|
||||
<view>¥{{ orderData.orderAmount }}</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx] flex justify-between" v-if="isUseCoupon">
|
||||
<view>抵扣金额</view>
|
||||
<view class="text-primary">¥{{ orderData.deductionMoney }}</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx] flex justify-between">
|
||||
<view>实付金额</view>
|
||||
<view class="text-primary">¥{{ orderData.orderAmount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备注 -->
|
||||
<view class="card flex justify-between flex-wrap normal text-base">
|
||||
<view>备注</view>
|
||||
<view style="width: 100%; word-wrap: break-word">{{ orderData.userRemark }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="card normal text-base">
|
||||
<view class="flex justify-between">
|
||||
<view>订单编号</view>
|
||||
<view>{{ orderData.sn }}</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx] flex justify-between">
|
||||
<view>支付方式</view>
|
||||
<view>{{ orderData.payWayName }}</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx] flex justify-between">
|
||||
<view>下单时间</view>
|
||||
<view>{{ orderData.createTime }}</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx] flex justify-between" v-if="orderData.orderStatus === 3">
|
||||
<view>完成时间</view>
|
||||
<view>{{ orderData.finishTime }}</view>
|
||||
</view>
|
||||
<view class="mt-[30rpx] flex justify-between" v-if="orderData.orderStatus === 4">
|
||||
<view>关闭时间</view>
|
||||
<view>{{ orderData.cancelTime }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- Main End -->
|
||||
</view>
|
||||
</uni-transition>
|
||||
<!-- Footer Start -->
|
||||
<view class="footer flex justify-end" v-if="orderData.serviceBtn || orderData.verificationBtn">
|
||||
<order-footer
|
||||
:orderId="orderData.id"
|
||||
:confirmService="orderData.serviceBtn"
|
||||
:verification="orderData.verificationBtn"
|
||||
:goodsName="orderData.orderGoods.goodsName"
|
||||
:goodsImage="orderData.orderGoods.imageUrl"
|
||||
:mobile="orderData.mobile"
|
||||
:appointTime="orderData.appointTime"
|
||||
:appointTimeStartStr="orderData.appointTimeStartStr"
|
||||
@refresh="initOrderDetail"
|
||||
/>
|
||||
</view>
|
||||
<!-- Footer End -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue'
|
||||
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { apiStaffOrderDetail } from '@/api/order'
|
||||
import Price from '@/components/price/index.vue'
|
||||
import OrderFooter from '@/components/order-footer/index.vue'
|
||||
|
||||
/** Data Start **/
|
||||
const orderData = ref<any>({
|
||||
order_goods: [],
|
||||
order_amount: '',
|
||||
total_amount: '',
|
||||
total_goods_price: ''
|
||||
})
|
||||
const orderId = ref<number | string>('')
|
||||
/** Data End **/
|
||||
|
||||
/** Methods Start **/
|
||||
// 初始化订单详情
|
||||
const initOrderDetail = async (): Promise<void> => {
|
||||
orderData.value = await apiStaffOrderDetail({ id: orderId.value })
|
||||
}
|
||||
/** Methods End **/
|
||||
const isUseCoupon = computed(() => unref(orderData.value.couponDetailVo))
|
||||
|
||||
/** Life Cycle Start **/
|
||||
onLoad(options => {
|
||||
// 获取订单ID
|
||||
orderId.value = parseInt(options?.id)
|
||||
|
||||
// 初始化订单信息
|
||||
initOrderDetail()
|
||||
})
|
||||
/** Life Cycle End **/
|
||||
|
||||
// 订单地址显示到地图上
|
||||
const openLocation = () => {
|
||||
// latitude: 23.18139
|
||||
// longitude: 113.48067
|
||||
console.log(
|
||||
'orderData.latitude:',
|
||||
orderData.value.latitude,
|
||||
'orderData.longitude:',
|
||||
orderData.value.longitude
|
||||
)
|
||||
uni.openLocation({
|
||||
latitude: parseFloat(orderData.value.latitude),
|
||||
longitude: parseFloat(orderData.value.longitude),
|
||||
success: function () {
|
||||
console.log('openLocation:success')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.order_detail {
|
||||
height: 100%;
|
||||
padding: 30rpx 24rpx;
|
||||
padding-bottom: 140rpx;
|
||||
background: linear-gradient(to bottom, #1296db 200rpx, transparent 0);
|
||||
|
||||
.statusDec {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
|
||||
.header-image {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
|
||||
&--header {
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #000;
|
||||
font-size: 28rpx;
|
||||
|
||||
.num {
|
||||
color: #666;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
display: flex;
|
||||
|
||||
&--name {
|
||||
width: 430rpx;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 108rpx;
|
||||
position: fixed;
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 2rpx 2rpx 22rpx rgba($color: #000, $alpha: 0.2);
|
||||
|
||||
.btn {
|
||||
width: 320rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,216 @@
|
|||
<template>
|
||||
<view class="qrcode">
|
||||
<view class="core">
|
||||
<view class="code">
|
||||
<image class="image" :src="src" :show-menu-by-longpress="true" v-if="src" />
|
||||
<u-loading :size="60" mode="flower" v-else />
|
||||
</view>
|
||||
<view>长按识别二维码</view>
|
||||
</view>
|
||||
<view class="operation">
|
||||
<view>
|
||||
<button class="part bg-white" open-type="share">
|
||||
<text class="icon share"></text>
|
||||
<text class="label">分享</text>
|
||||
</button>
|
||||
</view>
|
||||
<view>
|
||||
<button class="part-photo bg-white" :disabled="isDisabled" @click="saveToAlbum">
|
||||
<text class="icon download"></text>
|
||||
<text class="label">保存到相册</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, unref } from 'vue'
|
||||
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useLockFn } from '@/hooks/useLockFn'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { getDistributorQrcode } from '@/api/distributor_center'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
const src = ref('')
|
||||
const isDisabled = computed(() => !unref(src))
|
||||
|
||||
const { lockFn: fetchShareQrcode } = useLockFn(async () => {
|
||||
const base64Src = await getDistributorQrcode({ distributorId: unref(userInfo).distributorId })
|
||||
src.value = base64Src
|
||||
})
|
||||
onMounted(() => fetchShareQrcode())
|
||||
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
title: '粤好生活',
|
||||
path: `/pages/index/index?scene=${unref(userInfo).distributorId}`,
|
||||
imageUrl: '../../../static/images/index.jpg'
|
||||
}
|
||||
})
|
||||
const saveToAlbum = () => {
|
||||
uni.getSetting({
|
||||
success: res => {
|
||||
let authKey = ''
|
||||
// #ifdef MP-WEIXIN
|
||||
authKey = 'scope.writePhotosAlbum'
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-TOUTIAO
|
||||
authKey = 'scope.album'
|
||||
// #endif
|
||||
|
||||
// 无权限
|
||||
if (!res.authSetting[authKey]) {
|
||||
uni.authorize({
|
||||
scope: authKey,
|
||||
success: async () => {
|
||||
saveImg()
|
||||
},
|
||||
// 拒绝授权
|
||||
fail: () => {
|
||||
uni.showModal({
|
||||
title: '您已拒绝获取相册权限',
|
||||
content: '是否进入权限管理,调整授权',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
uni.openSetting({
|
||||
success: res => {
|
||||
res.authSetting[authKey]
|
||||
? saveImg()
|
||||
: uni.$u.toast('已取消')
|
||||
}
|
||||
})
|
||||
} else if (res.cancel) {
|
||||
uni.$u.toast('已取消')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 有权限
|
||||
saveImg()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/**保存图片 */
|
||||
async function saveImg() {
|
||||
try {
|
||||
const filePath = (await base64ToSave()) as string
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath,
|
||||
success: () => {
|
||||
uni.$u.toast('图片保存成功')
|
||||
removeSave()
|
||||
},
|
||||
fail: res => {
|
||||
console.log(res)
|
||||
|
||||
uni.$u.toast('图片保存失败')
|
||||
}
|
||||
})
|
||||
} catch (error) {}
|
||||
}
|
||||
/**base64转图片 */
|
||||
function base64ToSave() {
|
||||
const fsm = uni.getFileSystemManager()
|
||||
return new Promise((resolve, reject) => {
|
||||
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(unref(src)) || []
|
||||
if (!format) {
|
||||
reject(new Error('图片格式错误'))
|
||||
}
|
||||
const filePath = wx.env.USER_DATA_PATH + '/img.jpg'
|
||||
const buffer = uni.base64ToArrayBuffer(bodyData)
|
||||
fsm.writeFile({
|
||||
filePath,
|
||||
data: buffer,
|
||||
encoding: 'base64',
|
||||
success: () => {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail: () => {
|
||||
reject(new Error('图片写入失败'))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
/**删除文件 */
|
||||
function removeSave(format = 'jpeg') {
|
||||
return new Promise(resolve => {
|
||||
const fsm = uni.getFileSystemManager()
|
||||
const filePath = wx.env.USER_DATA_PATH + '/img.jpg'
|
||||
fsm.unlink({
|
||||
filePath,
|
||||
success: () => {
|
||||
console.log('删除成功')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.qrcode {
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
.core {
|
||||
@include center;
|
||||
flex-direction: column;
|
||||
padding: 40rpx 0;
|
||||
background-color: $white;
|
||||
|
||||
.code {
|
||||
@include center;
|
||||
width: 500rpx;
|
||||
height: 500rpx;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operation {
|
||||
@include center;
|
||||
|
||||
> view {
|
||||
flex: 1;
|
||||
@include flex-center;
|
||||
margin-top: 40rpx;
|
||||
|
||||
.icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.part {
|
||||
@include flex-column;
|
||||
|
||||
.share {
|
||||
background-image: url('~@/static/images/share-friend.png');
|
||||
}
|
||||
}
|
||||
|
||||
.part-photo {
|
||||
@include flex-center;
|
||||
|
||||
.download {
|
||||
background-image: url('~@/static/images/download.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,240 @@
|
|||
<template>
|
||||
<!-- 收货地址空状态 -->
|
||||
<!-- 收货地址的列表数据 -->
|
||||
<view class="address-list">
|
||||
<view v-if="!type" class="text-error fixed top-0 left-0 pl-[20rpx] pt-[20rpx]">
|
||||
点击地址,将该地址设为默认地址
|
||||
</view>
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="addressList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
use-page-scroll
|
||||
height="100%"
|
||||
>
|
||||
<view :style="{ 'padding-top': !type ? '62rpx' : '0' }">
|
||||
<block v-for="(item, index) in addressList" :key="item.id">
|
||||
<view
|
||||
class="address-list--item flex justify-between mt-[20rpx]"
|
||||
:data-id="item.id"
|
||||
@click.stop="onSelect(item)"
|
||||
>
|
||||
<view class="flex-1">
|
||||
<view>
|
||||
<text class="text-xl font-medium">
|
||||
{{ item.contact }} {{ item.mobile }}
|
||||
</text>
|
||||
<text class="default" v-if="item.isDefault">默认</text>
|
||||
</view>
|
||||
<view class="mt-[20rpx] sm text-muted">
|
||||
{{ item.province }}
|
||||
{{ item.city }}
|
||||
{{ item.district }}
|
||||
{{ item.address }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting" @click.stop="goEditAddress(item.id)">编辑</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
|
||||
<view class="fixed flex items-center justify-between bg-white footer">
|
||||
<!-- #ifdef H5 || MP-WEIXIN -->
|
||||
<!-- <view v-if="isWeixin" class="mr-[20rpx]">
|
||||
<button
|
||||
class="bg-color text-lg text-black border-none rounded-full flex items-center justify-center leading-[80rpx] h-[80rpx]"
|
||||
@click="getWxAddressFun"
|
||||
>
|
||||
<image
|
||||
class="icon-md mr-[10rpx]"
|
||||
:src="'../../static/images/icon_wechat.png'"
|
||||
></image>
|
||||
微信导入
|
||||
</button>
|
||||
</view> -->
|
||||
<!-- #endif -->
|
||||
<view>
|
||||
<button
|
||||
class="text-lg text-white rounded-full leading-[80rpx] h-[80rpx] custom-button-bgColor"
|
||||
@click="goEditAddress('')"
|
||||
>
|
||||
添加地址
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef, ref, watch } from 'vue'
|
||||
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { getGeocoder } from '@/api/app'
|
||||
import { apiAddressLists, apiAddressEdit } from '@/api/user'
|
||||
import { wxOaAddress } from '@/hooks/wechat'
|
||||
import { isWeixinClient } from '@/utils/client'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const type = ref(false)
|
||||
const addressList = ref<any>([]) // 地址数据列表
|
||||
const isWeixin = ref(true) // 是不是微信,如果是就显示微信导入地址按钮
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
// 获取地址列表
|
||||
const queryList = async (pageNo, pageSize) => {
|
||||
try {
|
||||
const { lists } = await apiAddressLists({ pageNo, pageSize })
|
||||
paging.value.setLocalPaging(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.setLocalPaging(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 选择当前地址作为支付地址
|
||||
const onSelect = async (data: any) => {
|
||||
try {
|
||||
if (type.value) {
|
||||
uni.$emit('address', data)
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
await apiAddressEdit({
|
||||
...data,
|
||||
isDefault: 1,
|
||||
userId: userInfo.value.id
|
||||
})
|
||||
uni.redirectTo({
|
||||
url: '/bundle/pages/user_address/index'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error, '设置默认地址捕捉错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑地址
|
||||
const goEditAddress = (params = '') => {
|
||||
uni.navigateTo({
|
||||
url: `/bundle/pages/user_address_edit/index?id=${params}`
|
||||
})
|
||||
}
|
||||
|
||||
// 微信导入地址
|
||||
const getWxAddressFun = async () => {
|
||||
// #ifdef H5
|
||||
try {
|
||||
const res = await wxOaAddress()
|
||||
getLocation(res.cityName + res.countyName + res.detailInfo, res)
|
||||
} catch (error) {
|
||||
console.log('捕捉微信公众号导入地址报错', error)
|
||||
}
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
const { errMsg } = await uni.authorize({ scope: 'scope.address' })
|
||||
if (errMsg === 'authorize:ok') {
|
||||
try {
|
||||
const res = await uni.chooseAddress()
|
||||
console.log(res)
|
||||
getLocation(res.cityName + res.countyName + res.detailInfo, res)
|
||||
} catch (error) {
|
||||
console.log('导入微信地址地址', error)
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 解析地址转坐标d
|
||||
const getLocation = async (address: string, address_info: any) => {
|
||||
try {
|
||||
const res = await getGeocoder({ keyWord: address })
|
||||
const obj = {
|
||||
latitude: res.lat,
|
||||
longitude: res.lng,
|
||||
name: address_info.userName,
|
||||
mobile: address_info.telNumber
|
||||
}
|
||||
console.log('obj', obj)
|
||||
|
||||
uni.setStorageSync('wxAddress', JSON.stringify(obj))
|
||||
goEditAddress()
|
||||
} catch (error) {
|
||||
console.log('逆解析地址', error)
|
||||
}
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
paging.value?.reload()
|
||||
})
|
||||
|
||||
onLoad((options: { type?: boolean }) => {
|
||||
if (options.type) type.value = options.type
|
||||
//#ifdef H5
|
||||
isWeixin.value = isWeixinClient()
|
||||
//#endif
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
uni.$emit('changeAddress')
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
page {
|
||||
// overflow: hidden;
|
||||
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.icon-md {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.address-list {
|
||||
&--item {
|
||||
border-radius: 14rpx;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx 20rpx 0 20rpx;
|
||||
padding: 30rpx 30rpx 36rpx 30rpx;
|
||||
|
||||
.default {
|
||||
padding: 0 4rpx;
|
||||
margin-left: 20rpx;
|
||||
color: #f36161;
|
||||
font-size: 22rpx;
|
||||
background: rgba(#f36161, 0.1);
|
||||
}
|
||||
|
||||
.setting {
|
||||
height: 90rpx;
|
||||
width: 100rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: right;
|
||||
color: $blue5;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
z-index: 999;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 118rpx;
|
||||
line-height: 118rpx;
|
||||
position: fixed;
|
||||
padding: 0 30rpx;
|
||||
box-sizing: content-box;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
> view {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,336 @@
|
|||
<template>
|
||||
<view class="m-[24rpx]">
|
||||
<!-- Mian Start -->
|
||||
<u-form :model="formData" ref="formRef" :error-type="['message', 'toast']">
|
||||
<!-- 联系人 -->
|
||||
<view class="card">
|
||||
<u-form-item prop="contact">
|
||||
<view class="flex w-full">
|
||||
<view class="label">联系人</view>
|
||||
<u-input
|
||||
v-model="formData.contact"
|
||||
placeholder="请输入联系人"
|
||||
class="flex-1"
|
||||
/>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<!-- 手机号 -->
|
||||
<view class="card">
|
||||
<u-form-item prop="mobile">
|
||||
<view class="flex w-full">
|
||||
<view class="label">手机号</view>
|
||||
<u-input
|
||||
v-model="formData.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
class="flex-1"
|
||||
/>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<!-- 省市区 -->
|
||||
<view class="card">
|
||||
<u-form-item prop="region">
|
||||
<view class="flex w-full" @click="chooseLocation">
|
||||
<view class="label">省市区</view>
|
||||
<view class="flex-1">
|
||||
<u-input
|
||||
@click="chooseLocation"
|
||||
v-model="formData.region"
|
||||
placeholder="请选择"
|
||||
:disabled="true"
|
||||
/>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="22" color="#888888"></u-icon>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<!-- 门牌号 -->
|
||||
<view class="card">
|
||||
<u-form-item prop="address">
|
||||
<view class="flex w-full col-start">
|
||||
<view class="label">门牌号</view>
|
||||
<view class="flex-1 pt-[6rpx]">
|
||||
<u-input
|
||||
v-model="formData.address"
|
||||
type="textarea"
|
||||
placeholder="请选择"
|
||||
height="124"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<!-- 是否默认地址 -->
|
||||
<view class="is-default flex my-[20rpx] items-center">
|
||||
<u-checkbox shape="circle" active-color="#2468f2" v-model="formData.isDefault">
|
||||
设为默认
|
||||
</u-checkbox>
|
||||
</view>
|
||||
</u-form>
|
||||
<!-- Mian End -->
|
||||
</view>
|
||||
|
||||
<!-- Footer Start -->
|
||||
<view class="flex m-[20rpx] pt-[30rpx]">
|
||||
<!-- #ifdef H5 || MP-WEIXIN -->
|
||||
<view v-if="addressId" class="flex-1 mr-[20rpx]">
|
||||
<button
|
||||
class="bg-white text-lg text-info rounded-full leading-[80rpx] h-[80rpx]"
|
||||
@click="delPupop = true"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<view class="flex-1 mx-[24rpx]">
|
||||
<button
|
||||
class="custom-button-bgColor text-lg text-white rounded-full leading-[80rpx] h-[80rpx]"
|
||||
@click="onSubmit"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- Footer End -->
|
||||
|
||||
<!-- 删除地址 弹窗 Start -->
|
||||
<u-modal
|
||||
id="delete-dialog"
|
||||
v-model="delPupop"
|
||||
:showCancelButton="true"
|
||||
confirm-text="狠心删除"
|
||||
title="温馨提示"
|
||||
confirm-color="#1296DB"
|
||||
@confirm="handleAddressDel"
|
||||
@cancel="delPupop = false"
|
||||
>
|
||||
<view class="text-center p-[50rpx]">
|
||||
<text>确认删除该地址吗?</text>
|
||||
</view>
|
||||
</u-modal>
|
||||
<!-- 删除地址 弹窗 End -->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLoad, onReady, onUnload } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { getGeocoderCoordinate, getGeocoder } from '@/api/app'
|
||||
import { apiAddressDetail, apiAddressEdit, apiAddressAdd, apiAddressDel } from '@/api/user'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
type FORM = {
|
||||
contact: string // 联系方式
|
||||
mobile: string | number // 手机号码
|
||||
province: string | number // 省
|
||||
provinceId: string | number // 省
|
||||
city: string | number // 市
|
||||
cityId: string | number // 市
|
||||
district: string | number // 区
|
||||
districtId: string | number // 区
|
||||
address: string // 详细地址
|
||||
isDefault: number | boolean // 是否默认
|
||||
region: string
|
||||
longitude: number // 经度
|
||||
latitude: number // 纬度
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
const formData = ref<FORM>({
|
||||
contact: '',
|
||||
mobile: '',
|
||||
province: '',
|
||||
provinceId: '',
|
||||
city: '',
|
||||
cityId: '',
|
||||
district: '',
|
||||
districtId: '',
|
||||
address: '',
|
||||
isDefault: true,
|
||||
region: '',
|
||||
longitude: 0,
|
||||
latitude: 0
|
||||
})
|
||||
const formRef = ref()
|
||||
const addressId = ref<string | number>('')
|
||||
const delPupop = ref<boolean | null>(false)
|
||||
const rules = ref<object>({
|
||||
contact: [
|
||||
{ required: true, message: '请输入联系人', trigger: ['change', 'blur'] },
|
||||
{
|
||||
min: 1,
|
||||
max: 20,
|
||||
message: '输入长度不得超过20位',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
mobile: [
|
||||
{ required: true, message: '请输入手机号码', trigger: ['change', 'blur'] },
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
transform(value: any) {
|
||||
return String(value)
|
||||
},
|
||||
message: '请输入正确的手机号'
|
||||
}
|
||||
],
|
||||
region: [{ required: true, message: '请选择省市区', trigger: ['change', 'blur'] }],
|
||||
address: [{ required: true, message: '请输入门牌号', trigger: ['change', 'blur'] }]
|
||||
})
|
||||
|
||||
// 处理默认地址类型值
|
||||
const booleanToNumber = () => {
|
||||
return formData.value.isDefault ? 1 : 0
|
||||
}
|
||||
|
||||
// 获取地址详情
|
||||
const getAddressDetail = async (): Promise<void> => {
|
||||
formData.value = await apiAddressDetail({ id: addressId.value })
|
||||
formData.value.region = `${formData.value.province} ${formData.value.city} ${formData.value.district}`
|
||||
}
|
||||
|
||||
// 校验
|
||||
const onSubmit = () => {
|
||||
formRef.value.validate((valid: boolean) => {
|
||||
if (!valid) return false
|
||||
if (!addressId.value) handleAddressAdd()
|
||||
else handleAddressEdit()
|
||||
})
|
||||
}
|
||||
// 添加地址
|
||||
const handleAddressAdd = async (): Promise<void> => {
|
||||
await apiAddressAdd({
|
||||
...formData.value,
|
||||
userId: userInfo.value.id,
|
||||
isDefault: booleanToNumber()
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 300)
|
||||
}
|
||||
// 编辑地址
|
||||
const handleAddressEdit = async (): Promise<void> => {
|
||||
await apiAddressEdit({
|
||||
...formData.value,
|
||||
userId: userInfo.value.id,
|
||||
isDefault: booleanToNumber()
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 300)
|
||||
}
|
||||
// 选择省市区
|
||||
const chooseLocation = () => {
|
||||
uni.chooseLocation({
|
||||
success(res) {
|
||||
getLocation(res.name, res.latitude, res.longitude)
|
||||
},
|
||||
fail(error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 逆解析地址
|
||||
const getLocation = async (name: string, latitude: string | number, longitude: string | number) => {
|
||||
try {
|
||||
const res1 = await getGeocoderCoordinate({
|
||||
lat: latitude,
|
||||
lng: longitude
|
||||
})
|
||||
handleAddressInfo(name, res1, longitude, latitude)
|
||||
} catch (error) {
|
||||
console.log('地址逆解析', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理接口返回的地址数据
|
||||
const handleAddressInfo = (name: string, event: any, longitude: number, latitude: number) => {
|
||||
let cityId = event.cityId
|
||||
const adInfo = event.amapGenCoderResponse.amapAddressComponent
|
||||
if (cityId == 110000 || cityId == 310000 || cityId == 210000 || cityId == 410000) {
|
||||
cityId = cityId * 1
|
||||
cityId += 100
|
||||
}
|
||||
formData.value.cityId = cityId + '' // 市id
|
||||
formData.value.provinceId = event.provinceId + '' // 省id
|
||||
formData.value.districtId = event.districtId + '' // 区id
|
||||
formData.value.region = `${adInfo.province} ${adInfo.city} ${adInfo.district}` //省 市 区
|
||||
// 地址截取门牌号
|
||||
const idx = event.amapGenCoderResponse.address.indexOf(adInfo.street)
|
||||
let streetName = event.amapGenCoderResponse.address.slice(idx)
|
||||
if (name) {
|
||||
streetName = name // name是地图器选择时,选中的名称或街道名称
|
||||
}
|
||||
formData.value.address = streetName
|
||||
formData.value.longitude = longitude.toFixed(10) // 经度
|
||||
formData.value.latitude = latitude.toFixed(10) // 纬度
|
||||
// console.log('地址', name, event)
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
const handleAddressDel = async (): Promise<void> => {
|
||||
try {
|
||||
await apiAddressDel({ id: addressId.value })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 300)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options: { id?: number }) => {
|
||||
addressId.value = Number(options.id)
|
||||
if (options.id) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: '编辑地址'
|
||||
})
|
||||
getAddressDetail()
|
||||
} else {
|
||||
uni.setNavigationBarTitle({
|
||||
title: '添加地址'
|
||||
})
|
||||
let wxAddress = uni.getStorageSync('wxAddress')
|
||||
console.log('wxAddress', wxAddress)
|
||||
if (!wxAddress) return
|
||||
wxAddress = JSON.parse(wxAddress)
|
||||
formData.value.contact = wxAddress.name
|
||||
formData.value.mobile = wxAddress.mobile
|
||||
getLocation(wxAddress.latitude, wxAddress.longitude)
|
||||
}
|
||||
})
|
||||
|
||||
onReady(() => {
|
||||
formRef.value?.setRules(rules.value)
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
uni.removeStorageSync('wxAddress')
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.card {
|
||||
// margin: 20rpx;
|
||||
margin-bottom: 0;
|
||||
padding: 0 24rpx;
|
||||
// border-radius: 14rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
.label {
|
||||
color: #222222;
|
||||
font-size: 28rpx;
|
||||
margin-right: 30rpx;
|
||||
line-height: 70rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.is-default {
|
||||
image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,216 @@
|
|||
<template>
|
||||
<view class="user-recharge">
|
||||
<view class="wrapper">
|
||||
<view class="balance">充值金额</view>
|
||||
<view class="flex items-center balance-recharge-input">
|
||||
<text style="font-size: 46rpx">¥</text>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="0.00"
|
||||
type="digit"
|
||||
v-model="rechargeData.orderAmount"
|
||||
/>
|
||||
</view>
|
||||
<view class="balance-recharge-tips mt-[20rpx]">
|
||||
<view>当前可用余额为 ¥ {{ walletData.userMoney }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="px-[30rpx]">
|
||||
<button class="btn text-lg text-white rounded-full" @click="handleRecharge()">
|
||||
立即充值
|
||||
</button>
|
||||
|
||||
<navigator
|
||||
class="record"
|
||||
hover-class="none"
|
||||
url="/bundle/pages/recharge_record/recharge_record"
|
||||
>
|
||||
充值记录
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 充值成功组件 -->
|
||||
<u-popup v-model="rechargeSuccess" :closeable="true" mode="center" border-radius="14">
|
||||
<view class="bg-white recharge-success-card" style="width: 70vw">
|
||||
<view class="recharge-success">
|
||||
<image src="@/static/images/recharge_uccess.png" />
|
||||
<view class="recharge-success-text"> 充值成功 </view>
|
||||
</view>
|
||||
|
||||
<view class="p-[40rpx]">
|
||||
<u-button
|
||||
@click="changeRechargeComplete"
|
||||
type="primary"
|
||||
:ripple="true"
|
||||
:hair-line="false"
|
||||
shape="circle"
|
||||
>
|
||||
完成
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<payment
|
||||
v-if="payState.showPay || payState.showCheck"
|
||||
ref="paymentRef"
|
||||
v-model:show="payState.showPay"
|
||||
v-model:show-check="payState.showCheck"
|
||||
:order-id="payState.orderId"
|
||||
:order_amount="rechargeData?.orderAmount"
|
||||
:from="payState.from"
|
||||
:redirect="payState.redirect"
|
||||
@success="handlePayResult"
|
||||
@close="handlePayResult"
|
||||
@fail="handlePayResult"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, shallowRef } from 'vue'
|
||||
import { onShow, onUnload, onLoad } from '@dcloudio/uni-app'
|
||||
import { apiRecharge, apiUserWallet } from '@/api/wallet'
|
||||
import { PaymentStatusEnum } from '@/utils/enum'
|
||||
import { getClient } from '@/utils/client'
|
||||
|
||||
const walletData = ref({
|
||||
userMoney: '' // 可用余额
|
||||
})
|
||||
|
||||
const paymentRef = shallowRef()
|
||||
const rechargeData = ref({
|
||||
orderAmount: '', // 充值金额
|
||||
terminal: 1,
|
||||
payChannel: ''
|
||||
})
|
||||
const payState = reactive({
|
||||
orderId: '',
|
||||
from: '',
|
||||
showPay: false,
|
||||
showCheck: false,
|
||||
redirect: '/pages/order_buy/index'
|
||||
})
|
||||
|
||||
// 充值弹窗
|
||||
const rechargeSuccess = ref<boolean | null>(false)
|
||||
|
||||
// 获取钱包数据
|
||||
const getWalletData = () => {
|
||||
apiUserWallet('').then((res: any) => {
|
||||
walletData.value.userMoney = res.userMoney
|
||||
})
|
||||
}
|
||||
|
||||
// 充值
|
||||
const handleRecharge = () => {
|
||||
if (rechargeData.value.orderAmount === '') return uni.$u.toast('请输入充值金额')
|
||||
const scene = getClient()
|
||||
let payChannel = ''
|
||||
switch (scene) {
|
||||
case 1:
|
||||
payChannel = 'mp_channel'
|
||||
break
|
||||
case 2:
|
||||
payChannel = 'oa_channel'
|
||||
break
|
||||
case 3:
|
||||
payChannel = 'h5_channel'
|
||||
break
|
||||
}
|
||||
rechargeData.value.payChannel = payChannel
|
||||
apiRecharge({ ...rechargeData.value })
|
||||
.then((res: any) => {
|
||||
payState.orderId = res.orderId
|
||||
payState.from = 'recharge'
|
||||
payState.showPay = true
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('下单', err)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePayResult = async () => {
|
||||
payState.showPay = false
|
||||
payState.showCheck = false
|
||||
uni.redirectTo({
|
||||
url: `/bundle/pages/user_wallet/user_wallet`
|
||||
})
|
||||
}
|
||||
|
||||
const changeRechargeComplete = () => {
|
||||
rechargeSuccess.value = false
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
// 监听全局duringPayment事件
|
||||
uni.$on('duringPayment', (params) => {
|
||||
if (params.result === PaymentStatusEnum['SUCCESS']) {
|
||||
// uni.navigateBack()
|
||||
// setTimeout(() => toast('支付成功'), 0.5 * 1000)
|
||||
}
|
||||
})
|
||||
uni.$emit('send')
|
||||
|
||||
if (options.rechargeSuccess == 1) {
|
||||
rechargeSuccess.value = true
|
||||
}
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
uni.$off(['duringPayment', 'send'])
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
getWalletData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-recharge {
|
||||
.wrapper {
|
||||
margin: 20rpx 30rpx;
|
||||
padding: 40rpx;
|
||||
width: 690rpx;
|
||||
height: 330rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #fff;
|
||||
.balance {
|
||||
font-weight: 500;
|
||||
font-size: 30rpx;
|
||||
line-height: 36rpx;
|
||||
color: #666;
|
||||
margin-bottom: 38rpx;
|
||||
}
|
||||
.balance-recharge-input {
|
||||
margin-top: 35rpx;
|
||||
margin-right: 66rpx;
|
||||
|
||||
.input {
|
||||
height: 94rpx;
|
||||
text-align: left;
|
||||
font-size: 66rpx;
|
||||
margin-left: 30rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
border-bottom: 1rpx solid #ebebeb;
|
||||
}
|
||||
.balance-recharge-tips {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
background: $u-type-primary;
|
||||
}
|
||||
.record {
|
||||
margin-top: 24rpx;
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<view class="user-wallet">
|
||||
<view class="wrapper">
|
||||
<view class="top">
|
||||
<view class="text">总资产(元)</view>
|
||||
<view class="total">{{ walletData.userMoney || '0.00' }}</view>
|
||||
|
||||
<view class="flex">
|
||||
<!-- <view class="flex-1">
|
||||
<view class="text">可用余额(元)</view>
|
||||
<view class="num">{{ walletData.user_money }}</view>
|
||||
</view> -->
|
||||
<!-- <view class="flex-1">
|
||||
<view class="text">可提现余额(元)</view>
|
||||
<view class="num">{{ walletData.user_earnings }}</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex mt-[40rpx]">
|
||||
<navigator
|
||||
class="recharge"
|
||||
hover-class="none"
|
||||
url="/bundle/pages/user_recharge/user_recharge"
|
||||
v-if="walletData?.openRecharge"
|
||||
>
|
||||
<!-- <view class="btn1" v-if="walletData.recharge_open"> 充值 </view> -->
|
||||
<view class="btn1"> 充值 </view>
|
||||
</navigator>
|
||||
<!-- <navigator
|
||||
class="withdraw"
|
||||
hover-class="none"
|
||||
url="/bundle/pages/user_withdraw/user_withdraw"
|
||||
>
|
||||
<view class="btn2"> 提现 </view>
|
||||
</navigator> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menus grid grid-cols-3">
|
||||
<navigator
|
||||
class="menu"
|
||||
hover-class="none"
|
||||
:url="`/bundle/pages/account_detail/account_detail?changeObject=1`"
|
||||
>
|
||||
<image src="@/static/images/icon/balance.png" alt="" />
|
||||
<view class="text">余额明细</view>
|
||||
</navigator>
|
||||
<!-- <navigator
|
||||
class="menu"
|
||||
hover-class="none"
|
||||
:url="`/bundle/pages/account_detail/account_detail?changeObject=2`"
|
||||
>
|
||||
<image src="@/static/images/icon/brokerage.png" alt="" />
|
||||
<view class="text">提现明细</view>
|
||||
</navigator> -->
|
||||
<navigator
|
||||
class="menu"
|
||||
hover-class="none"
|
||||
:url="`/bundle/pages/recharge_record/recharge_record`"
|
||||
>
|
||||
<image src="@/static/images/icon/recharge.png" alt="" />
|
||||
<view class="text">充值记录</view>
|
||||
</navigator>
|
||||
<!-- <navigator
|
||||
class="menu"
|
||||
hover-class="none"
|
||||
:url="`/bundle/pages/withdraw_record/withdraw_record`"
|
||||
>
|
||||
<image src="@/static/images/icon/withdraw.png" alt="" />
|
||||
<view class="text">提现记录</view>
|
||||
</navigator> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { apiUserWallet } from '@/api/wallet'
|
||||
|
||||
const walletData = ref<any>({
|
||||
userMoney: '' // 可用余额
|
||||
// user_earnings: '', // 可提现余额
|
||||
// total_money: '', // 总资产
|
||||
})
|
||||
|
||||
const getWalletData = () => {
|
||||
apiUserWallet('').then((res: any) => {
|
||||
walletData.value = res
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
getWalletData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-wallet {
|
||||
.wrapper {
|
||||
margin: 20rpx 15rpx;
|
||||
width: 720rpx;
|
||||
height: 484rpx;
|
||||
border-radius: 14rpx;
|
||||
background: #fff;
|
||||
padding: 20rpx;
|
||||
.top {
|
||||
width: 680rpx;
|
||||
height: 320rpx;
|
||||
border-radius: 20rpx;
|
||||
background: $u-type-primary;
|
||||
padding: 30rpx;
|
||||
.text {
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
color: #e5fff5;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.total {
|
||||
font-size: 68rpx;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
color: #e5fff5;
|
||||
}
|
||||
.num {
|
||||
font-weight: 600;
|
||||
font-size: 38rpx;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.btn1,
|
||||
.btn2 {
|
||||
border-radius: 14rpx;
|
||||
background: #f4f4f4;
|
||||
width: 320rpx;
|
||||
height: 84rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.btn2 {
|
||||
margin-left: 40rpx;
|
||||
background: $u-type-primary;
|
||||
}
|
||||
.recharge,
|
||||
.withdraw {
|
||||
font-weight: 400;
|
||||
font-size: 30rpx;
|
||||
line-height: 40rpx;
|
||||
color: #333;
|
||||
}
|
||||
.withdraw {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.menus {
|
||||
margin: 20rpx 15rpx;
|
||||
width: 720rpx;
|
||||
height: 360rpx;
|
||||
border-radius: 14rpx;
|
||||
background: #fff;
|
||||
padding: 10rpx;
|
||||
.menu {
|
||||
width: 220rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 14rpx;
|
||||
background: #fff;
|
||||
image {
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
margin: 28rpx 78rpx 0;
|
||||
}
|
||||
.text {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,342 @@
|
|||
<template>
|
||||
<view class="user-withdraw">
|
||||
<view class="balance-withdrawal-card">
|
||||
<u-tabs
|
||||
:list="withdrawConfigData.type"
|
||||
:is-scroll="true"
|
||||
:current="operationCurrent"
|
||||
@change="operationChange"
|
||||
inactive-color="#666"
|
||||
active-color="#33D192"
|
||||
itemWidth="240"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 微信收款码 -->
|
||||
<template
|
||||
v-if="
|
||||
withdrawConfigData.type[operationCurrent]?.value ==
|
||||
BalanceWithdrawalEnum.WECHAT_COLLECTION_CODE
|
||||
"
|
||||
>
|
||||
<view class="payment-code-card">
|
||||
<u-form :model="withdrawApplyData" ref="uForm" label-width="150rpx">
|
||||
<u-form-item label="微信账号">
|
||||
<u-input v-model="withdrawApplyData.account" placeholder="请输入微信账号" />
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="真实姓名">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.real_name"
|
||||
placeholder="请输入真实姓名"
|
||||
/>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="备注">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.apply_remark"
|
||||
placeholder="请输入备注(选填)"
|
||||
/>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
|
||||
<view class="mt-[20rpx]">
|
||||
<uploader v-model="withdrawApplyData.money_qr_code"></uploader>
|
||||
<view class="ml-[10rpx]"> 微信收款码 </view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 支付宝收款码 -->
|
||||
<template
|
||||
v-if="
|
||||
withdrawConfigData.type[operationCurrent]?.value ==
|
||||
BalanceWithdrawalEnum.ALIPAY_COLLECTION_CODE
|
||||
"
|
||||
>
|
||||
<view class="payment-code-card">
|
||||
<u-form :model="withdrawApplyData" ref="uForm" label-width="150rpx">
|
||||
<u-form-item label="支付宝账号">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.account"
|
||||
placeholder="请输入支付宝账号"
|
||||
/>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="真实姓名">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.real_name"
|
||||
placeholder="请输入真实姓名"
|
||||
/>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="备注">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.apply_remark"
|
||||
placeholder="请输入备注(选填)"
|
||||
/>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
|
||||
<view class="mt-[20rpx]">
|
||||
<uploader v-model="withdrawApplyData.money_qr_code"></uploader>
|
||||
<view class="ml-[10rpx]"> 支付宝收款码 </view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 银行卡 -->
|
||||
<template
|
||||
v-if="
|
||||
withdrawConfigData.type[operationCurrent]?.value == BalanceWithdrawalEnum.BANK_CARD
|
||||
"
|
||||
>
|
||||
<view class="payment-code-card">
|
||||
<u-form :model="withdrawApplyData" ref="uForm" label-width="150rpx">
|
||||
<u-form-item label="银行卡账号">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.account"
|
||||
placeholder="请输入银行卡账号"
|
||||
/>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="持卡人姓名">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.real_name"
|
||||
placeholder="请输入持卡人姓名"
|
||||
/>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="提现银行">
|
||||
<u-input v-model="withdrawApplyData.bank" placeholder="请输入提现银行" />
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="银行支行">
|
||||
<u-input v-model="withdrawApplyData.subbank" placeholder="如:荔湾支行" />
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="备注">
|
||||
<u-input
|
||||
v-model="withdrawApplyData.apply_remark"
|
||||
placeholder="请输入备注(选填)"
|
||||
/>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 钱包余额 / 微信钱包 -->
|
||||
<view class="wallet-balance-card">
|
||||
<view class="wallet-balance-input flex">
|
||||
<text style="font-size: 46rpx">¥</text>
|
||||
<input
|
||||
class="flex-1"
|
||||
placeholder="0.00"
|
||||
type="digit"
|
||||
v-model="withdrawApplyData.money"
|
||||
/>
|
||||
|
||||
<view class="withdrawal-text">
|
||||
<view
|
||||
class="all-withdrawal"
|
||||
@click="withdrawApplyData.money = withdrawConfigData.user_earnings"
|
||||
>
|
||||
全部提现
|
||||
</view>
|
||||
<view class="can-withdrawal">
|
||||
可提现余额 ¥ {{ withdrawConfigData.user_earnings }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="wallet-balance-tips"
|
||||
v-if="
|
||||
withdrawConfigData.type[operationCurrent]?.value != BalanceWithdrawalEnum.WALLET
|
||||
"
|
||||
>
|
||||
提示:提现需要扣除服务费{{ withdrawConfigData.percentage }}%
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-[30rpx]">
|
||||
<u-button
|
||||
@click="handleWithdraw()"
|
||||
:ripple="true"
|
||||
:hair-line="false"
|
||||
shape="circle"
|
||||
type="primary"
|
||||
hover-class="none"
|
||||
>
|
||||
确认提现
|
||||
</u-button>
|
||||
|
||||
<view
|
||||
class="withdrawal-record"
|
||||
@click="goPage('/bundle/pages/withdraw_record/withdraw_record')"
|
||||
>
|
||||
提现记录
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { BalanceWithdrawalEnum } from '@/utils/enum'
|
||||
import { apiGetWithdrawConfig, apiWithdrawApply } from '@/api/wallet'
|
||||
import uploader from '@/components/uploader/index.vue'
|
||||
import formCard from './components/form-card.vue'
|
||||
|
||||
const operationCurrent = ref<any>(0)
|
||||
|
||||
// 提现配置数据
|
||||
const withdrawConfigData = ref({
|
||||
user_earnings: '', // 可提现金额
|
||||
min_withdraw: '', // 最低提现金额
|
||||
max_withdraw: '', // 最高提现金额
|
||||
percentage: '', // 手续费百分比
|
||||
type: '' // 提现类型
|
||||
})
|
||||
|
||||
// 提现申请数据
|
||||
const withdrawApplyData = ref({
|
||||
type: '', // 提现类型 1-余额 2-微信零钱 3-银行卡 4-微信收款码 5-支付宝收款码
|
||||
money: '', // 提现金额
|
||||
account: '', // 账号; 提现方式:银行卡、微信收款码、支付宝收款码 必填
|
||||
real_name: '', // 真实姓名; 提现方式:银行卡、微信收款码、支付宝收款码 必填
|
||||
money_qr_code: '', // 收款码 提现方式:微信收款码、支付宝收款码 必填
|
||||
bank: '', // 提现银行 提现方式:银行卡 必填
|
||||
subbank: '', // 提现支银行 提现方式:银行卡 必填
|
||||
apply_remark: '' // 备注
|
||||
})
|
||||
|
||||
const operationChange = (e: any) => {
|
||||
operationCurrent.value = e
|
||||
}
|
||||
|
||||
// 获取提现配置
|
||||
const getWithdrawConfig = async () => {
|
||||
const res = await apiGetWithdrawConfig('')
|
||||
withdrawConfigData.value = res
|
||||
}
|
||||
|
||||
// 提现
|
||||
const handleWithdraw = () => {
|
||||
if (withdrawApplyData.value.money == '') return uni.$u.toast('请输入提现金额')
|
||||
|
||||
const params = {
|
||||
type: withdrawConfigData.value.type[operationCurrent.value]?.value,
|
||||
money: withdrawApplyData.value.money,
|
||||
account: withdrawApplyData.value.account,
|
||||
real_name: withdrawApplyData.value.real_name,
|
||||
money_qr_code: withdrawApplyData.value.money_qr_code.toString(),
|
||||
bank: withdrawApplyData.value.bank,
|
||||
subbank: withdrawApplyData.value.subbank,
|
||||
apply_remark: withdrawApplyData.value.apply_remark
|
||||
}
|
||||
|
||||
apiWithdrawApply({ ...params }).then((res: any) => {
|
||||
uni.$u.toast('添加成功')
|
||||
withdrawApplyData.value.money = ''
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: `/bundle/pages/withdrawal_details/withdrawal_details?id=${res.id}`
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转页面
|
||||
const goPage = (url: any) => {
|
||||
uni.navigateTo({ url: url })
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
getWithdrawConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-withdraw {
|
||||
padding: 15rpx 30rpx;
|
||||
|
||||
// tab栏
|
||||
.balance-withdrawal-card {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
// 收款码/银行卡
|
||||
.payment-code-card {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
padding: 0 36rpx 30rpx 36rpx;
|
||||
|
||||
.u-list-item.data-v-f8c23944 {
|
||||
background-color: #fff !important;
|
||||
margin: 0;
|
||||
border: 1rpx dashed #ccc;
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 钱包余额 / 微信钱包
|
||||
.wallet-balance-card {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
padding: 35rpx 0 66rpx 66rpx;
|
||||
|
||||
.wallet-balance-input {
|
||||
margin-top: 20rpx;
|
||||
margin-right: 66rpx;
|
||||
|
||||
input {
|
||||
height: 94rpx;
|
||||
text-align: left;
|
||||
font-size: 66rpx;
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
|
||||
border-bottom: 1rpx solid #ebebeb;
|
||||
|
||||
.withdrawal-text {
|
||||
font-size: 24rpx;
|
||||
|
||||
.all-withdrawal {
|
||||
color: $u-type-primary;
|
||||
display: subgrid;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.can-withdrawal {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-balance-tips {
|
||||
margin-top: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.withdrawal-record {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,113 @@
|
|||
<!--
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-09-27 14:54:44
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-11-21 14:58:25
|
||||
* @FilePath: \housekeeping-uniapp\src\bundle\pages\withdraw_distributor_record\components\card.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<view class="card">
|
||||
<view class="card__top">
|
||||
<text>提现到账</text>
|
||||
<text :class="getStatusColor(item.status)">{{ WithdrawStatusMap[item?.status] }}</text>
|
||||
</view>
|
||||
<view class="card__content">
|
||||
<view class="money w-full">
|
||||
<view class="text-2xl">提现金额</view>
|
||||
<view class="text-5xl font-bold my-1 ml-[-50rpx]">
|
||||
<text class="mr-2">¥</text>
|
||||
<text>{{ item.withdrawMoney }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-for="(i, index) in fieldArr" :key="index" class="flex text-lg mb-2">
|
||||
<text class="w-[180rpx]">{{ i.name }}</text>
|
||||
<text>{{ item[i.prop] }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType, computed, watch } from 'vue'
|
||||
import { WithdrawStatusMap } from '@/enums/appEnums'
|
||||
|
||||
interface CardProps {
|
||||
id: number
|
||||
withdrawMoney: number
|
||||
createTime: string
|
||||
status: number
|
||||
updateTime: string
|
||||
withdrawTo: string
|
||||
remark: string
|
||||
}
|
||||
const props = defineProps({
|
||||
item: Object as PropType<CardProps>
|
||||
})
|
||||
const getStatusColor = computed(() => {
|
||||
return function (val: number) {
|
||||
return val === 0 ? 'text-warning' : val === 1 ? 'text-success' : 'text-error'
|
||||
}
|
||||
})
|
||||
watch(
|
||||
() => props.item,
|
||||
() => {
|
||||
;(props.item as CardProps).withdrawTo = '微信零钱'
|
||||
if ((props.item as CardProps).status == 1) {
|
||||
;(props.item as CardProps).remark = '您的佣金已提现到微信钱包'
|
||||
} else if ((props.item as CardProps).status == 2) {
|
||||
;(props.item as CardProps).remark = '提现失败,请联系客服'
|
||||
} else {
|
||||
;(props.item as CardProps).remark = ''
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
const fieldArr = [
|
||||
{
|
||||
name: '提现去向',
|
||||
prop: 'withdrawTo'
|
||||
},
|
||||
{
|
||||
name: '提现时间',
|
||||
prop: 'createTime'
|
||||
},
|
||||
{
|
||||
name: '到账时间',
|
||||
prop: 'updateTime'
|
||||
},
|
||||
{
|
||||
name: '备注',
|
||||
prop: 'remark'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
background-color: $white;
|
||||
border-radius: 12rpx;
|
||||
margin: 30rpx;
|
||||
|
||||
&__top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 72rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-top: 1px solid #eee;
|
||||
padding: 0 20rpx 20rpx;
|
||||
|
||||
.money {
|
||||
padding: 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,130 @@
|
|||
<!--
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-09-27 14:54:44
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2024-01-04 17:17:21
|
||||
* @FilePath: \housekeeping-uniapp\src\bundle\pages\withdraw_distributor_record\index.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<u-navbar title="" class="u-navbar">
|
||||
<view class="slot-wrap" @click="handleToggle">
|
||||
<text class="m-[5px]">收益记录</text>
|
||||
<u-icon name="arrow-down"></u-icon>
|
||||
<u-picker
|
||||
mode="time"
|
||||
v-model="show"
|
||||
:default-time="`${convertChineseMonthToDate(currentDate)}-${day}`"
|
||||
:params="params"
|
||||
@confirm="handleConfirm"
|
||||
></u-picker>
|
||||
</view>
|
||||
</u-navbar>
|
||||
<view class="record" :style="`height:calc(100vh - ${navbarHeight}px)`">
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<block v-for="(item, index) in dataList" :key="`${index} + 'unique'`">
|
||||
<card :item="item" />
|
||||
</block>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, unref, onMounted } from 'vue'
|
||||
import { apiWithdrawLists } from '@/api/wallet'
|
||||
import card from './components/card.vue'
|
||||
import { useZPaging } from '@/hooks/useCoupon'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useToggle } from '@/hooks/useLockFn'
|
||||
import { getDistributorWithDrawList } from '@/api/distributor_center'
|
||||
import { convertChineseMonthToDate } from '@/utils/util'
|
||||
|
||||
const date = `${new Date().getFullYear()}年${new Date().getMonth() + 1}月`
|
||||
const today = new Date().getDate()
|
||||
const day = today < 10 ? String(today).padStart(2, '0') : today
|
||||
|
||||
const params = {
|
||||
year: true,
|
||||
month: true,
|
||||
day: false,
|
||||
hour: false,
|
||||
minute: false,
|
||||
second: false
|
||||
}
|
||||
|
||||
const dataList = ref<any>([])
|
||||
const currentDate = ref(date)
|
||||
const monthTotalPeople = ref(0)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
const { show, handleToggle } = useToggle()
|
||||
|
||||
const config = {
|
||||
initParams: {
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
date: convertChineseMonthToDate(unref(currentDate))
|
||||
},
|
||||
requestApi: getDistributorWithDrawList
|
||||
}
|
||||
const { paging, queryList, refresh } = useZPaging(config.initParams, config.requestApi)
|
||||
|
||||
const getWithdrawRecord = async () => {
|
||||
try {
|
||||
const params = {
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
date: convertChineseMonthToDate(unref(currentDate))
|
||||
}
|
||||
const data = await getDistributorWithDrawList(params)
|
||||
monthTotalPeople.value = Number(data) ?? 0
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
onMounted(getWithdrawRecord)
|
||||
let navbarHeight = ref(95)
|
||||
onMounted(() => {
|
||||
uni.createSelectorQuery()
|
||||
.select('.u-navbar')
|
||||
.boundingClientRect(data => {
|
||||
navbarHeight.value = (data?.height || 95) as number
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
|
||||
/**选择日期 */
|
||||
const handleConfirm = res => {
|
||||
const { year, month } = res
|
||||
currentDate.value = `${year}年${month}月`
|
||||
refresh({ date: convertChineseMonthToDate(unref(currentDate)) })
|
||||
// getWithdrawRecord()
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.slot-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
margin-left: 110rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
.total-money {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
height: 86rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list {
|
||||
// height: calc(100vh - 88rpx);
|
||||
padding-top: 86rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,227 @@
|
|||
<template>
|
||||
<view class="withdraw">
|
||||
<view class="withdraw__total">
|
||||
<text>可提现金额(元)</text>
|
||||
<text>
|
||||
¥
|
||||
<text class="strong">
|
||||
{{ allowWithDrawMoney }}
|
||||
</text>
|
||||
</text>
|
||||
</view>
|
||||
<view class="withdraw__money">
|
||||
<view class="title">提现金额</view>
|
||||
<view class="line">
|
||||
<text class="unit">¥</text>
|
||||
<input
|
||||
:value="withdrawMoney"
|
||||
class="input"
|
||||
placeholder-class="placeholder"
|
||||
placeholder-style="placeholder"
|
||||
placeholder="请输入金额"
|
||||
@input="handleWithDrawMoneyInput"
|
||||
/>
|
||||
<text class="all" @click="allWithdraw">全部提现</text>
|
||||
</view>
|
||||
<div v-if="notValidKey" class="text-error">{{ notValidMap[notValidKey] }}</div>
|
||||
<view class="channel" @click="handleShowPicker">
|
||||
<text>提现至</text>
|
||||
<view class="value">
|
||||
<text class="label">{{ channel.label }}</text>
|
||||
<u-picker
|
||||
mode="selector"
|
||||
v-model="showPicker"
|
||||
:range="channelOptions"
|
||||
range-key="label"
|
||||
:default-selector="[0]"
|
||||
@confirm="handleConfirm"
|
||||
></u-picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn" @click="handleWithDraw">
|
||||
<text>申请提现</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { toast } from '@/utils/util'
|
||||
import { computed, ref, unref, onMounted } from 'vue'
|
||||
import { applyForWithdraw } from '@/api/wallet'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useLockFn } from '@/hooks/useLockFn'
|
||||
import { getDistributorCenter } from '@/api/distributor_center'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
const channelOptions = [{ value: 1, label: '微信' }]
|
||||
const allowWithDrawMoney = ref('0')
|
||||
const withdrawMoney = ref()
|
||||
let channel = ref({
|
||||
label: '微信',
|
||||
value: 1
|
||||
})
|
||||
const showPicker = ref(false)
|
||||
const handleShowPicker = () => {
|
||||
showPicker.value = true
|
||||
}
|
||||
const notValidMap = {
|
||||
formatError: '金额格式错误',
|
||||
balance: '余额不足',
|
||||
less: '金额不能小于0.1'
|
||||
}
|
||||
const notValidKey = ref('')
|
||||
/**绑定提现金额 */
|
||||
const handleWithDrawMoneyInput = (event: any) => {
|
||||
const {
|
||||
detail: { value }
|
||||
} = event
|
||||
withdrawMoney.value = value
|
||||
validateAmount(value)
|
||||
}
|
||||
|
||||
// 全部提现
|
||||
const allWithdraw = () => {
|
||||
withdrawMoney.value = allowWithDrawMoney.value
|
||||
validateAmount(withdrawMoney.value)
|
||||
}
|
||||
|
||||
const validateAmount = (value: string) => {
|
||||
if (!value) return true
|
||||
const regex = /^(0|[1-9]\d{0,8})(\.\d{1,2})?$/
|
||||
if (!regex.test(value)) notValidKey.value = 'formatError'
|
||||
else if (withdrawMoney.value > allowWithDrawMoney.value) notValidKey.value = 'balance'
|
||||
else if (withdrawMoney.value < 0.1) notValidKey.value = 'less'
|
||||
else notValidKey.value = ''
|
||||
}
|
||||
/**选择提现方式 */
|
||||
const handleConfirm = (list: any) => {
|
||||
const index = list[0]
|
||||
channel.value = {
|
||||
label: channelOptions[index].label,
|
||||
value: channelOptions[index].value
|
||||
}
|
||||
}
|
||||
/**申请提现 */
|
||||
const handleWithDraw = () => {
|
||||
if (!withdrawMoney.value) return toast('请输入提现金额')
|
||||
if (notValidKey.value) return
|
||||
applyForWithdraw({
|
||||
distributorId: unref(userInfo).distributorId,
|
||||
withdrawMoney: withdrawMoney.value
|
||||
// userName: ''
|
||||
}).then(res => {
|
||||
withdrawMoney.value = ''
|
||||
toast('已发起提现申请')
|
||||
lockFn()
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => lockFn())
|
||||
|
||||
const { lockFn } = useLockFn(async () => {
|
||||
const data = await getDistributorCenter({ distributorId: unref(userInfo).distributorId })
|
||||
if (data?.canWithdrawCommission) {
|
||||
allowWithDrawMoney.value = Number(data?.canWithdrawCommission).toFixed(2)
|
||||
} else {
|
||||
allowWithDrawMoney.value = '0.00'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.withdraw {
|
||||
height: 600rpx;
|
||||
background-color: #2077fe;
|
||||
|
||||
&__total {
|
||||
color: $white;
|
||||
padding: 40rpx 30rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
line-height: 1.6;
|
||||
|
||||
.strong {
|
||||
font-size: 60rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&__money {
|
||||
background-color: #fff;
|
||||
margin: 30rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 30rpx 40rpx;
|
||||
|
||||
.unit {
|
||||
font-size: 84rpx;
|
||||
}
|
||||
|
||||
.line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
padding: 0 12rpx;
|
||||
font-size: 60rpx;
|
||||
height: 90rpx;
|
||||
}
|
||||
|
||||
:deep(.placeholder) {
|
||||
color: #777;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.all {
|
||||
color: #2077fe;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.channel {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 30rpx;
|
||||
color: #919191;
|
||||
|
||||
.value {
|
||||
position: relative;
|
||||
|
||||
.label {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-top: 1px solid #919191;
|
||||
border-right: 1px solid #919191;
|
||||
transform: translateY(-50%) rotate(45deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #2077fe;
|
||||
height: 100rpx;
|
||||
border-radius: 50rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
margin-top: 80rpx;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view class="withdrawal-record" v-for="(item, index) in withdrawApplyLists" :key="index">
|
||||
<view class="withdrawal-record-item" @click="toDetail(item.id)">
|
||||
<view class="flex">
|
||||
<view class="flex-1 withdrawal-record-item-text">提现至{{ item.type_desc }}</view>
|
||||
<view class="withdrawal-record-item-amount">+{{ item.money }}</view>
|
||||
</view>
|
||||
|
||||
<view class="flex justify-between mt-[10rpx]">
|
||||
<view class="withdrawal-record-item-time"> {{ item.create_time }} </view>
|
||||
|
||||
<template v-if="item.status == 1">
|
||||
<view class="text-xs" style="color: #0cc267">{{ item.status_desc }}</view>
|
||||
</template>
|
||||
|
||||
<template v-if="item.status == 2">
|
||||
<view class="text-xs" style="color: #ff2c3c">{{ item.status_desc }}</view>
|
||||
</template>
|
||||
|
||||
<template v-if="item.status == 3">
|
||||
<view class="text-xs" style="color: #666">{{ item.status_desc }}</view>
|
||||
</template>
|
||||
|
||||
<template v-if="item.status == 4">
|
||||
<view class="text-xs" style="color: #ff2c3c">{{ item.status_desc }}</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<template v-if="item.verify_remark != null">
|
||||
<template v-if="item.verify_remark != ''">
|
||||
<view class="review-tips">审核提示:{{ item.verify_remark }}</view>
|
||||
</template>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
withdrawApplyLists: any
|
||||
}>(),
|
||||
{
|
||||
withdrawApplyLists: []
|
||||
}
|
||||
)
|
||||
|
||||
const toDetail = (id: any) => {
|
||||
uni.navigateTo({
|
||||
url: `/bundle/pages/withdrawal_details/withdrawal_details?id=${id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.withdrawal-record {
|
||||
padding-top: 10rpx;
|
||||
|
||||
.withdrawal-record-item {
|
||||
background-color: #fff;
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1rpx solid #ebebeb;
|
||||
|
||||
.withdrawal-record-item-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.withdrawal-record-item-amount {
|
||||
font-size: 34rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.withdrawal-record-item-time {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.review-tips {
|
||||
font-size: 24rpx;
|
||||
color: #ff2c3c;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.success {
|
||||
font-size: 24rpx;
|
||||
color: #0cc267;
|
||||
}
|
||||
|
||||
.fail {
|
||||
font-size: 24rpx;
|
||||
color: #ff2c3c;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<z-paging
|
||||
auto-show-back-to-top
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
@query="queryList"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
>
|
||||
<Card :withdrawApplyLists="dataList" />
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import Card from './card.vue'
|
||||
import { apiWithdrawLists } from '@/api/wallet'
|
||||
|
||||
const paging = shallowRef<any>(null)
|
||||
const dataList = ref<any>([])
|
||||
|
||||
const queryList = async (page_no: number, page_size: number) => {
|
||||
try {
|
||||
const { lists } = await apiWithdrawLists({
|
||||
page_no,
|
||||
page_size
|
||||
})
|
||||
paging.value.complete(lists)
|
||||
} catch (e) {
|
||||
console.log('报错=>', e)
|
||||
//TODO handle the exception
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<view class="withdrawal-record">
|
||||
<view class="List pt-[20rpx]" v-if="isLogin">
|
||||
<List />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import List from './components/list.vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
// 是否登录
|
||||
const userStore = useUserStore()
|
||||
const isLogin = computed(() => userStore.token)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.List {
|
||||
height: calc(100vh - env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,307 @@
|
|||
<template>
|
||||
<view class="withdrawal-details">
|
||||
<view class="bg-white withdrawal-details-item">
|
||||
<view class="whether-pass" v-if="formData.status == 1">
|
||||
<image src="@/bundle/static/images/icon_cashOut_wait.png" />
|
||||
<view class="mt-[12rpx]">{{ formData.status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<view class="whether-pass" v-if="formData.status == 2">
|
||||
<image src="@/bundle/static/images/icon_cashOut_wait.png" />
|
||||
<view class="mt-[12rpx]">{{ formData.status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<view class="whether-pass" v-if="formData.status == 3">
|
||||
<image src="@/bundle/static/images/icon_cashOut_success.png" />
|
||||
<view class="mt-[12rpx]">{{ formData.status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<view class="whether-pass" v-if="formData.status == 4">
|
||||
<image src="@/bundle/static/images/icon_cashOut_fail.png" />
|
||||
<view class="mt-[12rpx]">{{ formData.status_desc }}</view>
|
||||
</view>
|
||||
|
||||
<view class="withdrawal-money">
|
||||
<view class="withdrawal-money-icon"> ¥ </view>
|
||||
<view class="withdrawal-money-text"> {{ formData.money }} </view>
|
||||
</view>
|
||||
|
||||
<view class="ml-[30rpx] mt-[20rpx] mr-[30rpx] pb-[20rpx]">
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>提现单号</view>
|
||||
<view>{{ formData.sn }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>申请时间</view>
|
||||
<view>{{ formData.create_time }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>提现至</view>
|
||||
<view>{{ formData.type_desc }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>服务费</view>
|
||||
<view>{{ formData.handling_fee }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>实际到账</view>
|
||||
<view>{{ formData.left_money }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 银行卡提现 -->
|
||||
<view
|
||||
class="ml-[30rpx] mt-[20rpx] mr-[30rpx] pt-[20rpx]"
|
||||
style="border-top: 1rpx solid #e5e5e5"
|
||||
v-if="formData.type == 3"
|
||||
>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>银行卡账号</view>
|
||||
<view>{{ formData.account }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>持卡人姓名</view>
|
||||
<view>{{ formData.real_name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>提现银行</view>
|
||||
<view>{{ formData.bank }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>银行支行</view>
|
||||
<view>{{ formData.subbank }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>备注说明</view>
|
||||
<view>{{ formData.transfer_remark }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付宝收款码提现 -->
|
||||
<view
|
||||
class="ml-[30rpx] mt-[20rpx] mr-[30rpx] pt-[20rpx]"
|
||||
style="border-top: 1rpx solid #e5e5e5"
|
||||
v-if="formData.type == 5"
|
||||
>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>支付宝账号</view>
|
||||
<view>{{ formData.account }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>真实姓名</view>
|
||||
<view>{{ formData.real_name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>支付宝收款码</view>
|
||||
<u-image
|
||||
height="160"
|
||||
width="160"
|
||||
:src="formData.money_qr_code"
|
||||
@click="showImage([formData.money_qr_code])"
|
||||
>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>备注说明</view>
|
||||
<view>{{ formData.transfer_remark }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 微信收款码提现 -->
|
||||
<view
|
||||
class="ml-[30rpx] mt-[20rpx] mr-[30rpx] pt-[20rpx]"
|
||||
style="border-top: 1rpx solid #e5e5e5"
|
||||
v-if="formData.type == 4"
|
||||
>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>微信账号</view>
|
||||
<view>{{ formData.account }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>真实姓名</view>
|
||||
<view>{{ formData.real_name }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>微信收款码</view>
|
||||
<u-image
|
||||
height="160"
|
||||
width="160"
|
||||
:src="formData.money_qr_code"
|
||||
@click="showImage([formData.money_qr_code])"
|
||||
>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>备注说明</view>
|
||||
<view>{{ formData.transfer_remark }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转账凭证 -->
|
||||
<view
|
||||
class="mx-[30rpx] my-[20rpx] pt-[20rpx]"
|
||||
style="border-top: 1rpx solid #e5e5e5"
|
||||
v-if="formData.status == 3 || formData.status == 4"
|
||||
>
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>转账凭证</view>
|
||||
<u-image
|
||||
height="160"
|
||||
width="160"
|
||||
:src="formData.transfer_voucher"
|
||||
v-if="formData.transfer_voucher"
|
||||
@click="showImage([formData.transfer_voucher])"
|
||||
>
|
||||
</u-image>
|
||||
</view>
|
||||
|
||||
<view class="flex justify-between withdrawal-content">
|
||||
<view>转账说明</view>
|
||||
<view>{{ formData.transfer_remark || '-' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="check-withdrawal-record">
|
||||
<button class="Btn" @click="toRecord">查看历史提现记录</button>
|
||||
|
||||
<view class="mt-[20rpx]">
|
||||
<button class="Btn del_Btn" @click="toHome">返回首页</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="review-success-tips">* 审核成功后约72小时内到账,请留意账户明细</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { apiWithdrawDetail } from '@/api/wallet'
|
||||
|
||||
const withdrawId = ref<number | string>('')
|
||||
|
||||
const formData = ref({
|
||||
type: '', // 提现类型:1-钱包余额;2-微信零钱;3-银行卡;4-微信收款码;5-支付宝收款码
|
||||
type_desc: '', // 提现类型描述
|
||||
status: '', // 提现状态:1-待提现2-提现中3-提现成功4-提现失败
|
||||
status_desc: '', // 提现状态描述
|
||||
money: '', // 提现金额
|
||||
money_qr_code: '', // 收款码
|
||||
create_time: '', // 提现申请时间
|
||||
sn: '', // 提现编号
|
||||
handling_fee: '', // 服务费
|
||||
account: '', // 提现账号
|
||||
real_name: '', // 提现人姓名
|
||||
bank: '', // 提现银行
|
||||
subbank: '', // 提现支行
|
||||
transfer_voucher: '', // 转账凭证
|
||||
transfer_remark: '' // 转账备注
|
||||
})
|
||||
|
||||
// 获取提现申请详情
|
||||
const getWithdrawDetail = () => {
|
||||
apiWithdrawDetail({ id: withdrawId.value }).then((res: any) => {
|
||||
formData.value = res
|
||||
})
|
||||
}
|
||||
|
||||
const toRecord = () => {
|
||||
uni.redirectTo({
|
||||
url: '/bundle/pages/withdraw_record/withdraw_record'
|
||||
})
|
||||
}
|
||||
|
||||
const toHome = () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
|
||||
const showImage = (list: any) => {
|
||||
uni.previewImage({
|
||||
urls: list,
|
||||
current: 1
|
||||
})
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
withdrawId.value = options?.id || ''
|
||||
getWithdrawDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.withdrawal-details {
|
||||
padding: 20rpx;
|
||||
|
||||
.withdrawal-details-item {
|
||||
.whether-pass {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: 34rpx;
|
||||
font-weight: 500;
|
||||
padding-top: 40rpx;
|
||||
|
||||
image {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.withdrawal-money {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #ff2c3c;
|
||||
margin-top: 20rpx;
|
||||
padding-bottom: 52rpx;
|
||||
|
||||
.withdrawal-money-icon {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.withdrawal-money-text {
|
||||
font-size: 46rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.withdrawal-content {
|
||||
padding-bottom: 34rpx;
|
||||
}
|
||||
|
||||
.check-withdrawal-record {
|
||||
padding: 40rpx 0rpx 50rpx 0;
|
||||
margin: 0 30rpx;
|
||||
border-top: 1px solid #ebebeb;
|
||||
|
||||
.Btn {
|
||||
@apply bg-white text-base text-black leading-[72rpx] h-[72rpx] rounded-full;
|
||||
border: 1px solid $u-type-primary;
|
||||
background-color: $u-type-primary;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
.del_Btn {
|
||||
border: 1px solid $u-type-primary;
|
||||
background-color: #c9c9c900;
|
||||
color: $u-type-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.review-success-tips {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
button::after {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="28" height="28" style="border-color: rgba(0,0,0,0);border-width: bpx;border-style: undefined" filter="none">
|
||||
|
||||
<g>
|
||||
<path d="M25.467 11.733c3.239 0 4.442 2.018 3.739 4.629l-1.438 5.403-1.44 5.402-0.183 0.693h-23.611v-15.235h8.621l2.496-7.532c0.060-0.285 0.195-0.631 0.452-0.981 0.477-0.649 1.219-1.045 2.187-1.045 1.039 0 1.78 0.517 2.165 1.323 0.212 0.446 0.279 0.869 0.279 1.195v6.149h6.733zM24.709 25.994v0zM25.963 21.285l1.44-5.405c0.409-1.521-0.043-2.281-1.936-2.281h-8.599v-8.016c-0.008-0.143-0.043-0.276-0.101-0.397l0.003 0.006c-0.089-0.185-0.197-0.26-0.48-0.26-0.357 0-0.548 0.102-0.683 0.284-0.057 0.075-0.102 0.163-0.129 0.259l-0.001 0.006-0.035 0.141-2.939 8.869h-8.102v11.502h20.309l1.253-4.708zM8.8 13.733v13.867h1.867v-13.867z" fill="rgba(243.015,96.9,96.9,1)"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 877 B |
|
@ -0,0 +1,105 @@
|
|||
<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 { addUnit } from '@/utils/util'
|
||||
import { isBoolean } from 'lodash'
|
||||
import { computed, CSSProperties, onUnmounted } from 'vue'
|
||||
import { uploadFile } from '@/utils/util'
|
||||
|
||||
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 styles = computed<CSSProperties>(() => {
|
||||
const size = addUnit(props.size)
|
||||
return {
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: '50%' // 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 (path: string) => {
|
||||
uni.showLoading({
|
||||
title: '正在上传中...'
|
||||
})
|
||||
try {
|
||||
const res: any = await uploadFile(path)
|
||||
uni.hideLoading()
|
||||
emit('update:modelValue', res.path)
|
||||
} 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>
|
|
@ -0,0 +1,123 @@
|
|||
<template>
|
||||
<u-popup v-model="show" mode="bottom" border-radius="30" :closeable="true" @close="close">
|
||||
<view class="box">
|
||||
<view class="cheap">优惠</view>
|
||||
<view
|
||||
class="flex justify-center items-center text-[#A1A1A1] h-full"
|
||||
v-if="userCoupon?.length == 0 && waituselists?.length == 0"
|
||||
>
|
||||
暂无可用优惠券</view
|
||||
>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="state"
|
||||
@query="upCallback"
|
||||
:fixed="false"
|
||||
height="100%"
|
||||
v-else
|
||||
>
|
||||
<view v-if="userCoupon?.length !== 0"
|
||||
>已领取优惠券(共{{ userCoupon?.length }}张)</view
|
||||
>
|
||||
|
||||
<!-- <view class="flex mt-[20rpx]" v-for="(item, index) in 3" :key="index"> -->
|
||||
<cheap-card v-for="item in userCoupon" :key="item.id" :item="item">
|
||||
<template #btn>
|
||||
<button class="use-btn" @click="toUse">去使用</button>
|
||||
</template>
|
||||
</cheap-card>
|
||||
|
||||
<view class="mt-[20rpx]" v-if="waituselists?.length !== 0"
|
||||
>待领取优惠券(共{{ waituselists?.length }}张)</view
|
||||
>
|
||||
<cheap-card v-for="item in waituselists" :key="item.id" :item="item">
|
||||
<template #btn>
|
||||
<button class="take-btn" @click="toTake">立即领取</button>
|
||||
</template>
|
||||
</cheap-card>
|
||||
</z-paging>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
// import { apiUserCouponList } from '@/api/coupon'
|
||||
const show = ref(false)
|
||||
const state = ref({})
|
||||
const upCallback = () => {
|
||||
console.log(111)
|
||||
}
|
||||
const props = defineProps({
|
||||
couponlists: {
|
||||
type: Array<any>
|
||||
},
|
||||
good_id: {
|
||||
type: Number
|
||||
}
|
||||
})
|
||||
const open = () => {
|
||||
show.value = true
|
||||
}
|
||||
const close = () => {
|
||||
show.value = false
|
||||
}
|
||||
defineExpose({ open })
|
||||
const waituselists = computed(() => {
|
||||
return props.couponlists?.filter((item) => {
|
||||
return item.btn == 1 || item.btn == 2
|
||||
})
|
||||
})
|
||||
const userCoupon = ref([])
|
||||
// const getCouponLists = async () => {
|
||||
// const { lists } = await apiUserCouponList({ goods_id: props.good_id, status: 1 })
|
||||
// userCoupon.value = lists
|
||||
// }
|
||||
// getCouponLists()
|
||||
const toUse = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
close()
|
||||
}
|
||||
const toTake = () => {
|
||||
uni.navigateTo({
|
||||
url: '/bundle/pages/receive_coupon/receive_coupon'
|
||||
})
|
||||
close()
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
.box {
|
||||
height: 70vh;
|
||||
padding: 30rpx;
|
||||
background-color: #f7f7f7;
|
||||
padding-top: 100rpx;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
.cheap {
|
||||
color: rgba(0, 0, 0, 1);
|
||||
font-weight: bold;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
top: 32rpx;
|
||||
}
|
||||
.use-btn {
|
||||
border-radius: 40rpx;
|
||||
background-color: rgba(255, 215, 0, 1);
|
||||
width: 130rpx;
|
||||
height: 56rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 56rpx;
|
||||
margin: 0;
|
||||
}
|
||||
.take-btn {
|
||||
border-radius: 40rpx;
|
||||
background-color: rgba(255, 0, 0, 1);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
height: 56rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 56rpx;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,280 @@
|
|||
<template>
|
||||
<view class="card flex mt-[20rpx]" :class="{ 'gray': !isHidden }">
|
||||
<view class="card__top">
|
||||
<view class="card__left">
|
||||
<text class="font-bold text-xl">{{ item.name }}</text>
|
||||
<text>{{ couponDesc(item) }}</text>
|
||||
<text class="useTime">使用有效期:2023.08.25-2023.09.25</text>
|
||||
<view class="detail" @click="handleToggle(item)">
|
||||
<text>详细信息</text>
|
||||
<text :class="['icon', { 'active': item.isToggle }]"></text>
|
||||
</view>
|
||||
<!-- 已使用、已过期 -->
|
||||
<!-- <view class="outime" v-if="!isHidden">
|
||||
<text>{{ textMap[tabStatus!] }}</text>
|
||||
</view> -->
|
||||
<slot name="selection"></slot>
|
||||
</view>
|
||||
<view class="card__right">
|
||||
<text>¥<text class="money">20.00</text></text>
|
||||
<text class="label">立减券</text>
|
||||
<text v-if="isHidden" class="btn__link" @click="handleNavigate">立即使用</text>
|
||||
<slot name="manual"></slot>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card__bottom" v-show="item.isToggle">
|
||||
<view>限品类:仅可购买生鲜部分商品</view>
|
||||
<view>券编号:35394387276</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { COUPON_INJECTION_KEY } from '@/config/symbol'
|
||||
import { computed, inject, ref, watchEffect } from 'vue'
|
||||
// import { apiReceiveCoupon } from '@/api/coupon'
|
||||
|
||||
interface CouponProp {
|
||||
name: string
|
||||
money: number
|
||||
getType: number //发放方式
|
||||
channelType: number //分销员/指定用户
|
||||
useGoodsType: number,//优惠券类型
|
||||
conditionType: number,//使用门槛
|
||||
sendTimeStart: number, //发放开始时间
|
||||
sendTimeEnd: number,//发放结束时间
|
||||
useTimeStart: number,//使用开始时间
|
||||
useTimeEnd: number,//使用结束时间
|
||||
sendTotal: number,
|
||||
sendTotalType: number//发放数量
|
||||
getNum: number, //领取数量
|
||||
status: number,//发放状态
|
||||
conditionMoney: number | string,//有门槛金额
|
||||
distributors: any[]//渠道选中分销员
|
||||
goodsCategoryDetailVos: any[],//品类券选中值
|
||||
goodsDetailVos: any[]//商品券选中列表
|
||||
isToggle?: boolean //是否展开
|
||||
}
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item: any
|
||||
status?: number | undefined | string // 状态:1-未使用;2-已使用;3-已过期
|
||||
showBtn?: boolean // 是否显示按钮
|
||||
}>(),
|
||||
{
|
||||
item: {},
|
||||
status: 1,
|
||||
showBtn: true
|
||||
}
|
||||
)
|
||||
const textMap: Record<number, string> = {
|
||||
2: '已使用',
|
||||
3: '已过期'
|
||||
}
|
||||
const parent = inject(COUPON_INJECTION_KEY)
|
||||
|
||||
const tabStatus = computed(() => parent?.tabStatus.value)
|
||||
const isHidden = computed(() => tabStatus.value === 1) /**status:已使用不可见 */
|
||||
|
||||
/*优惠券类型+有门槛/优惠券类型+无门槛*/
|
||||
function generateCouponText(row: any) {
|
||||
const { useGoodsType, conditionType, conditionMoney, money } = row
|
||||
const conditions = [
|
||||
{ useGoodsType: 1, conditionType: 1 },
|
||||
{ useGoodsType: 1, conditionType: 2 },
|
||||
{ useGoodsType: 2, conditionType: 1 },
|
||||
{ useGoodsType: 2, conditionType: 2 },
|
||||
{ useGoodsType: 3, conditionType: 1 },
|
||||
{ useGoodsType: 3, conditionType: 2 },
|
||||
]
|
||||
const useGoodsTypeMap: Record<number, string> = {
|
||||
1: '所有',
|
||||
2: '相应品类',
|
||||
3: '相应商品'
|
||||
}
|
||||
const flag = conditions.some(condition => condition.useGoodsType === useGoodsType && condition.conditionType === conditionType)
|
||||
if (!flag) return
|
||||
const condition = conditionType === 1 ? `消费立减${money}` : `满${money}减${conditionMoney}`
|
||||
return `${useGoodsTypeMap[useGoodsType]}服务${condition}元`
|
||||
}
|
||||
|
||||
const couponDesc = computed(() => (row: CouponProp) => generateCouponText(row))
|
||||
|
||||
/**切换显示详细信息 */
|
||||
const handleToggle = (item: CouponProp) => {
|
||||
item.isToggle = !item.isToggle
|
||||
}
|
||||
|
||||
const timeStamp = ref<number | string | undefined>()
|
||||
const StartTime = Date.now() / 1000
|
||||
const EndTime = +props.item.invalid_time
|
||||
watchEffect(() => {
|
||||
timeStamp.value = Math.floor(EndTime - StartTime) * 1000
|
||||
})
|
||||
|
||||
/**
|
||||
* 1、通用券:跳转到首页
|
||||
* 2、品类券:对应品类页面
|
||||
* 3、商品券:对应商品页面
|
||||
*/
|
||||
const navigateMap: Record<number, string> = {
|
||||
1: '/pages/index/index',
|
||||
2: '/bundle/pages/category_goods_coupon/goods_category',
|
||||
3: '/bundle/pages/category_goods_coupon/goods'
|
||||
}
|
||||
const handleNavigate = (e: Event) => {
|
||||
e.stopPropagation()
|
||||
const { useGoodsType } = props.item
|
||||
const url = navigateMap[useGoodsType]
|
||||
uni.navigateTo({ url: url })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 8rpx 20rpx 0 #F3F3F4;
|
||||
margin: 30rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&__top {
|
||||
display: flex;
|
||||
|
||||
.card__left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.8;
|
||||
background-color: #fff;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
position: relative;
|
||||
|
||||
.useTime {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.detail {
|
||||
color: #999;
|
||||
font-size: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #999;
|
||||
border-radius: 999px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 6rpx;
|
||||
height: 6rpx;
|
||||
border-left: 1px solid #fff;
|
||||
border-bottom: 1px solid #fff;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::before {
|
||||
transform: translate(-50%, -50%) rotate(135deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.outime {
|
||||
position: absolute;
|
||||
top: 6rpx;
|
||||
right: -78rpx;
|
||||
padding: 10rpx 80rpx;
|
||||
background-color: #777;
|
||||
color: #fff;
|
||||
transform: rotate(45deg);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.card__right {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background-image: linear-gradient(to left, #495aff 0%, #4481eb 100%);
|
||||
color: #fff;
|
||||
// background-image: linear-gradient(45deg, #FEF4F0 0%, #ffdde1 100%);
|
||||
// color: #fc5531;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.money {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
left: -10rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #F8F8F9;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -10rpx;
|
||||
left: -10rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #F8F8F9;
|
||||
border-radius: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.btn__link {
|
||||
background-color: #fff;
|
||||
color: #485FFC;
|
||||
padding: 8rpx 16rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 999px;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__bottom {
|
||||
width: 100%;
|
||||
padding: 12rpx 20rpx;
|
||||
border-top: 1px solid #eee;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.8;
|
||||
color: #999;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.gray {
|
||||
// filter: grayscale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,197 @@
|
|||
<template>
|
||||
<view :class="status !== 1 ? 'gray-scale' : ''" class="card flex mt-[20rpx]">
|
||||
<view class="left text-center">
|
||||
<view class="pt-[36rpx] text-[#ff2c3c]">
|
||||
<text class="text-2xl text-semibold">¥</text
|
||||
><text class="text-[40rpx] text-semibold">{{ item.money }}</text>
|
||||
</view>
|
||||
<view class="pt-[8rpx] pb-[32rpx] text-muted text-[24rpx] truncate">{{
|
||||
item.coupon_describe
|
||||
}}</view>
|
||||
</view>
|
||||
|
||||
<view class="right flex-1 px-[20rpx] pt-[20rpx] bg-white">
|
||||
<u-image
|
||||
v-show="item.btn === 2 || showDoneBtn"
|
||||
class="img"
|
||||
src="@/static/images/received.png"
|
||||
width="70"
|
||||
height="54"
|
||||
/>
|
||||
<view class="title text-lg text-[#333] font-medium pb-[16rpx] truncate">{{
|
||||
item.name
|
||||
}}</view>
|
||||
<view class="flex justify-between">
|
||||
<view
|
||||
class="date text-xs text-[#666] py-[11rpx]"
|
||||
:class="{ 'w-[307rpx] break-all': showBtn }"
|
||||
>
|
||||
<view v-if="item.is_countdown && timeStamp >= 0" class="text-[#ff2c3c] flex">
|
||||
即将过期,仅剩余
|
||||
|
||||
<u-count-down
|
||||
:timestamp="timeStamp"
|
||||
format="HH:mm:ss"
|
||||
:font-size="24"
|
||||
:separator-size="26"
|
||||
@end="timeStamp = 0"
|
||||
/>
|
||||
</view>
|
||||
<text v-else>{{ item.use_time_desc }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="showBtn" class="btns flex-1">
|
||||
<!-- 1-立即领取;2-继续领取;3-已领完; -->
|
||||
<button
|
||||
class="btn-one text-[#ff2c3c] bg-white"
|
||||
v-if="item.btn !== 3 && showDoneBtn"
|
||||
@click="goPage"
|
||||
>
|
||||
去使用
|
||||
</button>
|
||||
<button
|
||||
class="btn-two bg-[#ff2c3c] text-white"
|
||||
v-if="item.btn === 1 && !showDoneBtn"
|
||||
@click="toReceive(item.id)"
|
||||
>
|
||||
立即领取
|
||||
</button>
|
||||
<button
|
||||
class="btn-two bg-[#ff2c3c] text-white"
|
||||
v-if="item.btn === 2 && !showDoneBtn"
|
||||
@click="toReceive(item.id)"
|
||||
>
|
||||
继续领取
|
||||
</button>
|
||||
<button class="btn-three bg-[#ccc] text-white" v-if="item.btn === 3" disabled>
|
||||
已领完
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer flex justify-between py-[8rpx] mt-[16rpx]">
|
||||
<!-- 1=全部商品;2=指定商品;3=指定商品不可用 -->
|
||||
<view class="text-xs text-[#666]" v-if="item?.use_goods_type === 1"
|
||||
>全部商品可用</view
|
||||
>
|
||||
<view class="text-xs text-[#666]" v-if="item?.use_goods_type === 2"
|
||||
>指定商品可用</view
|
||||
>
|
||||
<view class="text-xs text-[#666]" v-if="item?.use_goods_type === 3"
|
||||
>指定商品不可用</view
|
||||
>
|
||||
<u-icon
|
||||
v-show="item?.use_goods_type !== 1 && !isShow"
|
||||
name="arrow-up"
|
||||
@click="isShow = !isShow"
|
||||
/>
|
||||
<u-icon
|
||||
v-show="item?.use_goods_type !== 1 && isShow"
|
||||
name="arrow-down"
|
||||
@click="isShow = !isShow"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bg-white py-[15rpx] px-[20rpx]" v-show="isShow">
|
||||
<view class="text-[#999] text-xs pt-2">{{
|
||||
item.use_goods_type === 3 ? '不可用商品:' : '可用商品:'
|
||||
}}</view>
|
||||
<view v-for="iten in item.goods" :key="iten.id" class="text-[#999] text-xs pt-2">{{
|
||||
iten.name
|
||||
}}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watchEffect } from 'vue'
|
||||
// import { apiReceiveCoupon } from '@/api/coupon'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
item: any
|
||||
status?: number | undefined | string // 状态:1-未使用;2-已使用;3-已过期
|
||||
showBtn?: boolean // 是否显示按钮
|
||||
}>(),
|
||||
{
|
||||
item: {},
|
||||
status: 1,
|
||||
showBtn: true
|
||||
}
|
||||
)
|
||||
|
||||
const timeStamp = ref<number | string | undefined>()
|
||||
const StartTime = Date.now() / 1000
|
||||
const EndTime = +props.item.invalid_time
|
||||
watchEffect(() => {
|
||||
timeStamp.value = Math.floor(EndTime - StartTime) * 1000
|
||||
})
|
||||
|
||||
const isShow = ref(false)
|
||||
|
||||
// 去领优惠券
|
||||
const showDoneBtn = ref(false)
|
||||
const toReceive = async (id) => {
|
||||
// try {
|
||||
// await apiReceiveCoupon({ id })
|
||||
// uni.$u.toast('领取成功')
|
||||
// showDoneBtn.value = true
|
||||
// } catch (error) {
|
||||
// uni.$u.toast(error)
|
||||
// }
|
||||
}
|
||||
|
||||
// 页面跳转
|
||||
const goPage = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
-webkit-mask-image: radial-gradient(circle at 178rpx 10rpx, transparent 10rpx, red 11rpx),
|
||||
radial-gradient(closest-side circle at 50%, red 99%, transparent 100%);
|
||||
-webkit-mask-size: 100%, 4rpx 8rpx;
|
||||
-webkit-mask-repeat: repeat, repeat-y;
|
||||
-webkit-mask-position: 0 -10rpx, 175rpx;
|
||||
-webkit-mask-composite: source-out;
|
||||
mask-composite: subtract;
|
||||
.left {
|
||||
box-sizing: border-box;
|
||||
width: 180rpx;
|
||||
background: linear-gradient(to right, #ff2c3c 11rpx, white 15rpx);
|
||||
|
||||
border-top-left-radius: 14rpx;
|
||||
border-bottom-left-radius: 14rpx;
|
||||
}
|
||||
.right {
|
||||
position: relative;
|
||||
.img {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
.btn-one,
|
||||
.btn-two,
|
||||
.btn-three {
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
border-radius: 9999rpx;
|
||||
border: 1rpx solid #ff2c3c;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.btn-three {
|
||||
border: 1rpx solid #ccc;
|
||||
}
|
||||
.footer {
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
}
|
||||
}
|
||||
.gray-scale {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,328 @@
|
|||
<template>
|
||||
<view class="card flex mt-[20rpx]" :class="{ 'gray': isGray(item!) }">
|
||||
<view class="card__top">
|
||||
<view class="card__left overflow-hidden">
|
||||
<!-- 领取次数多张 -->
|
||||
<slot name="count"></slot>
|
||||
<text class="font-bold text-xl name truncate">{{ item!.name }}</text>
|
||||
<text>{{ couponDesc(item!) }}</text>
|
||||
<text class="useTime">{{ timeText }}{{ parseTime(item) }}</text>
|
||||
<!-- 详细信息 -->
|
||||
<block>
|
||||
<view
|
||||
class="detail"
|
||||
v-if="isVisibleCouponDetail(item!)"
|
||||
@click="handleToggle(item!)"
|
||||
>
|
||||
<text>详细信息</text>
|
||||
<text :class="['icon', { 'active': item!.isToggle }]"></text>
|
||||
</view>
|
||||
<view class="placeholder" v-else></view>
|
||||
</block>
|
||||
<slot name="selection"></slot>
|
||||
</view>
|
||||
<view class="card__right" :class="{ default: slots.selection }">
|
||||
<text>
|
||||
¥
|
||||
<text class="money">{{ item!.money }}</text>
|
||||
</text>
|
||||
<text class="label">{{ couponCondition(item!) }}</text>
|
||||
<!-- 已使用/已过期 -->
|
||||
<slot name="status"></slot>
|
||||
<!-- 立即使用 -->
|
||||
<slot name="use" :row="item"></slot>
|
||||
<!-- 立即领取 -->
|
||||
<slot name="manual"></slot>
|
||||
<slot name="expiringSoon"></slot>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 详细信息内容 -->
|
||||
<view class="card__bottom" v-show="item!.isToggle">
|
||||
<view v-if="slots.manual">领取说明:{{ '每人限领' + item!.getNum }}张</view>
|
||||
<view v-if="slots.manual">有效期截至时间:{{ item!.useTimeEnd }}</view>
|
||||
<view v-if="isLen(item!)">使用限制:{{ parseDetailLabel(item!) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType, computed, unref, useSlots } from 'vue'
|
||||
import {
|
||||
CouponEnum,
|
||||
UseConditionEnum,
|
||||
conditions,
|
||||
useGoodsTypeMap,
|
||||
CouponCountMap,
|
||||
CouponStatusEnum,
|
||||
showExpiringSoonDays
|
||||
} from '@/enums/appEnums'
|
||||
|
||||
interface CouponProp {
|
||||
name: string
|
||||
money: number
|
||||
getType: number //发放方式
|
||||
channelType: number //分销员/指定用户
|
||||
useGoodsType: number //优惠券类型
|
||||
conditionType: number //使用门槛
|
||||
sendTimeStart: string //发放开始时间
|
||||
sendTimeEnd: string //发放结束时间
|
||||
useTimeStart: string //使用开始时间
|
||||
useTimeEnd: string //使用结束时间
|
||||
sendTotal: number
|
||||
sendTotalType: number //发放数量
|
||||
getNum: number //领取数量
|
||||
status: number //发放状态
|
||||
conditionMoney: number | string //有门槛金额
|
||||
distributors: any[] //渠道选中分销员
|
||||
goodsCategoryDetailVos: any[] //品类券选中值
|
||||
goodsDetailVos: any[] //商品券选中列表
|
||||
isToggle?: boolean //是否展开
|
||||
goodsCategories: any[] //品类信息
|
||||
goods: any[] //商品信息
|
||||
useStatus: number //使用状态
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
item: Object as PropType<CouponProp>,
|
||||
default: () => ({}),
|
||||
activeTabName: String
|
||||
})
|
||||
const slots = useSlots()
|
||||
|
||||
const couponDesc = computed(() => (row: CouponProp) => generateCouponText(row))
|
||||
/**无门槛:立减 有门槛:满x元可用 */
|
||||
const couponCondition = computed(() => (row: CouponProp) => {
|
||||
const { conditionType, conditionMoney } = row
|
||||
return conditionType === UseConditionEnum.NO_CONDITION ? '立减券' : `满${conditionMoney}可用`
|
||||
})
|
||||
const isLen = computed(() => (row: CouponProp) => {
|
||||
const { goodsCategories, goods } = row
|
||||
return goodsCategories.length >= 1 || goods.length >= 1
|
||||
})
|
||||
/**
|
||||
* 1、通用券不需要
|
||||
* 2、useStatus:1需要
|
||||
*/
|
||||
/**显示详细信息 */
|
||||
const isVisibleCouponDetail = computed(() => (row: CouponProp) => {
|
||||
return unref(isLen)(row) || slots.manual
|
||||
})
|
||||
/**已使用、已过期可见 */
|
||||
const isGray = computed(() => {
|
||||
return (row: CouponProp) => {
|
||||
const { useStatus } = row
|
||||
return (
|
||||
useStatus === CouponEnum.ALIPAY ||
|
||||
useStatus === CouponEnum.OVERDUE ||
|
||||
props.activeTabName == CouponCountMap.notAvailableCount
|
||||
)
|
||||
}
|
||||
})
|
||||
const parseTime = computed(() => {
|
||||
return (row: any) => {
|
||||
const { useTimeStart, useTimeEnd } = row
|
||||
// const [useTimeStartDate] = useTimeStart.split(' ')
|
||||
// const [useTimeEndDate] = useTimeEnd.split(' ')
|
||||
|
||||
return `${parseSingleTime(useTimeStart)} 至 ${parseSingleTime(useTimeEnd)}`
|
||||
}
|
||||
})
|
||||
const parseSingleTime = (t: string) => {
|
||||
if (!t) return
|
||||
try {
|
||||
const [day, time] = t.split(' ')
|
||||
let hour = time.split(':')[0]
|
||||
hour.startsWith('0') ? (hour = hour.replace('0', '')) : ''
|
||||
return `${day} ${hour}时`
|
||||
} catch {}
|
||||
return t
|
||||
}
|
||||
const parseDetailLabel = computed(() => {
|
||||
/**品类券:仅可购买指定xx商品 商品券:仅可购买xx部分商品 */
|
||||
return (row: CouponProp) => {
|
||||
const { goodsCategories, useGoodsType, goods } = row
|
||||
if (!unref(isVisibleCouponDetail) || useGoodsType === 1) return
|
||||
const catrgoryNames =
|
||||
useGoodsType === 2 ? transformGoods(goodsCategories) : transformGoods(goods)
|
||||
const text = useGoodsType === 2 ? `指定${catrgoryNames}` : `${catrgoryNames}`
|
||||
return `仅可购买${text}服务`
|
||||
}
|
||||
})
|
||||
const timeText = computed(() => (slots.manual ? '领取时间:' : '有效期:'))
|
||||
const showExpiringSoon = computed(() => {
|
||||
return (row: CouponProp) => {
|
||||
const { useTimeEnd, useStatus } = row
|
||||
const currentTime = new Date().getTime()
|
||||
const endTime = Date.parse(useTimeEnd)
|
||||
const daysDifference = Math.abs(endTime - currentTime) / (60 * 60 * 24 * 1000)
|
||||
return daysDifference <= showExpiringSoonDays && useStatus == CouponStatusEnum.AVALIABLE
|
||||
}
|
||||
})
|
||||
|
||||
/**切换显示详细信息 */
|
||||
const handleToggle = (item: CouponProp) => {
|
||||
item.isToggle = !item.isToggle
|
||||
}
|
||||
|
||||
function transformGoods(goods: any[], separator = '、') {
|
||||
return goods.map(category => category.name).join(separator)
|
||||
}
|
||||
|
||||
/*优惠券类型+有门槛/优惠券类型+无门槛*/
|
||||
function generateCouponText(row: any) {
|
||||
const { useGoodsType, conditionType, conditionMoney, money } = row
|
||||
const flag = conditions.some(
|
||||
condition =>
|
||||
condition.useGoodsType === useGoodsType && condition.conditionType === conditionType
|
||||
)
|
||||
if (!flag) return
|
||||
const condition =
|
||||
conditionType === UseConditionEnum.NO_CONDITION
|
||||
? `消费立减${money}`
|
||||
: `满${conditionMoney}减${money}`
|
||||
return `${useGoodsTypeMap[useGoodsType]}服务${condition}元`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 8rpx 20rpx 0 $white4;
|
||||
margin: 30rpx;
|
||||
overflow: hidden;
|
||||
|
||||
&__top {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
.card__left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.8;
|
||||
background-color: $white;
|
||||
padding: 0 20rpx 20rpx;
|
||||
border-radius: 12rpx;
|
||||
position: relative;
|
||||
|
||||
.name {
|
||||
margin-top: 34rpx;
|
||||
}
|
||||
|
||||
.useTime {
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.detail {
|
||||
color: $gray2;
|
||||
font-size: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: $gray2;
|
||||
border-radius: 999px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 6rpx;
|
||||
height: 6rpx;
|
||||
border-left: 1px solid $white;
|
||||
border-bottom: 1px solid $white;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::before {
|
||||
transform: translate(-50%, -50%) rotate(135deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.card__right {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
// justify-content: center;
|
||||
text-align: center;
|
||||
background-image: linear-gradient(to left, $blue3 0%, $blue4 100%);
|
||||
color: $white;
|
||||
// background-image: linear-gradient(45deg, #FEF4F0 0%, #ffdde1 100%);
|
||||
// color: #fc5531;
|
||||
// padding: 20rpx 30rpx;
|
||||
padding-top: 44rpx;
|
||||
width: 90px;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.money {
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
left: -10rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: $white1;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -10rpx;
|
||||
left: -10rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: $white1;
|
||||
border-radius: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
&.default {
|
||||
justify-content: center;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__bottom {
|
||||
width: 100%;
|
||||
padding: 12rpx 20rpx;
|
||||
border-top: 1px solid $white3;
|
||||
background-color: $white;
|
||||
box-sizing: border-box;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.8;
|
||||
color: $gray2;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.gray {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<template v-if="type == 'list'">
|
||||
<block v-for="goodsItem in goodsList" :key="goodsItem.id">
|
||||
<view
|
||||
class="goods-item goods-list w-[340rpx] h-[480rpx] mt-[20rpx] flex bg-white"
|
||||
@click="goPage(goodsItem.id)"
|
||||
>
|
||||
<u-image :src="goodsItem.image" width="340" height="340"></u-image>
|
||||
<view class="goods-name truncate pl-[20rpx] pt-[20rpx]">
|
||||
{{ goodsItem.name }}
|
||||
</view>
|
||||
<view class="pl-[20rpx] mt-[10rpx]">
|
||||
<price
|
||||
:price="goodsItem?.price"
|
||||
:desc="goodsItem.unit || goodsItem?.unit_name"
|
||||
></price>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</template>
|
||||
|
||||
<template v-if="type == 'row'">
|
||||
<block v-for="goodsItem in goodsList" :key="goodsItem.id">
|
||||
<view
|
||||
class="goods-item w-[200rpx] h-[310rpx] mr-[49rpx] flex bg-white"
|
||||
@click="goPage(goodsItem.id)"
|
||||
>
|
||||
<u-image :src="goodsItem.image" width="200" height="200"></u-image>
|
||||
<view class="goods-name truncate pt-[20rpx]">
|
||||
{{ goodsItem.name }}
|
||||
</view>
|
||||
<view class="mt-[10rpx]">
|
||||
<price
|
||||
:price="goodsItem?.price"
|
||||
:desc="goodsItem.unit || goodsItem?.unit_name"
|
||||
></price>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Price from '@/components/price/index.vue'
|
||||
|
||||
/** Props Start **/
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type: string // 列表类型
|
||||
goodsList?: any // 商品数据
|
||||
}>(),
|
||||
{
|
||||
type: 'list',
|
||||
goodsList: []
|
||||
}
|
||||
)
|
||||
/** Props End **/
|
||||
|
||||
/** Methods Start **/
|
||||
/**
|
||||
* @param { string } url
|
||||
* @return { void }
|
||||
* @description 跳转到商品
|
||||
*/
|
||||
const goPage = (id: number | string) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?id=${id}`
|
||||
})
|
||||
}
|
||||
/** Methods End **/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-item {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.goods-name {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 28rpx;
|
||||
color: #222222;
|
||||
}
|
||||
}
|
||||
.goods-list {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.goods-list:nth-child(2n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,207 @@
|
|||
<template>
|
||||
<view title="热门服务">
|
||||
<block v-for="item1 in lists" :key="item1.id">
|
||||
<view
|
||||
class="goods-item mr-[20rpx]"
|
||||
@click="goPage(`/pages/goods/index?id=${item1.id}`)"
|
||||
>
|
||||
<view class="info-column">
|
||||
<view style="text-align: left">
|
||||
<view class="info-column-title">
|
||||
<view class="goods-scribing-title" v-if="item1.scribingPrice > 0">
|
||||
活动
|
||||
</view>
|
||||
<view class="goods-title overflow-ellipsis-line ellipsis-line1 flex-1">
|
||||
{{ item1.name }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="goods-desc overflow-ellipsis-line ellipsis-line1">
|
||||
{{ item1.remarks || ' ' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="image-column">
|
||||
<u-image
|
||||
:src="item1.image"
|
||||
class="good-img"
|
||||
width="100%"
|
||||
height="350"
|
||||
border-radius="20"
|
||||
></u-image>
|
||||
</view>
|
||||
|
||||
<view class="price-column" v-if="item1.scribingPrice > 0">
|
||||
<view>
|
||||
<view class="price-wrap">
|
||||
¥
|
||||
<span class="price">{{ numFilter(item1.price) }}</span>
|
||||
元
|
||||
<view class="subprice line-through">
|
||||
原价:¥{{ numFilter(item1.scribingPrice) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-base normal mt-1" style="color: #b0aeae">
|
||||
<text class="font-bold">{{ item1.orderNum }}</text>
|
||||
人预约过
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn_con">
|
||||
<view class="btn_scribing bg-primary">立即抢购</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="price-column" v-else>
|
||||
<view>
|
||||
<view class="price-wrap">
|
||||
¥
|
||||
<span class="price">{{ numFilter(item1.price) }}</span>
|
||||
元
|
||||
</view>
|
||||
<view class="text-base normal mt-1" style="color: #b0aeae">
|
||||
<text class="font-bold">{{ item1.orderNum }}</text>
|
||||
人预约过
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn_con">
|
||||
<view class="btn bg-primary">立即抢购</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
lists: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
const goPage = (url: string) => {
|
||||
uni.navigateTo({ url: url })
|
||||
}
|
||||
//截取当前数据到小数点后两位
|
||||
const numFilter = value => {
|
||||
const realVal = parseFloat(value).toFixed(2)
|
||||
return realVal
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 商品列表
|
||||
.goods-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
// padding: 20rpx;
|
||||
border-radius: 17rpx;
|
||||
background-color: #fff;
|
||||
//box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
margin: 20rpx;
|
||||
|
||||
.image-column {
|
||||
margin-left: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
width: 94%;
|
||||
|
||||
.good-img {
|
||||
width: 100%;
|
||||
//border-radius: 20rpx; //u-image故从属性中传入
|
||||
border-radius: 25px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.info-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10rpx;
|
||||
justify-content: space-between;
|
||||
|
||||
text-align: left;
|
||||
padding-left: 26rpx;
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.info-column-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.goods-scribing-title {
|
||||
font-size: 0.79rem;
|
||||
// box-sizing: border-box;
|
||||
margin-right: 15rpx;
|
||||
color: white;
|
||||
padding: 3rpx; //让字体与背景上下有一定的间隔
|
||||
background-color: #ff5856;
|
||||
border-radius: 20rpx;
|
||||
width: 80rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-desc {
|
||||
font-size: 0.775rem;
|
||||
color: #808695;
|
||||
}
|
||||
}
|
||||
|
||||
.price-column {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-radius: 10rpx;
|
||||
justify-content: space-between;
|
||||
margin: 20rpx;
|
||||
|
||||
.price-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
color: #ff5856;
|
||||
|
||||
.price {
|
||||
font-size: 1.5rem; //¥
|
||||
display: flexbox;
|
||||
}
|
||||
|
||||
.subprice {
|
||||
color: #aeaeae;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn,
|
||||
.btn_scribing {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
width: 180rpx;
|
||||
height: 66rpx;
|
||||
border-radius: 50rpx;
|
||||
margin-right: 10rpx;
|
||||
|
||||
font-size: 0.79rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
// background-color: #34afff;
|
||||
}
|
||||
.btn_scribing {
|
||||
// background-color: $u-type-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,51 @@
|
|||
<!--
|
||||
* @Author: micky 1254597151@qq.com
|
||||
* @Date: 2023-08-14 15:38:40
|
||||
* @LastEditors: micky 1254597151@qq.com
|
||||
* @LastEditTime: 2023-11-24 14:36:17
|
||||
* @FilePath: \housekeeping-uniapp\src\components\la-swiper\la-swiper.vue
|
||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||
-->
|
||||
<template>
|
||||
<view class="banner h-[560rpx] bg-white translate-y-0">
|
||||
<swiper
|
||||
class="swiper h-full"
|
||||
:indicator-dots="content.length && content.length > 1"
|
||||
indicator-active-color="#2468f2"
|
||||
:autoplay="true"
|
||||
v-if="content.length"
|
||||
>
|
||||
<swiper-item v-for="(item, index) in content" :key="index">
|
||||
<u-image
|
||||
mode="scaleToFill"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:src="item"
|
||||
:lazy-load="true"
|
||||
@click="handlePreviewImage(index)"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<u-empty v-else></u-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const handlePreviewImage = (index: number) => {
|
||||
if (!props.content.length) return
|
||||
uni.previewImage({
|
||||
urls: props.content as string[],
|
||||
current: index
|
||||
})
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,97 @@
|
|||
<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({
|
||||
title: '请添加头像',
|
||||
})
|
||||
if (!nickname)
|
||||
return uni.$u.toast({
|
||||
title: '请输入昵称',
|
||||
})
|
||||
emit('update', {
|
||||
avatar: avatar.value,
|
||||
nickname,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -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-lg font-medium">{{ item.title }}</view>
|
||||
<view class="news-card-content-intro text-gray-400 text-sm mt-[16rpx]">{{
|
||||
item.intro
|
||||
}}</view>
|
||||
|
||||
<view class="text-muted text-xs w-full flex justify-between mt-[12rpx]">
|
||||
<view>{{ item.createTime }}</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.visit }}</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>
|
|
@ -0,0 +1,598 @@
|
|||
<template>
|
||||
<view class="flex">
|
||||
<!-- 联系客服 -->
|
||||
<!-- <template v-if="contract">
|
||||
<button
|
||||
class="Btn mr-2 bg-white text-base text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleShowModal"
|
||||
>
|
||||
联系客服
|
||||
</button>
|
||||
</template> -->
|
||||
|
||||
<!-- 取消订单 -->
|
||||
<template v-if="cancel">
|
||||
<button
|
||||
class="Btn mr-2 bg-white text-base text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleOrderCancel(orderId)"
|
||||
>
|
||||
取消订单
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- 删除订单 -->
|
||||
<template v-if="orderStatus == 4 || del">
|
||||
<button
|
||||
class="Btn mr-2 bg-white text-base text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleOrderDel(orderId)"
|
||||
>
|
||||
删除订单
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- 去评价:0-隐藏 1-显示 -->
|
||||
<!-- @click.stop="goPage('/bundle/pages/evaluate_list/index')" -->
|
||||
<template v-if="evaluate">
|
||||
<button
|
||||
class="Btn evaluate bg-white text-sm text-muted leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="goPage(`/bundle/pages/evaluate_submit/index?id=${orderId}`)"
|
||||
>
|
||||
去评价
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- 联系师傅 -->
|
||||
<!-- <template
|
||||
v-if="
|
||||
staffMobile?.length > 0 &&
|
||||
!(confirmService || verification) &&
|
||||
((isDispatch && orderStatus == 1) || orderStatus == 2)
|
||||
"
|
||||
>
|
||||
<button
|
||||
class="Btn flex-1 mr-2 bg-white text-sm text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleContactMobile()"
|
||||
>
|
||||
联系师傅
|
||||
</button>
|
||||
</template> -->
|
||||
|
||||
<!-- 联系客户 -->
|
||||
<template v-if="mobile && (confirmService || verification)">
|
||||
<button
|
||||
class="Btn flex-1 mr-2 bg-white text-sm text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleCustomerMobile()"
|
||||
>
|
||||
联系客户
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- 去支付 -->
|
||||
<template v-if="pay">
|
||||
<button
|
||||
class="bg-primary text-lg ml-[20rpx] text-white leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handlePayment"
|
||||
>
|
||||
去支付
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- 确认服务 -->
|
||||
<template v-if="confirmService && isCanConfirmService">
|
||||
<button
|
||||
class="bg-primary text-lg text-white leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleConfirmService(orderId)"
|
||||
>
|
||||
开始服务
|
||||
</button>
|
||||
</template>
|
||||
<!-- 还不可以确认服务 -->
|
||||
<template v-if="confirmService && !isCanConfirmService">
|
||||
<button
|
||||
class="BtnDisable text-lg text-[#cecece] leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleNotConfirmService(orderId)"
|
||||
>
|
||||
开始服务
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- 核销订单 -->
|
||||
|
||||
<template v-if="verification">
|
||||
<view class="flex justify-around">
|
||||
<button
|
||||
class="Btn flex-1 mr-2 bg-white text-sm text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="showInputCode = true"
|
||||
>
|
||||
手动核销码
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 bg-primary text-sm text-white leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleScanQRCode"
|
||||
>
|
||||
扫码核销
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 手动输入核销码MODAL -->
|
||||
<u-modal
|
||||
ref="uModalInput"
|
||||
v-model="showInputCode"
|
||||
show-cancel-button
|
||||
confirmColor="#1296DB"
|
||||
confirm-text="确定"
|
||||
@confirm="inputOrderCode"
|
||||
@cancel="handleCancel"
|
||||
title="手动核销"
|
||||
>
|
||||
<view class="slot-content justify-center" style="padding: 60rpx 40rpx">
|
||||
<u-input
|
||||
type="number"
|
||||
v-model="code"
|
||||
:border="true"
|
||||
placeholder="请输入核销码"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</view>
|
||||
</u-modal>
|
||||
</template>
|
||||
|
||||
<!-- 已预约:用户确认服务 -->
|
||||
<template v-if="orderStatus == 2">
|
||||
<button
|
||||
class="bg-primary text-lg text-white leading-[70rpx] h-[70rpx] rounded-full"
|
||||
@click.stop="handleUserConfirm"
|
||||
>
|
||||
确认服务
|
||||
</button>
|
||||
</template>
|
||||
</view>
|
||||
<u-popup
|
||||
v-model="showConfirmPop"
|
||||
mode="center"
|
||||
height="500"
|
||||
border-radius="14"
|
||||
:mask-close-able="false"
|
||||
closeable
|
||||
>
|
||||
<view class="text-center w-[560rpx] py-[40rpx]">
|
||||
<view class="w-[160rpx] h-[160rpx] px-[200rpx]">
|
||||
<u-image width="160" height="160" :src="goodsImage" />
|
||||
</view>
|
||||
<view class="mt-[20rpx] truncate">{{ goodsName }}</view>
|
||||
<view class="mt-[10rpx] mb-[40rpx] text-[#f36161]">完成服务请点击确认</view>
|
||||
<button
|
||||
class="flex-1 bg-primary text-sm text-white leading-[70rpx] h-[70rpx] rounded-full mx-[40rpx]"
|
||||
@click.stop="confirmVerification"
|
||||
>
|
||||
确认核销
|
||||
</button>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup v-model="isHide" mode="center" closeable border-radius="14" v-if="isHide">
|
||||
<view class="text-center w-[660rpx] py-[40rpx]">
|
||||
<view class="reason">
|
||||
<view v-if="isShowScore" class="mb-[20rpx]">是否确认取消订单?</view>
|
||||
<!-- <view v-if="isShowScore" class="mb-[20rpx]">
|
||||
上门服务前
|
||||
<text class="text-error mx-[8rpx]">{{ state.intervalTime }}</text>
|
||||
分钟内取消订单,扣款比例
|
||||
<text class="text-error mx-[8rpx]">{{ state.value }}</text>
|
||||
%,扣款金额
|
||||
<text class="text-error mx-[8rpx]">{{ state.deductMoney }}</text>
|
||||
元
|
||||
</view> -->
|
||||
<u-input
|
||||
class="textarea"
|
||||
v-model="reason"
|
||||
type="textarea"
|
||||
:maxlength="255"
|
||||
:trim="true"
|
||||
:border="true"
|
||||
placeholder="请输入取消订单原因"
|
||||
:height="200"
|
||||
/>
|
||||
<text class="limit">{{ reason.length }}/255</text>
|
||||
</view>
|
||||
<view class="operation-btn">
|
||||
<button
|
||||
@click="handleShowModal"
|
||||
class="Btn flex-1 mr-2 bg-white text-sm text-black leading-[70rpx] h-[70rpx] rounded-full"
|
||||
>
|
||||
联系客服
|
||||
</button>
|
||||
<button
|
||||
@click="handleConfirmModal"
|
||||
class="bg-primary text-lg text-white leading-[70rpx] h-[70rpx] rounded-full"
|
||||
>
|
||||
确定
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onUnmounted, onMounted, unref, reactive } from 'vue'
|
||||
import {
|
||||
apiOrderCancel,
|
||||
apiStaffOrderConfirmService,
|
||||
apiStaffOrderVerification,
|
||||
apiOrderDel,
|
||||
apiByteQueryRefund,
|
||||
apiCancelOrderRule,
|
||||
apiUserOrderVerification
|
||||
} from '@/api/order'
|
||||
import { OrderStatusEnum, PayFromType } from '@/enums/appEnums'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { getClient } from '@/utils/client'
|
||||
import { isWeixinClient } from '@/utils/client'
|
||||
import { useToggle } from '@/hooks/useLockFn'
|
||||
import { toast } from '@/utils/util'
|
||||
|
||||
const emit = defineEmits(['refresh', 'backRefresh', 'contact'])
|
||||
const userStore = useUserStore()
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
orderId?: any // 订单ID
|
||||
orderSn?: any // 订单编号
|
||||
cancel?: boolean | number | null // 取消订单
|
||||
evaluate?: boolean | number | null // 评价
|
||||
pay?: boolean | number | null // 支付
|
||||
confirmService?: boolean | number | null // 确认服务
|
||||
verification?: boolean | number | null // 核销订单
|
||||
goodsImage?: string | null // 商品图片
|
||||
goodsName?: string | null // 商品名称
|
||||
order_amount?: number | string | null // 订单金额
|
||||
mobile?: number | string | null // 手机号码
|
||||
contact?: number | boolean // 联系师傅
|
||||
staffId?: any
|
||||
type?: number | null // 支付方式
|
||||
orderStatus?: number | string | null // 订单状态;0-待支付;1-预约中;2-服务中;3-已完成;4-已退款;5-退款中
|
||||
del?: boolean | number | null // 删除订单
|
||||
appointTime?: string // 上门日期
|
||||
appointTimeStartStr?: string //上门服务开始时间
|
||||
isDispatch?: number | null // 是否派单
|
||||
staffMobile?: string | null // 师傅联系电话
|
||||
contract: number | null
|
||||
goodsId?: number | string
|
||||
}>(),
|
||||
{
|
||||
orderId: '',
|
||||
orderSn: '',
|
||||
cancel: false,
|
||||
evaluate: false,
|
||||
pay: false,
|
||||
confirmService: false,
|
||||
verification: false,
|
||||
goodsImage: '',
|
||||
goodsName: '',
|
||||
del: false,
|
||||
contact: false,
|
||||
mobile: '',
|
||||
staffId: '',
|
||||
type: 2,
|
||||
orderStatus: '',
|
||||
appointTime: '',
|
||||
appointTimeStartStr: '',
|
||||
isDispatch: 0, // 是否派单 0=否 1=是 默认为0
|
||||
staffMobile: '',
|
||||
contract: null,
|
||||
goodsId: ''
|
||||
}
|
||||
)
|
||||
const code = ref<number | string>('') // 输入核销码
|
||||
const showInputCode = ref<boolean>(false) // 显示(输入核销码):是 | 否
|
||||
|
||||
// 页面跳转
|
||||
const goPage = (url: string) => {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const isHide = ref(false)
|
||||
const reason = ref('')
|
||||
const isShowScore = ref(false)
|
||||
const state = reactive({
|
||||
intervalTime: '',
|
||||
value: '',
|
||||
deductMoney: ''
|
||||
})
|
||||
|
||||
/**取消订单-取消 */
|
||||
const handleCloseModal = () => {
|
||||
reason.value = ''
|
||||
}
|
||||
/**取消订单-确定 */
|
||||
const handleConfirmModal = async () => {
|
||||
if (!unref(reason).length) return toast('请输入取消订单原因')
|
||||
await cancelOrderAction()
|
||||
isHide.value = false
|
||||
}
|
||||
// 取消订单
|
||||
const handleOrderCancel = async (id: number | string): Promise<void> => {
|
||||
/**待支付 */
|
||||
if (props.orderStatus === OrderStatusEnum.NO_PAYMENT) {
|
||||
uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '确定取消订单?',
|
||||
success: async res => {
|
||||
if (res.cancel) return
|
||||
await cancelOrderAction()
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
try {
|
||||
const data = {
|
||||
orderId: props.orderId,
|
||||
goodsId: props.goodsId
|
||||
}
|
||||
const res = await apiCancelOrderRule(data)
|
||||
isShowScore.value = !Object.keys(res).length ? false : true
|
||||
if (isShowScore.value) {
|
||||
state.intervalTime = res.intervalTime ?? '--'
|
||||
state.value = res.value ?? '--'
|
||||
state.deductMoney = res.deductMoney ?? '--'
|
||||
}
|
||||
isHide.value = true
|
||||
} catch (error) {}
|
||||
}
|
||||
/**调用取消订单接口 */
|
||||
async function cancelOrderAction() {
|
||||
try {
|
||||
const data = {
|
||||
id: props.orderId,
|
||||
userId: userInfo.value.id,
|
||||
source: getClient(),
|
||||
refundReason: unref(reason),
|
||||
goodsId: props.goodsId,
|
||||
serviceTimeBeforeMinute: state.intervalTime,
|
||||
deductionRatio: state.value
|
||||
}
|
||||
await apiOrderCancel(data)
|
||||
// #ifdef MP-TOUTIAO
|
||||
if (props.orderStatus === 0 || props.orderStatus === 1) {
|
||||
// 非抖音不需要,先指定MP-TOUTIAO,防止微信小程序报数据不存在
|
||||
await apiByteQueryRefund({
|
||||
order_id: props.orderId
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
uni.$u.toast('操作成功')
|
||||
} catch (error) {
|
||||
console.log('错误信息:', error, props.orderId)
|
||||
}
|
||||
emit('refresh')
|
||||
}
|
||||
// 删除订单
|
||||
const handleOrderDel = async (id: number | string): Promise<void> => {
|
||||
const modelRes = await uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '确认删除该订单吗?'
|
||||
})
|
||||
if (modelRes.cancel) return
|
||||
|
||||
try {
|
||||
await apiOrderDel({ id })
|
||||
uni.$u.toast('操作成功')
|
||||
} catch (error) {
|
||||
console.log('错误信息:', error, props.orderId)
|
||||
}
|
||||
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
// 支付
|
||||
const handlePayment = () => {
|
||||
const param = {
|
||||
order_id: props.orderId,
|
||||
sn: props.orderSn,
|
||||
from: PayFromType.ORDER,
|
||||
order_amount: props.order_amount
|
||||
}
|
||||
uni.$emit('payment', param)
|
||||
}
|
||||
// 联系师傅
|
||||
const handleContactMobile = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: props.staffMobile //仅为示例
|
||||
})
|
||||
}
|
||||
// 联系客户
|
||||
const handleCustomerMobile = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: props.mobile //仅为示例
|
||||
})
|
||||
}
|
||||
// 确认服务
|
||||
const handleConfirmService = async (id: number | string): Promise<void> => {
|
||||
const modelRes = await uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '确认开始服务该订单吗?'
|
||||
})
|
||||
if (modelRes.cancel) return
|
||||
await apiStaffOrderConfirmService({ id })
|
||||
uni.$u.toast('操作成功')
|
||||
emit('refresh')
|
||||
}
|
||||
const handleNotConfirmService = async (id: number | string): Promise<void> => {
|
||||
const modelRes = await uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '预约前2小时才可以开始服务'
|
||||
})
|
||||
}
|
||||
|
||||
// 扫码
|
||||
const handleScanQRCode = () => {
|
||||
// #ifdef H5
|
||||
if (!isWeixinClient()) return uni.$u.toast('h5暂不支持扫码')
|
||||
showInputCode.value = true
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
wx.scanCode({
|
||||
success: data => {
|
||||
code.value = data.result
|
||||
showConfirmPop.value = true
|
||||
},
|
||||
fail: err => {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 确认核销
|
||||
const showConfirmPop = ref(false)
|
||||
const confirmVerification = async () => {
|
||||
try {
|
||||
await apiStaffOrderVerification({ id: props.orderId, code: code.value })
|
||||
code.value = ''
|
||||
uni.$u.toast('订单核销成功')
|
||||
showConfirmPop.value = false
|
||||
emit('refresh')
|
||||
} catch (error) {
|
||||
uni.$u.toast(error)
|
||||
}
|
||||
}
|
||||
// 手动输入核销码
|
||||
const inputOrderCode = async (): Promise<void> => {
|
||||
if (code.value === '') return uni.$u.toast('请输入核销码')
|
||||
try {
|
||||
await apiStaffOrderVerification({ id: props.orderId, code: code.value })
|
||||
uni.$u.toast('订单核销成功')
|
||||
emit('refresh')
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
code.value = ''
|
||||
}
|
||||
|
||||
// 取消核销码弹框
|
||||
const handleCancel = () => {
|
||||
code.value = ''
|
||||
emit('refresh')
|
||||
}
|
||||
// 用户确认核销
|
||||
const handleUserConfirm = async () => {
|
||||
const modelRes = await uni.showModal({
|
||||
title: '温馨提示',
|
||||
content: '确认核销该订单吗?'
|
||||
})
|
||||
if (modelRes.cancel) return
|
||||
await apiUserOrderVerification({ id: props.orderId })
|
||||
uni.$u.toast('订单核销成功')
|
||||
emit('refresh')
|
||||
}
|
||||
// 以下为确认服务相关判定,在预约时间的前2小时前,才可以确认服务
|
||||
const isCanConfirmService = ref(false) // 检查是否可以确认服务,在预约时间的前2小时前,才可以确认
|
||||
const checkconfirmService = () => {
|
||||
if (!props.confirmService) {
|
||||
return
|
||||
}
|
||||
if (isCanConfirmService.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const date = new Date(props.appointTime + ' ' + props.appointTimeStartStr)
|
||||
const now = new Date()
|
||||
|
||||
const isBefore = now.getTime() < date.getTime()
|
||||
const isAfter = date.getTime() < now.getTime()
|
||||
|
||||
const minutesBeforeAndAfter = Math.abs(now.getTime() - date.getTime()) / 1000 / 60 // 毫秒到分钟
|
||||
|
||||
console.log(
|
||||
'当前时间',
|
||||
new Date(Date.parse(new Date().toString())),
|
||||
props.appointTime + ' ' + props.appointTimeStartStr,
|
||||
isBefore,
|
||||
isAfter,
|
||||
minutesBeforeAndAfter
|
||||
)
|
||||
|
||||
//即前2小时内
|
||||
if (isBefore && minutesBeforeAndAfter < 60 * 2) {
|
||||
console.log('即前2小时内')
|
||||
isCanConfirmService.value = true
|
||||
}
|
||||
|
||||
//即后10小时内
|
||||
// if (isAfter && minutesBeforeAndAfter < 60 * 10) {
|
||||
if (isAfter) {
|
||||
isCanConfirmService.value = true
|
||||
}
|
||||
|
||||
// 临时更改- 都可以确认服务(初步上线)
|
||||
isCanConfirmService.value = true
|
||||
}
|
||||
/**联系客服 */
|
||||
const handleShowModal = () => {
|
||||
emit('contact')
|
||||
}
|
||||
const _timer = ref(null) // 定时器用于检查卡片是否被中断,防止死锁。开始时有一
|
||||
onMounted(() => {
|
||||
isCanConfirmService.value = false
|
||||
if (props.confirmService) {
|
||||
checkconfirmService()
|
||||
_timer.value = setInterval(() => {
|
||||
checkconfirmService()
|
||||
}, 10000)
|
||||
}
|
||||
})
|
||||
|
||||
// 清除定时器
|
||||
onUnmounted(() => {
|
||||
clearInterval(_timer.value)
|
||||
if (props.confirmService) {
|
||||
console.log('--- 退出定时器 --', new Date(Date.parse(new Date().toString())))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.reason {
|
||||
padding: 30rpx;
|
||||
position: relative;
|
||||
font-size: 28rpx;
|
||||
|
||||
.limit {
|
||||
position: absolute;
|
||||
right: 50rpx;
|
||||
bottom: 40rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.operation-btn {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
padding: 0 30rpx;
|
||||
|
||||
> button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.Btn {
|
||||
border: 1px solid rgba(187, 187, 187, 1);
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
color: rgba(16, 16, 16, 1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.evaluate {
|
||||
border: 1px solid $blue5;
|
||||
color: $blue5;
|
||||
}
|
||||
|
||||
.BtnDisable {
|
||||
border: 1px solid rgba(187, 187, 187, 1);
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
color: rgb(88, 88, 88);
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -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>
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<view
|
||||
@click="onSelect(item.id)"
|
||||
v-for="(item, index) in lists"
|
||||
:key="index"
|
||||
class="px-[20rpx] flex justify-between items-center h-[120rpx] mb-[2rpx] bg-white"
|
||||
>
|
||||
<image class="w-[48rpx] h-[48rpx]" :src="item.icon" />
|
||||
<view class="flex-1 mx-[20rpx]">
|
||||
<view class="text-base text-main">{{ item.pay_way_name }}</view>
|
||||
<view class="text-muted text-xs mt-[6rpx]" v-if="item.id === 1"
|
||||
>可用余额:{{ user_money }}</view
|
||||
>
|
||||
</view>
|
||||
<image
|
||||
class="w-[34rpx] h-[34rpx]"
|
||||
v-show="modelValue == item.id"
|
||||
src="@/static/images/icon/icon_select.png"
|
||||
/>
|
||||
<image
|
||||
class="w-[34rpx] h-[34rpx]"
|
||||
v-show="modelValue != item.id"
|
||||
src="@/static/images/icon/icon_null_select.png"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:modelValue', value: number): void
|
||||
}>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
lists: any
|
||||
user_money: string | number
|
||||
modelValue: any
|
||||
}>(),
|
||||
{
|
||||
lists: [],
|
||||
user_money: 0,
|
||||
modelValue: 1
|
||||
}
|
||||
)
|
||||
console.log('props', props.lists)
|
||||
|
||||
const onSelect = (way: number) => {
|
||||
emit('update:modelValue', way)
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,525 @@
|
|||
<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="flex flex-col w-full h-full payment">
|
||||
<view class="header py-[50rpx] flex flex-col items-center">
|
||||
<price
|
||||
:content="props.order_amount"
|
||||
mainSize="44rpx"
|
||||
minorSize="40rpx"
|
||||
fontWeight="500"
|
||||
color="#333"
|
||||
></price>
|
||||
|
||||
<template v-if="payData.cancelTime">
|
||||
<view
|
||||
class="count-down flex items-center rounded-[30px] text-xs"
|
||||
v-if="timeStamp >= 0"
|
||||
>
|
||||
<text>支付剩余时间:</text>
|
||||
<u-count-down
|
||||
:timestamp="timeStamp"
|
||||
format="HH:mm:ss"
|
||||
:font-size="22"
|
||||
:separator-size="26"
|
||||
@end="payData.cancelTime = 0"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</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.payWayList"
|
||||
:key="index"
|
||||
@click="selectPayWay(item.payWay)"
|
||||
>
|
||||
<u-image
|
||||
class="flex-none"
|
||||
:src="item.payImage"
|
||||
width="48"
|
||||
height="48"
|
||||
></u-image>
|
||||
<view class="mx-[16rpx] flex-1">
|
||||
<view class="payway-item--name flex-1">
|
||||
{{ item.payName }}
|
||||
</view>
|
||||
<view
|
||||
class="text-muted text-xs"
|
||||
v-if="item.payWay === 2"
|
||||
>
|
||||
可用余额:{{ payData.money }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-radio
|
||||
class="mr-[-20rpx]"
|
||||
:name="item.payWay"
|
||||
active-color="#5ac725"
|
||||
></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"
|
||||
:custom-style="{ background: '#2468f2' }"
|
||||
>
|
||||
立即支付
|
||||
</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"
|
||||
@close="handleClose"
|
||||
>
|
||||
<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]">
|
||||
<button
|
||||
class="Btn bg-white text-xs text-black leading-[60rpx] h-[60rpx] rounded-full"
|
||||
@click="queryPayResult(false)"
|
||||
>
|
||||
重新支付
|
||||
</button>
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<button
|
||||
class="bg-primary text-xs ml-[20rpx] text-white leading-[60rpx] h-[60rpx] rounded-full"
|
||||
@click="queryPayResult()"
|
||||
>
|
||||
已完成支付
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import qs from 'qs'
|
||||
import { pay, PayWayEnum } from '@/utils/pay/index'
|
||||
import { computed, ref, watch, watchEffect } 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'
|
||||
import { getClient } from '@/utils/client'
|
||||
import {
|
||||
apiPayPayWay,
|
||||
apiPayPrepay,
|
||||
apiQueryOrder,
|
||||
apiH5PayPrepay,
|
||||
apiBytePayPrepay,
|
||||
apiByteQueryOrder,
|
||||
apiQueryRecharge
|
||||
} from '@/api/app'
|
||||
import price from '@/components/price/price-v2.vue'
|
||||
import { apiOrderClose } from '@/api/order'
|
||||
|
||||
/*
|
||||
页面参数 orderId:订单id,from:订单来源
|
||||
*/
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
showCheck: {
|
||||
type: Boolean
|
||||
},
|
||||
// 订单id
|
||||
orderId: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
sn: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
order_amount: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
//订单来源
|
||||
from: {
|
||||
default: 'order',
|
||||
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>({
|
||||
payWayList: [],
|
||||
cancelTime: null,
|
||||
money: 0
|
||||
})
|
||||
|
||||
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 {
|
||||
// #ifdef MP-TOUTIAO
|
||||
payData.value.payWayList = [
|
||||
{
|
||||
id: 2,
|
||||
isDefault: 2,
|
||||
payId: 2,
|
||||
payImage:
|
||||
'http://nvqryx.natappfree.cc/api/uploads/image/20230110/8608cf8d-555d-415a-9100-c87dac00fe8e.png',
|
||||
payName: '抖音支付',
|
||||
payWay: 2,
|
||||
scene: 2,
|
||||
status: 2
|
||||
}
|
||||
]
|
||||
popupStatus.value = PageStatusEnum.NORMAL
|
||||
payWay.value = 2
|
||||
// #endif
|
||||
payData.value = await apiPayPayWay({
|
||||
scene: getClient(),
|
||||
orderId: props.orderId,
|
||||
type: props.from
|
||||
})
|
||||
popupStatus.value = PageStatusEnum.NORMAL
|
||||
const checkPay =
|
||||
payData.value.payWayList.find((item: any) => item.isDefault) ||
|
||||
payData.value.payWayList[0]
|
||||
payWay.value = checkPay?.payWay
|
||||
} catch (error) {
|
||||
uni.$u.toast(error)
|
||||
popupStatus.value = PageStatusEnum.ERROR
|
||||
}
|
||||
}
|
||||
|
||||
const { bindMnp } = useUserStore().userInfo as { bindMnp: boolean }
|
||||
const selectPayWay = (pay: number) => {
|
||||
payWay.value = pay
|
||||
}
|
||||
const payment = (() => {
|
||||
// 查询是否绑定微信
|
||||
const checkIsBindWx = async () => {
|
||||
if (
|
||||
[ClientEnum.OA_WEIXIN, ClientEnum.MP_WEIXIN].includes(client) &&
|
||||
!bindMnp &&
|
||||
payWay.value == PayWayEnum.WECHAT
|
||||
) {
|
||||
showPay.value = false
|
||||
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: '正在支付中'
|
||||
})
|
||||
let payChannel = ''
|
||||
switch (client) {
|
||||
case 1:
|
||||
payChannel = 'mp_channel'
|
||||
break
|
||||
case 2:
|
||||
payChannel = 'oa_channel'
|
||||
break
|
||||
case 3:
|
||||
payChannel = 'h5_channel'
|
||||
break
|
||||
default:
|
||||
payChannel = 'mp_channel'
|
||||
break
|
||||
}
|
||||
try {
|
||||
const params = {
|
||||
orderId: props.orderId,
|
||||
payChannel,
|
||||
scene: props.from,
|
||||
payWay: payWay.value // 1-微信 2-余额
|
||||
}
|
||||
// 微信or公众号支付
|
||||
if (payChannel === 'mp_channel' || payChannel === 'oa_channel') {
|
||||
const res = await apiPayPrepay({ ...params })
|
||||
return { pay_way: payWay.value, config: res }
|
||||
} else if (payChannel === 'h5_channel') {
|
||||
const res = await apiH5PayPrepay({ ...params })
|
||||
const redirect = `&redirect_url=${encodeURIComponent(
|
||||
window.location.href + '&checkPay=' + true + '&order_id=' + props.orderId
|
||||
)}`
|
||||
return { pay_way: payWay.value, config: res.url + redirect }
|
||||
}
|
||||
} catch (err) {
|
||||
return uni.$u.toast(err)
|
||||
}
|
||||
}
|
||||
|
||||
//拉起支付
|
||||
const payTask = async (data: any) => {
|
||||
try {
|
||||
const res = await pay.payment(data.pay_way, data.config)
|
||||
console.log('resPayTask: ' + res)
|
||||
|
||||
return res
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
const ttpay = async () => {}
|
||||
|
||||
// #ifdef MP-TOUTIAO
|
||||
return series(ttpay)
|
||||
// #endif
|
||||
// #ifndef MP-TOUTIAO
|
||||
return series(checkIsBindWx, prepayTask, payTask)
|
||||
// #endif
|
||||
})()
|
||||
|
||||
const { isLock, lockFn: handlePay } = useLockFn(async () => {
|
||||
try {
|
||||
// #ifdef MP-TOUTIAO
|
||||
const ttpay = async () => {
|
||||
const params = { order_id: props.orderId, pay_channel: 'byte_channel' }
|
||||
const tempRes = await apiBytePayPrepay(params)
|
||||
// debugger
|
||||
console.log(tempRes.data)
|
||||
console.log(tempRes?.data?.order_id)
|
||||
console.log(tempRes?.data?.order_token)
|
||||
let payResult: any = ''
|
||||
|
||||
try {
|
||||
tt.pay({
|
||||
orderInfo: {
|
||||
order_id: tempRes.data.order_id,
|
||||
order_token: tempRes.data.order_token
|
||||
},
|
||||
service: 5,
|
||||
success(res) {
|
||||
payResult = res
|
||||
console.log('收到成功回调啦', res)
|
||||
if (res.code === 0) {
|
||||
console.log('支付成功啦~~')
|
||||
const queryResult = apiByteQueryOrder({ order_id: props.orderId })
|
||||
console.log(queryResult)
|
||||
emit('success')
|
||||
return 'success'
|
||||
} else {
|
||||
uni.$u.toast(res.msg)
|
||||
emit('fail')
|
||||
return 'fail'
|
||||
}
|
||||
},
|
||||
fail(res) {
|
||||
payResult = res
|
||||
console.log('支付失败了', res)
|
||||
emit('fail')
|
||||
}
|
||||
})
|
||||
// debugger
|
||||
// setTimeout(()=> {
|
||||
// const res = {
|
||||
// code: 200,
|
||||
// data: [],
|
||||
// msg: "成功"
|
||||
// }
|
||||
// // return Promise.resolve('success')
|
||||
// emit('success')
|
||||
// return "success"
|
||||
// }, 1000)
|
||||
} catch (e) {
|
||||
console.log('ttpay catch:', e)
|
||||
// Promise.reject('fail')
|
||||
return 'fail'
|
||||
}
|
||||
}
|
||||
// debugger
|
||||
const res: PayStatusEnum = ttpay()
|
||||
console.log('ttpay:', res)
|
||||
|
||||
// #endif
|
||||
// #ifndef MP-TOUTIAO
|
||||
const res: PayStatusEnum = await payment()
|
||||
console.log('支付结果状态:', res)
|
||||
handlePayResult(res)
|
||||
// #endif
|
||||
|
||||
uni.hideLoading()
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
|
||||
const handlePayResult = async (status: PayStatusEnum) => {
|
||||
await queryPayStatus()
|
||||
switch (status) {
|
||||
case PayStatusEnum.SUCCESS:
|
||||
emit('success')
|
||||
break
|
||||
case PayStatusEnum.PENDING:
|
||||
showCheckPay.value = true
|
||||
break
|
||||
case PayStatusEnum.FAIL:
|
||||
emit('fail')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const queryPayResult = async (confirm = true) => {
|
||||
const res = await queryPayStatus()
|
||||
if (res.payStatus === 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
|
||||
}
|
||||
|
||||
const queryPayStatus = async () => {
|
||||
if (props.from === 'order' && payWay.value !== 2) {
|
||||
const res = await apiQueryOrder({ id: props.orderId })
|
||||
return res
|
||||
} else if (props.from === 'recharge') {
|
||||
await apiQueryRecharge({ id: props.orderId })
|
||||
}
|
||||
}
|
||||
|
||||
const timeStamp = ref<number | null>(0)
|
||||
|
||||
watchEffect(() => {
|
||||
// 获取倒计时段
|
||||
const endTimestamp = payData.value.cancelTime
|
||||
const startTimestamp = new Date().getTime() / 1000
|
||||
timeStamp.value = (endTimestamp - startTimestamp) * 1000
|
||||
if (timeStamp.value === 0) orderClose()
|
||||
})
|
||||
// 超时关闭订单
|
||||
const orderClose = async () => {
|
||||
await apiOrderClose({
|
||||
outTradeNo: props.orderId
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
async value => {
|
||||
if (value) {
|
||||
if (!props.orderId) {
|
||||
uni.$u.toast('订单信息错误,无法查询到订单信息')
|
||||
return
|
||||
}
|
||||
getPayData()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.payway-lists {
|
||||
.payway-item {
|
||||
border-bottom: 1px solid;
|
||||
@apply border-page;
|
||||
}
|
||||
}
|
||||
|
||||
.Btn {
|
||||
border: 1px solid rgba(187, 187, 187, 1);
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
color: rgba(16, 16, 16, 1);
|
||||
}
|
||||
|
||||
.count-down {
|
||||
color: #666666;
|
||||
padding: 10rpx 30rpx 10rpx 30rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
:deep(.u-btn--primary) {
|
||||
background-color: $blue5 !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<view class="primary">
|
||||
<text class="text-lg font-medium text">{{ price }}</text>
|
||||
<text class="text-xs text">{{ desc }}</text>
|
||||
<text v-if="scribingPrice" class="text-xs line-through ml-2 text-muted">
|
||||
{{ scribingPrice }}{{ desc }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, withDefaults } from 'vue'
|
||||
|
||||
/** Props Start **/
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
price?: string | number // 价格
|
||||
desc?: string // 介绍
|
||||
scribingPrice?: string | number // 划线价
|
||||
}>(),
|
||||
{
|
||||
price: '',
|
||||
desc: '',
|
||||
scribingPrice: ''
|
||||
}
|
||||
)
|
||||
/** Props End **/
|
||||
|
||||
/** Methods Start **/
|
||||
|
||||
/** Methods End **/
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.text {
|
||||
color: rgba(248, 113, 113);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,155 @@
|
|||
<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 { ref, computed } from 'vue'
|
||||
|
||||
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: '#FF2C3C',
|
||||
mainSize: '36rpx',
|
||||
minorSize: '28rpx',
|
||||
lineThrough: false,
|
||||
fontWeight: 'normal',
|
||||
prefix: '¥',
|
||||
suffix: ''
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* @description 格式化输出价格
|
||||
* @param { string } price 价格
|
||||
* @param { string } take 小数点操作
|
||||
* @param { string } prec 小数位补
|
||||
*/
|
||||
const formatPrice = ({
|
||||
price,
|
||||
take = 'all',
|
||||
prec = undefined
|
||||
}: {
|
||||
price: string | number
|
||||
take: string
|
||||
prec?: number
|
||||
}) => {
|
||||
let [integer, decimals = ''] = (price + '').split('.')
|
||||
|
||||
// 小数位补
|
||||
if (prec !== undefined) {
|
||||
const LEN = decimals.length
|
||||
for (let i = prec - LEN; i > 0; --i) decimals += '0'
|
||||
decimals = decimals.substr(0, prec)
|
||||
}
|
||||
|
||||
switch (take) {
|
||||
case 'int':
|
||||
return integer
|
||||
case 'dec':
|
||||
return decimals
|
||||
case 'all':
|
||||
return integer + '.' + decimals
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 金额主体部分
|
||||
*/
|
||||
const integer = computed(() => {
|
||||
return formatPrice({
|
||||
price: props.content,
|
||||
take: 'int'
|
||||
})
|
||||
})
|
||||
/**
|
||||
* @description 金额小数部分
|
||||
*/
|
||||
const decimals = computed(() => {
|
||||
let decimals: any = 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 : ''
|
||||
})
|
||||
</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>
|
|
@ -0,0 +1,231 @@
|
|||
<template xlang="wxml" minapp="mpvue">
|
||||
<view class="tki-qrcode">
|
||||
<!-- #ifndef MP-ALIPAY -->
|
||||
<canvas
|
||||
class="tki-qrcode-canvas"
|
||||
:canvas-id="cid"
|
||||
:style="{ width: cpSize + 'px', height: cpSize + 'px' }"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-ALIPAY -->
|
||||
<canvas :id="cid" :width="cpSize" :height="cpSize" class="tki-qrcode-canvas" />
|
||||
<!-- #endif -->
|
||||
<image
|
||||
v-show="show"
|
||||
:src="result"
|
||||
:style="{ width: cpSize + 'px', height: cpSize + 'px' }"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QRCode from './qrcode.js'
|
||||
let qrcode
|
||||
export default {
|
||||
name: 'tki-qrcode',
|
||||
props: {
|
||||
cid: {
|
||||
type: String,
|
||||
default: 'tki-qrcode-canvas'
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'upx'
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
val: {
|
||||
type: String,
|
||||
default: 'asfsdfsdf'
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: '#ffffff'
|
||||
},
|
||||
foreground: {
|
||||
type: String,
|
||||
default: '#000000'
|
||||
},
|
||||
pdground: {
|
||||
type: String,
|
||||
default: '#000000'
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
iconSize: {
|
||||
type: Number,
|
||||
default: 40
|
||||
},
|
||||
lv: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
onval: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loadMake: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
usingComponents: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showLoading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loadingText: {
|
||||
type: String,
|
||||
default: '二维码生成中'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_makeCode() {
|
||||
const that = this
|
||||
if (!this._empty(this.val)) {
|
||||
try {
|
||||
qrcode = new QRCode({
|
||||
context: that, // 上下文环境
|
||||
canvasId: that.cid, // canvas-id
|
||||
usingComponents: that.usingComponents, // 是否是自定义组件
|
||||
showLoading: that.showLoading, // 是否显示loading
|
||||
loadingText: that.loadingText, // loading文字
|
||||
text: that.val, // 生成内容
|
||||
size: that.cpSize, // 二维码大小
|
||||
background: that.background, // 背景色
|
||||
foreground: that.foreground, // 前景色
|
||||
pdground: that.pdground, // 定位角点颜色
|
||||
correctLevel: that.lv, // 容错级别
|
||||
image: that.icon, // 二维码图标
|
||||
imageSize: that.iconSize, // 二维码图标大小
|
||||
cbResult: function (res) {
|
||||
// 生成二维码的回调
|
||||
that._result(res)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
//TODO handle the exception
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '二维码内容不能为空',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
},
|
||||
_clearCode() {
|
||||
this._result('')
|
||||
qrcode.clear()
|
||||
},
|
||||
_saveCode() {
|
||||
const that = this
|
||||
if (this.result != '') {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: that.result,
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: '二维码保存成功',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
_result(res) {
|
||||
this.result = res
|
||||
this.$emit('result', res)
|
||||
},
|
||||
_empty(v) {
|
||||
let tp = typeof v,
|
||||
rt = false
|
||||
if (tp == 'number' && String(v) == '') {
|
||||
rt = true
|
||||
} else if (tp == 'undefined') {
|
||||
rt = true
|
||||
} else if (tp == 'object') {
|
||||
if (JSON.stringify(v) == '{}' || JSON.stringify(v) == '[]' || v == null) rt = true
|
||||
} else if (tp == 'string') {
|
||||
if (v == '' || v == 'undefined' || v == 'null' || v == '{}' || v == '[]') rt = true
|
||||
} else if (tp == 'function') {
|
||||
rt = false
|
||||
}
|
||||
return rt
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
size: {
|
||||
handler(n, o) {
|
||||
console.log(n, '为什么!!!!!!!!')
|
||||
if (n != o && !this._empty(n)) {
|
||||
this.cSize = n
|
||||
if (!this._empty(this.val)) {
|
||||
setTimeout(() => {
|
||||
this._makeCode()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
val(n, o) {
|
||||
console.log(n, '为什么!!!!!!!!')
|
||||
if (this.onval) {
|
||||
if (n != o && !this._empty(n)) {
|
||||
setTimeout(() => {
|
||||
this._makeCode()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cpSize() {
|
||||
if (this.unit == 'upx') {
|
||||
return uni.upx2px(this.size)
|
||||
} else {
|
||||
return this.size
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
if (this.loadMake) {
|
||||
if (!this._empty(this.val)) {
|
||||
setTimeout(() => {
|
||||
this._makeCode()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.tki-qrcode {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tki-qrcode-canvas {
|
||||
position: fixed;
|
||||
top: -99999upx;
|
||||
left: -99999upx;
|
||||
z-index: -99999;
|
||||
}
|
||||
</style>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,85 @@
|
|||
<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, 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: any) => {
|
||||
inited.value = inited.value || value
|
||||
active.value = value
|
||||
shouldRender.value = inited.value!
|
||||
shouldShow.value = value
|
||||
}
|
||||
// const update = () => {
|
||||
// if (updateTabs) {
|
||||
// updateTabs()
|
||||
// }
|
||||
// }
|
||||
|
||||
const instance = getCurrentInstance()
|
||||
uni.$emit('handleChange', { event: instance?.props, fun: updateRender })
|
||||
|
||||
onMounted(() => {
|
||||
// update()
|
||||
uni.$emit('updateTabs')
|
||||
})
|
||||
|
||||
const changeData = computed(() => {
|
||||
const { dot, info } = props
|
||||
return {
|
||||
dot,
|
||||
info
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => changeData.value,
|
||||
() => {
|
||||
uni.$emit('updateTabs')
|
||||
// update()
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.name,
|
||||
val => {
|
||||
uni.$emit('updateTabs')
|
||||
// update()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tab.active {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tab.inactive {
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue