商城测试版v8_1

This commit is contained in:
czm 2022-07-30 17:11:42 +08:00
parent e2b0b0c52b
commit 09e34ecabe
39 changed files with 3360 additions and 3274 deletions

View File

@ -1,9 +1,14 @@
const path = require('path') const path = require('path')
const childProcess = require('child_process'); const childProcess = require('child_process')
const versions = childProcess.execSync('git rev-parse --abbrev-ref HEAD', { 'encoding': 'utf8' }) != "HEAD\n" ? childProcess.execSync('git rev-parse --abbrev-ref HEAD', { 'encoding': 'utf8' }) : childProcess.execSync('git describe --tags --abbrev=0', { 'encoding': 'utf8' }) const versions =
const CURRENT_GITHASH = childProcess.execSync('git rev-parse --short HEAD', { 'encoding': 'utf8' }) childProcess.execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }) != 'HEAD\n'
const CURRENT_VERSION = `Version: ${JSON.stringify(process.env.CODE_BRANCH || versions)} ${CURRENT_GITHASH} ${new Date().toLocaleString()}`.replace(/\"|\\n/g, ''); ? childProcess.execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' })
: childProcess.execSync('git describe --tags --abbrev=0', { encoding: 'utf8' })
const CURRENT_GITHASH = childProcess.execSync('git rev-parse --short HEAD', { encoding: 'utf8' })
const CURRENT_VERSION = `Version: ${JSON.stringify(process.env.CODE_BRANCH || versions)} ${CURRENT_GITHASH} ${new Date().toLocaleString()}`.replace(
/\"|\\n/g,
'',
)
const config = { const config = {
projectName: 'EShop', projectName: 'EShop',
@ -12,7 +17,7 @@ const config = {
deviceRatio: { deviceRatio: {
640: 2.34 / 2, 640: 2.34 / 2,
750: 1, 750: 1,
828: 1.81 / 2 828: 1.81 / 2,
}, },
sourceRoot: 'src', sourceRoot: 'src',
outputRoot: 'dist', outputRoot: 'dist',
@ -20,37 +25,33 @@ const config = {
defineConstants: { defineConstants: {
CURRENT_VERSION: JSON.stringify(CURRENT_VERSION), CURRENT_VERSION: JSON.stringify(CURRENT_VERSION),
CURRENT_GITHASH: JSON.stringify(CURRENT_GITHASH), CURRENT_GITHASH: JSON.stringify(CURRENT_GITHASH),
CURRENT_ENV: JSON.stringify(process.env.NODE_ENV) CURRENT_ENV: JSON.stringify(process.env.NODE_ENV),
}, },
copy: { copy: {
patterns: [ patterns: [],
], options: {},
options: {
}
}, },
framework: 'react', framework: 'react',
mini: { mini: {
postcss: { postcss: {
pxtransform: { pxtransform: {
enable: true, enable: true,
config: { config: {},
}
}, },
url: { url: {
enable: true, enable: true,
config: { config: {
limit: 1024 // 设定转换尺寸上限 limit: 1024, // 设定转换尺寸上限
} },
}, },
cssModules: { cssModules: {
enable: true, // 默认为 false如需使用 css modules 功能,则设为 true enable: true, // 默认为 false如需使用 css modules 功能,则设为 true
config: { config: {
namingPattern: 'module', // 转换模式,取值为 global/module namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]' generateScopedName: '[name]__[local]___[hash:base64:5]',
} },
} },
} },
}, },
h5: { h5: {
publicPath: '/', publicPath: '/',
@ -58,23 +59,25 @@ const config = {
postcss: { postcss: {
autoprefixer: { autoprefixer: {
enable: true, enable: true,
config: { config: {},
}
}, },
cssModules: { cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: { config: {
namingPattern: 'module', // 转换模式,取值为 global/module namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]' generateScopedName: '[name]__[local]___[hash:base64:5]',
} },
} },
} },
} },
} }
module.exports = function (merge) { module.exports = function (merge) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev')) return merge({}, config, require('./dev'))
} }
if (process.env.NODE_ENV === 'pre') {
return merge({}, config, require('./pre'))
}
return merge({}, config, require('./prod')) return merge({}, config, require('./prod'))
} }

51
config/pre.js Normal file
View File

@ -0,0 +1,51 @@
const path = require('path')
module.exports = {
env: {
NODE_ENV: '"pre"',
},
outputRoot: 'pre_dis',
defineConstants: {},
mini: {
optimizeMainPackage: {
enable: true,
},
webpackChain: (chain, webpack) => {
chain.merge({
plugin: {
install: {
plugin: require('terser-webpack-plugin'),
args: [
{
terserOptions: {
// compress: true, // 默认使用terser压缩
compress: {
drop_console: true, // 去掉打印
}, // 默认使用terser压缩
// mangle: false,
keep_classnames: true, // 不改变class名称
keep_fnames: true, // 不改变函数名称
},
},
],
},
},
})
},
},
h5: {
/**
* 如果h5端编译后体积过大可以使用webpack-bundle-analyzer插件对打包体积进行分析
* 参考代码如下
* webpackChain (chain) {
* chain.plugin('analyzer')
* .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
* }
*/
},
alias: {
'@': path.resolve(__dirname, '..', 'src'),
},
sass: {
resource: path.resolve(__dirname, '..', 'src/styles/common.scss'),
},
}

View File

@ -1,35 +1,35 @@
const path = require('path') const path = require('path')
module.exports = { module.exports = {
env: { env: {
NODE_ENV: '"production"' NODE_ENV: '"production"',
},
defineConstants: {
}, },
defineConstants: {},
mini: { mini: {
optimizeMainPackage: { optimizeMainPackage: {
enable: true enable: true,
}, },
webpackChain: (chain, webpack) => { webpackChain: (chain, webpack) => {
chain.merge({ chain.merge({
plugin: { plugin: {
install: { install: {
plugin: require('terser-webpack-plugin'), plugin: require('terser-webpack-plugin'),
args: [{ args: [
terserOptions: { {
// compress: true, // 默认使用terser压缩 terserOptions: {
compress: { // compress: true, // 默认使用terser压缩
drop_console: true, // 去掉打印 compress: {
}, // 默认使用terser压缩 drop_console: true, // 去掉打印
// mangle: false, }, // 默认使用terser压缩
keep_classnames: true, // 不改变class名称 // mangle: false,
keep_fnames: true // 不改变函数名称 keep_classnames: true, // 不改变class名称
} keep_fnames: true, // 不改变函数名称
}] },
} },
} ],
},
},
}) })
} },
}, },
h5: { h5: {
/** /**
@ -45,6 +45,6 @@ module.exports = {
'@': path.resolve(__dirname, '..', 'src'), '@': path.resolve(__dirname, '..', 'src'),
}, },
sass: { sass: {
resource: path.resolve(__dirname, '..', 'src/styles/common.scss') resource: path.resolve(__dirname, '..', 'src/styles/common.scss'),
} },
} }

View File

@ -1,56 +1,56 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const addressListApi = () => { export const addressListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/address/list`, url: `/v1/mall/address/list`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const addressAddApi = () => { export const addressAddApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/address`, url: `/v1/mall/address`,
method: "post", method: 'post',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const addressDetailApi = () => { export const addressDetailApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/address`, url: `/v1/mall/address`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const addressEditApi = () => { export const addressEditApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/address`, url: `/v1/mall/address`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const addressDeleteApi = () => { export const addressDeleteApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/address`, url: `/v1/mall/address`,
method: "delete", method: 'delete',
}) })
} }

View File

@ -1,12 +1,12 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const GetBannerList = () => { export const GetBannerList = () => {
return useRequest({ return useRequest({
url: `/v1/mall/carouselBanner/list`, url: `/v1/mall/carouselBanner/list`,
method: "get", method: 'get',
}) })
} }

View File

@ -1,13 +1,12 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* cdn / * cdn /
* @returns * @returns
*/ */
export const GetSignApi = () => { export const GetSignApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/cdn/token`, url: `/v1/mall/cdn/token`,
method: "get" method: 'get',
}) })
} }

View File

@ -1,23 +1,23 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const companyDetailApi = () => { export const companyDetailApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/company/info`, url: `/v1/mall/company/info`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const companyUpdateApi = () => { export const companyUpdateApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/company/info`, url: `/v1/mall/company/info`,
method: "put", method: 'put',
}) })
} }

View File

@ -1,23 +1,23 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const creditInfoApi = () => { export const creditInfoApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/credit`, url: `/v1/mall/credit`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const creditListApi = () => { export const creditListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/credit/list`, url: `/v1/mall/credit/list`,
method: "get", method: 'get',
}) })
} }

View File

@ -1,34 +1,34 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const depositInfoApi = () => { export const depositInfoApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/rechargeApplication`, url: `/v1/mall/rechargeApplication`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const depositListApi = () => { export const depositListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/rechargeApplication/list`, url: `/v1/mall/rechargeApplication/list`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const depositDetailApi = () => { export const depositDetailApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/rechargeApplication/order`, url: `/v1/mall/rechargeApplication/order`,
method: "get", method: 'get',
}) })
} }

View File

@ -1,91 +1,89 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const FavoriteListApi = () => { export const FavoriteListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite/list`, url: `/v1/mall/favorite/list`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const CreateFavoriteApi = () => { export const CreateFavoriteApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite`, url: `/v1/mall/favorite`,
method: "post", method: 'post',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const DelFavoriteApi = () => { export const DelFavoriteApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite`, url: `/v1/mall/favorite`,
method: "delete", method: 'delete',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const UpdateFavoriteApi = () => { export const UpdateFavoriteApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite`, url: `/v1/mall/favorite`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const AddFavoriteApi = () => { export const AddFavoriteApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite/product`, url: `/v1/mall/favorite/product`,
method: "post", method: 'post',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const DelFavoriteProductApi = () => { export const DelFavoriteProductApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite/product`, url: `/v1/mall/favorite/product`,
method: "delete", method: 'delete',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const DetailFavoriteProductApi = () => { export const DetailFavoriteProductApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite`, url: `/v1/mall/favorite`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const MoveFavoriteProductApi = () => { export const MoveFavoriteProductApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/favorite/product`, url: `/v1/mall/favorite/product`,
method: "put", method: 'put',
}) })
} }

View File

@ -1,12 +1,12 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const LoginApi = () => { export const LoginApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/login`, url: `/v1/mall/login`,
method: "post", method: 'post',
}) })
} }

View File

@ -1,112 +1,111 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
*/ */
export const SaleOrderApi = () => { export const SaleOrderApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder`, url: `/v1/mall/saleOrder`,
method: "post", method: 'post',
}) })
} }
/** /**
* *
*/ */
export const SaleOrderPreViewApi = () => { export const SaleOrderPreViewApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/preView`, url: `/v1/mall/saleOrder/preView`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const GetSaleOrderDetailApi = () => { export const GetSaleOrderDetailApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/detail`, url: `/v1/mall/saleOrder/detail`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
*/ */
export const EditSaleOrderRemarkApi = () => { export const EditSaleOrderRemarkApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/remark`, url: `/v1/mall/saleOrder/remark`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const EditSaleOrderAddressApi = () => { export const EditSaleOrderAddressApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/address`, url: `/v1/mall/saleOrder/address`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const EditSaleOrderShipmentModeApi = () => { export const EditSaleOrderShipmentModeApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/shipmentMode`, url: `/v1/mall/saleOrder/shipmentMode`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const GetOrderStatusListApi = () => { export const GetOrderStatusListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/enum/sale/order/status`, url: `/v1/mall/enum/sale/order/status`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
*/ */
export const GetOrderListApi = () => { export const GetOrderListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/list`, url: `/v1/mall/saleOrder/list`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
*/ */
export const CancelOrderApi = () => { export const CancelOrderApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/cancel`, url: `/v1/mall/saleOrder/cancel`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const ReceiveOrderApi = () => { export const ReceiveOrderApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/saleOrder/receive`, url: `/v1/mall/saleOrder/receive`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const OrderStatusListApi = () => { export const OrderStatusListApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/enum/filterSaleOrderStatus`, url: `/v1/mall/enum/filterSaleOrderStatus`,
method: "get", method: 'get',
}) })
} }

View File

@ -1,41 +1,41 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
*/ */
export const GetOrderPayApi = () => { export const GetOrderPayApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/orderPayment/orderPaymentMethodInfo`, url: `/v1/mall/orderPayment/orderPaymentMethodInfo`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
*/ */
export const SubmitOrderPayApi = () => { export const SubmitOrderPayApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/orderPayment/orderPaymentSubmission`, url: `/v1/mall/orderPayment/orderPaymentSubmission`,
method: "put", method: 'put',
}) })
} }
/** /**
* *
*/ */
export const GetPrepayOrderPayApi = () => { export const GetPrepayOrderPayApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/orderPayment/preCollectOrder/orderPaymentMethodInfo`, url: `/v1/mall/orderPayment/preCollectOrder/orderPaymentMethodInfo`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
*/ */
export const SubmitPrepayOrderPayApi = () => { export const SubmitPrepayOrderPayApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/orderPayment/preCollectOrder/orderPaymentSubmission`, url: `/v1/mall/orderPayment/preCollectOrder/orderPaymentSubmission`,
method: "put", method: 'put',
}) })
} }

View File

@ -1,45 +1,45 @@
import { useRequest } from "@/use/useHttp" import { useRequest } from '@/use/useHttp'
/** /**
* *
* @returns * @returns
*/ */
export const GetShoppingCartApi = () => { export const GetShoppingCartApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/shoppingCart/productColor`, url: `/v1/mall/shoppingCart/productColor`,
method: "get", method: 'get',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const AddShoppingCartApi = () => { export const AddShoppingCartApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/shoppingCart/productColor/list`, url: `/v1/mall/shoppingCart/productColor/list`,
method: "post", method: 'post',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const DelShoppingCartApi = () => { export const DelShoppingCartApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/shoppingCart/productColor`, url: `/v1/mall/shoppingCart/productColor`,
method: "delete", method: 'delete',
}) })
} }
/** /**
* *
* @returns * @returns
*/ */
export const UpdateShoppingCartApi = () => { export const UpdateShoppingCartApi = () => {
return useRequest({ return useRequest({
url: `/v1/mall/shoppingCart/productColor`, url: `/v1/mall/shoppingCart/productColor`,
method: "put", method: 'put',
}) })
} }

View File

@ -21,15 +21,11 @@ export const UPLOAD_CDN_URL = `https://v0.api.upyun.com/`
// cdn // cdn
// export const IMG_CND_Prefix = CURRENT_ENV.includes('development')? "https://test.cdn.zzfzyc.com":"https://cdn.zzfzyc.com" // export const IMG_CND_Prefix = CURRENT_ENV.includes('development')? "https://test.cdn.zzfzyc.com":"https://cdn.zzfzyc.com"
export const IMG_CND_Prefix = CURRENT_ENV.includes('development') export const IMG_CND_Prefix = CURRENT_ENV.includes('development') ? 'https://test.cdn.zzfzyc.com' : 'https://test.cdn.zzfzyc.com'
? 'https://test.cdn.zzfzyc.com'
: 'https://test.cdn.zzfzyc.com'
//在线支付图片baseUrl //在线支付图片baseUrl
// export const CAP_HTML_TO_IMAGE_BASE_URL = CURRENT_ENV.includes('development')? "https://test.zzfzyc.com":"https://www.zzfzyc.com" // export const CAP_HTML_TO_IMAGE_BASE_URL = CURRENT_ENV.includes('development')? "https://test.zzfzyc.com":"https://www.zzfzyc.com"
export const CAP_HTML_TO_IMAGE_BASE_URL = CURRENT_ENV.includes('development') export const CAP_HTML_TO_IMAGE_BASE_URL = CURRENT_ENV.includes('development') ? 'https://test.zzfzyc.com' : 'https://test.zzfzyc.com'
? 'https://test.zzfzyc.com'
: 'https://test.zzfzyc.com'
// 上传图片视频 // 上传图片视频
export const CDN_UPLOAD_IMG = `${UPLOAD_CDN_URL || ''}` export const CDN_UPLOAD_IMG = `${UPLOAD_CDN_URL || ''}`

View File

@ -1,18 +1,16 @@
.labAndImg_main {
.labAndImg_main{ width: 100%;
height: 100%;
.boxColor {
width: 100%; width: 100%;
height: 100%; height: 100%;
.boxColor{ border-radius: 20px;
width: 100%; border: 1px solid #818181;
height: 100%; box-sizing: border-box;
border-radius: 20px; }
border:1PX solid #818181; .labAndImg_image {
box-sizing: border-box; width: 100%;
} height: 100%;
image{ border-radius: 20px !important;
width: 100%; }
height: 100%;
border-radius: 20px;
}
} }

View File

@ -1,62 +1,61 @@
import { formatImgUrl, formatRemoveHashTag } from "@/common/fotmat"; import { formatImgUrl, formatRemoveHashTag } from '@/common/fotmat'
import Preview from "@/pages/details/components/preview"; import Preview from '@/pages/details/components/preview'
import { Image, View } from "@tarojs/components"; import { Image, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import styles from './index.module.scss' import styles from './index.module.scss'
import Taro from "@tarojs/taro"; import Taro from '@tarojs/taro'
import LabAndImgShow from "../LabAndImgShow"; import LabAndImgShow from '../LabAndImgShow'
//该组件宽高为100%需调整外层元素宽高 //该组件宽高为100%需调整外层元素宽高
type Param = { type Param = {
value?: { value?: {
texture_url?: string, //纹理图路径 texture_url?: string //纹理图路径
lab?: {l:number, a:number, b:number}, //lab lab?: { l: number; a: number; b: number } //lab
rgb?: {r:number, g:number, b:number} //rgb rgb?: { r: number; g: number; b: number } //rgb
title?: string title?: string
}, }
showStatus?: true|false, showStatus?: true | false
onClick?: (val: Param['value']) => void onClick?: (val: Param['value']) => void
} }
export default memo(({value, onClick, showStatus = false}:Param) => { export default memo(({ value, onClick, showStatus = false }: Param) => {
const [imgs, setImgs] = useState<string[]>([]) const [imgs, setImgs] = useState<string[]>([])
//lab是否都是0 //lab是否都是0
const rgbStyle = useMemo(() => { const rgbStyle = useMemo(() => {
if(value?.lab&&(value.lab.l||value.lab.a||value.lab.b)) { if (value?.lab && (value.lab.l || value.lab.a || value.lab.b)) {
return {'backgroundColor':`rgb(${value.rgb?.r} ${value.rgb?.g} ${value.rgb?.b})`} return { backgroundColor: `rgb(${value.rgb?.r} ${value.rgb?.g} ${value.rgb?.b})` }
} else { } else {
return null return null
}
}, [value])
useEffect(() => {
if(value?.texture_url) {
let res = value.texture_url.split(',').map(item => {
return formatImgUrl(item)
})
setImgs(() => res)
}
}, [value])
const [labAndImgShow, setLabAndImgShow] = useState(false)
const closeLabAndImgShow = useCallback(() => {
setLabAndImgShow(false)
}, [])
const onShowLabAndImg = () => {
onClick?.(value)
if(!showStatus) return false
setLabAndImgShow(true)
} }
}, [value])
return ( useEffect(() => {
<> if (value?.texture_url) {
<View className={styles.labAndImg_main} onClick={() => onShowLabAndImg()}> let res = value.texture_url.split(',').map((item) => {
{imgs?.length > 0&&<Image mode="aspectFill" src={imgs[0]}></Image>} return formatImgUrl(item)
{(!imgs?.length&&rgbStyle)&&<View className={styles.boxColor} style={{...rgbStyle}}></View>} })
{(!imgs?.length&&!rgbStyle)&&<Image mode="aspectFill" src={formatImgUrl('')}></Image>} setImgs(() => res)
</View> }
<LabAndImgShow value={value} show={labAndImgShow} onClose={closeLabAndImgShow}/> }, [value])
</>
) const [labAndImgShow, setLabAndImgShow] = useState(false)
}) const closeLabAndImgShow = useCallback(() => {
setLabAndImgShow(false)
}, [])
const onShowLabAndImg = () => {
onClick?.(value)
if (!showStatus) return false
setLabAndImgShow(true)
}
return (
<>
<View className={styles.labAndImg_main} onClick={() => onShowLabAndImg()}>
{imgs?.length > 0 && <Image mode='aspectFill' src={imgs[0]} className={styles.labAndImg_image}></Image>}
{!imgs?.length && rgbStyle && <View className={styles.boxColor} style={{ ...rgbStyle }}></View>}
{!imgs?.length && !rgbStyle && <Image mode='aspectFill' src={formatImgUrl('')} className={styles.labAndImg_image}></Image>}
</View>
<LabAndImgShow value={value} show={labAndImgShow} onClose={closeLabAndImgShow} />
</>
)
})

View File

@ -1,220 +1,219 @@
import { GetAddressListApi } from "@/api/addressList"; import { GetAddressListApi } from '@/api/addressList'
import { addressListApi } from "@/api/addressManager"; import { addressListApi } from '@/api/addressManager'
import { EditSaleOrderAddressApi, EditSaleOrderShipmentModeApi } from "@/api/order"; import { EditSaleOrderAddressApi, EditSaleOrderShipmentModeApi } from '@/api/order'
import { alert, goLink } from "@/common/common"; import { alert, goLink } from '@/common/common'
import { ORDER_STATUS } from "@/common/enum"; import { ORDER_STATUS } from '@/common/enum'
import { debounce, throttle } from "@/common/util"; import { debounce, throttle } from '@/common/util'
import AddressList from "@/components/AddressList"; import AddressList from '@/components/AddressList'
import Popup from "@/components/popup"; import Popup from '@/components/popup'
import { Text, View } from "@tarojs/components" import { Text, View } from '@tarojs/components'
import classnames from "classnames"; import classnames from 'classnames'
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
import Taro from "@tarojs/taro"; import Taro from '@tarojs/taro'
import styles from './index.module.scss' import styles from './index.module.scss'
type Param = {
type Param = { onSelect?: (val: any) => void //选择地址
onSelect?: (val:any) => void, //选择地址 disabled?: false | true //true禁用后只用于展示
disabled?: false|true, //true禁用后只用于展示 onChangeShipmentMode?: (val: number) => void //返回收货方式
onChangeShipmentMode?: (val: number) => void, //返回收货方式 onLogistics?: () => void //查看物流
onLogistics?: () => void, //查看物流 status?: 1 | 2 //1确认订单时使用 2订单流程
status?: 1|2, //1确认订单时使用 2订单流程 orderInfo?: {
orderInfo?: { id?: number //订单id
id?: number //订单id shipment_mode?: 1 | 2 //1自提 2物流
shipment_mode?: 1|2, //1自提 2物流 status?: number //订单状态
status?: number //订单状态 province_name: string
province_name: string, city_name: string
city_name: string, district_name: string
district_name: string, address_detail: string
address_detail: string, take_goods_address: string
take_goods_address: string, take_goods_phone: string
take_goods_phone: string, target_user_name: string
target_user_name: string, target_user_phone: string
target_user_phone: string }
}
} }
//订单状态 //订单状态
const { const {
SaleorderstatusWaitingPrePayment, SaleorderstatusWaitingPrePayment,
SaleOrderStatusBooking, SaleOrderStatusBooking,
SaleOrderStatusArranging, SaleOrderStatusArranging,
SaleOrderStatusArranged, SaleOrderStatusArranged,
SaleOrderStatusWaitingPayment, SaleOrderStatusWaitingPayment,
SaleOrderStatusWaitingReceipt, SaleOrderStatusWaitingReceipt,
SaleOrderStatusAlreadyReceipt, SaleOrderStatusAlreadyReceipt,
SaleOrderStatusComplete, SaleOrderStatusComplete,
SaleOrderStatusRefund, SaleOrderStatusRefund,
SaleOrderStatusCancel, SaleOrderStatusCancel,
} = ORDER_STATUS } = ORDER_STATUS
export default memo(forwardRef(({onSelect, onChangeShipmentMode, orderInfo, status = 2, disabled = false, onLogistics}: Param, ref) => { export default memo(
forwardRef(({ onSelect, onChangeShipmentMode, orderInfo, status = 2, disabled = false, onLogistics }: Param, ref) => {
const [addressInfo, setAddressInfo] = useState<any>() const [addressInfo, setAddressInfo] = useState<any>()
useEffect(() => { useEffect(() => {
if(orderInfo) { if (orderInfo) {
setReceivingStatus(() => orderInfo.shipment_mode||2) setReceivingStatus(() => orderInfo.shipment_mode || 2)
setAddressInfo(() => orderInfo) setAddressInfo(() => orderInfo)
} }
}, [orderInfo]) }, [orderInfo])
//打开地址列表 //打开地址列表
const [showAddressList, setShowAddressList] = useState(false) const [showAddressList, setShowAddressList] = useState(false)
const changeShow = () => { const changeShow = () => {
if(receivingStatus == 2 && !logisticsShow && limitEdit()) if (receivingStatus == 2 && !logisticsShow && limitEdit()) setShowAddressList(() => true)
setShowAddressList(() => true)
} }
//把内部方法提供给外部 //把内部方法提供给外部
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeShow changeShow,
})) }))
//收货方法,1:自提2物流 //收货方法,1:自提2物流
const [receivingStatus, setReceivingStatus] = useState(2) const [receivingStatus, setReceivingStatus] = useState(2)
const {fetchData: shipmentModeFetchData} = EditSaleOrderShipmentModeApi() const { fetchData: shipmentModeFetchData } = EditSaleOrderShipmentModeApi()
const onReceivingStatus = async (value, e) => { const onReceivingStatus = async (value, e) => {
e.stopPropagation() e.stopPropagation()
if(limitEdit()) changeReceivingStatus(value) if (limitEdit()) changeReceivingStatus(value)
} }
//当没有地址时获取地址列表中的第一个数据 //当没有地址时获取地址列表中的第一个数据
const {fetchData: addressListFetchData} = addressListApi() const { fetchData: addressListFetchData } = addressListApi()
const getAddressListOne = async () => { const getAddressListOne = async () => {
if(orderInfo?.address_detail) return true if (orderInfo?.address_detail) return true
let res = await addressListFetchData() let res = await addressListFetchData()
if( res.data.list && res.data.list?.length > 0 ) { if (res.data.list && res.data.list?.length > 0) {
let info = res.data.list[0] let info = res.data.list[0]
await addressFetchData({id: orderInfo?.id, address_id: info.id}) await addressFetchData({ id: orderInfo?.id, address_id: info.id })
setAddressInfo((e) => ({...e, ...info, target_user_name: info.name, target_user_phone: info.phone})) setAddressInfo((e) => ({ ...e, ...info, target_user_name: info.name, target_user_phone: info.phone }))
return true return true
} else { } else {
Taro.showModal({ Taro.showModal({
content: '您还没有地址,请前去新增地址', content: '您还没有地址,请前去新增地址',
success: function (res) { success: function (res) {
if (res.confirm) { if (res.confirm) {
goLink('/pages/addressManager/index') goLink('/pages/addressManager/index')
} else if (res.cancel) { } else if (res.cancel) {
console.log('用户点击取消') console.log('用户点击取消')
} }
} },
}) })
return false return false
} }
} }
const changeReceivingStatus = debounce(async (value) => { const changeReceivingStatus = debounce(async (value) => {
if(!orderInfo || value == receivingStatus) return false if (!orderInfo || value == receivingStatus) return false
if(status == 1) { if (status == 1) {
onChangeShipmentMode?.(value) onChangeShipmentMode?.(value)
setReceivingStatus(value) setReceivingStatus(value)
return false return false
} }
if(value == 2) { if (value == 2) {
let res = await getAddressListOne() let res = await getAddressListOne()
if(!res) return false if (!res) return false
} }
alert.loading('正在修改') alert.loading('正在修改')
const res = await shipmentModeFetchData({id: orderInfo.id, shipment_mode:value}) const res = await shipmentModeFetchData({ id: orderInfo.id, shipment_mode: value })
if(res.success) { if (res.success) {
alert.success('收货方式修改成功') alert.success('收货方式修改成功')
onChangeShipmentMode?.(value) onChangeShipmentMode?.(value)
setReceivingStatus(() => value) setReceivingStatus(() => value)
} else { } else {
alert.none(res.msg) alert.none(res.msg)
} }
}, 300) }, 300)
//修改地址 //修改地址
const [addressId, setAddressId] = useState(0) const [addressId, setAddressId] = useState(0)
const {fetchData: addressFetchData} = EditSaleOrderAddressApi() const { fetchData: addressFetchData } = EditSaleOrderAddressApi()
const getAddress = async (value) => { const getAddress = async (value) => {
if(!orderInfo) return false if (!orderInfo) return false
if(status == 1) { if (status == 1) {
setShowAddressList(() => false) setShowAddressList(() => false)
setAddressId(value.id) setAddressId(value.id)
setAddressInfo((e) => ({...e, ...value, target_user_name: value.name, target_user_phone: value.phone})) setAddressInfo((e) => ({ ...e, ...value, target_user_name: value.name, target_user_phone: value.phone }))
onSelect?.(value) onSelect?.(value)
return false return false
} }
alert.loading('正在修改') alert.loading('正在修改')
const res = await addressFetchData({id: orderInfo.id, address_id: value.id}) const res = await addressFetchData({ id: orderInfo.id, address_id: value.id })
if(res.success) { if (res.success) {
alert.success('地址修改成功') alert.success('地址修改成功')
onSelect?.(value) onSelect?.(value)
setShowAddressList(() => false) setShowAddressList(() => false)
setAddressId(value.id) setAddressId(value.id)
setAddressInfo((e) => ({...e, ...value, target_user_name: value.name, target_user_phone: value.phone})) setAddressInfo((e) => ({ ...e, ...value, target_user_name: value.name, target_user_phone: value.phone }))
} else { } else {
alert.none(res.msg) alert.none(res.msg)
} }
} }
//根据订单状态判断是否可修改 //根据订单状态判断是否可修改
const limitEdit = () => { const limitEdit = () => {
let res = [ let res = [SaleorderstatusWaitingPrePayment.value, SaleOrderStatusBooking.value, SaleOrderStatusArranging.value, SaleOrderStatusArranged.value, SaleOrderStatusWaitingPayment.value].includes(
SaleorderstatusWaitingPrePayment.value, orderInfo?.status as number,
SaleOrderStatusBooking.value, )
SaleOrderStatusArranging.value, if (!res && status != 1) alert.none('该订单状态不能修改地址!')
SaleOrderStatusArranged.value, return status == 1 ? true : res
SaleOrderStatusWaitingPayment.value,
].includes(orderInfo?.status as number)
if(!res && status != 1) alert.none('该订单状态不能修改地址!')
return status == 1? true : res
} }
//根据订单状态判断是否显示物流 //根据订单状态判断是否显示物流
const logisticsShowList = [SaleOrderStatusWaitingReceipt.value, SaleOrderStatusAlreadyReceipt.value, SaleOrderStatusComplete.value, SaleOrderStatusRefund.value] const logisticsShowList = [SaleOrderStatusWaitingReceipt.value, SaleOrderStatusAlreadyReceipt.value, SaleOrderStatusComplete.value, SaleOrderStatusRefund.value]
const logisticsShow = useMemo(() => { const logisticsShow = useMemo(() => {
return logisticsShowList.includes(orderInfo?.status as number) return logisticsShowList.includes(orderInfo?.status as number)
}, [orderInfo]) }, [orderInfo])
//地址格式 //地址格式
const formatAddress = useMemo(() => { const formatAddress = useMemo(() => {
if(receivingStatus == 2) { if (receivingStatus == 2) {
return addressInfo?.address_detail?addressInfo.province_name + addressInfo.city_name + addressInfo.district_name + addressInfo.address_detail:'' return addressInfo?.address_detail ? addressInfo.province_name + addressInfo.city_name + addressInfo.district_name + addressInfo.address_detail : ''
} else { } else {
return addressInfo?.take_goods_address return addressInfo?.take_goods_address
} }
}, [receivingStatus, addressInfo]) }, [receivingStatus, addressInfo])
return ( return (
<View> <View>
<View className={styles.order_address} onClick={() => changeShow()}> <View className={styles.order_address} onClick={() => changeShow()}>
<View className={classnames(styles.order_address_icon, 'iconfont', receivingStatus == 2?'icon-daohang':'icon-fahuo')}></View> <View className={classnames(styles.order_address_icon, 'iconfont', receivingStatus == 2 ? 'icon-daohang' : 'icon-fahuo')}></View>
<View className={styles.order_address_text_con}> <View className={styles.order_address_text_con}>
<View className={styles.order_address_text_title}> <View className={styles.order_address_text_title}>
<Text className={classnames(styles.address_text, styles.address_text_no)}>{formatAddress||'请选择收货地址及信息'}</Text> <Text className={classnames(styles.address_text, styles.address_text_no)}>{formatAddress || '请选择收货地址及信息'}</Text>
{(receivingStatus == 2 && !logisticsShow)&&<Text className={classnames(styles.moreIconfont,'iconfont icon-a-moreback')}></Text>} {receivingStatus == 2 && !logisticsShow && <Text className={classnames(styles.moreIconfont, 'iconfont icon-a-moreback')}></Text>}
</View>
<View className={styles.order_address_text_name}>
<Text>{receivingStatus == 1?'管理员':addressInfo?.target_user_name}</Text>
<Text>{receivingStatus == 1?addressInfo?.take_goods_phone: addressInfo?.target_user_phone}</Text>
</View>
</View>
{!logisticsShow&&<View className={styles.updateBtn}>
<View className={styles.updateBtn_list}>
<View className={classnames(styles.updateBtn_item, receivingStatus==1&&styles.updateBtn_item_select)} onClick={(e) => onReceivingStatus(1,e)}></View>
<View className={classnames(styles.updateBtn_item, receivingStatus==2&&styles.updateBtn_item_select)} onClick={(e) => onReceivingStatus(2,e)}></View>
</View>
<View style={{transform: receivingStatus==1?'translateX(0)':'translateX(100%)'}} className={classnames(styles.updateBtn_select)}></View>
</View>||
(orderInfo?.status != SaleOrderStatusRefund.value)&&<View className={styles.logisticsBtn} onClick={onLogistics}>
</View>}
</View> </View>
<Popup show={showAddressList} showTitle={false} onClose={() => setShowAddressList(false)}> <View className={styles.order_address_text_name}>
<View className={styles.order_address_list}> <Text>{receivingStatus == 1 ? '谭先生' : addressInfo?.target_user_name}</Text>
<View className={styles.order_address_title}></View> <Text>{receivingStatus == 1 ? addressInfo?.take_goods_phone : addressInfo?.target_user_phone}</Text>
<View className={styles.addressList_con}> </View>
<AddressList onSelect={getAddress} id={addressId}/> </View>
</View> {(!logisticsShow && (
<View className={styles.updateBtn}>
<View className={styles.updateBtn_list}>
<View className={classnames(styles.updateBtn_item, receivingStatus == 1 && styles.updateBtn_item_select)} onClick={(e) => onReceivingStatus(1, e)}>
</View> </View>
</Popup> <View className={classnames(styles.updateBtn_item, receivingStatus == 2 && styles.updateBtn_item_select)} onClick={(e) => onReceivingStatus(2, e)}>
</View>
</View>
<View style={{ transform: receivingStatus == 1 ? 'translateX(0)' : 'translateX(100%)' }} className={classnames(styles.updateBtn_select)}></View>
</View>
)) ||
(orderInfo?.status != SaleOrderStatusRefund.value && (
<View className={styles.logisticsBtn} onClick={onLogistics}>
</View>
))}
</View> </View>
<Popup show={showAddressList} showTitle={false} onClose={() => setShowAddressList(false)}>
<View className={styles.order_address_list}>
<View className={styles.order_address_title}></View>
<View className={styles.addressList_con}>
<AddressList onSelect={getAddress} id={addressId} />
</View>
</View>
</Popup>
</View>
) )
})) }),
)

View File

@ -1,66 +1,67 @@
.apply_after_sales_con {
.apply_after_sales_con{ padding: 20px;
padding: 20px; .returnSaleInput_item {
.returnSaleInput_item{ display: flex;
display: flex; align-items: center;
align-items: center; padding-bottom: 50px;
padding-bottom: 50px; flex-wrap: wrap;
flex-wrap: wrap; .title {
.title{ font-size: $font_size;
font-size: $font_size; font-weight: 700;
font-weight: 700; width: 119px;
width: 119px;
}
.select{
flex:1;
height: 60px;
border: 2px solid #e6e6e6;
border-radius: 10px;
margin-left: 20px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
font-size: 26px;
color: $color_font_two;
.miconfont{
font-size: 30px;
}
}
.upload_image{
flex:1;
}
} }
.btns_con{ .select {
width: 100%; flex: 1;
bottom:0; height: 60px;
box-sizing: border-box; border: 2px solid #e6e6e6;
margin-top: 50px; border-radius: 10px;
.btns_two{ margin-left: 20px;
display: flex; display: flex;
height: 82px; align-items: center;
// border: 1PX solid #007aff; justify-content: space-between;
font-size: $font_size_big; padding: 0 20px;
border-radius: 40px; font-size: 26px;
margin-bottom: 20px; color: $color_font_two;
.rest_btn{ .miconfont {
flex:1; font-size: 30px;
border: 1PX solid #007aff; }
border-radius: 40px 0 0 40px; .selected {
text-align: center; color: #000;
line-height: 82px; }
color: $color_main;
background-color: #fff;
}
.verify_btn{
flex:1;
border-radius: 0 40px 40px 0;
background: #007aff;
text-align: center;
line-height: 82px;
color: #fff;
}
}
} }
} .upload_image {
flex: 1;
}
}
.btns_con {
width: 100%;
bottom: 0;
box-sizing: border-box;
margin-top: 50px;
.btns_two {
display: flex;
height: 82px;
// border: 1PX solid #007aff;
font-size: $font_size_big;
border-radius: 40px;
margin-bottom: 20px;
.rest_btn {
flex: 1;
border: 1px solid #007aff;
border-radius: 40px 0 0 40px;
text-align: center;
line-height: 82px;
color: $color_main;
background-color: #fff;
}
.verify_btn {
flex: 1;
border-radius: 0 40px 40px 0;
background: #007aff;
text-align: center;
line-height: 82px;
color: #fff;
}
}
}
}

View File

@ -1,111 +1,112 @@
import Popup from "@/components/popup"; import Popup from '@/components/popup'
import TextareaEnhance from "@/components/textareaEnhance"; import TextareaEnhance from '@/components/textareaEnhance'
import { ScrollView, Text, View } from "@tarojs/components"; import { ScrollView, Text, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useRef, useState } from "react"; import { memo, useCallback, useEffect, useRef, useState } from 'react'
import ReasonPopup from "../reasonPopup"; import ReasonPopup from '../reasonPopup'
import styles from './index.module.scss' import styles from './index.module.scss'
import classnames from "classnames"; import classnames from 'classnames'
import { ApplyRefundApi, RefundExplainApi } from "@/api/salesAfterOrder"; import { ApplyRefundApi, RefundExplainApi } from '@/api/salesAfterOrder'
import { alert } from "@/common/common"; import { alert } from '@/common/common'
type Param = { type Param = {
show?: true|false, show?: true | false
onClose?: () => void, onClose?: () => void
orderId?: number orderId?: number
onSuccess?: () => void
} }
export default memo(({show, onClose, orderId}:Param) => { export default memo(({ show, onClose, orderId, onSuccess }: Param) => {
//提交的数据
const submitData = useRef({
return_explain: 0,
sale_order_id: 0,
reason_describe: '',
})
//提交的数据 useEffect(() => {
const submitData = useRef({ if (show && orderId) {
submitData.current.sale_order_id = orderId
refundExplain()
}
}, [orderId, show])
//申请退款
const { fetchData } = ApplyRefundApi()
const getApplyRefund = async () => {
if (!submitData.current.return_explain) return alert.error('请选择说明原因')
let res = await fetchData(submitData.current)
if (res.success) {
alert.success('申请成功')
onSuccess?.()
} else {
alert.error('申请失败')
}
onClose?.()
}
//获取说明数据
const [list, setList] = useState<any[]>([])
const { fetchData: refundExplainFetchdata } = RefundExplainApi()
const refundExplain = async () => {
let res = await refundExplainFetchdata()
setList(() => res.data.list)
}
const [reason, setReason] = useState({ id: 0, name: '' })
const reasonSelect = useCallback((e) => {
setReason({ ...reason, name: e.name, id: e.id })
submitData.current.return_explain = e.id
closeReason()
}, [])
//备注
const getOtherReason = useCallback((val) => {
submitData.current.reason_describe = val
}, [])
//显示说明
const [showReason, setShowReason] = useState(false)
const closeReason = useCallback(() => {
setShowReason(false)
}, [])
//提交
const onSubmit = (val) => {
if (val == 2) {
getApplyRefund()
} else {
onClose?.()
submitData.current = {
return_explain: 0, return_explain: 0,
sale_order_id: 0, sale_order_id: 0,
reason_describe: '' reason_describe: '',
}) }
useEffect(() => {
console.log('show&&orderId::', show)
if(show&&orderId) {
submitData.current.sale_order_id = orderId
refundExplain()
}
}, [orderId, show])
//申请退款
const {fetchData} = ApplyRefundApi()
const getApplyRefund = async () => {
if(!submitData.current.return_explain) return alert.error('请选择说明原因')
let res = await fetchData(submitData.current)
if(res.success) {
alert.error('申请成功')
} else {
alert.error('申请失败')
}
onClose?.()
} }
}
//获取说明数据 return (
const [list, setList] = useState<any[]>([]) <>
const {fetchData: refundExplainFetchdata} = RefundExplainApi() <Popup show={show} title='申请退款' onClose={onClose}>
const refundExplain = async () => { <View className={styles.apply_after_sales_con}>
let res = await refundExplainFetchdata() <View className={styles.returnSaleInput_item}>
setList(() => res.data.list) <View className={styles.title}>退</View>
} <View className={styles.select} onClick={() => setShowReason(true)}>
const [reason, setReason] = useState({id:0, name:''}) <Text className={reason.name ? styles.selected : ''}>{reason.name || '请选择'}</Text>
const reasonSelect = useCallback((e) => { <Text className={classnames(styles.miconfont, 'iconfont icon-a-moreback')}></Text>
setReason({...reason, name:e.name, id:e.id}) </View>
submitData.current.return_explain = e.id </View>
closeReason() <TextareaEnhance onChange={getOtherReason} title='备注' placeholder='请输入退款备注' />
}, []) <View className={styles.btns_con}>
<View className={styles.btns_two}>
<View className={styles.rest_btn} onClick={() => onSubmit(1)}>
//备注
const getOtherReason = useCallback((val) => { </View>
submitData.current.reason_describe = val <View className={styles.verify_btn} onClick={() => onSubmit(2)}>
}, [])
</View>
</View>
//显示说明 </View>
const [showReason, setShowReason] = useState(false) </View>
const closeReason = useCallback(() => { </Popup>
setShowReason(false) <ReasonPopup defaultValue={reason.id} show={showReason} onClose={closeReason} list={list} title='退款说明' onSelect={reasonSelect} />
}, []) </>
)
})
//提交
const onSubmit = (val) => {
if(val == 2) {
getApplyRefund()
} else {
onClose?.()
submitData.current = {
return_explain: 0,
sale_order_id: 0,
reason_describe: ''
}
}
}
return (
<>
<Popup show={show} title="申请退款" onClose={onClose} >
<View className={styles.apply_after_sales_con}>
<View className={styles.returnSaleInput_item}>
<View className={styles.title}>退</View>
<View className={styles.select} onClick={() => setShowReason(true)}>
<Text>{reason.name||'请选择'}</Text>
<Text className={classnames(styles.miconfont, 'iconfont icon-a-moreback')}></Text>
</View>
</View>
<TextareaEnhance onChange={getOtherReason} title='备注' placeholder="请输入退款备注"/>
<View className={styles.btns_con}>
<View className={styles.btns_two}>
<View className={styles.rest_btn} onClick={() => onSubmit(1)}></View>
<View className={styles.verify_btn } onClick={() => onSubmit(2)}></View>
</View >
</View>
</View>
</Popup>
<ReasonPopup defaultValue={reason.id} show={showReason} onClose={closeReason} list={list} title="退款说明" onSelect={reasonSelect}/>
</>
)
})

View File

@ -221,6 +221,9 @@ export default () => {
const applyRefundClose = useCallback(() => { const applyRefundClose = useCallback(() => {
setRefundShow(false) setRefundShow(false)
}, []) }, [])
const applyRefundSuccess = useCallback(() => {
getSaleOrderPreView()
}, [])
//查看物流 //查看物流
const getLogistics = useCallback(() => { const getLogistics = useCallback(() => {
@ -281,17 +284,9 @@ export default () => {
return ( return (
<View className={styles.order_main}> <View className={styles.order_main}>
{(orderDetail?.status != SaleorderstatusWaitingPrePayment.value && <OrderState orderInfo={orderDetail} />) || ( {(orderDetail?.status != SaleorderstatusWaitingPrePayment.value && <OrderState orderInfo={orderDetail} />) || <AdvanceOrderState orderInfo={orderDetail} onRefresh={refresh} />}
<AdvanceOrderState orderInfo={orderDetail} onRefresh={refresh} />
)}
<View> <View>
<AddressInfoDetail <AddressInfoDetail orderInfo={defaultAddress} onLogistics={getLogistics} onSelect={getAddress} onChangeShipmentMode={getShipmentMode} ref={addressRef} />
orderInfo={defaultAddress}
onLogistics={getLogistics}
onSelect={getAddress}
onChangeShipmentMode={getShipmentMode}
ref={addressRef}
/>
</View> </View>
<KindList order={formatPreViewOrderMemo} /> <KindList order={formatPreViewOrderMemo} />
<View className={styles.order_info}> <View className={styles.order_info}>
@ -315,9 +310,7 @@ export default () => {
</View> </View>
<View className={styles.order_desc} onClick={descOpen}> <View className={styles.order_desc} onClick={descOpen}>
<View className={styles.order_desc_con}></View> <View className={styles.order_desc_con}></View>
{(orderRemark && <View className={styles.order_desc_text}>{orderDetail?.remark}</View>) || ( {(orderRemark && <View className={styles.order_desc_text}>{orderDetail?.remark}</View>) || <View className={styles.order_desc_text_hint}></View>}
<View className={styles.order_desc_text_hint}></View>
)}
<View className={classnames(styles.miconfont, 'iconfont icon-a-moreback')}></View> <View className={classnames(styles.miconfont, 'iconfont icon-a-moreback')}></View>
</View> </View>
{orderDetail?.status != SaleOrderStatusCancel.value && ( {orderDetail?.status != SaleOrderStatusCancel.value && (
@ -331,7 +324,7 @@ export default () => {
</Popup> </Popup>
<Payment onSubmitSuccess={onPaySuccess} show={payMentShow} onClose={closePayShow} orderInfo={orderDetail} /> <Payment onSubmitSuccess={onPaySuccess} show={payMentShow} onClose={closePayShow} orderInfo={orderDetail} />
<ScanPayCheck show={showScanPayCheck} onClose={() => setShowScanPayCheck(false)} orderInfo={orderDetail} /> <ScanPayCheck show={showScanPayCheck} onClose={() => setShowScanPayCheck(false)} orderInfo={orderDetail} />
<ApplyRefund show={refundShow} onClose={applyRefundClose} orderId={orderDetail?.id} /> <ApplyRefund show={refundShow} onSuccess={applyRefundSuccess} onClose={applyRefundClose} orderId={orderDetail?.id} />
<ShopCart intoStatus='again' show={showCart} onClose={() => setShowCart(false)} /> <ShopCart intoStatus='again' show={showCart} onClose={() => setShowCart(false)} />
<ReturnRecord show={returnRecordShow} onClose={closeReturnRecord} id={orderDetail?.id} /> <ReturnRecord show={returnRecordShow} onClose={closeReturnRecord} id={orderDetail?.id} />
<View className='common_safe_area_y'></View> <View className='common_safe_area_y'></View>

View File

@ -1,146 +1,163 @@
import { goLink } from "@/common/common"; import { goLink } from '@/common/common'
import { ORDER_STATUS } from "@/common/enum"; import { ORDER_STATUS } from '@/common/enum'
import { formatHashTag, formatImgUrl, formatPriceDiv } from "@/common/fotmat"; import { formatHashTag, formatImgUrl, formatPriceDiv } from '@/common/fotmat'
import LabAndImg from "@/components/LabAndImg"; import LabAndImg from '@/components/LabAndImg'
import OrderBtns from "@/components/orderBtns"; import OrderBtns from '@/components/orderBtns'
import Payment from "@/pages/order/components/payment"; import Payment from '@/pages/order/components/payment'
import { useSelector } from "@/reducers/hooks"; import { useSelector } from '@/reducers/hooks'
import { Image, Text, View } from "@tarojs/components" import { Image, Text, View } from '@tarojs/components'
import { useRouter } from "@tarojs/taro"; import { useRouter } from '@tarojs/taro'
import classnames from "classnames"; import classnames from 'classnames'
import { memo, useCallback, useMemo, useRef, useState } from "react"; import { memo, useCallback, useMemo, useRef, useState } from 'react'
import styles from './index.module.scss' import styles from './index.module.scss'
type Param = { type Param = {
value: { value: {
order_no: string, order_no: string
sale_mode: number, sale_mode: number
sale_mode_name: string, sale_mode_name: string
status_name: string, status_name: string
shipment_mode: number, shipment_mode: number
shipment_mode_name: string, shipment_mode_name: string
product_list: any[], product_list: any[]
total_fabrics: number, total_fabrics: number
total_colors: number, total_colors: number
total_number: number, total_number: number
status: 0, status: 0
id: number, id: number
lab: any, lab: any
rgb: any, rgb: any
texture_url: string, texture_url: string
payment_method: number, //支付方式 payment_method: number //支付方式
actual_amount: number, //实付金额 actual_amount: number //实付金额
wait_pay_amount: number, //待付金额 wait_pay_amount: number //待付金额
should_collect_order_id: number, //应付单id should_collect_order_id: number //应付单id
av_return_roll: number, av_return_roll: number
total_sale_price: number, total_sale_price: number
estimate_amount: number, estimate_amount: number
is_return: true|false is_return: true | false
}, }
onClickBtn?: (val:{status:number, orderInfo:Param['value']}) => void onClickBtn?: (val: { status: number; orderInfo: Param['value'] }) => void
} }
export default memo(({value, onClickBtn}: Param) => { export default memo(({ value, onClickBtn }: Param) => {
const userInfo = useSelector((state) => state.userInfo)
//对应数量
const formatCount = useCallback(
(item, sale_mode) => {
return sale_mode == 0 ? item.roll : Number(item.length / 100)
},
[value],
)
//对应单价
const standardPrice = useCallback(
(price, sale_mode) => {
return '¥' + formatPriceDiv(price).toLocaleString() + '/' + (sale_mode == 1 ? 'm' : 'kg')
},
[value],
)
const userInfo = useSelector(state => state.userInfo) //点击订单按钮
//对应数量 const orderBtnsClick = useCallback(
const formatCount = useCallback((item, sale_mode) => { (status) => {
return sale_mode == 0? item.roll : Number(item.length / 100) onClickBtn?.({ status, orderInfo: value })
}, [value]) },
//对应单价 [value],
const standardPrice = useCallback((price, sale_mode) => { )
return '¥' + formatPriceDiv(price).toLocaleString() + '/' + (sale_mode == 1?'m':'kg')
}, [value])
//点击订单按钮 let { SaleOrderStatusTaking, SaleOrderStatusWaitingReceipt } = ORDER_STATUS
const orderBtnsClick = useCallback((status) => {
onClickBtn?.({status, orderInfo:value})
}, [value])
let {SaleOrderStatusTaking, SaleOrderStatusWaitingReceipt} = ORDER_STATUS //订单状态
// const orderStatus = useCallback((item) => {
// return item.status == SaleOrderStatusTaking.value?'装车中':item.status_name
// }, [value])
//订单状态 //按钮所需数据
// const orderStatus = useCallback((item) => { const orderInfo = useMemo(() => {
// return item.status == SaleOrderStatusTaking.value?'装车中':item.status_name return {
// }, [value]) orderId: value?.id,
...value,
}
}, [value])
//按钮所需数据 //总条数
const orderInfo = useMemo(() => { const numText = useMemo(() => {
return { let total_number_new = value?.sale_mode == 0 ? value?.total_number : value?.total_number / 100
orderId: value?.id, return `${value?.total_fabrics} 种面料,${value?.total_colors} 种颜色,共 ${total_number_new}${value?.sale_mode == 0 ? ' 条' : ' 米'}`
...value }, [value])
}
}, [value])
//总条数 //订单状态
const numText = useMemo(() => { const orderStatus = useMemo(() => {
let total_number_new = value?.sale_mode == 0? value?.total_number:(value?.total_number/100) if (value.status == SaleOrderStatusWaitingReceipt.value && value.shipment_mode == 1) {
return `${value?.total_fabrics} 种面料,${value?.total_colors} 种颜色,共 ${total_number_new}${value?.sale_mode == 0? ' 条':' 米'}` return '待提货'
}, [value]) } else {
return value?.status_name
}
}, [value])
//订单状态 return (
const orderStatus = useMemo(() => { <View className={styles.order_item}>
if(value.status == SaleOrderStatusWaitingReceipt.value && value.shipment_mode == 1) { <View className={styles.header} onClick={() => goLink('/pages/order/index', { id: value?.id })}>
return '待提货' <View className={styles.user}>
} else { <Image src={`${userInfo?.adminUserInfo?.avatar_url}`} />
return value?.status_name
}
}, [value])
return (
<View className={styles.order_item}>
<View className={styles.header} onClick={() => goLink('/pages/order/index', {id: value?.id})}>
<View className={styles.user}>
<Image src={`${userInfo?.adminUserInfo?.avatar_url}`}/>
</View>
<View className={styles.order_con}>
<Text className={styles.name}>{userInfo?.adminUserInfo?.user_name}</Text>
<View className={styles.order_num}>
<View className={styles.order_num_con}>
<Text className={styles.order_no}>{value?.order_no}</Text>
<Text className={classnames(styles.miconfont, 'iconfont, icon-a-moreback')}></Text>
</View>
<View className={styles.product_status}>{orderStatus}</View>
</View>
</View>
</View>
<View className={styles.product_con} onClick={() => goLink('/pages/order/index', {id: value?.id})}>
<View className={styles.product_title}>
<View className={styles.product_tag}>{value?.sale_mode_name}</View>
<View className={styles.product_name}>{formatHashTag(value?.product_list?.[0].code, value?.product_list?.[0].name)}</View>
<View className={styles.tag}>{value?.shipment_mode_name}</View>
</View>
<View className={styles.product_list}>
<View className={styles.image}>
<LabAndImg value={{lab:value.lab,rgb:value.rgb,texture_url:value.texture_url}}/>
<View className={styles.color_num}>{value?.product_list?.[0].product_colors?.[0].code}</View>
</View>
<View className={styles.color_list}>
{value?.product_list?.[0].product_colors.map((itemColor, index) => {
return (
(index <= 1)&&<View className={styles.color_item}>
<View className={styles.color_title}>{formatHashTag(itemColor.code, itemColor.name)}</View>
<View className={styles.color_price}>{standardPrice(itemColor.sale_price, value.sale_mode)}</View>
<View className={styles.color_num}>×{formatCount(itemColor, value.sale_mode) + (value.sale_mode == 0?' 条':' 米')}</View>
</View>
)
})
}
{value?.product_list?.[0].length > 2 && <View className={styles.color_item}>
<View className={styles.color_more}></View>
<View className={styles.color_more}></View>
<View className={styles.color_more}></View>
</View>}
</View>
</View>
<View className={styles.color_count_num}>
<Text>{numText}</Text>
<Text className={styles.price}><Text>¥</Text>{value.total_sale_price?formatPriceDiv(value.total_sale_price, 100, true):formatPriceDiv(value.estimate_amount, 100, true)}</Text>
</View>
</View>
<OrderBtns orderInfo={orderInfo} onClick={orderBtnsClick} showStatus='list'/>
</View> </View>
) <View className={styles.order_con}>
<Text className={styles.name}>{userInfo?.adminUserInfo?.user_name}</Text>
<View className={styles.order_num}>
<View className={styles.order_num_con}>
<Text className={styles.order_no}>{value?.order_no}</Text>
<Text className={classnames(styles.miconfont, 'iconfont, icon-a-moreback')}></Text>
</View>
<View className={styles.product_status}>{orderStatus}</View>
</View>
</View>
</View>
<View className={styles.product_con} onClick={() => goLink('/pages/order/index', { id: value?.id })}>
<View className={styles.product_title}>
<View className={styles.product_tag}>{value?.sale_mode_name}</View>
<View className={styles.product_name}>{formatHashTag(value?.product_list?.[0].code, value?.product_list?.[0].name)}</View>
<View className={styles.tag}>{value?.shipment_mode_name}</View>
</View>
<View className={styles.product_list}>
<View className={styles.image}>
<LabAndImg
value={{
lab: value?.product_list?.[0].product_colors?.[0].lab,
rgb: value?.product_list?.[0].product_colors?.[0].rgb,
texture_url: value?.product_list?.[0].product_colors?.[0].texture_url,
}}
/>
<View className={styles.color_num}>{value?.product_list?.[0].product_colors?.[0].code}</View>
</View>
<View className={styles.color_list}>
{value?.product_list?.[0].product_colors.map((itemColor, index) => {
return (
index <= 1 && (
<View className={styles.color_item}>
<View className={styles.color_title}>{formatHashTag(itemColor.code, itemColor.name)}</View>
<View className={styles.color_price}>{standardPrice(itemColor.sale_price, value.sale_mode)}</View>
<View className={styles.color_num}>×{formatCount(itemColor, value.sale_mode) + (value.sale_mode == 0 ? ' 条' : ' 米')}</View>
</View>
)
)
})}
{value?.product_list?.[0].length > 2 && (
<View className={styles.color_item}>
<View className={styles.color_more}></View>
<View className={styles.color_more}></View>
<View className={styles.color_more}></View>
</View>
)}
</View>
</View>
<View className={styles.color_count_num}>
<Text>{numText}</Text>
<Text className={styles.price}>
<Text>¥</Text>
{value.total_sale_price ? formatPriceDiv(value.total_sale_price, 100, true) : formatPriceDiv(value.estimate_amount, 100, true)}
</Text>
</View>
</View>
<OrderBtns orderInfo={orderInfo} onClick={orderBtnsClick} showStatus='list' />
</View>
)
}) })

View File

@ -181,6 +181,9 @@ export default () => {
const applyRefundClose = useCallback(() => { const applyRefundClose = useCallback(() => {
setRefundShow(false) setRefundShow(false)
}, []) }, [])
const applyRefundSuccess = useCallback(() => {
getOrderList()
}, [])
//显示售后记录 //显示售后记录
const [returnRecordShow, setReturnRecordShow] = useState(false) const [returnRecordShow, setReturnRecordShow] = useState(false)
@ -210,14 +213,13 @@ export default () => {
{orderData?.list?.map((item) => { {orderData?.list?.map((item) => {
return ( return (
<View key={item.id} className={styles.order_item_con}> <View key={item.id} className={styles.order_item_con}>
{' '}
<Order value={item} onClickBtn={clickOrderBtn} /> <Order value={item} onClickBtn={clickOrderBtn} />
</View> </View>
) )
})} })}
</InfiniteScroll> </InfiniteScroll>
</View> </View>
<ApplyRefund show={refundShow} onClose={applyRefundClose} orderId={callBackOrderInfo?.id} /> <ApplyRefund show={refundShow} onClose={applyRefundClose} onSuccess={applyRefundSuccess} orderId={callBackOrderInfo?.id} />
<ShopCart intoStatus='again' show={showCart} onClose={() => setShowCart(false)} default_sale_mode={callBackOrderInfo?.sale_mode} /> <ShopCart intoStatus='again' show={showCart} onClose={() => setShowCart(false)} default_sale_mode={callBackOrderInfo?.sale_mode} />
<ReturnRecord show={returnRecordShow} onClose={closeReturnRecord} id={callBackOrderInfo?.id} /> <ReturnRecord show={returnRecordShow} onClose={closeReturnRecord} id={callBackOrderInfo?.id} />
<Payment onSubmitSuccess={onPaySuccess} show={payMentShow} onClose={closePayShow} orderInfo={callBackOrderInfo} /> <Payment onSubmitSuccess={onPaySuccess} show={payMentShow} onClose={closePayShow} orderInfo={callBackOrderInfo} />

View File

@ -1,84 +1,81 @@
import { AFTER_ORDER_STATUS, REFUND_STATUS_ORDER } from "@/common/enum"; import { AFTER_ORDER_STATUS, REFUND_STATUS_ORDER } from '@/common/enum'
import { Text, View } from "@tarojs/components" import { Text, View } from '@tarojs/components'
import classnames from "classnames"; import classnames from 'classnames'
import {memo, useMemo} from "react"; import { memo, useMemo } from 'react'
import styles from './index.module.scss' import styles from './index.module.scss'
type Param = { type Param = {
onLogistics?: (val: 1|2) => void, //1 上传物流 2 查看物流 onLogistics?: (val: 1 | 2) => void //1 上传物流 2 查看物流
orderInfo: { orderInfo: {
return_user_name?:string, return_user_name?: string
return_user_phone?: string, return_user_phone?: string
stage?: number, stage?: number
sale_mode?: 0|1|2, //0 大货 1剪板 2散剪 sale_mode?: 0 | 1 | 2 //0 大货 1剪板 2散剪
type?: number, //申请单退款状态 type?: number //申请单退款状态
} return_address?: string
}
} }
export default memo(({orderInfo, onLogistics}:Param) => { export default memo(({ orderInfo, onLogistics }: Param) => {
const { const {
ReturnStageApplying, ReturnStageApplying,
ReturnStageWaitCheck, ReturnStageWaitCheck,
ReturnStageChecked, ReturnStageChecked,
ReturnStageReturned, ReturnStageReturned,
ReturnStageCancel, ReturnStageCancel,
ReturnStageQualityCheckPendingRefund, ReturnStageQualityCheckPendingRefund,
ReturnStageServiceOrderPendingRefund, ReturnStageServiceOrderPendingRefund,
ReturnStageRejected ReturnStageRejected,
} = AFTER_ORDER_STATUS } = AFTER_ORDER_STATUS
const { const {
ReturnApplyOrderTypeAdvanceReceiptRefund, // 预收退款 ReturnApplyOrderTypeAdvanceReceiptRefund, // 预收退款
ReturnApplyOrderTypeReturnForRefund, // 退货退款 ReturnApplyOrderTypeReturnForRefund, // 退货退款
ReturnApplyOrderTypeSalesRefund, // 销售退款 ReturnApplyOrderTypeSalesRefund, // 销售退款
} = REFUND_STATUS_ORDER } = REFUND_STATUS_ORDER
//是否显示地址 //是否显示地址
const showAddress = useMemo(() => { const showAddress = useMemo(() => {
let after_list = [ let after_list = [ReturnStageApplying.value, ReturnStageCancel.value, ReturnStageRejected.value]
ReturnStageApplying.value, let refurn_list = [ReturnApplyOrderTypeSalesRefund.value, ReturnApplyOrderTypeAdvanceReceiptRefund.value]
ReturnStageCancel.value, return !after_list.includes(orderInfo?.stage!) && orderInfo?.sale_mode != 1 && !refurn_list.includes(orderInfo?.type!)
ReturnStageRejected.value }, [orderInfo])
]
let refurn_list = [
ReturnApplyOrderTypeSalesRefund.value,
ReturnApplyOrderTypeAdvanceReceiptRefund.value
]
return (!after_list.includes(orderInfo?.stage!)) && (orderInfo?.sale_mode != 1) && (!refurn_list.includes(orderInfo?.type!))
}, [orderInfo])
//上传物流 //上传物流
const upLogistics = useMemo(() => { const upLogistics = useMemo(() => {
return orderInfo?.stage == ReturnStageWaitCheck.value return orderInfo?.stage == ReturnStageWaitCheck.value
}, [orderInfo]) }, [orderInfo])
return (
return ( <>
<> {showAddress && (
{showAddress&&<View className={styles.address_main}> <View className={styles.address_main}>
<View className={styles.address_title_tag}> <View className={styles.address_title_tag}>
<Text className={classnames(styles.miconfont, 'iconfont icon-zhuyi')}></Text> <Text className={classnames(styles.miconfont, 'iconfont icon-zhuyi')}></Text>
退退 退退
</View> </View>
<View className={styles.order_address} > <View className={styles.order_address}>
<View className={classnames(styles.order_address_icon, 'iconfont','icon-fahuo')}></View> <View className={classnames(styles.order_address_icon, 'iconfont', 'icon-fahuo')}></View>
<View className={styles.order_address_text_con}> <View className={styles.order_address_text_con}>
<View className={styles.order_address_text_title}> <View className={styles.order_address_text_title}>
<Text className={classnames(styles.address_text, styles.address_text_no)}>{orderInfo?.return_user_name}</Text> <Text className={classnames(styles.address_text, styles.address_text_no)}>{orderInfo?.return_address}</Text>
</View> </View>
<View className={styles.order_address_text_name}> <View className={styles.order_address_text_name}>
<Text></Text> <Text></Text>
<Text>{orderInfo?.return_user_phone}</Text> <Text>{orderInfo?.return_user_phone}</Text>
{upLogistics&&<View className={styles.updateBtn} onClick={() => onLogistics?.(1)}> {(upLogistics && (
<View className={styles.updateBtn} onClick={() => onLogistics?.(1)}>
</View>
||<View className={styles.updateBtn} onClick={() => onLogistics?.(2)}> </View>
)) || (
</View>} <View className={styles.updateBtn} onClick={() => onLogistics?.(2)}>
</View>
</View> </View>
</View> )}
</View>} </View>
</> </View>
) </View>
}) </View>
)}
</>
)
})

View File

@ -1,136 +1,148 @@
import { SaleOrderOrderDetailApi } from "@/api/salesAfterOrder"; import { SaleOrderOrderDetailApi } from '@/api/salesAfterOrder'
import { formatHashTag, formatPriceDiv, formatWeightDiv } from "@/common/fotmat"; import { formatHashTag, formatPriceDiv, formatWeightDiv } from '@/common/fotmat'
import LabAndImg from "@/components/LabAndImg"; import LabAndImg from '@/components/LabAndImg'
import Popup from "@/components/popup"; import Popup from '@/components/popup'
import { ScrollView, Text, View } from "@tarojs/components"; import { ScrollView, Text, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import Taro from "@tarojs/taro"; import Taro from '@tarojs/taro'
import styles from './index.module.scss' import styles from './index.module.scss'
type Param = { type Param = {
show?: true|false, show?: true | false
onClose?: () => void, onClose?: () => void
onSubmit?: () => void, onSubmit?: () => void
id?: number id?: number
} }
export default memo(({show, onClose, onSubmit, id}:Param) => { export default memo(({ show, onClose, onSubmit, id }: Param) => {
useEffect(() => { useEffect(() => {
if(show && id) getSaleOrderPreView() if (show && id) getSaleOrderPreView()
if(!show) setFormatDetailOrder(() => null) if (!show) setFormatDetailOrder(() => null)
}, [show, id]) }, [show, id])
//获取订单详情
//获取订单详情 const [orderDetail, setOrderDetail] = useState<any>(null) //获取到的原始数据
const [orderDetail, setOrderDetail] = useState<any>(null) //获取到的原始数据 const { fetchData: saleOrderOrderDetailData } = SaleOrderOrderDetailApi()
const {fetchData: saleOrderOrderDetailData} = SaleOrderOrderDetailApi() const getSaleOrderPreView = async () => {
const getSaleOrderPreView = async () => { if (id) {
if(id) { let res = await saleOrderOrderDetailData({ id: id })
let res = await saleOrderOrderDetailData({id: id}) setOrderDetail(res.data)
setOrderDetail(res.data)
}
Taro.stopPullDownRefresh()
} }
//格式化数据格式 Taro.stopPullDownRefresh()
const [formatDetailOrder, setFormatDetailOrder] = useState<any>() //格式化后的数据 }
const formatData = () => { //格式化数据格式
setFormatDetailOrder({ const [formatDetailOrder, setFormatDetailOrder] = useState<any>() //格式化后的数据
estimate_amount: orderDetail.estimate_amount, //预估金额 const formatData = () => {
sale_mode: orderDetail.sale_mode, setFormatDetailOrder({
sale_mode_name: orderDetail.sale_mode_name, estimate_amount: orderDetail.estimate_amount, //预估金额
total_colors: orderDetail.total_colors, //总颜色数量 sale_mode: orderDetail.sale_mode,
total_number: orderDetail.total_number, //总数量 sale_mode_name: orderDetail.sale_mode_name,
total_fabrics: orderDetail.total_fabrics, //面料数量 total_colors: orderDetail.total_colors, //总颜色数量
unit: orderDetail.sale_mode == 0?'条':'m', //单位 total_number: orderDetail.total_number, //总数量
list: orderDetail.product_list, total_fabrics: orderDetail.total_fabrics, //面料数量
stage: orderDetail.stage, //订单状态 unit: orderDetail.sale_mode == 0 ? '条' : 'm', //单位
type: orderDetail.type, //退货or退款 list: orderDetail.product_list,
total_sale_price: orderDetail.total_sale_price, //销售金额 stage: orderDetail.stage, //订单状态
total_should_collect_money: orderDetail.total_should_collect_money, //应收金额 type: orderDetail.type, //退货or退款
total_weight_error_discount: orderDetail.total_weight_error_discount, //空差优惠 total_sale_price: orderDetail.total_sale_price, //销售金额
actual_amount: orderDetail.actual_amount, //实付金额 total_should_collect_money: orderDetail.total_should_collect_money, //应收金额
the_previous_status: orderDetail.the_previous_status, //取消订单时的订单状态 total_weight_error_discount: orderDetail.total_weight_error_discount, //空差优惠
}) actual_amount: orderDetail.actual_amount, //实付金额
the_previous_status: orderDetail.the_previous_status, //取消订单时的订单状态
})
}
//监听获取到的数据
useEffect(() => {
if (orderDetail) formatData()
}, [orderDetail])
//对应数量
const formatCount = useCallback(
(item) => {
return formatDetailOrder?.sale_mode == 0 ? item.roll : Number(item.length / 100)
},
[formatDetailOrder],
)
//对应单价
const standardPrice = useCallback(
(price) => {
return formatPriceDiv(price).toLocaleString() + '/' + (formatDetailOrder?.sale_mode == 1 ? 'm' : 'kg')
},
[formatDetailOrder],
)
//数量格式
const numText = useMemo(() => {
if (formatDetailOrder) {
let tatal_number = formatDetailOrder?.sale_mode == 0 ? formatDetailOrder?.total_number : formatDetailOrder?.total_number / 100
return `${formatDetailOrder?.total_fabrics} 种面料,${formatDetailOrder?.total_colors} 种颜色,共 ${tatal_number} ${formatDetailOrder?.unit}`
} }
}, [formatDetailOrder])
//监听获取到的数据 //整理颜色
useEffect(() => { const labAndRgbAndUrl = useCallback((item) => {
if(orderDetail) return { lab: { ...item?.lab }, rgb: { ...item?.rgb }, texturl_url: item?.texturl_url }
formatData() }, [])
}, [orderDetail])
//对应数量 return (
const formatCount = useCallback((item) => { <>
return formatDetailOrder?.sale_mode == 0? item.roll : Number(item.length / 100) <Popup show={show} title='申请记录' onClose={onClose}>
}, [formatDetailOrder]) <View className={styles.apply_record_main}>
{formatDetailOrder && (
//对应单价 <>
const standardPrice = useCallback(price => { <View className={styles.kind_number}>
return formatPriceDiv(price).toLocaleString() + '/' + (formatDetailOrder?.sale_mode == 1?'m':'kg') <Text>{numText}</Text>
}, [formatDetailOrder]) </View>
<ScrollView scrollY className={styles.apply_record_scroll}>
//数量格式 <View className={styles.orders_list_con}>
const numText = useMemo(() => { {formatDetailOrder?.list?.map((item) => {
if(formatDetailOrder) { return (
let tatal_number = formatDetailOrder?.sale_mode == 0?formatDetailOrder?.total_number:formatDetailOrder?.total_number/100 <View key={item.product_code} className={styles.order_list}>
return `${formatDetailOrder?.total_fabrics} 种面料,${formatDetailOrder?.total_colors} 种颜色,共 ${tatal_number}${formatDetailOrder?.unit}` <View className={styles.order_list_title}>
} <View className={styles.tag}>{formatDetailOrder.sale_mode_name}</View>
}, [formatDetailOrder]) <View className={styles.title}>{formatHashTag(item.code, item.name)}</View>
<View className={styles.num}>{item?.product_colors.length}</View>
//整理颜色 </View>
const labAndRgbAndUrl = useCallback((item) => { <View className={styles.order_list_scroll}>
return {lab:{...item?.lab}, rgb:{...item?.rgb}, texturl_url: item?.texturl_url} {item?.product_colors?.map((colorItem) => {
}, []) return (
<View key={colorItem.id} className={styles.order_list_item}>
return ( <View className={styles.order_list_item_img}>
<> <LabAndImg value={labAndRgbAndUrl(colorItem)} />
<Popup show={show} title="申请记录" onClose={onClose}> </View>
<View className={styles.apply_record_main}> <View className={styles.order_list_item_con}>
{formatDetailOrder&&<> <View className={styles.order_list_item_des}>
<View className={styles.kind_number}><Text>{numText}</Text></View> <View className={styles.order_list_item_title}>{colorItem.code + ' ' + colorItem.name}</View>
<ScrollView scrollY className={styles.apply_record_scroll}> <View className={styles.order_list_item_price}>
<View className={styles.orders_list_con}> <Text>¥{standardPrice(colorItem.sale_price)}</Text>
{ <Text>{formatWeightDiv(colorItem.estimate_weight)}kg</Text>
formatDetailOrder?.list?.map(item => { </View>
return <View key={item.product_code} className={styles.order_list}> </View>
<View className={styles.order_list_title}> <View className={styles.order_list_item_count}>
<View className={styles.tag}>{formatDetailOrder.sale_mode_name}</View> <View className={styles.count_num}>
<View className={styles.title}>{formatHashTag(item.code, item.name)}</View> ×{formatCount(colorItem)}
<View className={styles.num}>{item?.product_colors.length}</View> <text>{formatDetailOrder.unit}</text>
</View> </View>
<View className={styles.order_list_scroll}> <View className={styles.count_price}>
{item?.product_colors?.map(colorItem => { <text>¥</text>
return <View key={colorItem.id} className={styles.order_list_item}> {formatPriceDiv(colorItem.estimate_amount, 100, true)}
<View className={styles.order_list_item_img}> </View>
<LabAndImg value={labAndRgbAndUrl(colorItem)}/> </View>
</View> </View>
<View className={styles.order_list_item_con}> </View>
<View className={styles.order_list_item_des}> )
<View className={styles.order_list_item_title}>{colorItem.code + ' ' + colorItem.name}</View> })}
<View className={styles.order_list_item_price}> </View>
<Text>¥{standardPrice(colorItem.sale_price)}</Text> </View>
<Text>{formatWeightDiv(colorItem.estimate_weight)}kg</Text> )
</View> })}
</View>
<View className={styles.order_list_item_count}>
<View className={styles.count_num}>×{formatCount(colorItem)}<text>{formatDetailOrder.unit}</text></View>
<View className={styles.count_price}><text>¥</text>{formatPriceDiv(colorItem.estimate_amount, 100, true)}</View>
</View>
</View>
</View>
})}
</View>
</View>
})
}
{/* <View className={styles.order_total}>
<Text></Text>
<Text>×{orderDetail?.total_number}</Text>
</View> */}
</View>
</ScrollView>
</>}
</View> </View>
</Popup> </ScrollView>
</> </>
) )}
}) </View>
</Popup>
</>
)
})

View File

@ -1,74 +1,78 @@
import Popup from "@/components/popup"; import Popup from '@/components/popup'
import { Text, View } from "@tarojs/components"; import { Text, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useRef, useState } from "react"; import { memo, useCallback, useEffect, useRef, useState } from 'react'
import UploadImage from "@/components/uploadImage" import UploadImage from '@/components/uploadImage'
import styles from './index.module.scss' import styles from './index.module.scss'
import TextareaEnhance from "@/components/textareaEnhance"; import TextareaEnhance from '@/components/textareaEnhance'
import { ReturnApplyLogisticsApi } from "@/api/salesAfterOrder"; import { ReturnApplyLogisticsApi } from '@/api/salesAfterOrder'
import { alert } from "@/common/common"; import { alert } from '@/common/common'
type Param = { type Param = {
show?: true|false, show?: true | false
onClose?: () => void, onClose?: () => void
onSubmit?: () => void, onSubmit?: () => void
id?: number, //订单id id?: number //订单id
images: string[], //图片列表 images: string[] //图片列表
descValue?: string, //描述 descValue?: string //描述
onlyRead?: false|true //true 只读 onlyRead?: false | true //true 只读
} }
export default memo(({show = false, onClose, onSubmit, id = 0, images = [], descValue = '', onlyRead = false}: Param) => { export default memo(({ show = false, onClose, onSubmit, id = 0, images = [], descValue = '', onlyRead = false }: Param) => {
//需要提交的数据 //需要提交的数据
const submitData = useRef({ const submitData = useRef({
accessory_url: [], accessory_url: [],
remark: '', remark: '',
id: 0 id: 0,
}) })
useEffect(() => { useEffect(() => {
if(id) submitData.current.id = id if (id) submitData.current.id = id
}, [id]) }, [id])
//获取图片列表 //获取图片列表
const getImageList = useCallback((list) => { const getImageList = useCallback((list) => {
submitData.current.accessory_url = list submitData.current.accessory_url = list
}, []) }, [])
//备注 //备注
const getOtherReason = useCallback((val) => { const getOtherReason = useCallback((val) => {
submitData.current.remark = val submitData.current.remark = val
}, []) }, [])
//确定 //确定
const {fetchData} = ReturnApplyLogisticsApi() const { fetchData } = ReturnApplyLogisticsApi()
const onSubmitEven = async () => { const onSubmitEven = async () => {
if(!id) return alert.error('参数有误') if (!id) return alert.error('参数有误')
if(submitData.current.accessory_url.length <= 0) return alert.error('请上传附件') if (submitData.current.accessory_url.length <= 0) return alert.error('请上传附件')
let res = await fetchData(submitData.current) let res = await fetchData(submitData.current)
if(res.success) { if (res.success) {
alert.success('上传成功') alert.success('上传成功')
} else { } else {
alert.error('上传失败') alert.error('上传失败')
}
onSubmit?.()
} }
onSubmit?.()
}
return ( return (
<> <>
<Popup show={show} title={onlyRead?'查看物流':"上传物流"} onClose={onClose}> <Popup show={show} title={onlyRead ? '查看物流' : '上传物流'} onClose={onClose}>
<View className={styles.logistics_main}> <View className={styles.logistics_main}>
<View className={styles.logistics_image}> <View className={styles.logistics_image}>
<Text className={styles.title_desc}></Text> <Text className={styles.title_desc}></Text>
<View className={styles.upload_image}> <View className={styles.upload_image}>
<UploadImage onChange={getImageList} defaultList={images} onlyRead={onlyRead}/> <UploadImage onChange={getImageList} defaultList={images} onlyRead={onlyRead} />
</View> </View>
</View> </View>
<View className={styles.logistics_desc}> <View className={styles.logistics_desc}>
<TextareaEnhance defaultValue={descValue} onChange={getOtherReason} title="备注:" onlyRead={onlyRead} placeholder="请输入备注信息"/> <TextareaEnhance defaultValue={descValue} onChange={getOtherReason} title='备注:' onlyRead={onlyRead} placeholder='请输入备注信息' />
</View> </View>
{!onlyRead&&<View className={styles.btns_two}> {!onlyRead && (
<View className={styles.verify_btn } onClick={() => onSubmitEven()}></View> <View className={styles.btns_two}>
</View >} <View className={styles.verify_btn} onClick={() => onSubmitEven()}>
</View>
</Popup> </View>
</> </View>
) )}
}) </View>
</Popup>
</>
)
})

View File

@ -1,193 +1,204 @@
import { SaleOrderOrderDetailApi } from '@/api/salesAfterOrder'
import { AFTER_ORDER_STATUS, ORDER_STATUS } from '@/common/enum'
import { formatDateTime, formatImgUrl, formatPriceDiv } from '@/common/fotmat'
import AfterOrderBtns from '@/components/afterOrderBtns'
import SearchInput from '@/components/searchInput'
import useLogin from '@/use/useLogin'
import { Image, Text, Textarea, View } from '@tarojs/components'
import Taro, { useDidShow, usePullDownRefresh, useRouter } from '@tarojs/taro'
import classnames from 'classnames'
import { useCallback, useEffect, useMemo, useRef, useState, memo } from 'react'
import AddressInfoDetail from './components/addressInfoDetail'
import ApplyRecord from './components/applyRecord'
import ContentBox from './components/contentBox'
import KindList from './components/kindList'
import OrderState from './components/orderState'
import ReturnLogistics from './components/returnLogistics'
import styles from './index.module.scss'
import { SaleOrderOrderDetailApi } from "@/api/salesAfterOrder"; export default () => {
import { AFTER_ORDER_STATUS, ORDER_STATUS } from "@/common/enum"; useLogin()
import { formatDateTime, formatImgUrl, formatPriceDiv } from "@/common/fotmat"; const router = useRouter()
import AfterOrderBtns from "@/components/afterOrderBtns"; const orderId = useRef<number>(Number(router.params.id))
import SearchInput from "@/components/searchInput"; useDidShow(() => {
import useLogin from "@/use/useLogin"; getSaleOrderPreView()
import { Image, Text, Textarea, View } from "@tarojs/components" })
import Taro, {useDidShow, usePullDownRefresh, useRouter } from "@tarojs/taro";
import classnames from "classnames"; //获取订单详情
import { useCallback, useEffect, useMemo, useRef, useState, memo } from "react"; const [orderDetail, setOrderDetail] = useState<any>() //获取到的原始数据
import AddressInfoDetail from "./components/addressInfoDetail"; const { fetchData: saleOrderOrderDetailData } = SaleOrderOrderDetailApi()
import ApplyRecord from "./components/applyRecord"; const getSaleOrderPreView = async () => {
import ContentBox from "./components/contentBox"; if (orderId.current) {
import KindList from "./components/kindList"; let res = await saleOrderOrderDetailData({ id: orderId.current })
import OrderState from "./components/orderState"; setOrderDetail(res.data)
import ReturnLogistics from "./components/returnLogistics"; }
import styles from './index.module.scss' Taro.stopPullDownRefresh()
}
export default () => {
useLogin() //监听获取到的数据
const router = useRouter() useEffect(() => {
const orderId = useRef<number>(Number(router.params.id)) if (orderDetail) formatData()
useDidShow(() => { }, [orderDetail])
getSaleOrderPreView()
//格式化数据格式
const [formatDetailOrder, setFormatDetailOrder] = useState<any>() //格式化后的数据
const formatData = () => {
setFormatDetailOrder({
...orderDetail,
unit: orderDetail.sale_mode == 0 ? '条' : 'm', //单位
}) })
}
const formatPreViewOrderMemo = useMemo(() => {
return formatDetailOrder
}, [formatDetailOrder])
//获取订单详情 //获取底部按钮点击, 获取按钮状态
const [orderDetail, setOrderDetail] = useState<any>() //获取到的原始数据 const orderStateClick = useCallback(
const {fetchData: saleOrderOrderDetailData} = SaleOrderOrderDetailApi() (val) => {
const getSaleOrderPreView = async () => { if (val == 1 || val == 6) {
if(orderId.current) {
let res = await saleOrderOrderDetailData({id: orderId.current})
setOrderDetail(res.data)
}
Taro.stopPullDownRefresh()
}
//监听获取到的数据
useEffect(() => {
if(orderDetail)
formatData()
}, [orderDetail])
//格式化数据格式
const [formatDetailOrder, setFormatDetailOrder] = useState<any>() //格式化后的数据
const formatData = () => {
setFormatDetailOrder({
...orderDetail,
unit: orderDetail.sale_mode == 0?'条':'m', //单位
})
}
const formatPreViewOrderMemo = useMemo(() => {
return formatDetailOrder
}, [formatDetailOrder])
//获取底部按钮点击, 获取按钮状态
const orderStateClick = useCallback((val) => {
if(val == 1 || val == 6) {
getSaleOrderPreView()
} else if(val == 8) {
//申请记录
setApplyRecord(true)
} else if (val == 5) {
onShowLogistics(1)
}
}, [orderDetail])
//页面下拉刷新
usePullDownRefresh(() => {
getSaleOrderPreView() getSaleOrderPreView()
} else if (val == 8) {
//申请记录
setApplyRecord(true)
} else if (val == 5) {
onShowLogistics(1)
}
},
[orderDetail],
)
//页面下拉刷新
usePullDownRefresh(() => {
getSaleOrderPreView()
})
//按钮所需数据
const orderInfo = useMemo(() => {
return {
...orderDetail,
}
}, [orderDetail])
//售后订单状态枚举
const {} = AFTER_ORDER_STATUS
//物流显示
const [logisticsShow, setLogisticsShow] = useState(false)
const [logistics, setLogistics] = useState(false)
const onShowLogistics = useCallback((val) => {
setLogisticsShow(true)
if (val != 1) setLogistics(true)
}, [])
const onCloseLogistics = useCallback(() => {
setLogisticsShow(false)
}, [])
//物流成功上传
const logisticsSuccess = useCallback(() => {
setLogisticsShow(false)
getSaleOrderPreView()
}, [])
//显示记录
const [applyRecord, setApplyRecord] = useState(false)
return (
<View className={styles.order_main}>
<OrderState orderInfo={orderDetail} />
<AddressInfoDetail orderInfo={orderDetail} onLogistics={onShowLogistics} />
<KindList order={formatPreViewOrderMemo} />
<OrderDes orderInfo={orderDetail} />
<AfterOrderBtns orderInfo={orderInfo} onClick={orderStateClick} />
<AfterSalePricture urls={orderDetail?.fabric_piece_accessory_url} />
<ReturnLogistics
onlyRead={logistics}
images={orderDetail?.accessory_url}
descValue={orderDetail?.take_goods_remark}
show={logisticsShow}
id={orderDetail?.return_apply_order_id}
onClose={onCloseLogistics}
onSubmit={logisticsSuccess}
/>
<ApplyRecord show={applyRecord} id={orderDetail?.id} onClose={() => setApplyRecord(false)} />
<View className='common_safe_area_y'></View>
</View>
)
}
const OrderDes = memo(({ orderInfo }: { orderInfo?: any }) => {
//复制功能
const clipboardData = (val) => {
Taro.setClipboardData({
data: val,
success: function (res) {
Taro.showToast({
icon: 'none',
title: '复制成功',
})
},
}) })
}
//按钮所需数据 return (
const orderInfo = useMemo(() => { <View className={styles.order_info}>
return { <View className={styles.order_info_title}></View>
...orderDetail <SearchInput showBorder={false} title='售后单号' height='50rpx'>
} <View className={styles.order_num}>
}, [orderDetail]) <Text>{orderInfo?.return_order_no}</Text>
<View className={styles.order_num_btn} onClick={() => clipboardData(orderInfo?.return_order_no)}>
//售后订单状态枚举
const { </View>
} = AFTER_ORDER_STATUS
//物流显示
const [logisticsShow, setLogisticsShow] = useState(false)
const [logistics, setLogistics] = useState(false)
const onShowLogistics = useCallback((val) => {
setLogisticsShow(true)
if(val != 1) setLogistics(true)
}, [])
const onCloseLogistics = useCallback(() => {
setLogisticsShow(false)
}, [])
//物流成功上传
const logisticsSuccess = useCallback(() => {
setLogisticsShow(false)
getSaleOrderPreView()
}, [])
//显示记录
const [applyRecord, setApplyRecord] = useState(false)
return (
<View className={styles.order_main}>
<OrderState orderInfo={orderDetail}/>
<AddressInfoDetail orderInfo={orderDetail} onLogistics={onShowLogistics}/>
<KindList order={formatPreViewOrderMemo}/>
<OrderDes orderInfo={orderDetail}/>
<AfterOrderBtns orderInfo={orderInfo} onClick={orderStateClick}/>
<AfterSalePricture urls={orderDetail?.fabric_piece_accessory_url}/>
<ReturnLogistics onlyRead={logistics} images={orderDetail?.accessory_url} descValue={orderDetail?.take_goods_remark} show={logisticsShow} id={orderDetail?.return_apply_order_id} onClose={onCloseLogistics} onSubmit={logisticsSuccess}/>
<ApplyRecord show={applyRecord} id={orderDetail?.id} onClose={() => setApplyRecord(false)}/>
<View className="common_safe_area_y"></View>
</View> </View>
) </SearchInput>
} <SearchInput showBorder={false} title='订单号' height='50rpx'>
<View className={styles.order_num}>
const OrderDes = memo(({orderInfo}:{orderInfo?:any}) => { <Text>{orderInfo?.order_no}</Text>
//复制功能 <View className={styles.order_num_btn} onClick={() => clipboardData(orderInfo?.order_no)}>
const clipboardData = (val) => {
Taro.setClipboardData({ </View>
data: val,
success: function (res) {
Taro.showToast({
icon: 'none',
title: '复制成功'
})
}
})
}
return (
<View className={styles.order_info} >
<View className={styles.order_info_title}></View>
<SearchInput showBorder={false} title='售后单号' height='50rpx'>
<View className={styles.order_num}>
<Text>{orderInfo?.return_order_no}</Text>
<View className={styles.order_num_btn} onClick={() => clipboardData(orderInfo?.return_order_no)}></View>
</View>
</SearchInput>
<SearchInput showBorder={false} title='订单号' height='50rpx'>
<View className={styles.order_num}>
<Text>{orderInfo?.order_no}</Text>
<View className={styles.order_num_btn} onClick={() => clipboardData(orderInfo?.order_no)}></View>
</View>
</SearchInput>
<SearchInput showBorder={false} title='退货原因' height='50rpx'>
<Text>{orderInfo?.return_reason_name}</Text>
</SearchInput>
<SearchInput showBorder={false} title='退货说明' height='50rpx'>
<Text>{orderInfo?.return_explain_name}</Text>
</SearchInput>
<SearchInput showBorder={false} title='货物状况' height='50rpx'>
<Text>{orderInfo?.goods_status_name}</Text>
</SearchInput>
<SearchInput showBorder={false} title='申请时间' height='50rpx'>
<Text>{formatDateTime(orderInfo?.apply_time)}</Text>
</SearchInput>
<SearchInput showBorder={false} title='其它说明' height='auto'>
<Text>{orderInfo?.reason_describe}</Text>
</SearchInput>
</View> </View>
) </SearchInput>
}) <SearchInput showBorder={false} title='退货原因' height='50rpx'>
<Text>{orderInfo?.return_reason_name}</Text>
</SearchInput>
<SearchInput showBorder={false} title='退货说明' height='50rpx'>
<Text>{orderInfo?.return_explain_name}</Text>
</SearchInput>
<SearchInput showBorder={false} title='货物状况' height='50rpx'>
<Text>{orderInfo?.goods_status_name}</Text>
</SearchInput>
<SearchInput showBorder={false} title='申请时间' height='50rpx'>
<Text>{formatDateTime(orderInfo?.apply_time)}</Text>
</SearchInput>
<SearchInput showBorder={false} title='其它说明' height='auto'>
<Text>{orderInfo?.reason_describe}</Text>
</SearchInput>
</View>
)
})
const AfterSalePricture = memo(({urls = []}:{urls: string[]}) => { const AfterSalePricture = memo(({ urls = [] }: { urls: string[] }) => {
const showList = useMemo(() => {
let res = urls.map((item) => {
return formatImgUrl(item, '!w800')
})
return res
}, [urls])
const showList = useMemo(() => { //预览图片
let res = urls.map(item => { const showImage = () => {
return formatImgUrl(item, "!w800") Taro.previewImage({
}) current: showList[0], // 当前显示
return res urls: showList, // 需要预览的图片http链接列表
}, [urls]) })
}
//预览图片 return (
const showImage = () => { <ContentBox title='售后图片'>
Taro.previewImage({ <View className={styles.after_sale_picture_list}>
current: showList[0], // 当前显示 {urls?.map((item) => (
urls: showList // 需要预览的图片http链接列表 <View className={styles.after_sale_picture_item} onClick={showImage}>
}) <Image src={formatImgUrl(item)} />
} </View>
return ( ))}
<ContentBox title="售后图片"> </View>
<View className={styles.after_sale_picture_list}> </ContentBox>
{urls?.map(item=> <View className={styles.after_sale_picture_item} onClick={showImage}> )
<Image src={formatImgUrl(item)} /> })
</View>)}
</View>
</ContentBox>
)
})

View File

@ -1,110 +1,111 @@
import Search from "@/components/search" import Search from '@/components/search'
import useLogin from "@/use/useLogin" import useLogin from '@/use/useLogin'
import {View } from "@tarojs/components" import { View } from '@tarojs/components'
import Taro, { useDidShow} from "@tarojs/taro" import Taro, { useDidShow } from '@tarojs/taro'
import { useCallback, useEffect, useMemo, useRef, useState } from "react" import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import styles from './index.module.scss' import styles from './index.module.scss'
import classnames from "classnames"; import classnames from 'classnames'
import Order from "./components/order" import Order from './components/order'
import InfiniteScroll from "@/components/infiniteScroll" import InfiniteScroll from '@/components/infiniteScroll'
import { dataLoadingStatus, getFilterData } from "@/common/util" import { dataLoadingStatus, getFilterData } from '@/common/util'
import OrderStatusList from "./components/orderStatusList" import OrderStatusList from './components/orderStatusList'
import { GetSaleOrderListApi, RefundOrderSatausApi } from "@/api/salesAfterOrder" import { GetSaleOrderListApi, RefundOrderSatausApi } from '@/api/salesAfterOrder'
import ApplyRecord from "../components/applyRecord" import ApplyRecord from '../components/applyRecord'
import ReturnLogistics from "../components/returnLogistics" import ReturnLogistics from '../components/returnLogistics'
export default () => { export default () => {
useLogin() useLogin()
//搜索参数 //搜索参数
const [searchField, setSearchField] = useState<{status: number|null, page: number, size: number, name: string}>({ const [searchField, setSearchField] = useState<{ status: number | null; page: number; size: number; name: string }>({
status: null, status: null,
page : 1, page: 1,
size : 10, size: 10,
name:'' name: '',
}) })
//获取订单状态 //获取订单状态
const [statusList, setStatusList] = useState<any[]>([{id: -1, name: '全部'}]) const [statusList, setStatusList] = useState<any[]>([{ id: -1, name: '全部' }])
const {fetchData: fetchDataStatus} = RefundOrderSatausApi() const { fetchData: fetchDataStatus } = RefundOrderSatausApi()
const getOrderStatusList = async () => { const getOrderStatusList = async () => {
let res = await fetchDataStatus() let res = await fetchDataStatus()
setStatusList((e) => [...e, ...res.data.list]) setStatusList((e) => [...e, ...res.data.list])
} }
useEffect(() => { useEffect(() => {
getOrderStatusList() getOrderStatusList()
setSearchField((e) => ({...e, status:-1})) setSearchField((e) => ({ ...e, status: -1 }))
}, []) }, [])
//获取订单列表 //获取订单列表
const {fetchData: listFetchData, state:orderState} = GetSaleOrderListApi() const { fetchData: listFetchData, state: orderState } = GetSaleOrderListApi()
const [orderData, setOrderData] = useState<{list:any[], total:number}>({list:[], total:0}) const [orderData, setOrderData] = useState<{ list: any[]; total: number }>({ list: [], total: 0 })
const getOrderList = async () => { const getOrderList = async () => {
let res = await listFetchData(getFilterData(searchField)) let res = await listFetchData(getFilterData(searchField))
setOrderData({list: res.data.list, total: res.data.total}) setOrderData({ list: res.data.list, total: res.data.total })
setRefresherTriggeredStatus(() => false) setRefresherTriggeredStatus(() => false)
} }
useDidShow(() => { useDidShow(() => {
if(searchField.status != null) getOrderList() if (searchField.status != null) getOrderList()
}) })
//监听筛选条件变化 //监听筛选条件变化
useEffect(() => { useEffect(() => {
if(searchField.status != null) getOrderList() if (searchField.status != null) getOrderList()
}, [searchField]) }, [searchField])
//上拉加载数据 //上拉加载数据
const pageNum = useRef({size: searchField.size, page: searchField.page}) const pageNum = useRef({ size: searchField.size, page: searchField.page })
const getScrolltolower = useCallback(() => { const getScrolltolower = useCallback(() => {
if(orderData.list.length < orderData.total) { if (orderData.list.length < orderData.total) {
pageNum.current.page++ pageNum.current.page++
const size = pageNum.current.size * pageNum.current.page const size = pageNum.current.size * pageNum.current.page
setSearchField((e) => ({...e, size })) setSearchField((e) => ({ ...e, size }))
} }
}, [orderData]) }, [orderData])
//状态改变 //状态改变
const changeStatus = useCallback((e) => { const changeStatus = useCallback((e) => {
pageNum.current.page = 1 pageNum.current.page = 1
setSearchField((value) => ({...value, status:e, size:10})) setSearchField((value) => ({ ...value, status: e, size: 10 }))
setOrderData(() => ({list:[], total:0})) setOrderData(() => ({ list: [], total: 0 }))
}, []) }, [])
//数据加载状态 //数据加载状态
const statusMore = useMemo(() => { const statusMore = useMemo(() => {
return dataLoadingStatus({list:orderData.list, total: orderData.total, status: orderState.loading}) return dataLoadingStatus({ list: orderData.list, total: orderData.total, status: orderState.loading })
}, [orderData, orderState]) }, [orderData, orderState])
//输入了搜索关键字 //输入了搜索关键字
const getSearchData = useCallback((e) => { const getSearchData = useCallback((e) => {
pageNum.current.page = 1 pageNum.current.page = 1
setOrderData(() => ({list:[], total:0})) setOrderData(() => ({ list: [], total: 0 }))
setSearchField((val) => ({...val, name:e, size:10})) setSearchField((val) => ({ ...val, name: e, size: 10 }))
}, []) }, [])
//列表下拉刷新 //列表下拉刷新
const [refresherTriggeredStatus, setRefresherTriggeredStatus] = useState(false) const [refresherTriggeredStatus, setRefresherTriggeredStatus] = useState(false)
const getRefresherRefresh = async () => { const getRefresherRefresh = async () => {
pageNum.current.size = 1 pageNum.current.size = 1
setRefresherTriggeredStatus(true) setRefresherTriggeredStatus(true)
setSearchField((val) => ({...val, size:10})) setSearchField((val) => ({ ...val, size: 10 }))
} }
//监听点击的按钮 //监听点击的按钮
const [callBackOrderInfo, setCallBackPayOrderInfo] = useState<any>() const [callBackOrderInfo, setCallBackPayOrderInfo] = useState<any>()
const clickOrderBtn = useCallback(({status, orderInfo}) => { const clickOrderBtn = useCallback(
if(status == 1 || status == 6) { ({ status, orderInfo }) => {
getOrderList() if (status == 1 || status == 6) {
} else if (status == 8) { getOrderList()
setApplyRecord(true) } else if (status == 8) {
} else if (status == 5) { setApplyRecord(true)
onShowLogistics(() => true) } else if (status == 5) {
} onShowLogistics(() => true)
setCallBackPayOrderInfo(orderInfo) }
}, [orderData]) setCallBackPayOrderInfo(orderInfo)
},
[orderData],
)
//显示记录 //显示记录
const [applyRecord, setApplyRecord] = useState(false) const [applyRecord, setApplyRecord] = useState(false)
@ -112,32 +113,48 @@ export default () => {
//物流显示 //物流显示
const [logisticsShow, setLogisticsShow] = useState(false) const [logisticsShow, setLogisticsShow] = useState(false)
const onShowLogistics = useCallback((val) => { const onShowLogistics = useCallback((val) => {
setLogisticsShow(true) setLogisticsShow(true)
}, []) }, [])
const onCloseLogistics = useCallback(() => { const onCloseLogistics = useCallback(() => {
setLogisticsShow(false) setLogisticsShow(false)
}, []) }, [])
//物流成功上传 //物流成功上传
const logisticsSuccess = useCallback(() => { const logisticsSuccess = useCallback(() => {
setLogisticsShow(false) setLogisticsShow(false)
getOrderList() getOrderList()
}, [orderData]) }, [orderData])
return ( return (
<View className={styles.order_list_main}> <View className={styles.order_list_main}>
<View className={styles.title}> <View className={styles.title}>
<Search placeIcon="out" placeholder="搜索商品/名称/颜色/订单号" showBtn={true} changeOnSearch={getSearchData} debounceTime={300}/> <Search placeIcon='out' placeholder='搜索商品/名称/颜色/订单号' showBtn={true} changeOnSearch={getSearchData} debounceTime={300} />
<OrderStatusList list={statusList} onSelect={changeStatus} defaultId={1}/> <OrderStatusList list={statusList} onSelect={changeStatus} defaultId={1} />
</View>
<View className={styles.order_list}>
<InfiniteScroll statusMore={statusMore} selfonScrollToLower={getScrolltolower} refresherEnabled={true} refresherTriggered={refresherTriggeredStatus} selfOnRefresherRefresh={getRefresherRefresh}>
{orderData?.list.map(item => {
return <View key={item.id} className={styles.order_item_con}> <Order value={item} onClickBtn={clickOrderBtn}/></View>
})}
</InfiniteScroll>
</View> </View>
<ApplyRecord show={applyRecord} id={callBackOrderInfo?.id} onClose={() => setApplyRecord(false)}/> <View className={styles.order_list}>
<ReturnLogistics images={callBackOrderInfo?.accessory_url} descValue={callBackOrderInfo?.take_goods_remark} show={logisticsShow} id={callBackOrderInfo?.return_apply_order_id} onClose={onCloseLogistics} onSubmit={logisticsSuccess}/> <InfiniteScroll
statusMore={statusMore}
selfonScrollToLower={getScrolltolower}
refresherEnabled={true}
refresherTriggered={refresherTriggeredStatus}
selfOnRefresherRefresh={getRefresherRefresh}>
{orderData?.list.map((item) => {
return (
<View key={item.id} className={styles.order_item_con}>
<Order value={item} onClickBtn={clickOrderBtn} />
</View>
)
})}
</InfiniteScroll>
</View>
<ApplyRecord show={applyRecord} id={callBackOrderInfo?.id} onClose={() => setApplyRecord(false)} />
<ReturnLogistics
images={callBackOrderInfo?.accessory_url}
descValue={callBackOrderInfo?.take_goods_remark}
show={logisticsShow}
id={callBackOrderInfo?.return_apply_order_id}
onClose={onCloseLogistics}
onSubmit={logisticsSuccess}
/>
</View> </View>
) )
} }

View File

@ -1,214 +1,214 @@
.main{ .main {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
background-color: $color_bg_one; background-color: $color_bg_one;
.search{ .search {
padding: 20px; padding: 20px;
.SearchInput{ .SearchInput {
background-color: #fff; background-color: #fff;
padding: 10px 20px; padding: 10px 20px;
box-sizing: border-box; box-sizing: border-box;
border-radius: 10px; border-radius: 10px;
}
.bluetooth_color{
.color_bock{
width: 100px;
height: 46px;
}
.color_bock_no{
font-size: $font_size_medium;
color: $color_font_three;
}
}
} }
.filter{
.filter_all { .bluetooth_color {
display: flex; .color_bock {
justify-content: space-between; width: 100px;
align-items: center; height: 46px;
padding: 20px 130px; }
.color_bock_no {
font-size: $font_size_medium;
color: $color_font_three;
}
}
}
.filter {
.filter_all {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 130px;
font-size: $font_size_medium;
color: $color_font_three;
.text_zh,
.text_sc {
color: $color_main;
display: flex;
align-items: center;
.sortIcon {
display: flex;
flex-direction: column;
position: relative;
.icon_one {
font-size: $font_size_medium; font-size: $font_size_medium;
color: $color_font_three; position: absolute;
.text_zh, .text_sc{ margin: auto;
color: $color_main; top: 0;
display: flex; }
align-items: center;
.sortIcon{
display: flex;
flex-direction: column;
position: relative;
.icon_one{
font-size: $font_size_medium;
position: absolute;
margin:auto;
top:0;
}
}
}
.text_ss{
position: relative;
.miconfont{
font-size: 20px;
margin-left: 5px;
}
&::before{
content: '';
width: 2px;
height: 32px;
background-color: #C2C2C2;
position: absolute;
top: 0;
left: -30px;
}
}
} }
.filter_btn_con{ }
display: flex; .text_ss {
justify-content: space-between; position: relative;
align-items: center; .miconfont {
height: 86px; font-size: 20px;
margin-left: 5px;
} }
.filter_scroll{ &::before {
flex:1; content: '';
width: 0; width: 2px;
padding-left: 20px; height: 32px;
::-webkit-scrollbar { background-color: #c2c2c2;
display:none; position: absolute;
width:0; top: 0;
height:0; left: -30px;
color:transparent;
}
} }
.filter_btn{ }
display: flex; }
justify-content: space-between; .filter_btn_con {
padding: 20px; display: flex;
margin-right: 20px; justify-content: space-between;
flex:1; align-items: center;
view{ height: 86px;
font-size: $font_size_medium; }
background-color: #F0F0F0; .filter_scroll {
border-radius: 24px; flex: 1;
min-width: 126px; width: 0;
height: 46.93px; padding-left: 20px;
text-align: center; ::-webkit-scrollbar {
line-height: 46.93px; display: none;
color: $color_font_three; width: 0;
&:nth-last-child(n+2) { height: 0;
margin-right: 10px; color: transparent;
} }
&:nth-last-child(1) { }
margin-right: 30px; .filter_btn {
} display: flex;
} justify-content: space-between;
.selected{ padding: 20px;
background-color: #ecf5ff; margin-right: 20px;
border: 2px solid #cde5ff; flex: 1;
color: $color_main; view {
width: 122px; font-size: $font_size_medium;
height: 42.93px; background-color: #f0f0f0;
} border-radius: 24px;
min-width: 126px;
height: 46.93px;
text-align: center;
line-height: 46.93px;
color: $color_font_three;
&:nth-last-child(n + 2) {
margin-right: 10px;
} }
.filter_more{ &:nth-last-child(1) {
font-size: $font_size_medium; margin-right: 30px;
color: $color_font_three; }
padding: 0 30px 0 20px; }
position: relative; .selected {
background-color: #ecf5ff;
border: 2px solid #cde5ff;
color: $color_main;
width: 122px;
height: 42.93px;
}
}
.filter_more {
font-size: $font_size_medium;
color: $color_font_three;
padding: 0 30px 0 20px;
position: relative;
height: 100%;
line-height: 86px;
&::before {
content: '';
opacity: 1;
width: 30px;
height: 100%;
position: absolute;
left: -15px;
background-image: linear-gradient(to right, rgba(248, 248, 248, 0.3), rgba(248, 248, 248, 1) 60%);
// z-index: 99;
}
.miconfont {
font-size: 27px;
}
}
}
.list {
height: calc(100vh - 440px);
.list_num {
font-size: $font_size_min;
color: $color_font_two;
padding: 10px 38px;
border-bottom: 1px solid rgb(233, 233, 233);
}
.scroll {
height: 100%;
padding-top: 3px;
}
.product_list {
padding: 38px;
display: grid;
grid-template-columns: 321px 321px;
justify-content: space-between;
.product_item {
width: 321px;
background-color: #fff;
border-radius: 20px;
margin-bottom: 20px;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
.product_img {
width: 100%;
height: 224px;
border-radius: 20px 20px 0px 0px;
position: relative;
image {
width: 100%;
height: 100%; height: 100%;
line-height: 86px; border-radius: 20px 20px 0px 0px;
&::before{ }
content: ''; .color_num {
opacity: 1; background: rgba(0, 0, 0, 0.5);
width: 30px; border-radius: 0px 50px 0px 30px;
height: 100%;
position: absolute;
left: -15px;
background-image: linear-gradient(to right, rgba(248, 248, 248, 0.3), rgba(248, 248, 248, 1) 60% );
// z-index: 99;
}
.miconfont{
font-size: 27px;
}
}
}
.list{
height: calc(100vh - 440px);
.list_num {
font-size: $font_size_min; font-size: $font_size_min;
color:$color_font_two; color: #fff;
padding: 10px 38px; position: absolute;
border-bottom: 1PX solid rgb(233, 233, 233); left: 0;
bottom: 0;
padding: 5px 20px;
box-sizing: border-box;
}
} }
}
.scroll{ .product_info {
height: 100%; padding: 20px;
padding-top: 3px; .title {
font-size: $font_size;
color: $color_font_three;
@include common_ellipsis();
} }
.product_list{ .tag_list {
padding: 38px; display: flex;
display: grid; margin-top: 16px;
grid-template-columns: 321px 321px; .tag {
justify-content: space-between; padding: 3px 10px;
.product_item{ background-color: #cde5ff;
width: 321px; font-size: $font_size_min;
background-color: #fff; border-radius: 5px;
border-radius: 20px; color: $color_main;
margin-bottom: 20px; &:nth-child(2) {
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1) ; margin-left: 10px;
.product_img{
width: 100%;
height: 224px;
border-radius: 20px 20px 0px 0px;
position: relative;
image{
width: 100%;
height: 100%;
border-radius: 20px 20px 0px 0px;
}
.color_num {
background: rgba(0,0,0, 0.5);
border-radius: 0px 50px 0px 30px;
font-size: $font_size_min;
color: #fff;
position: absolute;
left:0;
bottom:0;
padding: 5px 20px;
box-sizing: border-box;
}
}
}
.product_info{
padding: 20px;
.title{
font-size: $font_size;
color: $color_font_three;
@include common_ellipsis()
}
.tag_list{
display: flex;
margin-top: 16px;
.tag{
padding: 3px 10px;
background-color: #CDE5FF;
font-size: $font_size_min;
border-radius: 5px;
color: $color_main;
&:nth-child(2) {
margin-left: 10px;
}
}
}
.introduce{
font-size: $font_size_medium;
color: $color_font_two;
margin-top: 16px;
@include common_ellipsis()
}
} }
}
} }
.introduce {
font-size: $font_size_medium;
color: $color_font_two;
margin-top: 16px;
@include common_ellipsis();
}
}
} }
} }
}

View File

@ -1,198 +1,198 @@
.main{ .main {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
background-color: #F8F8F8; background-color: #f8f8f8;
.search{ .search {
padding: 20px; padding: 20px;
}
.filter {
.filter_all {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 50px;
font-size: $font_size_medium;
color: $color_font_three;
.text_zh,
.text_sc {
color: $color_main;
display: flex;
align-items: center;
.sortIcon {
display: flex;
flex-direction: column;
position: relative;
.icon_one {
font-size: $font_size_medium;
position: absolute;
margin: auto;
top: 0;
}
}
}
.text_ss {
position: relative;
.miconfont {
font-size: 30px;
margin-left: 5px;
}
&::before {
content: '';
width: 2px;
height: 32px;
background-color: #c2c2c2;
position: absolute;
top: 0;
left: -30px;
}
}
} }
.filter{ .filter_btn_con {
.filter_all { display: flex;
display: flex; justify-content: space-between;
justify-content: space-between; align-items: center;
align-items: center; height: 86px;
padding: 20px 50px; }
font-size: $font_size_medium; .filter_scroll {
color: $color_font_three; flex: 1;
.text_zh, .text_sc{ width: 0;
color: $color_main; padding-left: 20px;
display: flex; ::-webkit-scrollbar {
align-items: center; display: none;
.sortIcon{ width: 0;
display: flex; height: 0;
flex-direction: column; color: transparent;
position: relative; }
.icon_one{ }
font-size: $font_size_medium; .filter_btn {
position: absolute; display: flex;
margin:auto; justify-content: space-between;
top:0; padding: 20px;
} margin-right: 20px;
} flex: 1;
} view {
.text_ss{ font-size: $font_size_medium;
position: relative; background-color: #f0f0f0;
.miconfont{ border-radius: 24px;
font-size: 30px; min-width: 126px;
margin-left: 5px; height: 46.93px;
} text-align: center;
&::before{ line-height: 46.93px;
content: ''; color: $color_font_three;
width: 2px; &:nth-last-child(n + 2) {
height: 32px; margin-right: 10px;
background-color: #C2C2C2;
position: absolute;
top: 0;
left: -30px;
}
}
} }
.filter_btn_con{ &:nth-last-child(1) {
display: flex; margin-right: 30px;
justify-content: space-between;
align-items: center;
height: 86px;
} }
.filter_scroll{ }
flex:1; .selected {
width: 0; background-color: #ecf5ff;
padding-left: 20px; border: 2px solid #cde5ff;
::-webkit-scrollbar { color: $color_main;
display:none; width: 122px;
width:0; height: 42.93px;
height:0; }
color:transparent; }
} .filter_more {
} font-size: $font_size_medium;
.filter_btn{ color: $color_font_three;
display: flex; padding: 0 30px 0 20px;
justify-content: space-between; position: relative;
padding: 20px; height: 100%;
margin-right: 20px; line-height: 86px;
flex:1; &::before {
view{ content: '';
font-size: $font_size_medium; opacity: 1;
background-color: #F0F0F0; width: 30px;
border-radius: 24px; height: 100%;
min-width: 126px; position: absolute;
height: 46.93px; left: -15px;
text-align: center; background-image: linear-gradient(to right, rgba(248, 248, 248, 0.3), rgba(248, 248, 248, 1) 60%);
line-height: 46.93px; // z-index: 99;
color: $color_font_three; }
&:nth-last-child(n+2) { .miconfont {
margin-right: 10px; font-size: 27px;
} }
&:nth-last-child(1) { }
margin-right: 30px; }
} .list {
} height: calc(100vh - 330px);
.selected{ .list_num {
background-color: #ecf5ff; font-size: $font_size_min;
border: 2px solid #cde5ff; color: $color_font_two;
color: $color_main; padding: 10px 38px;
width: 122px; border-bottom: 1px solid #e9e9e9;
height: 42.93px; }
}
} .scroll {
.filter_more{ height: 100%;
font-size: $font_size_medium; padding-top: 3px;
color: $color_font_three; }
padding: 0 30px 0 20px; .product_list {
position: relative; padding: 38px;
display: grid;
grid-template-columns: 321px 321px;
justify-content: space-between;
.product_item {
width: 321px;
background-color: #fff;
border-radius: 20px;
margin-bottom: 20px;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
.product_img {
width: 100%;
height: 224px;
border-radius: 20px 20px 0px 0px;
position: relative;
image {
width: 100%;
height: 100%; height: 100%;
line-height: 86px; border-radius: 20px;
&::before{ }
content: ''; .color_num {
opacity: 1; background: rgba(0, 0, 0, 0.5);
width: 30px; border-radius: 50px 0px 0px 0px;
height: 100%;
position: absolute;
left: -15px;
background-image: linear-gradient(to right, rgba(248, 248, 248, 0.3), rgba(248, 248, 248, 1) 60% );
// z-index: 99;
}
.miconfont{
font-size: 27px;
}
}
}
.list{
height: calc(100vh - 330px);
.list_num {
font-size: $font_size_min; font-size: $font_size_min;
color:$color_font_two; color: #fff;
padding: 10px 38px; position: absolute;
border-bottom: 1PX solid #e9e9e9; right: 0;
bottom: 0;
padding: 5px 20px;
box-sizing: border-box;
}
} }
}
.scroll{ .product_info {
height: 100%; padding: 20px;
padding-top: 3px; .title {
font-size: $font_size;
color: $color_font_three;
@include common_ellipsis();
} }
.product_list{ .tag_list {
padding: 38px; display: flex;
display: grid; margin-top: 16px;
grid-template-columns: 321px 321px; .tag {
justify-content: space-between; padding: 3px 10px;
.product_item{ background-color: #cde5ff;
width: 321px; font-size: $font_size_min;
background-color: #fff; border-radius: 5px;
border-radius: 20px; color: $color_main;
margin-bottom: 20px; @include common_ellipsis();
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1) ; &:nth-child(2) {
.product_img{ margin-left: 10px;
width: 100%;
height: 224px;
border-radius: 20px 20px 0px 0px;
position: relative;
image{
width: 100%;
height: 100%;
border-radius: 20px 20px 0px 0px;
}
.color_num {
background: rgba(0,0,0, 0.5);
border-radius: 50px 0px 0px 0px;
font-size: $font_size_min;
color: #fff;
position: absolute;
right:0;
bottom:0;
padding: 5px 20px;
box-sizing: border-box;
}
}
}
.product_info{
padding: 20px;
.title{
font-size: $font_size;
color: $color_font_three;
@include common_ellipsis();
}
.tag_list{
display: flex;
margin-top: 16px;
.tag{
padding: 3px 10px;
background-color: #CDE5FF;
font-size: $font_size_min;
border-radius: 5px;
color: $color_main;
@include common_ellipsis();
&:nth-child(2) {
margin-left: 10px;
}
}
}
.introduce{
font-size: $font_size_medium;
color: $color_font_two;
margin-top: 16px;
@include common_ellipsis();
}
} }
}
} }
.introduce {
font-size: $font_size_medium;
color: $color_font_two;
margin-top: 16px;
@include common_ellipsis();
}
}
} }
} }
}

View File

@ -1,477 +1,477 @@
import React, {useRef, useState } from "react" import React, { useRef, useState } from 'react'
import Taro from "@tarojs/taro"; import Taro from '@tarojs/taro'
import { Command } from "@/common/bluetooth/command"; import { Command } from '@/common/bluetooth/command'
import { uint8ArrayToFloat32, uint8ArrayToHex, waitFor } from "@/common/bluetooth/utils"; import { uint8ArrayToFloat32, uint8ArrayToHex, waitFor } from '@/common/bluetooth/utils'
interface params { interface params {
init: () => void init: () => void
state: Object, state: Object
startScan: () => void, startScan: () => void
measureAndGetLab: () => any, measureAndGetLab: () => any
getAdapterState: () => void, getAdapterState: () => void
connect: (any) => void, connect: (any) => void
disconnect: () => void disconnect: () => void
} }
const Context = React.createContext<params|unknown>(null) const Context = React.createContext<params | unknown>(null)
interface stateStype { interface stateStype {
listeners: any, listeners: any
discovering: boolean, discovering: boolean
available: boolean, available: boolean
connected: any, connected: any
connecting: any, connecting: any
serviceRule: any, serviceRule: any
serviceId: any, serviceId: any
characteristicRule: any, characteristicRule: any
characteristicId: any, characteristicId: any
/** 正在执行的命令 */ /** 正在执行的命令 */
command: any, command: any
responseResolve: any, responseResolve: any
responseReject: any, responseReject: any
responseTimer: any, responseTimer: any
/** 是否显示蓝牙调试信息 */ /** 是否显示蓝牙调试信息 */
debug: any, debug: any
//搜索到的设备 //搜索到的设备
devices: any, devices: any
//取色仪主动返回的数据 //取色仪主动返回的数据
deviceLab: any deviceLab: any
} }
let stateObj: stateStype = { let stateObj: stateStype = {
/** 事件监听器 */ /** 事件监听器 */
listeners: new Set(), listeners: new Set(),
/** 正在扫描设备 */ /** 正在扫描设备 */
discovering: false, discovering: false,
/** 蓝牙是否可用 */ /** 蓝牙是否可用 */
available: true, available: true,
/** 当前连接的设备 */ /** 当前连接的设备 */
connected: null, connected: null,
/** 正在连接的设备 */ /** 正在连接的设备 */
connecting: null, connecting: null,
serviceRule: /^0000FFE0/, serviceRule: /^0000FFE0/,
serviceId: null, serviceId: null,
characteristicRule: /^0000FFE1/, characteristicRule: /^0000FFE1/,
characteristicId: null, characteristicId: null,
/** 正在执行的命令 */ /** 正在执行的命令 */
command: null, command: null,
responseResolve: null, responseResolve: null,
responseReject: null, responseReject: null,
responseTimer: null, responseTimer: null,
/** 是否显示蓝牙调试信息 */ /** 是否显示蓝牙调试信息 */
debug: true, debug: true,
//搜索到的设备 //搜索到的设备
devices: [], devices: [],
//取色仪主动返回的数据 //取色仪主动返回的数据
deviceLab: null deviceLab: null,
} }
export default (props) => { export default (props) => {
let refStatus = useRef(stateObj) let refStatus = useRef(stateObj)
let [state, setState] = useState(refStatus.current) let [state, setState] = useState(refStatus.current)
const changeStatus = (obj:Object): void => { const changeStatus = (obj: Object): void => {
refStatus.current = {...refStatus.current, ...obj} refStatus.current = { ...refStatus.current, ...obj }
setState({...refStatus.current}) setState({ ...refStatus.current })
}
const init = async () => {
try {
await openAdapter()
} catch (e) {
changeStatus({ available: false })
} }
const init = async () => { // 绑定事件通知
try{ Taro.onBluetoothAdapterStateChange((res) => {
await openAdapter(); emit({ type: 'stateUpdate', detail: res })
}catch(e) { })
changeStatus({available:false}) Taro.onBLEConnectionStateChange((res) => {
} emit({ type: res.connected ? 'connected' : 'disconnect', detail: res })
})
// 绑定事件通知 Taro.onBLECharacteristicValueChange(({ value }) => notifySubscriber(value))
Taro.onBluetoothAdapterStateChange(res => { subscribe(async (ev) => {
emit({ type: 'stateUpdate', detail: res }); if (ev.type === 'stateUpdate') {
}); // 蓝牙状态发生的变化
Taro.onBLEConnectionStateChange(res => { changeStatus({ discovering: ev.detail.discovering, available: ev.detail.available })
emit({ type: res.connected ? 'connected' : 'disconnect', detail: res }); } else if (ev.type === 'disconnect' && refStatus.current.connected && refStatus.current.connected.deviceId === ev.detail.deviceId) {
}); // 断开连接
Taro.onBLECharacteristicValueChange(({ value }) => notifySubscriber(value));
subscribe(async ev => {
if (ev.type === 'stateUpdate') {
// 蓝牙状态发生的变化
changeStatus({discovering:ev.detail.discovering, available:ev.detail.available})
} else if (ev.type === 'disconnect' && refStatus.current.connected && refStatus.current.connected.deviceId === ev.detail.deviceId) {
// 断开连接
changeStatus({
connected:null,
serviceId:null,
characteristicId:null,
deviceLab:null,
devices:[]
})
Taro.showToast({ icon: 'none', title: '蓝牙连接已断开' });
} else if (ev.type === 'connected' && refStatus.current.connecting) {
// 连接成功
changeStatus({connected: refStatus.current.connecting, connecting: null})
Taro.showToast({ title: '蓝牙已连接' });
} else if (ev.type === 'measure') {
//监听取色仪主动推送lab
await measureAndGetLab()
}
});
}
/** 打开蓝牙适配器 */
const openAdapter = () => {
return new Promise((resolve, reject) => {
Taro.openBluetoothAdapter({
success: resolve,
fail: reject
});
});
}
/**
*
* @param {{type: string; data: any}} event
*/
const emit = (event) => {
refStatus.current.listeners.forEach(cb => {
cb && cb(event);
});
}
const subscribe = (cb) => {
if (cb) {
changeStatus({
listeners: refStatus.current.listeners.add(cb)
})
}
}
/**
*
* @returns {Promise<{discovering: boolean; available: boolean}>}
*/
const getAdapterState = () => {
return new Promise((resolve, reject) => {
Taro.getBluetoothAdapterState({
success: resolve,
fail: reject
})
})
}
/**
*
* @param {(res: { devices: { name: string, deviceId: string, RSSI: number }[] }) => void} cb
* @param {number} duration
*/
const startScan = (duration = 30000) => {
console.log('开始寻找')
changeStatus({devices:[]})
Taro.onBluetoothDeviceFound(getDevices);
return new Promise((resolve, reject) => {
Taro.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: resolve,
fail: reject
});
if (duration > 0) {
setTimeout(() => {
Taro.offBluetoothDeviceFound(getDevices);
Taro.stopBluetoothDevicesDiscovery();
console.log("停止搜索")
}, duration);
}
});
}
//获取搜索到的设备
const getDevices = (res) => {
res.devices.forEach(device => {
// 排除掉已搜索到的设备和名称不合法的设备, 将新发现的设备添加到列表中
if (/^CM/.test(device.name) && !refStatus.current.devices.find(i => i.deviceId === device.deviceId)) {
changeStatus({devices: [ ...refStatus.current.devices, device ]})
}
});
}
/**
*
* @param {{ name: string, deviceId: string, RSSI: number }} device
*/
const connect = async (device) => {
try {
changeStatus({connecting: device})
console.log('connecting::', device)
await createConnection(device.deviceId);
await discoverService(device.deviceId);
await discoverCharacteristic(device.deviceId);
await notifyCharacteristicValueChange(device.deviceId);
} catch (e) {
changeStatus({connecting: null})
Taro.showToast({ icon: 'none', title: '蓝牙连接失败' });
throw e;
}
}
/** 断开当前连接的设备 */
const disconnect = async () => {
if (!refStatus.current.connected && !refStatus.current.connecting) return;
if (refStatus.current.connected) {
await closeConnection(refStatus.current.connected.deviceId);
resetCommand();
changeStatus({
connected: null,
serviceId: null,
characteristicId: null,
devices: [],
deviceLab: null
})
}
if (refStatus.current.connecting) {
await closeConnection(refStatus.current.connecting.deviceId);
changeStatus({connecting:null})
}
}
/** 创建 BLE 连接 */
function createConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.createBLEConnection({
deviceId,
timeout: 2000,
success: resolve,
fail: reject
});
});
}
/** 关闭 BLE 连接 */
function closeConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.closeBLEConnection({
deviceId,
success: resolve,
fail: reject
});
});
}
/** 搜索服务 */
function discoverService(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceServices({
deviceId,
success: ({ services }) => {
const service = services.find(i => refStatus.current.serviceRule.test(i.uuid));
if (!service) {
reject(new Error('服务不可用'));
} else {
changeStatus({serviceId: service.uuid})
resolve(service);
}
},
fail: reject
});
});
}
/** 搜索特征 */
function discoverCharacteristic(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceCharacteristics({
deviceId,
serviceId: refStatus.current.serviceId,
success: ({ characteristics }) => {
const characteristic = characteristics.find(i => refStatus.current.characteristicRule.test(i.uuid));
if (!characteristic) {
reject(new Error('特征不可用'));
} else {
changeStatus({characteristicId: characteristic.uuid})
resolve(characteristic);
}
},
fail: reject
})
});
}
/** 启动特征通知 */
function notifyCharacteristicValueChange(deviceId, stateParm = true) {
return new Promise((resolve, reject) => {
Taro.notifyBLECharacteristicValueChange({
deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
state:stateParm,
success: resolve,
fail: reject
});
});
}
/**
*
* @param {ArrayBuffer} buffer
*/
function notifySubscriber(buffer) {
if (refStatus.current.command) {
if (refStatus.current.debug) {
console.log(`[BLE RESP] ${uint8ArrayToHex(new Uint8Array(buffer))}`);
}
refStatus.current.command.fillResponse(buffer);
if (refStatus.current.command.isComplete) {
if (refStatus.current.command.isValid && refStatus.current.responseResolve) {
refStatus.current.responseResolve(refStatus.current.command.response);
} else if (!refStatus.current.command.isValid) {
refStatus.current.responseReject(new Error('无效数据'));
}
resetCommand();
}
} else {
const uint8Array = new Uint8Array(buffer);
if (uint8Array[0] === 0xbb && uint8Array[1] === 1 && uint8Array[3] === 0) {
const ev = { type: 'measure', detail: { mode: uint8Array[2] } };
emit(ev);
}
}
}
/**
*
* @param {Command}} command
* @returns {Promise<Uint8Array>}
*/
function exec(command) {
return new Promise(async (resolve, reject) => {
if (refStatus.current.command) {
reject(new Error('正在执行其他命令'));
} else {
try {
refStatus.current.command = command;
const data = command.data;
for (let i = 0; i < data.length; i++) {
await sendData(data[i]);
}
if (command.responseSize <= 0) {
resolve(true);
resetCommand();
} else {
refStatus.current.responseReject = reject;
refStatus.current.responseResolve = resolve;
refStatus.current.responseTimer = setTimeout(() => {
reject(new Error('命令响应超时'));
resetCommand();
}, command.timeout);
}
} catch (e) {
reject(e);
}
}
});
}
/**
*
* @param {ArrayBuffer} buffer
*/
function sendData(buffer) {
if (refStatus.current.debug) {
console.log(`[BLE SEND] ${uint8ArrayToHex(new Uint8Array(buffer))}`);
}
return new Promise((resolve, reject) => {
console.log('current:::',refStatus.current)
Taro.writeBLECharacteristicValue({
deviceId: refStatus.current.connected.deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
value: buffer,
success: resolve,
fail: reject
})
});
}
function resetCommand() {
if (refStatus.current.responseTimer) {
clearTimeout(refStatus.current.responseTimer);
}
changeStatus({ changeStatus({
command: null, connected: null,
responseResolve: null, serviceId: null,
responseReject: null, characteristicId: null,
responseTimer: null deviceLab: null,
devices: [],
}) })
Taro.showToast({ icon: 'none', title: '蓝牙连接已断开' })
} else if (ev.type === 'connected' && refStatus.current.connecting) {
// 连接成功
changeStatus({ connected: refStatus.current.connecting, connecting: null })
Taro.showToast({ title: '蓝牙已连接' })
} else if (ev.type === 'measure') {
//监听取色仪主动推送lab
await measureAndGetLab()
}
})
}
/** 打开蓝牙适配器 */
const openAdapter = () => {
return new Promise((resolve, reject) => {
Taro.openBluetoothAdapter({
success: resolve,
fail: reject,
})
})
}
/**
*
* @param {{type: string; data: any}} event
*/
const emit = (event) => {
refStatus.current.listeners.forEach((cb) => {
cb && cb(event)
})
}
const subscribe = (cb) => {
if (cb) {
changeStatus({
listeners: refStatus.current.listeners.add(cb),
})
}
}
/**
*
* @returns {Promise<{discovering: boolean; available: boolean}>}
*/
const getAdapterState = () => {
return new Promise((resolve, reject) => {
Taro.getBluetoothAdapterState({
success: resolve,
fail: reject,
})
})
}
/**
*
* @param {(res: { devices: { name: string, deviceId: string, RSSI: number }[] }) => void} cb
* @param {number} duration
*/
const startScan = (duration = 30000) => {
console.log('开始寻找')
changeStatus({ devices: [] })
Taro.onBluetoothDeviceFound(getDevices)
return new Promise((resolve, reject) => {
Taro.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: resolve,
fail: reject,
})
if (duration > 0) {
setTimeout(() => {
Taro.offBluetoothDeviceFound(getDevices)
Taro.stopBluetoothDevicesDiscovery()
console.log('停止搜索')
}, duration)
}
})
}
//获取搜索到的设备
const getDevices = (res) => {
res.devices.forEach((device) => {
// 排除掉已搜索到的设备和名称不合法的设备, 将新发现的设备添加到列表中
if (/^CM/.test(device.name) && !refStatus.current.devices.find((i) => i.deviceId === device.deviceId)) {
changeStatus({ devices: [...refStatus.current.devices, device] })
}
})
}
/**
*
* @param {{ name: string, deviceId: string, RSSI: number }} device
*/
const connect = async (device) => {
try {
changeStatus({ connecting: device })
console.log('connecting::', device)
await createConnection(device.deviceId)
await discoverService(device.deviceId)
await discoverCharacteristic(device.deviceId)
await notifyCharacteristicValueChange(device.deviceId)
} catch (e) {
changeStatus({ connecting: null })
Taro.showToast({ icon: 'none', title: '蓝牙连接失败' })
throw e
}
}
/** 断开当前连接的设备 */
const disconnect = async () => {
if (!refStatus.current.connected && !refStatus.current.connecting) return
if (refStatus.current.connected) {
await closeConnection(refStatus.current.connected.deviceId)
resetCommand()
changeStatus({
connected: null,
serviceId: null,
characteristicId: null,
devices: [],
deviceLab: null,
})
} }
/** if (refStatus.current.connecting) {
* await closeConnection(refStatus.current.connecting.deviceId)
* @param {number} mode changeStatus({ connecting: null })
* @returns {Promise}
*/
async function measure (mode = 0) {
console.log('current1:::',Command.WakeUp)
await exec(Command.WakeUp);
console.log('current2:::',Command.WakeUp)
await waitFor(50);
console.log('current3:::',Command.WakeUp)
return await exec(Command.measure(mode));
} }
}
/** /** 创建 BLE 连接 */
* lab function createConnection(deviceId) {
* @param {number} mode return new Promise((resolve, reject) => {
* @returns {Promise<{ L: number, a: number, b: number }>} Taro.createBLEConnection({
*/ deviceId,
async function getLab(mode = 0) { timeout: 2000,
await exec(Command.WakeUp); success: resolve,
await waitFor(50); fail: reject,
const data: any = await exec(Command.getLab(mode)); })
return { })
L: uint8ArrayToFloat32(data.slice(5, 9)), }
a: uint8ArrayToFloat32(data.slice(9, 13)),
b: uint8ArrayToFloat32(data.slice(13, 17)), /** 关闭 BLE 连接 */
}; function closeConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.closeBLEConnection({
deviceId,
success: resolve,
fail: reject,
})
})
}
/** 搜索服务 */
function discoverService(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceServices({
deviceId,
success: ({ services }) => {
const service = services.find((i) => refStatus.current.serviceRule.test(i.uuid))
if (!service) {
reject(new Error('服务不可用'))
} else {
changeStatus({ serviceId: service.uuid })
resolve(service)
}
},
fail: reject,
})
})
}
/** 搜索特征 */
function discoverCharacteristic(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceCharacteristics({
deviceId,
serviceId: refStatus.current.serviceId,
success: ({ characteristics }) => {
const characteristic = characteristics.find((i) => refStatus.current.characteristicRule.test(i.uuid))
if (!characteristic) {
reject(new Error('特征不可用'))
} else {
changeStatus({ characteristicId: characteristic.uuid })
resolve(characteristic)
}
},
fail: reject,
})
})
}
/** 启动特征通知 */
function notifyCharacteristicValueChange(deviceId, stateParm = true) {
return new Promise((resolve, reject) => {
Taro.notifyBLECharacteristicValueChange({
deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
state: stateParm,
success: resolve,
fail: reject,
})
})
}
/**
*
* @param {ArrayBuffer} buffer
*/
function notifySubscriber(buffer) {
if (refStatus.current.command) {
if (refStatus.current.debug) {
console.log(`[BLE RESP] ${uint8ArrayToHex(new Uint8Array(buffer))}`)
}
refStatus.current.command.fillResponse(buffer)
if (refStatus.current.command.isComplete) {
if (refStatus.current.command.isValid && refStatus.current.responseResolve) {
refStatus.current.responseResolve(refStatus.current.command.response)
} else if (!refStatus.current.command.isValid) {
refStatus.current.responseReject(new Error('无效数据'))
}
resetCommand()
}
} else {
const uint8Array = new Uint8Array(buffer)
if (uint8Array[0] === 0xbb && uint8Array[1] === 1 && uint8Array[3] === 0) {
const ev = { type: 'measure', detail: { mode: uint8Array[2] } }
emit(ev)
}
} }
}
/** /**
* lab *
* @param {number} mode * @param {Command}} command
* @returns {Promise<{L: number, a: number, b: number}>} * @returns {Promise<Uint8Array>}
*/ */
async function measureAndGetLab(mode = 0) { function exec(command) {
await measure(mode); return new Promise(async (resolve, reject) => {
await waitFor(50); if (refStatus.current.command) {
const lab = await getLab(mode); reject(new Error('正在执行其他命令'))
console.log('lab2::',lab) } else {
changeStatus({deviceLab:lab}) try {
return lab refStatus.current.command = command
const data = command.data
for (let i = 0; i < data.length; i++) {
await sendData(data[i])
}
if (command.responseSize <= 0) {
resolve(true)
resetCommand()
} else {
refStatus.current.responseReject = reject
refStatus.current.responseResolve = resolve
refStatus.current.responseTimer = setTimeout(() => {
reject(new Error('命令响应超时'))
resetCommand()
}, command.timeout)
}
} catch (e) {
reject(e)
}
}
})
}
/**
*
* @param {ArrayBuffer} buffer
*/
function sendData(buffer) {
if (refStatus.current.debug) {
console.log(`[BLE SEND] ${uint8ArrayToHex(new Uint8Array(buffer))}`)
} }
return new Promise((resolve, reject) => {
console.log('current:::', refStatus.current)
Taro.writeBLECharacteristicValue({
deviceId: refStatus.current.connected.deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
value: buffer,
success: resolve,
fail: reject,
})
})
}
return <Context.Provider children={props.children} value={{ function resetCommand() {
init, if (refStatus.current.responseTimer) {
clearTimeout(refStatus.current.responseTimer)
}
changeStatus({
command: null,
responseResolve: null,
responseReject: null,
responseTimer: null,
})
}
/**
*
* @param {number} mode
* @returns {Promise}
*/
async function measure(mode = 0) {
console.log('current1:::', Command.WakeUp)
await exec(Command.WakeUp)
console.log('current2:::', Command.WakeUp)
await waitFor(50)
console.log('current3:::', Command.WakeUp)
return await exec(Command.measure(mode))
}
/**
* lab
* @param {number} mode
* @returns {Promise<{ L: number, a: number, b: number }>}
*/
async function getLab(mode = 0) {
await exec(Command.WakeUp)
await waitFor(50)
const data: any = await exec(Command.getLab(mode))
return {
L: uint8ArrayToFloat32(data.slice(5, 9)),
a: uint8ArrayToFloat32(data.slice(9, 13)),
b: uint8ArrayToFloat32(data.slice(13, 17)),
}
}
/**
* lab
* @param {number} mode
* @returns {Promise<{L: number, a: number, b: number}>}
*/
async function measureAndGetLab(mode = 0) {
await measure(mode)
await waitFor(50)
const lab = await getLab(mode)
console.log('lab2::', lab)
changeStatus({ deviceLab: lab })
return lab
}
return (
<Context.Provider
children={props.children}
value={{
init,
state, state,
startScan, startScan,
measureAndGetLab, measureAndGetLab,
getAdapterState, getAdapterState,
connect, connect,
disconnect disconnect,
}} /> }}
/>
)
} }
export const useBluetoothTwo = () => { export const useBluetoothTwo = () => {
const res = React.useContext<any>(Context) const res = React.useContext<any>(Context)
if(res) { if (res) {
return {...res} return { ...res }
} else { } else {
return {} return {}
} }
}
}

View File

@ -1,477 +1,477 @@
import React, {useRef, useState } from "react" import React, { useRef, useState } from 'react'
import Taro from "@tarojs/taro"; import Taro from '@tarojs/taro'
import { Command } from "@/common/bluetooth/command"; import { Command } from '@/common/bluetooth/command'
import { uint8ArrayToFloat32, uint8ArrayToHex, waitFor } from "@/common/bluetooth/utils"; import { uint8ArrayToFloat32, uint8ArrayToHex, waitFor } from '@/common/bluetooth/utils'
interface params { interface params {
init: () => void init: () => void
state: Object, state: Object
startScan: () => void, startScan: () => void
measureAndGetLab: () => any, measureAndGetLab: () => any
getAdapterState: () => void, getAdapterState: () => void
connect: (any) => void, connect: (any) => void
disconnect: () => void disconnect: () => void
} }
const Context = React.createContext<params|unknown>(null) const Context = React.createContext<params | unknown>(null)
interface stateStype { interface stateStype {
listeners: any, listeners: any
discovering: boolean, discovering: boolean
available: boolean, available: boolean
connected: any, connected: any
connecting: any, connecting: any
serviceRule: any, serviceRule: any
serviceId: any, serviceId: any
characteristicRule: any, characteristicRule: any
characteristicId: any, characteristicId: any
/** 正在执行的命令 */ /** 正在执行的命令 */
command: any, command: any
responseResolve: any, responseResolve: any
responseReject: any, responseReject: any
responseTimer: any, responseTimer: any
/** 是否显示蓝牙调试信息 */ /** 是否显示蓝牙调试信息 */
debug: any, debug: any
//搜索到的设备 //搜索到的设备
devices: any, devices: any
//取色仪主动返回的数据 //取色仪主动返回的数据
deviceLab: any deviceLab: any
} }
let stateObj: stateStype = { let stateObj: stateStype = {
/** 事件监听器 */ /** 事件监听器 */
listeners: new Set(), listeners: new Set(),
/** 正在扫描设备 */ /** 正在扫描设备 */
discovering: false, discovering: false,
/** 蓝牙是否可用 */ /** 蓝牙是否可用 */
available: true, available: true,
/** 当前连接的设备 */ /** 当前连接的设备 */
connected: null, connected: null,
/** 正在连接的设备 */ /** 正在连接的设备 */
connecting: null, connecting: null,
serviceRule: /^0000FFE0/, serviceRule: /^0000FFE0/,
serviceId: null, serviceId: null,
characteristicRule: /^0000FFE1/, characteristicRule: /^0000FFE1/,
characteristicId: null, characteristicId: null,
/** 正在执行的命令 */ /** 正在执行的命令 */
command: null, command: null,
responseResolve: null, responseResolve: null,
responseReject: null, responseReject: null,
responseTimer: null, responseTimer: null,
/** 是否显示蓝牙调试信息 */ /** 是否显示蓝牙调试信息 */
debug: true, debug: true,
//搜索到的设备 //搜索到的设备
devices: [], devices: [],
//取色仪主动返回的数据 //取色仪主动返回的数据
deviceLab: null deviceLab: null,
} }
export default (props) => { export default (props) => {
let refStatus = useRef(stateObj) let refStatus = useRef(stateObj)
let [state, setState] = useState(refStatus.current) let [state, setState] = useState(refStatus.current)
const changeStatus = (obj:Object): void => { const changeStatus = (obj: Object): void => {
refStatus.current = {...refStatus.current, ...obj} refStatus.current = { ...refStatus.current, ...obj }
setState({...refStatus.current}) setState({ ...refStatus.current })
}
const init = async () => {
try {
await openAdapter()
} catch (e) {
changeStatus({ available: false })
} }
const init = async () => { // 绑定事件通知
try{ Taro.onBluetoothAdapterStateChange((res) => {
await openAdapter(); emit({ type: 'stateUpdate', detail: res })
}catch(e) { })
changeStatus({available:false}) Taro.onBLEConnectionStateChange((res) => {
} emit({ type: res.connected ? 'connected' : 'disconnect', detail: res })
})
// 绑定事件通知 Taro.onBLECharacteristicValueChange(({ value }) => notifySubscriber(value))
Taro.onBluetoothAdapterStateChange(res => { subscribe(async (ev) => {
emit({ type: 'stateUpdate', detail: res }); if (ev.type === 'stateUpdate') {
}); // 蓝牙状态发生的变化
Taro.onBLEConnectionStateChange(res => { changeStatus({ discovering: ev.detail.discovering, available: ev.detail.available })
emit({ type: res.connected ? 'connected' : 'disconnect', detail: res }); } else if (ev.type === 'disconnect' && refStatus.current.connected && refStatus.current.connected.deviceId === ev.detail.deviceId) {
}); // 断开连接
Taro.onBLECharacteristicValueChange(({ value }) => notifySubscriber(value));
subscribe(async ev => {
if (ev.type === 'stateUpdate') {
// 蓝牙状态发生的变化
changeStatus({discovering:ev.detail.discovering, available:ev.detail.available})
} else if (ev.type === 'disconnect' && refStatus.current.connected && refStatus.current.connected.deviceId === ev.detail.deviceId) {
// 断开连接
changeStatus({
connected:null,
serviceId:null,
characteristicId:null,
deviceLab:null,
devices:[]
})
Taro.showToast({ icon: 'none', title: '蓝牙连接已断开' });
} else if (ev.type === 'connected' && refStatus.current.connecting) {
// 连接成功
changeStatus({connected: refStatus.current.connecting, connecting: null})
Taro.showToast({ title: '蓝牙已连接' });
} else if (ev.type === 'measure') {
//监听取色仪主动推送lab
await measureAndGetLab()
}
});
}
/** 打开蓝牙适配器 */
const openAdapter = () => {
return new Promise((resolve, reject) => {
Taro.openBluetoothAdapter({
success: resolve,
fail: reject
});
});
}
/**
*
* @param {{type: string; data: any}} event
*/
const emit = (event) => {
refStatus.current.listeners.forEach(cb => {
cb && cb(event);
});
}
const subscribe = (cb) => {
if (cb) {
changeStatus({
listeners: refStatus.current.listeners.add(cb)
})
}
}
/**
*
* @returns {Promise<{discovering: boolean; available: boolean}>}
*/
const getAdapterState = () => {
return new Promise((resolve, reject) => {
Taro.getBluetoothAdapterState({
success: resolve,
fail: reject
})
})
}
/**
*
* @param {(res: { devices: { name: string, deviceId: string, RSSI: number }[] }) => void} cb
* @param {number} duration
*/
const startScan = (duration = 30000) => {
console.log('开始寻找')
changeStatus({devices:[]})
Taro.onBluetoothDeviceFound(getDevices);
return new Promise((resolve, reject) => {
Taro.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: resolve,
fail: reject
});
if (duration > 0) {
setTimeout(() => {
Taro.offBluetoothDeviceFound(getDevices);
Taro.stopBluetoothDevicesDiscovery();
console.log("停止搜索")
}, duration);
}
});
}
//获取搜索到的设备
const getDevices = (res) => {
res.devices.forEach(device => {
// 排除掉已搜索到的设备和名称不合法的设备, 将新发现的设备添加到列表中
if (/^CM/.test(device.name) && !refStatus.current.devices.find(i => i.deviceId === device.deviceId)) {
changeStatus({devices: [ ...refStatus.current.devices, device ]})
}
});
}
/**
*
* @param {{ name: string, deviceId: string, RSSI: number }} device
*/
const connect = async (device) => {
try {
changeStatus({connecting: device})
console.log('connecting::', device)
await createConnection(device.deviceId);
await discoverService(device.deviceId);
await discoverCharacteristic(device.deviceId);
await notifyCharacteristicValueChange(device.deviceId);
} catch (e) {
changeStatus({connecting: null})
Taro.showToast({ icon: 'none', title: '蓝牙连接失败' });
throw e;
}
}
/** 断开当前连接的设备 */
const disconnect = async () => {
if (!refStatus.current.connected && !refStatus.current.connecting) return;
if (refStatus.current.connected) {
await closeConnection(refStatus.current.connected.deviceId);
resetCommand();
changeStatus({
connected: null,
serviceId: null,
characteristicId: null,
devices: [],
deviceLab: null
})
}
if (refStatus.current.connecting) {
await closeConnection(refStatus.current.connecting.deviceId);
changeStatus({connecting:null})
}
}
/** 创建 BLE 连接 */
function createConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.createBLEConnection({
deviceId,
timeout: 2000,
success: resolve,
fail: reject
});
});
}
/** 关闭 BLE 连接 */
function closeConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.closeBLEConnection({
deviceId,
success: resolve,
fail: reject
});
});
}
/** 搜索服务 */
function discoverService(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceServices({
deviceId,
success: ({ services }) => {
const service = services.find(i => refStatus.current.serviceRule.test(i.uuid));
if (!service) {
reject(new Error('服务不可用'));
} else {
changeStatus({serviceId: service.uuid})
resolve(service);
}
},
fail: reject
});
});
}
/** 搜索特征 */
function discoverCharacteristic(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceCharacteristics({
deviceId,
serviceId: refStatus.current.serviceId,
success: ({ characteristics }) => {
const characteristic = characteristics.find(i => refStatus.current.characteristicRule.test(i.uuid));
if (!characteristic) {
reject(new Error('特征不可用'));
} else {
changeStatus({characteristicId: characteristic.uuid})
resolve(characteristic);
}
},
fail: reject
})
});
}
/** 启动特征通知 */
function notifyCharacteristicValueChange(deviceId, stateParm = true) {
return new Promise((resolve, reject) => {
Taro.notifyBLECharacteristicValueChange({
deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
state:stateParm,
success: resolve,
fail: reject
});
});
}
/**
*
* @param {ArrayBuffer} buffer
*/
function notifySubscriber(buffer) {
if (refStatus.current.command) {
if (refStatus.current.debug) {
console.log(`[BLE RESP] ${uint8ArrayToHex(new Uint8Array(buffer))}`);
}
refStatus.current.command.fillResponse(buffer);
if (refStatus.current.command.isComplete) {
if (refStatus.current.command.isValid && refStatus.current.responseResolve) {
refStatus.current.responseResolve(refStatus.current.command.response);
} else if (!refStatus.current.command.isValid) {
refStatus.current.responseReject(new Error('无效数据'));
}
resetCommand();
}
} else {
const uint8Array = new Uint8Array(buffer);
if (uint8Array[0] === 0xbb && uint8Array[1] === 1 && uint8Array[3] === 0) {
const ev = { type: 'measure', detail: { mode: uint8Array[2] } };
emit(ev);
}
}
}
/**
*
* @param {Command}} command
* @returns {Promise<Uint8Array>}
*/
function exec(command) {
return new Promise(async (resolve, reject) => {
if (refStatus.current.command) {
reject(new Error('正在执行其他命令'));
} else {
try {
refStatus.current.command = command;
const data = command.data;
for (let i = 0; i < data.length; i++) {
await sendData(data[i]);
}
if (command.responseSize <= 0) {
resolve(true);
resetCommand();
} else {
refStatus.current.responseReject = reject;
refStatus.current.responseResolve = resolve;
refStatus.current.responseTimer = setTimeout(() => {
reject(new Error('命令响应超时'));
resetCommand();
}, command.timeout);
}
} catch (e) {
reject(e);
}
}
});
}
/**
*
* @param {ArrayBuffer} buffer
*/
function sendData(buffer) {
if (refStatus.current.debug) {
console.log(`[BLE SEND] ${uint8ArrayToHex(new Uint8Array(buffer))}`);
}
return new Promise((resolve, reject) => {
console.log('current:::',refStatus.current)
Taro.writeBLECharacteristicValue({
deviceId: refStatus.current.connected.deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
value: buffer,
success: resolve,
fail: reject
})
});
}
function resetCommand() {
if (refStatus.current.responseTimer) {
clearTimeout(refStatus.current.responseTimer);
}
changeStatus({ changeStatus({
command: null, connected: null,
responseResolve: null, serviceId: null,
responseReject: null, characteristicId: null,
responseTimer: null deviceLab: null,
devices: [],
}) })
Taro.showToast({ icon: 'none', title: '蓝牙连接已断开' })
} else if (ev.type === 'connected' && refStatus.current.connecting) {
// 连接成功
changeStatus({ connected: refStatus.current.connecting, connecting: null })
Taro.showToast({ title: '蓝牙已连接' })
} else if (ev.type === 'measure') {
//监听取色仪主动推送lab
await measureAndGetLab()
}
})
}
/** 打开蓝牙适配器 */
const openAdapter = () => {
return new Promise((resolve, reject) => {
Taro.openBluetoothAdapter({
success: resolve,
fail: reject,
})
})
}
/**
*
* @param {{type: string; data: any}} event
*/
const emit = (event) => {
refStatus.current.listeners.forEach((cb) => {
cb && cb(event)
})
}
const subscribe = (cb) => {
if (cb) {
changeStatus({
listeners: refStatus.current.listeners.add(cb),
})
}
}
/**
*
* @returns {Promise<{discovering: boolean; available: boolean}>}
*/
const getAdapterState = () => {
return new Promise((resolve, reject) => {
Taro.getBluetoothAdapterState({
success: resolve,
fail: reject,
})
})
}
/**
*
* @param {(res: { devices: { name: string, deviceId: string, RSSI: number }[] }) => void} cb
* @param {number} duration
*/
const startScan = (duration = 30000) => {
console.log('开始寻找')
changeStatus({ devices: [] })
Taro.onBluetoothDeviceFound(getDevices)
return new Promise((resolve, reject) => {
Taro.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: resolve,
fail: reject,
})
if (duration > 0) {
setTimeout(() => {
Taro.offBluetoothDeviceFound(getDevices)
Taro.stopBluetoothDevicesDiscovery()
console.log('停止搜索')
}, duration)
}
})
}
//获取搜索到的设备
const getDevices = (res) => {
res.devices.forEach((device) => {
// 排除掉已搜索到的设备和名称不合法的设备, 将新发现的设备添加到列表中
if (/^CM/.test(device.name) && !refStatus.current.devices.find((i) => i.deviceId === device.deviceId)) {
changeStatus({ devices: [...refStatus.current.devices, device] })
}
})
}
/**
*
* @param {{ name: string, deviceId: string, RSSI: number }} device
*/
const connect = async (device) => {
try {
changeStatus({ connecting: device })
console.log('connecting::', device)
await createConnection(device.deviceId)
await discoverService(device.deviceId)
await discoverCharacteristic(device.deviceId)
await notifyCharacteristicValueChange(device.deviceId)
} catch (e) {
changeStatus({ connecting: null })
Taro.showToast({ icon: 'none', title: '蓝牙连接失败' })
throw e
}
}
/** 断开当前连接的设备 */
const disconnect = async () => {
if (!refStatus.current.connected && !refStatus.current.connecting) return
if (refStatus.current.connected) {
await closeConnection(refStatus.current.connected.deviceId)
resetCommand()
changeStatus({
connected: null,
serviceId: null,
characteristicId: null,
devices: [],
deviceLab: null,
})
} }
/** if (refStatus.current.connecting) {
* await closeConnection(refStatus.current.connecting.deviceId)
* @param {number} mode changeStatus({ connecting: null })
* @returns {Promise}
*/
async function measure (mode = 0) {
console.log('current1:::',Command.WakeUp)
await exec(Command.WakeUp);
console.log('current2:::',Command.WakeUp)
await waitFor(50);
console.log('current3:::',Command.WakeUp)
return await exec(Command.measure(mode));
} }
}
/** /** 创建 BLE 连接 */
* lab function createConnection(deviceId) {
* @param {number} mode return new Promise((resolve, reject) => {
* @returns {Promise<{ L: number, a: number, b: number }>} Taro.createBLEConnection({
*/ deviceId,
async function getLab(mode = 0) { timeout: 2000,
await exec(Command.WakeUp); success: resolve,
await waitFor(50); fail: reject,
const data: any = await exec(Command.getLab(mode)); })
return { })
L: uint8ArrayToFloat32(data.slice(5, 9)), }
a: uint8ArrayToFloat32(data.slice(9, 13)),
b: uint8ArrayToFloat32(data.slice(13, 17)), /** 关闭 BLE 连接 */
}; function closeConnection(deviceId) {
return new Promise((resolve, reject) => {
Taro.closeBLEConnection({
deviceId,
success: resolve,
fail: reject,
})
})
}
/** 搜索服务 */
function discoverService(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceServices({
deviceId,
success: ({ services }) => {
const service = services.find((i) => refStatus.current.serviceRule.test(i.uuid))
if (!service) {
reject(new Error('服务不可用'))
} else {
changeStatus({ serviceId: service.uuid })
resolve(service)
}
},
fail: reject,
})
})
}
/** 搜索特征 */
function discoverCharacteristic(deviceId) {
return new Promise((resolve, reject) => {
Taro.getBLEDeviceCharacteristics({
deviceId,
serviceId: refStatus.current.serviceId,
success: ({ characteristics }) => {
const characteristic = characteristics.find((i) => refStatus.current.characteristicRule.test(i.uuid))
if (!characteristic) {
reject(new Error('特征不可用'))
} else {
changeStatus({ characteristicId: characteristic.uuid })
resolve(characteristic)
}
},
fail: reject,
})
})
}
/** 启动特征通知 */
function notifyCharacteristicValueChange(deviceId, stateParm = true) {
return new Promise((resolve, reject) => {
Taro.notifyBLECharacteristicValueChange({
deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
state: stateParm,
success: resolve,
fail: reject,
})
})
}
/**
*
* @param {ArrayBuffer} buffer
*/
function notifySubscriber(buffer) {
if (refStatus.current.command) {
if (refStatus.current.debug) {
console.log(`[BLE RESP] ${uint8ArrayToHex(new Uint8Array(buffer))}`)
}
refStatus.current.command.fillResponse(buffer)
if (refStatus.current.command.isComplete) {
if (refStatus.current.command.isValid && refStatus.current.responseResolve) {
refStatus.current.responseResolve(refStatus.current.command.response)
} else if (!refStatus.current.command.isValid) {
refStatus.current.responseReject(new Error('无效数据'))
}
resetCommand()
}
} else {
const uint8Array = new Uint8Array(buffer)
if (uint8Array[0] === 0xbb && uint8Array[1] === 1 && uint8Array[3] === 0) {
const ev = { type: 'measure', detail: { mode: uint8Array[2] } }
emit(ev)
}
} }
}
/** /**
* lab *
* @param {number} mode * @param {Command}} command
* @returns {Promise<{L: number, a: number, b: number}>} * @returns {Promise<Uint8Array>}
*/ */
async function measureAndGetLab(mode = 0) { function exec(command) {
await measure(mode); return new Promise(async (resolve, reject) => {
await waitFor(50); if (refStatus.current.command) {
const lab = await getLab(mode); reject(new Error('正在执行其他命令'))
console.log('lab2::',lab) } else {
changeStatus({deviceLab:lab}) try {
return lab refStatus.current.command = command
const data = command.data
for (let i = 0; i < data.length; i++) {
await sendData(data[i])
}
if (command.responseSize <= 0) {
resolve(true)
resetCommand()
} else {
refStatus.current.responseReject = reject
refStatus.current.responseResolve = resolve
refStatus.current.responseTimer = setTimeout(() => {
reject(new Error('命令响应超时'))
resetCommand()
}, command.timeout)
}
} catch (e) {
reject(e)
}
}
})
}
/**
*
* @param {ArrayBuffer} buffer
*/
function sendData(buffer) {
if (refStatus.current.debug) {
console.log(`[BLE SEND] ${uint8ArrayToHex(new Uint8Array(buffer))}`)
} }
return new Promise((resolve, reject) => {
console.log('current:::', refStatus.current)
Taro.writeBLECharacteristicValue({
deviceId: refStatus.current.connected.deviceId,
serviceId: refStatus.current.serviceId,
characteristicId: refStatus.current.characteristicId,
value: buffer,
success: resolve,
fail: reject,
})
})
}
return <Context.Provider children={props.children} value={{ function resetCommand() {
init, if (refStatus.current.responseTimer) {
clearTimeout(refStatus.current.responseTimer)
}
changeStatus({
command: null,
responseResolve: null,
responseReject: null,
responseTimer: null,
})
}
/**
*
* @param {number} mode
* @returns {Promise}
*/
async function measure(mode = 0) {
console.log('current1:::', Command.WakeUp)
await exec(Command.WakeUp)
console.log('current2:::', Command.WakeUp)
await waitFor(50)
console.log('current3:::', Command.WakeUp)
return await exec(Command.measure(mode))
}
/**
* lab
* @param {number} mode
* @returns {Promise<{ L: number, a: number, b: number }>}
*/
async function getLab(mode = 0) {
await exec(Command.WakeUp)
await waitFor(50)
const data: any = await exec(Command.getLab(mode))
return {
L: uint8ArrayToFloat32(data.slice(5, 9)),
a: uint8ArrayToFloat32(data.slice(9, 13)),
b: uint8ArrayToFloat32(data.slice(13, 17)),
}
}
/**
* lab
* @param {number} mode
* @returns {Promise<{L: number, a: number, b: number}>}
*/
async function measureAndGetLab(mode = 0) {
await measure(mode)
await waitFor(50)
const lab = await getLab(mode)
console.log('lab2::', lab)
changeStatus({ deviceLab: lab })
return lab
}
return (
<Context.Provider
children={props.children}
value={{
init,
state, state,
startScan, startScan,
measureAndGetLab, measureAndGetLab,
getAdapterState, getAdapterState,
connect, connect,
disconnect disconnect,
}} /> }}
/>
)
} }
export const useBluetooth = () => { export const useBluetooth = () => {
const res = React.useContext<any>(Context) const res = React.useContext<any>(Context)
if(res) { if (res) {
return {...res} return { ...res }
} else { } else {
return {} return {}
} }
}
}

View File

@ -1,51 +1,62 @@
import { alert } from "@/common/common"; import { alert } from '@/common/common'
import Taro from "@tarojs/taro"; import Taro from '@tarojs/taro'
import { memo, useCallback, useState } from "react"; import { memo, useCallback, useState } from 'react'
type Scope = 'scope.userLocation'|'scope.userLocation'|'scope.record'|'scope.camera'|'scope.bluetooth'|'scope.writePhotosAlbum'|'scope.addPhoneContact'|'scope.addPhoneCalendar'|'scope.werun'|'scope.address'|'scope.invoiceTitle'|'scope.invoice'|'scope.userInfo' type Scope =
| 'scope.userLocation'
| 'scope.userLocation'
| 'scope.record'
| 'scope.camera'
| 'scope.bluetooth'
| 'scope.writePhotosAlbum'
| 'scope.addPhoneContact'
| 'scope.addPhoneCalendar'
| 'scope.werun'
| 'scope.address'
| 'scope.invoiceTitle'
| 'scope.invoice'
| 'scope.userInfo'
type Param = { type Param = {
scope: Scope, scope: Scope
msg: string //检查不通过时警告 msg: string //检查不通过时警告
} }
export default ({scope, msg}: Param) => { export default ({ scope, msg }: Param) => {
//这个hook微信授权检查授权 //这个hook微信授权检查授权
const check = useCallback(() => { const check = useCallback(() => {
return new Promise((reslove, reject) => { return new Promise((reslove, reject) => {
Taro.getSetting({ Taro.getSetting({
success: (res) => { success: (res) => {
if(res.authSetting[scope]) { if (res.authSetting[scope]) {
reslove(true) reslove(true)
} else if (res.authSetting[scope] === undefined) { } else if (res.authSetting[scope] === undefined) {
Taro.authorize({ Taro.authorize({
scope: scope, scope: scope,
success() { success() {
reslove(true) reslove(true)
}, },
fail() { fail() {
alert.none(msg) alert.none(msg)
reject(false) reject(false)
} },
})
} else {
Taro.openSetting({
success(res) {
if(res.authSetting[scope]) {
reslove(true)
} else {
alert.none(msg)
reject(false)
}
}
})
}
}
}) })
}) } else {
Taro.openSetting({
}, [scope]) success(res) {
if (res.authSetting[scope]) {
reslove(true)
} else {
alert.none(msg)
reject(false)
}
},
})
}
},
})
})
}, [scope])
return { return {
check, check,
} }
}
}

View File

@ -1,92 +1,90 @@
import { SubscriptionMessageApi } from "@/api/user" import { SubscriptionMessageApi } from '@/api/user'
import Taro from "@tarojs/taro" import Taro from '@tarojs/taro'
import dayjs from "dayjs" import dayjs from 'dayjs'
import { useEffect, useRef, useState } from "react" import { useEffect, useRef, useState } from 'react'
//倒计时hook //倒计时hook
export const useTimeCountDown = () => { export const useTimeCountDown = () => {
const [showTime, setShowTime] = useState({ const [showTime, setShowTime] = useState({
DD: '', DD: '',
HH: '', HH: '',
MM: '', MM: '',
SS: '' SS: '',
}) })
const [timeStatus, setTimeStatus] = useState<0|1|2>(0) //倒计时状体 0:倒计时未开始 1:倒计时中, 2倒计时已结束 const [timeStatus, setTimeStatus] = useState<0 | 1 | 2>(0) //倒计时状体 0:倒计时未开始 1:倒计时中, 2倒计时已结束
const timeObj:any = useRef() const timeObj: any = useRef()
const endTime = useRef('') const endTime = useRef('')
const onStart = (val = '') => { const onStart = (val = '') => {
console.log('time:::', val) console.log('time:::', val)
endTime.current = val endTime.current = val
if(endTime.current) { if (endTime.current) {
clearInterval(timeObj.current) clearInterval(timeObj.current)
timeObj.current = setInterval(() => { timeObj.current = setInterval(() => {
count_down() count_down()
}, 1000) }, 1000)
}
} }
useEffect(() => { }
return () => { useEffect(() => {
clearInterval(timeObj.current) return () => {
} clearInterval(timeObj.current)
}, [])
const count_down = () => {
var startData = dayjs();
var endDate = dayjs(endTime.current);
setTimeStatus(() => 1)
if(startData >= endDate) {
clearInterval(timeObj.current)
setShowTime((e) => ({...e, DD:'00', HH:'00', MM:'00', SS:'00'}))
setTimeStatus(() => 2)
return false
}
var _dd = endDate.diff(startData,'day');
var _hh = endDate.diff(startData,'hour');
var _mm = endDate.diff(startData,'minute');
var _ss = endDate.diff(startData,'second');
// 转换
var hh = _hh - (_dd*24);
var mm = _mm - (_hh*60);
var ss = _ss - (_mm*60);
// 格式化
var DD = ('00'+_dd).slice(-2);
var HH = ('00'+hh).slice(-2);
var MM = ('00'+mm).slice(-2);
var SS = ('00'+ss).slice(-2);
console.log('endTime::', `${DD}-${HH}-${MM}-${SS}`)
setShowTime((e) => ({...e, DD, HH, MM, SS}))
} }
return { }, [])
showTime, const count_down = () => {
onStart, var startData = dayjs()
timeStatus var endDate = dayjs(endTime.current)
setTimeStatus(() => 1)
if (startData >= endDate) {
clearInterval(timeObj.current)
setShowTime((e) => ({ ...e, DD: '00', HH: '00', MM: '00', SS: '00' }))
setTimeStatus(() => 2)
return false
} }
var _dd = endDate.diff(startData, 'day')
var _hh = endDate.diff(startData, 'hour')
var _mm = endDate.diff(startData, 'minute')
var _ss = endDate.diff(startData, 'second')
// 转换
var hh = _hh - _dd * 24
var mm = _mm - _hh * 60
var ss = _ss - _mm * 60
// 格式化
var DD = ('00' + _dd).slice(-2)
var HH = ('00' + hh).slice(-2)
var MM = ('00' + mm).slice(-2)
var SS = ('00' + ss).slice(-2)
console.log('endTime::', `${DD}-${HH}-${MM}-${SS}`)
setShowTime((e) => ({ ...e, DD, HH, MM, SS }))
}
return {
showTime,
onStart,
timeStatus,
}
} }
//订阅消息hook //订阅消息hook
export const UseSubscriptionMessage = () => { export const UseSubscriptionMessage = () => {
const {fetchData: fetchDataMessage} = SubscriptionMessageApi() const { fetchData: fetchDataMessage } = SubscriptionMessageApi()
const openSubscriptionMessage = ({orderId = 0, scenes = 0}:{orderId?: number, scenes: number}) => { const openSubscriptionMessage = ({ orderId = 0, scenes = 0 }: { orderId?: number; scenes: number }) => {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
let params:{sale_order_id?: number, scenes?: number} = {} let params: { sale_order_id?: number; scenes?: number } = {}
orderId&&(params.sale_order_id = orderId) orderId && (params.sale_order_id = orderId)
params.scenes = scenes params.scenes = scenes
let res = await fetchDataMessage(params) let res = await fetchDataMessage(params)
if(res.success&&res.data.TemplateID&&res.data.TemplateID.length > 0) { if (res.success && res.data.TemplateID && res.data.TemplateID.length > 0) {
Taro.requestSubscribeMessage({ Taro.requestSubscribeMessage({
tmplIds: res.data.TemplateID, tmplIds: res.data.TemplateID,
complete: function (res) { complete: function (res) {
resolve(res) resolve(res)
} },
})
} else {
resolve(true)
}
}) })
} } else {
resolve(true)
}
})
}
return { return {
openSubscriptionMessage openSubscriptionMessage,
} }
} }

View File

@ -1,33 +1,33 @@
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import {SET_SHOPCOUNT, CLEAR_SHOPCOUNT} from '@/constants/common' import { SET_SHOPCOUNT, CLEAR_SHOPCOUNT } from '@/constants/common'
import {DataParam} from '@/reducers/commonData' import { DataParam } from '@/reducers/commonData'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { GetShoppingCartApi } from '@/api/shopCart' import { GetShoppingCartApi } from '@/api/shopCart'
import { useSelector } from '@/reducers/hooks' import { useSelector } from '@/reducers/hooks'
export default () => { export default () => {
const commonData = useSelector(state => state.commonData) const commonData = useSelector((state) => state.commonData)
const dispatch = useDispatch() const dispatch = useDispatch()
const setShopCount = (shopCount: number) => {
dispatch({type:SET_SHOPCOUNT, data:{shopCount}})
}
const removeShopCount = () => { const setShopCount = (shopCount: number) => {
dispatch({type:CLEAR_SHOPCOUNT}) dispatch({ type: SET_SHOPCOUNT, data: { shopCount } })
} }
const {fetchData: fetchDataShopCount} = GetShoppingCartApi() const removeShopCount = () => {
const getShopCount = async () => { dispatch({ type: CLEAR_SHOPCOUNT })
//获取购物车数据数量 }
const {data} = await fetchDataShopCount()
let color_list = data.color_list||[] const { fetchData: fetchDataShopCount } = GetShoppingCartApi()
setShopCount(color_list.length) const getShopCount = async () => {
} //获取购物车数据数量
const { data } = await fetchDataShopCount()
return { let color_list = data.color_list || []
setShopCount, setShopCount(color_list.length)
removeShopCount, }
getShopCount,
commonData return {
} setShopCount,
} removeShopCount,
getShopCount,
commonData,
}
}

View File

@ -1,100 +1,98 @@
import Taro, { useRouter } from '@tarojs/taro' import Taro, { useRouter } from '@tarojs/taro'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import {BASE_URL, WX_APPID} from '@/common/constant' import { BASE_URL, WX_APPID } from '@/common/constant'
import useUserInfo from "./useUserInfo" import useUserInfo from './useUserInfo'
import qs from 'qs'; import qs from 'qs'
import useLogin from './useLogin'; import useLogin from './useLogin'
import useLoginRequest from './useLoginRequest'; import useLoginRequest from './useLoginRequest'
type Params = { type Params = {
code: string|null code: string | null
success: true|false success: true | false
data: any, data: any
msg: string, msg: string
loading: true|false, loading: true | false
error: any, error: any
query: any, query: any
filter: any, filter: any
sort: any, sort: any
total: number, total: number
multiple: true|false, // 请求多次 multiple: true | false // 请求多次
count: number, // 第几次请求 count: number // 第几次请求
token: string, // token token: string // token
page?: number, page?: number
pageSize?: number pageSize?: number
} }
type option = { type option = {
url?: string, url?: string
method?: 'get'|'post'|'put'|'delete', method?: 'get' | 'post' | 'put' | 'delete'
type?: string, type?: string
data?: any, data?: any
page?: number, page?: number
pageSize?: number, pageSize?: number
pagination?: true|false, pagination?: true | false
base_url?: string, base_url?: string
apiMsgStatus?: true|false apiMsgStatus?: true | false
} }
/** /**
* http * http
* @param {Number} status * @param {Number} status
* @returns * @returns
*/ */
const showStatus = (status) => { const showStatus = (status) => {
let message = '' let message = ''
switch (status) { switch (status) {
case 400: case 400:
message = '请求错误(400)' message = '请求错误(400)'
break break
case 401: case 401:
message = '未授权,请重新登录(401)' message = '未授权,请重新登录(401)'
break break
case 403: case 403:
message = '拒绝访问(403)' message = '拒绝访问(403)'
break break
case 404: case 404:
message = '请求出错(404)' message = '请求出错(404)'
break break
case 408: case 408:
message = '请求超时(408)' message = '请求超时(408)'
break break
case 500: case 500:
message = '服务器错误(500)' message = '服务器错误(500)'
break break
case 501: case 501:
message = '服务未实现(501)' message = '服务未实现(501)'
break break
case 502: case 502:
message = '网络错误(502)' message = '网络错误(502)'
break break
case 503: case 503:
message = '服务不可用(503)' message = '服务不可用(503)'
break break
case 504: case 504:
message = '网络超时(504)' message = '网络超时(504)'
break break
case 505: case 505:
message = 'HTTP版本不受支持(505)' message = 'HTTP版本不受支持(505)'
break break
default: default:
message = `连接出错(${status})!` message = `连接出错(${status})!`
} }
return `${message},请检查网络或联系管理员!` return `${message},请检查网络或联系管理员!`
} }
/** /**
* axios fetch(), loading, error, code, msg / * axios fetch(), loading, error, code, msg /
* @param {Object} options * @param {Object} options
* @param {String} options.url URL * @param {String} options.url URL
* @param {String} options.method * @param {String} options.method
* @param {Object} options.data * @param {Object} options.data
* @returns {Object} fetch(), loading, error, code, msg * @returns {Object} fetch(), loading, error, code, msg
*/ */
export const useRequest = (options:option = { export const useRequest = (
options: option = {
url: '/', url: '/',
method: 'get', method: 'get',
type: 'json', type: 'json',
@ -103,112 +101,107 @@ export const useRequest = (options:option = {
pageSize: 24, pageSize: 24,
pagination: false, // 是否分页 pagination: false, // 是否分页
base_url: '', base_url: '',
apiMsgStatus: true //是否直接弹出后端错误 apiMsgStatus: true, //是否直接弹出后端错误
}) => { },
) => {
options.url = `${options.base_url||BASE_URL}${options.url}` options.url = `${options.base_url || BASE_URL}${options.url}`
let params:Params = { let params: Params = {
code: null, // 业务码 code: null, // 业务码
success: false, // 请求是否成功 success: false, // 请求是否成功
data: {}, data: {},
msg: '', msg: '',
loading: false, loading: false,
error: null, error: null,
query: {}, query: {},
filter: null, filter: null,
sort: '', sort: '',
total: 0, total: 0,
multiple: true, // 请求多次 multiple: true, // 请求多次
count: 0, // 第几次请求 count: 0, // 第几次请求
token: '', // token token: '', // token
} }
const stateRef = useRef({...params}) const stateRef = useRef({ ...params })
const [state, setState] = useState({...stateRef.current}) const [state, setState] = useState({ ...stateRef.current })
const {removeToken, removeSessionKey, removeUserInfo} = useUserInfo() const { removeToken, removeSessionKey, removeUserInfo } = useUserInfo()
const {login} = useLoginRequest() const { login } = useLoginRequest()
// 请求函数 // 请求函数
const fetchData = async (sub_options?:any) => { const fetchData = async (sub_options?: any) => {
stateRef.current.loading = true stateRef.current.loading = true
setState((e) => ({...e, loading:true})) setState((e) => ({ ...e, loading: true }))
stateRef.current.query = { stateRef.current.query = {
...sub_options, ...sub_options,
...options.pagination && { ...(options.pagination && {
page: stateRef.current.page, page: stateRef.current.page,
size: stateRef.current.pageSize, size: stateRef.current.pageSize,
}, }),
...stateRef.current.filter, ...stateRef.current.filter,
...stateRef.current.sort ...stateRef.current.sort,
} }
try { try {
let token = Taro.getStorageSync('token') let token = Taro.getStorageSync('token')
const q = { const q = {
...options, ...options,
...{ ...{
header: { header: {
"Platform": 6, Platform: 6,
"Appid": WX_APPID, Appid: WX_APPID,
"Authorization": token || stateRef.current.token, Authorization: token || stateRef.current.token,
} },
}, },
...options.method?.toUpperCase() == 'GET' ? { ...(options.method?.toUpperCase() == 'GET'
data: stateRef.current.query ? {
} : { data: stateRef.current.query,
data: options.type?.toUpperCase() == 'FORMDATA' ? qs.stringify(stateRef.current.query) : stateRef.current.query
}
} }
const result = await Taro.request(q as any) : {
const { statusCode } = result data: options.type?.toUpperCase() == 'FORMDATA' ? qs.stringify(stateRef.current.query) : stateRef.current.query,
const { }),
code, }
msg, const result = await Taro.request(q as any)
data const { statusCode } = result
} = result.data const { code, msg, data } = result.data
if (statusCode === 200) { if (statusCode === 200) {
stateRef.current.success = (code === 0 ? true : false) stateRef.current.success = code === 0 ? true : false
stateRef.current.code = code stateRef.current.code = code
stateRef.current.msg = msg stateRef.current.msg = msg
stateRef.current.data = data stateRef.current.data = data
stateRef.current.total = data?.list ? data?.total : 0 stateRef.current.total = data?.list ? data?.total : 0
if(code !== 0) { if (code !== 0) {
options.apiMsgStatus !== false &&Taro.showToast({ options.apiMsgStatus !== false &&
title: `${msg}`, Taro.showToast({
icon: 'none' title: `${msg}`,
}) icon: 'none',
console.log('错误::',msg) })
} console.log('错误::', msg)
}else{
if (statusCode === 401) {
removeToken()
removeSessionKey()
removeUserInfo()
login()
} else {
Taro.showToast({
title: `错误:${showStatus(statusCode)}`,
icon: 'none'
})
}
}
} catch (e) {
stateRef.current.success = false
stateRef.current.error = true
stateRef.current.msg = e.errMsg
console.log('后台错误信息::',e.errMsg)
} }
stateRef.current.error = false } else {
stateRef.current.loading = false if (statusCode === 401) {
setState(() => ({...stateRef.current})) removeToken()
return stateRef.current removeSessionKey()
removeUserInfo()
login()
} else {
Taro.showToast({
title: `错误:${showStatus(statusCode)}`,
icon: 'none',
})
}
}
} catch (e) {
stateRef.current.success = false
stateRef.current.error = true
stateRef.current.msg = e.errMsg
console.log('后台错误信息::', e.errMsg)
} }
stateRef.current.error = false
stateRef.current.loading = false
setState(() => ({ ...stateRef.current }))
return stateRef.current
}
return { return {
fetchData, fetchData,
state, state,
} }
}
}

View File

@ -1,137 +1,134 @@
import useUserInfo from "./useUserInfo" import useUserInfo from './useUserInfo'
import Taro, { useDidShow, useRouter } from "@tarojs/taro" import Taro, { useDidShow, useRouter } from '@tarojs/taro'
import { GetWxUserInfoApi, GetAdminUserInfoApi, GetPhoneNumberApi, BindingCompanyApi } from "@/api/user" import { GetWxUserInfoApi, GetAdminUserInfoApi, GetPhoneNumberApi, BindingCompanyApi } from '@/api/user'
import useLoginRequest from "./useLoginRequest" import useLoginRequest from './useLoginRequest'
import { SHARE_SCENE } from "@/common/enum" import { SHARE_SCENE } from '@/common/enum'
import { GetShortCodeApi } from "@/api/share" import { GetShortCodeApi } from '@/api/share'
import { alert } from "@/common/common" import { alert } from '@/common/common'
import { LoginApi } from "@/api/login" import { LoginApi } from '@/api/login'
import { IMG_CND_Prefix } from "@/common/constant" import { IMG_CND_Prefix } from '@/common/constant'
export default () => { export default () => {
const {setUserInfo, setAdminUserInfo, setSortCode, userInfo} = useUserInfo() const { setUserInfo, setAdminUserInfo, setSortCode, userInfo } = useUserInfo()
useDidShow(() => { useDidShow(() => {
checkLogin() checkLogin()
})
//登录请求
const { login } = useLoginRequest()
// const {fetchData:login} = LoginApi()
const wxLogin = async () => {
try {
await login()
getAdminUserInfo()
} catch (e) {
console.log('登录失败::', e)
}
}
//获取用户信息
const { fetchData: useFetchData } = GetAdminUserInfoApi()
const getAdminUserInfo = async () => {
let res = await useFetchData()
setAdminUserInfo({ ...res.data })
getShortCode(res.data.user_id)
}
//登录加checkLogin检查
const checkLogin = () => {
return new Promise(async (reslove) => {
if (!userInfo.token) {
await wxLogin()
reslove(true)
} else {
//这个东西不要打开checkSession有调用频率问题
// Taro.checkSession({
// success: async () => {
// reslove(true)
// if(!userInfo.adminUserInfo) getAdminUserInfo()
// },
// fail: async () => {
// await wxLogin()
// reslove(true)
// }
// })
}
}) })
}
//登录请求 //获取用户头像等信息数据
const {login} = useLoginRequest() const { fetchData: fetchDataUserInfo } = GetWxUserInfoApi()
// const {fetchData:login} = LoginApi() const getSelfUserInfo = async () => {
const wxLogin = async () => { return new Promise((reslove, reject) => {
try { if (userInfo.adminUserInfo?.is_authorize_name) {
await login() reslove(true)
return true
}
Taro.getUserProfile({
desc: '用于完善会员资料',
success: async (res) => {
if (!userInfo.session_key) {
await wxLogin()
}
const user_res = await fetchDataUserInfo({
session_key: userInfo.session_key,
raw_data: res.rawData,
signature: res.signature,
encrypted_data: res.encryptedData,
iv: res.iv,
})
if (user_res.success) {
setUserInfo({ ...user_res.data })
getAdminUserInfo() getAdminUserInfo()
} catch(e) { reslove(true)
console.log('登录失败::',e) } else {
} reject(user_res.msg)
} }
},
fail: (e) => {
console.log('授权失败::', e)
reject(e.errMsg)
},
})
})
}
//获取用户信息 //获取手机号码
const {fetchData: useFetchData} = GetAdminUserInfoApi() const { fetchData: fetchDataUserPhone } = GetPhoneNumberApi()
const getAdminUserInfo = async () => { const { fetchData: fetchBindingCompany } = BindingCompanyApi()
let res = await useFetchData() const getPhoneNumber = (code) => {
setAdminUserInfo({...res.data}) return new Promise(async (reslove, reject) => {
getShortCode(res.data.user_id) if (userInfo.adminUserInfo?.is_authorize_phone) {
} reslove(true)
return true
//登录加checkLogin检查 }
const checkLogin = () => { const res = await fetchDataUserPhone({ code })
return new Promise( async (reslove) => { if (res.success) {
if(!userInfo.token) { setUserInfo({ ...userInfo.userInfo, phone: res.data.phone_number })
await wxLogin() await fetchBindingCompany()
reslove(true) getAdminUserInfo()
} else { reslove(res.data)
//这个东西不要打开checkSession有调用频率问题 } else {
// Taro.checkSession({ reject(res.msg)
// success: async () => { }
// reslove(true) })
// if(!userInfo.adminUserInfo) getAdminUserInfo() }
// },
// fail: async () => {
// await wxLogin()
// reslove(true)
// }
// })
}
})
}
//获取用户头像等信息数据 //获取分享码(右上角分享码)
const {fetchData: fetchDataUserInfo} = GetWxUserInfoApi() const { SharePage } = SHARE_SCENE
const getSelfUserInfo = async () => { const { fetchData: fetchDataShortCode } = GetShortCodeApi()
return new Promise((reslove, reject) => { const getShortCode = async (user_id) => {
if(userInfo.adminUserInfo?.is_authorize_name) { const { data: resPage } = await fetchDataShortCode({ share_user_id: user_id, type: SharePage.value })
reslove(true) setSortCode({ ...userInfo.sort_code, shareShortPage: { title: '打造面料爆品 专注客户服务', code: resPage.md5_key, img: IMG_CND_Prefix + '/mall/share_img_01.png' } })
return true }
}
Taro.getUserProfile({
desc: '用于完善会员资料',
success: async (res) => {
if(!userInfo.session_key) {
await wxLogin()
}
const user_res = await fetchDataUserInfo({
session_key: userInfo.session_key,
raw_data: res.rawData,
signature: res.signature,
encrypted_data: res.encryptedData,
iv: res.iv
})
if(user_res.success) {
setUserInfo({...user_res.data})
getAdminUserInfo()
reslove(true)
} else {
reject(user_res.msg)
}
},
fail:(e) => {
console.log('授权失败::',e)
reject(e.errMsg)
}
})
})
}
//获取手机号码 return {
const {fetchData: fetchDataUserPhone} = GetPhoneNumberApi() checkLogin,
const {fetchData: fetchBindingCompany} = BindingCompanyApi() wxLogin,
const getPhoneNumber = (code) => { getSelfUserInfo,
return new Promise( async (reslove, reject) => { getPhoneNumber,
if(userInfo.adminUserInfo?.is_authorize_phone) { userInfo,
reslove(true) getAdminUserInfo,
return true }
}
const res = await fetchDataUserPhone({code})
if(res.success) {
setUserInfo({...userInfo.userInfo, phone:res.data.phone_number})
await fetchBindingCompany()
getAdminUserInfo()
reslove(res.data)
} else {
reject(res.msg)
}
})
}
//获取分享码(右上角分享码)
const {SharePage} = SHARE_SCENE
const {fetchData: fetchDataShortCode} = GetShortCodeApi()
const getShortCode = async (user_id) => {
const {data: resPage} = await fetchDataShortCode({"share_user_id": user_id, type:SharePage.value})
setSortCode({...userInfo.sort_code, shareShortPage: {title: '打造面料爆品 专注客户服务', code: resPage.md5_key, img:IMG_CND_Prefix + '/mall/share_img_01.png'}})
}
return {
checkLogin,
wxLogin,
getSelfUserInfo,
getPhoneNumber,
userInfo,
getAdminUserInfo
}
} }

View File

@ -1,165 +1,155 @@
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro'
import {UPLOAD_CDN_URL } from '@/common/constant' import { UPLOAD_CDN_URL } from '@/common/constant'
import { GetSignApi } from '@/api/cdn' import { GetSignApi } from '@/api/cdn'
export default () => { export default () => {
const { fetchData: GetSign, state } = GetSignApi()
const { fetchData: GetSign, state} = GetSignApi() // 上传图片 获取authPolicy
/*
// 上传图片 获取authPolicy
/*
scene scene
type bucket type bucket
*/ */
const getSecret = (scene, type) => { const getSecret = (scene, type) => {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const SAVE_PATH = `/${scene}/{filemd5}{day}{hour}{min}{sec}{.suffix}`
const SAVE_PATH = `/${scene}/{filemd5}{day}{hour}{min}{sec}{.suffix}`; let params = {
method: 'post',
save_key: SAVE_PATH,
}
// 获取签名
let res = await GetSign(params)
if (res.success) {
resolve(res.data)
} else {
reject({
code: res.code || '9999',
msg: res.msg,
})
}
})
}
const getFileType = (name) => {
if (!name) return false
var imgType = ['gif', 'jpeg', 'jpg', 'bmp', 'png']
var videoType = ['avi', 'wmv', 'mkv', 'mp4', 'mov', 'rm', '3gp', 'flv', 'mpg', 'rmvb', 'quicktime']
let params = { if (RegExp('.?(' + imgType.join('|') + ')$', 'i').test(name.toLowerCase())) {
'method': 'post', return 'image'
'save_key': SAVE_PATH } else if (RegExp('.(' + videoType.join('|') + ')$', 'i').test(name.toLowerCase())) {
} return 'video'
// 获取签名 } else {
let res = await GetSign(params) return false
if (res.success) { }
resolve(res.data) }
/**
*
* @param {*} file
* @param {String} secene 'product'
* @param {String} type 'product'
* @returns
*/
const uploadCDNImg = (file, secene, type) => {
let filetype = file.path
console.log('filetype::', filetype)
if (!getFileType(filetype)) {
Taro.showToast({
title: '上传文件类型错误',
icon: 'none',
duration: 3800,
})
return false
}
return new Promise((resolve, reject) => {
getSecret(secene, type)
.then((result) => {
let res: any = result
console.log('bucket', res.bucket)
var formdata = {
authorization: res.authorization,
policy: res.policy,
}
const uploadTask = Taro.uploadFile({
url: `${UPLOAD_CDN_URL}${res.bucket}`,
formData: formdata,
filePath: file.path,
name: 'file',
success: (res) => {
resolve(JSON.parse(`${res.data}`))
},
fail: (err) => {
console.log(err)
reject(err)
},
})
uploadTask.progress((res) => {
console.log('上传进度', res.progress)
if (res.progress < 100) {
Taro.showLoading({
title: '上传中...',
})
} else { } else {
reject({ Taro.hideLoading()
code: res.code || '9999',
msg: res.msg
});
} }
})
}) })
} .catch((result) => {
const getFileType = (name) => { reject(result)
if (!name) return false; Taro.showToast({
var imgType = ["gif", "jpeg", "jpg", "bmp", "png"]; title: '获取密钥失败!',
var videoType = ["avi", "wmv", "mkv", "mp4", "mov", "rm", "3gp", "flv", "mpg", "rmvb", "quicktime"]; icon: 'none',
duration: 3800,
if (RegExp("\.?(" + imgType.join("|") + ")$", "i").test(name.toLowerCase())) { })
return 'image';
} else if (RegExp("\.(" + videoType.join("|") + ")$", "i").test(name.toLowerCase())) {
return 'video';
} else {
return false;
}
}
/**
*
* @param {*} file
* @param {String} secene 'product'
* @param {String} type 'product'
* @returns
*/
const uploadCDNImg = (file, secene, type) => {
let filetype = file.path
console.log('filetype::',filetype)
if (!getFileType(filetype)) {
Taro.showToast({
title: "上传文件类型错误",
icon: "none",
duration: 3800
})
return false
}
return new Promise((resolve, reject) => {
getSecret(secene, type)
.then(result => {
let res:any = result
console.log('bucket', res.bucket);
var formdata = {
'authorization': res.authorization,
'policy': res.policy,
}
const uploadTask = Taro.uploadFile({
url: `${UPLOAD_CDN_URL}${res.bucket}`,
formData: formdata,
filePath: file.path,
name: 'file',
success: res => {
resolve(JSON.parse(`${res.data}`))
},
fail: err => {
console.log(err)
reject(err)
}
})
uploadTask.progress(res => {
console.log('上传进度', res.progress);
if (res.progress < 100) {
Taro.showLoading({
title: '上传中...'
})
} else {
Taro.hideLoading()
}
})
})
.catch(result => {
reject(result)
Taro.showToast({
title: "获取密钥失败!",
icon: "none",
duration: 3800
})
})
}) })
} })
}
// product 产品相关,图片、纹理图等 全平台
// product 产品相关,图片、纹理图等 全平台 // after-sale 售后(申请退货、退款)相关的、图片、视频 全平台
// after-sale 售后(申请退货、退款)相关的、图片、视频 全平台 // mall 电子商城相关的 全平台
// mall 电子商城相关的 全平台 // logistics 物流(发货、提货)相关的、图片、视频 全平台
// logistics 物流(发货、提货)相关的、图片、视频 全平台 type cdn_upload_type_Param = 'product' | 'after-sale' | 'mall' | 'logistics'
type cdn_upload_type_Param = 'product'|'after-sale'|'mall'|'logistics' /**
/** *
* * @param cdn_upload_type
* @param cdn_upload_type * @param count // 1时返回一张图片信息 大于1时返回图片数组
* @param count // 1时返回一张图片信息 大于1时返回图片数组 * @returns
* @returns */
*/ const getWxPhoto = (cdn_upload_type: cdn_upload_type_Param, count: number = 1) => {
const getWxPhoto = (cdn_upload_type: cdn_upload_type_Param, count: number = 1) => { return new Promise((resolve, reject) => {
return new Promise((resolve, reject) => { let list: any[] = []
let list:any[] = [] Taro.chooseImage({
Taro.chooseImage({ count: count,
count: count, sizeType: ['original', 'compressed'],
sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'],
sourceType: ['album', 'camera'], success: async function (res) {
success: async function (res) { try {
try { if (count > 1) {
if(count > 1) { for (let i = 0; i < res.tempFiles.length; i++) {
for(let i = 0; i < res.tempFiles.length; i++) { let data = await uploadCDNImg(res.tempFiles[i], cdn_upload_type, cdn_upload_type)
let data = await uploadCDNImg(res.tempFiles[i], cdn_upload_type, cdn_upload_type) list.push(data)
list.push(data) }
} resolve(list)
resolve(list) } else {
} else { //兼容以前上传一张的情况
//兼容以前上传一张的情况 let data = await uploadCDNImg(res.tempFiles[0], cdn_upload_type, cdn_upload_type)
let data = await uploadCDNImg(res.tempFiles[0], cdn_upload_type, cdn_upload_type) resolve(data)
resolve(data) }
} } catch (res) {
reject(res)
} catch(res) { }
reject(res) },
} })
})
} }
})
})
}
return {
uploadCDNImg,
getWxPhoto
}
return {
uploadCDNImg,
getWxPhoto,
}
} }

View File

@ -1,51 +1,51 @@
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { CLEAR_SESSIONKEY, SET_USERINFO, SET_TOKEN, SET_SESSIONKEY, CLEAR_USERINFO, CLEAR_TOKEN, SET_ADMINUSERINFO, SET_SORTCODE} from '@/constants/userInfo' import { CLEAR_SESSIONKEY, SET_USERINFO, SET_TOKEN, SET_SESSIONKEY, CLEAR_USERINFO, CLEAR_TOKEN, SET_ADMINUSERINFO, SET_SORTCODE } from '@/constants/userInfo'
import {DataParam, UserParam, UserAdminParam, SortCodeParam} from '@/reducers/userInfo' import { DataParam, UserParam, UserAdminParam, SortCodeParam } from '@/reducers/userInfo'
export default () => { export default () => {
const userInfo = useSelector((state:DataParam) => state.userInfo) as DataParam const userInfo = useSelector((state: DataParam) => state.userInfo) as DataParam
const dispatch = useDispatch() const dispatch = useDispatch()
const setToken = (token: string) => {
dispatch({type:SET_TOKEN, data:{token}})
}
const setSessionKey = (sessionkey: string) => { const setToken = (token: string) => {
dispatch({type:SET_SESSIONKEY, data:{session_key: sessionkey}}) dispatch({ type: SET_TOKEN, data: { token } })
} }
const setUserInfo = (userInfo: UserParam) => { const setSessionKey = (sessionkey: string) => {
dispatch({type:SET_USERINFO, data:{userInfo}}) dispatch({ type: SET_SESSIONKEY, data: { session_key: sessionkey } })
} }
const setAdminUserInfo = (adminUserInfo: UserAdminParam) => { const setUserInfo = (userInfo: UserParam) => {
dispatch({type:SET_ADMINUSERINFO, data:{adminUserInfo}}) dispatch({ type: SET_USERINFO, data: { userInfo } })
} }
const setSortCode = (sortCode:SortCodeParam) => { const setAdminUserInfo = (adminUserInfo: UserAdminParam) => {
dispatch({type:SET_SORTCODE, data:{sort_code:sortCode}}) dispatch({ type: SET_ADMINUSERINFO, data: { adminUserInfo } })
} }
const removeUserInfo = () => { const setSortCode = (sortCode: SortCodeParam) => {
dispatch({type:CLEAR_USERINFO}) dispatch({ type: SET_SORTCODE, data: { sort_code: sortCode } })
} }
const removeToken = () => { const removeUserInfo = () => {
dispatch({type:CLEAR_TOKEN}) dispatch({ type: CLEAR_USERINFO })
} }
const removeSessionKey = () => { const removeToken = () => {
dispatch({type:CLEAR_SESSIONKEY}) dispatch({ type: CLEAR_TOKEN })
} }
return { const removeSessionKey = () => {
setToken, dispatch({ type: CLEAR_SESSIONKEY })
setUserInfo, }
setAdminUserInfo,
setSessionKey, return {
removeUserInfo, setToken,
removeToken, setUserInfo,
removeSessionKey, setAdminUserInfo,
setSortCode, setSessionKey,
userInfo, //响应式数据返回 removeUserInfo,
} removeToken,
} removeSessionKey,
setSortCode,
userInfo, //响应式数据返回
}
}