新增点餐首页和商品列表的轮播图

This commit is contained in:
gyq
2025-12-10 09:13:23 +08:00
parent 1e43269f65
commit 43e9f544e4
4 changed files with 802 additions and 815 deletions

View File

@@ -1,13 +1,14 @@
<template> <template>
<view class="content"> <view class="content">
<view <view class="contentbox">
class="contentbox" <template v-if="shopExtend.length">
:style=" <image class="bg" :src="shopExtend[0].value" mode="aspectFill" v-if="!isJsonArrayString(shopExtend[0].value)"></image>
'background:url(' + <swiper class="swiper" autoplay circular v-else>
(shopExtend ? shopExtend[0].value : 'https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png') + <swiper-item class="swiper-item" v-for="item in JSON.parse(shopExtend[0].value)">
') no-repeat center center / cover' <image class="swiper-bg" :src="item"></image>
" </swiper-item>
> </swiper>
</template>
<view class="contentboxitem flex-between"> <view class="contentboxitem flex-between">
<view class="contentboxitemleft flex-colum" @click="scanCodehandle(0)"> <view class="contentboxitemleft flex-colum" @click="scanCodehandle(0)">
<image src="https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/Xdiancan.png" mode="aspectFill"></image> <image src="https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/Xdiancan.png" mode="aspectFill"></image>
@@ -61,6 +62,7 @@
<script setup> <script setup>
import { getMemberConfig, getRechargeConfig } from '@/common/api/index/index.js'; import { getMemberConfig, getRechargeConfig } from '@/common/api/index/index.js';
import { ref, reactive, defineProps, defineEmits, onMounted } from 'vue'; import { ref, reactive, defineProps, defineEmits, onMounted } from 'vue';
import { isJsonArrayString } from '@/utils/util.js';
// 定义接收的属性 // 定义接收的属性
const props = defineProps({ const props = defineProps({
@@ -175,13 +177,31 @@ defineExpose({
page { page {
background: #f6f8fa; background: #f6f8fa;
} }
.swiper {
width: 100%;
height: 100%;
.swiper-item {
width: 100%;
height: 100%;
.swiper-bg {
width: 100%;
height: 100%;
}
}
}
.content { .content {
.contentbox { .contentbox {
position: relative; position: relative;
width: 100%; width: 100%;
height: 1046rpx; height: 1046rpx;
padding: 0 24rpx; .bg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
.contentboxitem { .contentboxitem {
position: absolute; position: absolute;

View File

@@ -3,11 +3,22 @@
<Nav /> <Nav />
<!-- 顶部面板 --> <!-- 顶部面板 -->
<view class="top--panel" :class="{ grayscale: !isBusinessTime }"> <view class="top--panel" :class="{ grayscale: !isBusinessTime }">
<image <template v-if="shopExtend">
class="panelimgbackground" <image
:src="shopExtend ? shopExtend.value : 'https://czg-qr-order.oss-cn-beijing.aliyuncs.com/shopDetails/topBanner.png'" class="panelimgbackground"
mode="aspectFill" :src="shopExtend ? shopExtend.value : 'https://czg-qr-order.oss-cn-beijing.aliyuncs.com/shopDetails/topBanner.png'"
></image> mode="aspectFill"
v-if="!isJsonArrayString(shopExtend.value)"
></image>
<swiper class="panelimgbackground" autoplay indicator-dots circular v-else>
<swiper-item v-for="(item, index) in JSON.parse(shopExtend.value)" :key="index">
<image :src="item" mode="aspectFill"></image>
</swiper-item>
</swiper>
</template>
<template v-else>
<image class="panelimgbackground" src="https://czg-qr-order.oss-cn-beijing.aliyuncs.com/shopDetails/topBanner.png" mode="aspectFill"></image>
</template>
<view class="panelone flex-start"> <view class="panelone flex-start">
<view class="u-flex u-flex-y-center"> <view class="u-flex u-flex-y-center">
<text> <text>
@@ -556,7 +567,7 @@
import xbSwiperPreview from '@/components/xb-swiper-preview/index.vue'; import xbSwiperPreview from '@/components/xb-swiper-preview/index.vue';
//价格计算辅助函数 //价格计算辅助函数
// import * as limitUtils from '@/utils/order-utils.js'; // import * as limitUtils from '@/utils/order-utils.js';
import {limitUtils } from 'ysk-utils' import { limitUtils } from 'ysk-utils';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import GoodsPrice from '@/components/goods-price.vue'; import GoodsPrice from '@/components/goods-price.vue';
@@ -578,7 +589,7 @@ import Loading from '@/components/Loading.vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween'; import isBetween from 'dayjs/plugin/isBetween';
dayjs.extend(isBetween); dayjs.extend(isBetween);
import { filterNumberInput } from '@/utils/util.js'; import { filterNumberInput, isJsonArrayString } from '@/utils/util.js';
import { APIproductqueryShop } from '@/common/api/member.js'; import { APIproductqueryShop } from '@/common/api/member.js';
import { getDistance } from '@/utils/address.js'; import { getDistance } from '@/utils/address.js';
@@ -680,9 +691,18 @@ const callChildMethod = () => {
} }
}; };
try { // const shopExtend = ref(uni.cache.get('shopTable').shopExtendMap.shopinfo_bg);
const shopExtend = uni.cache.get('shopTable').shopExtendMap.shopinfo_bg; // console.log('shopExtend', shopExtend);
} catch (error) {}
// 分步取值 + 每层兜底,避免某一环不存在导致 undefined
const shopExtendShopTable = uni.cache.get('shopTable') || {}; // 兜底空对象
const shopExtendMap = shopExtendShopTable.shopExtendMap || {}; // 兜底空对象
// 最终声明:即使 shopinfo_bg 不存在,也兜底为空字符串/默认值
const shopExtend = ref(shopExtendMap.shopinfo_bg || '');
// try {
// const shopExtend = uni.cache.get('shopTable').shopExtendMap.shopinfo_bg;
// } catch (error) {}
// 计算高度 // 计算高度
const navScroll = ref(null); const navScroll = ref(null);
@@ -1342,7 +1362,7 @@ const singleclick = async (item, i) => {
} else if (item.suitNum >= cartNumberFloat && i === '-') { } else if (item.suitNum >= cartNumberFloat && i === '-') {
suitNum = item.cartNumber; suitNum = item.cartNumber;
} }
const sendMsg={ const sendMsg = {
type: 'shopping', type: 'shopping',
suitNum: item.suitNum, suitNum: item.suitNum,
table_code: uni.cache.get('tableCode'), table_code: uni.cache.get('tableCode'),
@@ -1355,9 +1375,9 @@ const singleclick = async (item, i) => {
is_print: 1, is_print: 1,
product_type: item.type, product_type: item.type,
is_time_discount: item.is_time_discount is_time_discount: item.is_time_discount
} };
if(item.cartListId){ if (item.cartListId) {
sendMsg.id=item.cartListId; sendMsg.id = item.cartListId;
} }
websocketsendMessage(sendMsg); websocketsendMessage(sendMsg);
}; };
@@ -1681,7 +1701,7 @@ const confirmorderref = ref(null);
// 结账 // 结账
const orderdetail = async () => { const orderdetail = async () => {
isAutoClose = false; isAutoClose = false;
uni.navigateTo({ uni.navigateTo({
url: '/pages/order/confirm-order?tableCode=' + uni.cache.get('tableCode') + '&shopId=' + uni.cache.get('shopId') url: '/pages/order/confirm-order?tableCode=' + uni.cache.get('tableCode') + '&shopId=' + uni.cache.get('shopId')
}); });
@@ -2102,7 +2122,7 @@ function returnLimitPrice(item) {
} }
onShow(async () => { onShow(async () => {
isAutoClose=true isAutoClose = true;
// 监听页面显示和隐藏 // 监听页面显示和隐藏
useSocket.setOnMessage(onMessage); useSocket.setOnMessage(onMessage);
useSocket.onShowconnect(); useSocket.onShowconnect();
@@ -2124,21 +2144,20 @@ onShow(async () => {
//是否自动关闭socket //是否自动关闭socket
let isAutoClose = true; let isAutoClose = true;
function closeSocket(){ function closeSocket() {
if(isAutoClose){ if (isAutoClose) {
useSocket.closeSocket(); useSocket.closeSocket();
} }
useSocket.setOnMessage(() => {}); useSocket.setOnMessage(() => {});
} }
onHide(() => { onHide(() => {
closeSocket() closeSocket();
}); });
onUnload(() => { onUnload(() => {
closeSocket() closeSocket();
}); });
const oldOrder=ref(null) const oldOrder = ref(null);
onMounted(async () => { onMounted(async () => {
await proxy.$onLaunched; await proxy.$onLaunched;
// 获取当前页面栈 // 获取当前页面栈
@@ -2192,12 +2211,12 @@ onMounted(async () => {
//跳转历史订单 //跳转历史订单
function toHistory() { function toHistory() {
isAutoClose = false; isAutoClose = false;
const tableCode = uni.cache.get('tableCode'); const tableCode = uni.cache.get('tableCode');
const shopId = uni.cache.get('shopId'); const shopId = uni.cache.get('shopId');
let url= `/pages/order/confirm-order?tableCode=${tableCode}&shopId=${shopId}` let url = `/pages/order/confirm-order?tableCode=${tableCode}&shopId=${shopId}`;
const res=oldOrder.value; const res = oldOrder.value;
console.log('toHistory',res); console.log('toHistory', res);
// if (res && res.id && shopInfo.registerType == 'after') { // if (res && res.id && shopInfo.registerType == 'after') {
// url+='&orderId='+res.id // url+='&orderId='+res.id
// } // }

823
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,17 +4,17 @@
* @returns {string} 脱敏后手机号 * @returns {string} 脱敏后手机号
*/ */
export function desensitizePhone(phone) { export function desensitizePhone(phone) {
// 1. 提取纯数字(过滤非数字字符) // 1. 提取纯数字(过滤非数字字符)
const purePhone = (phone || "").replace(/[^\d]/g, ""); const purePhone = (phone || "").replace(/[^\d]/g, "");
// 2. 边界判断非11位手机号返回原字符串或自定义提示 // 2. 边界判断非11位手机号返回原字符串或自定义提示
if (purePhone.length !== 11) { if (purePhone.length !== 11) {
console.warn("手机号格式不正确需11位纯数字"); console.warn("手机号格式不正确需11位纯数字");
return phone; // 或返回 ''、'手机号格式错误' 等 return phone; // 或返回 ''、'手机号格式错误' 等
} }
// 3. 脱敏前3位 + **** + 后4位 // 3. 脱敏前3位 + **** + 后4位
return purePhone.replace(/(\d{3})(\d{4})(\d{4})/, "$1****$3"); return purePhone.replace(/(\d{3})(\d{4})(\d{4})/, "$1****$3");
} }
/** /**
@@ -23,46 +23,46 @@ export function desensitizePhone(phone) {
* @returns {Object} 校验结果:{ valid: boolean, msg: string } * @returns {Object} 校验结果:{ valid: boolean, msg: string }
*/ */
export function validateName(name) { export function validateName(name) {
// 1. 空值校验 // 1. 空值校验
if (!name || name.trim() === "") { if (!name || name.trim() === "") {
return { return {
valid: false, valid: false,
msg: "姓名不能为空", msg: "姓名不能为空",
}; };
} }
const pureName = name.trim(); const pureName = name.trim();
// 2. 长度校验2-6位含少数民族中间点 // 2. 长度校验2-6位含少数民族中间点
if (pureName.length < 2 || pureName.length > 6) { if (pureName.length < 2 || pureName.length > 6) {
return { return {
valid: false, valid: false,
msg: "姓名长度应为2-6位", msg: "姓名长度应为2-6位",
}; };
} }
// 3. 正则校验:仅允许中文、少数民族中间点(·),且中间点不能在开头/结尾 // 3. 正则校验:仅允许中文、少数民族中间点(·),且中间点不能在开头/结尾
// 中文范围:[\u4e00-\u9fa5],中间点:[\u00b7]Unicode 标准中间点,非小数点) // 中文范围:[\u4e00-\u9fa5],中间点:[\u00b7]Unicode 标准中间点,非小数点)
const nameReg = /^[\u4e00-\u9fa5]+([\u00b7][\u4e00-\u9fa5]+)*$/; const nameReg = /^[\u4e00-\u9fa5]+([\u00b7][\u4e00-\u9fa5]+)*$/;
if (!nameReg.test(pureName)) { if (!nameReg.test(pureName)) {
return { return {
valid: false, valid: false,
msg: "姓名仅支持中文和少数民族中间点(·),且不能包含数字、字母或特殊符号", msg: "姓名仅支持中文和少数民族中间点(·),且不能包含数字、字母或特殊符号",
}; };
} }
// 4. 额外限制:中间点不能连续(如“李··四”) // 4. 额外限制:中间点不能连续(如“李··四”)
if (/[\u00b7]{2,}/.test(pureName)) { if (/[\u00b7]{2,}/.test(pureName)) {
return { return {
valid: false, valid: false,
msg: "姓名中的中间点(·)不能连续", msg: "姓名中的中间点(·)不能连续",
}; };
} }
// 校验通过 // 校验通过
return { return {
valid: true, valid: true,
msg: "姓名格式合法", msg: "姓名格式合法",
}; };
} }
/** /**
@@ -72,117 +72,117 @@ export function validateName(name) {
* info 可选返回:{ birthDate: string, gender: string }(出生日期、性别) * info 可选返回:{ birthDate: string, gender: string }(出生日期、性别)
*/ */
export function validateIdCard(idCard) { export function validateIdCard(idCard) {
// 1. 空值校验 // 1. 空值校验
if (!idCard || idCard.trim() === "") { if (!idCard || idCard.trim() === "") {
return { return {
valid: false, valid: false,
msg: "身份证号码不能为空", msg: "身份证号码不能为空",
}; };
} }
const pureIdCard = idCard.trim().toUpperCase(); // 统一转为大写处理X const pureIdCard = idCard.trim().toUpperCase(); // 统一转为大写处理X
// 2. 格式校验18位或15位 // 2. 格式校验18位或15位
const id18Reg = const id18Reg =
/^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([12]\d)|(3[01]))\d{3}([0-9]|X)$/; /^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([12]\d)|(3[01]))\d{3}([0-9]|X)$/;
const id15Reg = const id15Reg =
/^[1-9]\d{5}\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([12]\d)|(3[01]))\d{3}$/; /^[1-9]\d{5}\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([12]\d)|(3[01]))\d{3}$/;
if (!id18Reg.test(pureIdCard) && !id15Reg.test(pureIdCard)) { if (!id18Reg.test(pureIdCard) && !id15Reg.test(pureIdCard)) {
return { return {
valid: false, valid: false,
msg: "身份证号码格式错误需18位最后一位可含X或15位纯数字", msg: "身份证号码格式错误需18位最后一位可含X或15位纯数字",
}; };
} }
// 3. 提取出生日期并校验合法性 // 3. 提取出生日期并校验合法性
let birthDateStr, birthDate; let birthDateStr, birthDate;
if (pureIdCard.length === 18) { if (pureIdCard.length === 18) {
// 18位第7-14位为出生日期YYYYMMDD // 18位第7-14位为出生日期YYYYMMDD
birthDateStr = pureIdCard.slice(6, 14); birthDateStr = pureIdCard.slice(6, 14);
birthDate = new Date( birthDate = new Date(
`${birthDateStr.slice(0, 4)}-${birthDateStr.slice( `${birthDateStr.slice(0, 4)}-${birthDateStr.slice(
4, 4,
6 6
)}-${birthDateStr.slice(6, 8)}` )}-${birthDateStr.slice(6, 8)}`
); );
} else { } else {
// 15位第7-12位为出生日期YYMMDD补全为YYYYMMDD19xx或20xx默认19xx // 15位第7-12位为出生日期YYMMDD补全为YYYYMMDD19xx或20xx默认19xx
const year = `19${pureIdCard.slice(6, 8)}`; const year = `19${pureIdCard.slice(6, 8)}`;
const month = pureIdCard.slice(8, 10); const month = pureIdCard.slice(8, 10);
const day = pureIdCard.slice(10, 12); const day = pureIdCard.slice(10, 12);
birthDateStr = `${year}${month}${day}`; birthDateStr = `${year}${month}${day}`;
birthDate = new Date(`${year}-${month}-${day}`); birthDate = new Date(`${year}-${month}-${day}`);
} }
// 校验出生日期有效性如20230230 → 日期对象会是Invalid Date // 校验出生日期有效性如20230230 → 日期对象会是Invalid Date
if ( if (
isNaN(birthDate.getTime()) || isNaN(birthDate.getTime()) ||
birthDateStr.slice(0, 4) !== birthDate.getFullYear().toString() || birthDateStr.slice(0, 4) !== birthDate.getFullYear().toString() ||
birthDateStr.slice(4, 6) !== birthDateStr.slice(4, 6) !==
(birthDate.getMonth() + 1).toString().padStart(2, "0") || (birthDate.getMonth() + 1).toString().padStart(2, "0") ||
birthDateStr.slice(6, 8) !== birthDate.getDate().toString().padStart(2, "0") birthDateStr.slice(6, 8) !== birthDate.getDate().toString().padStart(2, "0")
) { ) {
return { return {
valid: false, valid: false,
msg: "身份证中的出生日期无效", msg: "身份证中的出生日期无效",
}; };
} }
// 4. 18位身份证额外校验校验码合法性加权算法 // 4. 18位身份证额外校验校验码合法性加权算法
if (pureIdCard.length === 18) { if (pureIdCard.length === 18) {
// 加权因子 // 加权因子
const weightFactors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; const weightFactors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校验码对应值0-10 → 10对应X // 校验码对应值0-10 → 10对应X
const checkCodeMap = [ const checkCodeMap = [
"1", "1",
"0", "0",
"X", "X",
"9", "9",
"8", "8",
"7", "7",
"6", "6",
"5", "5",
"4", "4",
"3", "3",
"2", "2",
]; ];
// 计算前17位与加权因子的乘积和 // 计算前17位与加权因子的乘积和
let sum = 0; let sum = 0;
for (let i = 0; i < 17; i++) { for (let i = 0; i < 17; i++) {
sum += parseInt(pureIdCard[i]) * weightFactors[i]; sum += parseInt(pureIdCard[i]) * weightFactors[i];
} }
// 计算预期校验码 // 计算预期校验码
const expectedCheckCode = checkCodeMap[sum % 11]; const expectedCheckCode = checkCodeMap[sum % 11];
// 对比实际校验码(最后一位) // 对比实际校验码(最后一位)
if (pureIdCard[17] !== expectedCheckCode) { if (pureIdCard[17] !== expectedCheckCode) {
return { return {
valid: false, valid: false,
msg: "身份证校验码错误,可能是无效身份证", msg: "身份证校验码错误,可能是无效身份证",
}; };
} }
} }
// 5. 可选提取性别18位第17位15位第15位奇数=男,偶数=女) // 5. 可选提取性别18位第17位15位第15位奇数=男,偶数=女)
let gender = ""; let gender = "";
if (pureIdCard.length === 18) { if (pureIdCard.length === 18) {
const genderCode = parseInt(pureIdCard[16]); const genderCode = parseInt(pureIdCard[16]);
gender = genderCode % 2 === 1 ? "男" : "女"; gender = genderCode % 2 === 1 ? "男" : "女";
} else { } else {
const genderCode = parseInt(pureIdCard[14]); const genderCode = parseInt(pureIdCard[14]);
gender = genderCode % 2 === 1 ? "男" : "女"; gender = genderCode % 2 === 1 ? "男" : "女";
} }
// 校验通过,返回额外信息(出生日期、性别) // 校验通过,返回额外信息(出生日期、性别)
return { return {
valid: true, valid: true,
msg: "身份证号码合法", msg: "身份证号码合法",
info: { info: {
birthDate: `${birthDate.getFullYear()}-${(birthDate.getMonth() + 1) birthDate: `${birthDate.getFullYear()}-${(birthDate.getMonth() + 1)
.toString() .toString()
.padStart(2, "0")}-${birthDate.getDate().toString().padStart(2, "0")}`, .padStart(2, "0")}-${birthDate.getDate().toString().padStart(2, "0")}`,
gender: gender, gender: gender,
}, },
}; };
} }
/** /**
@@ -192,46 +192,46 @@ export function validateIdCard(idCard) {
* @returns {string} 过滤后的合法值 * @returns {string} 过滤后的合法值
*/ */
export function filterNumberInput(value, isIntegerOnly = false) { export function filterNumberInput(value, isIntegerOnly = false) {
// 第一步就过滤所有非数字和非小数点的字符(包括字母) // 第一步就过滤所有非数字和非小数点的字符(包括字母)
let filtered = value.replace(/[^\d.]/g, ""); let filtered = value.replace(/[^\d.]/g, "");
// 整数模式处理 // 整数模式处理
if (isIntegerOnly !== false) { if (isIntegerOnly !== false) {
// 移除所有小数点 // 移除所有小数点
filtered = filtered.replace(/\./g, ""); filtered = filtered.replace(/\./g, "");
// 处理前导零 // 处理前导零
filtered = filtered.replace(/^0+(\d)/, "$1") || filtered; filtered = filtered.replace(/^0+(\d)/, "$1") || filtered;
// 空值处理(允许临时删除) // 空值处理(允许临时删除)
if (filtered === "") { if (filtered === "") {
return ""; return "";
} }
// 最小值限制 // 最小值限制
if (filtered === isIntegerOnly || parseInt(filtered, 10) < isIntegerOnly) { if (filtered === isIntegerOnly || parseInt(filtered, 10) < isIntegerOnly) {
return isIntegerOnly; return isIntegerOnly;
} }
return filtered; return filtered;
} }
// 小数模式处理 // 小数模式处理
const parts = filtered.split("."); const parts = filtered.split(".");
if (parts.length > 1) { if (parts.length > 1) {
filtered = parts[0] + "." + (parts[1].substring(0, 2) || ""); filtered = parts[0] + "." + (parts[1].substring(0, 2) || "");
} }
// 处理前导零 // 处理前导零
if ( if (
filtered.startsWith("0") && filtered.startsWith("0") &&
filtered.length > 1 && filtered.length > 1 &&
!filtered.startsWith("0.") !filtered.startsWith("0.")
) { ) {
filtered = filtered.replace(/^0+(\d)/, "$1"); filtered = filtered.replace(/^0+(\d)/, "$1");
} }
return filtered; return filtered;
} }
/** /**
@@ -243,157 +243,194 @@ export function filterNumberInput(value, isIntegerOnly = false) {
* - platform: 当前平台 * - platform: 当前平台
*/ */
export function checkPermission(permissionType) { export function checkPermission(permissionType) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 获取当前平台信息 // 获取当前平台信息
const systemInfo = uni.getSystemInfoSync(); const systemInfo = uni.getSystemInfoSync();
const platform = systemInfo.platform; // 'android'|'ios'|'devtools'(小程序模拟器) const platform = systemInfo.platform; // 'android'|'ios'|'devtools'(小程序模拟器)
const env = process.env.NODE_ENV; // 环境变量(用于区分小程序/H5/App const env = process.env.NODE_ENV; // 环境变量(用于区分小程序/H5/App
// 权限类型映射表:将通用权限名称映射到各平台具体权限名 // 权限类型映射表:将通用权限名称映射到各平台具体权限名
const permissionMap = { const permissionMap = {
// 通用权限名称: { 平台: 平台权限名 } // 通用权限名称: { 平台: 平台权限名 }
camera: { camera: {
weixin: "scope.camera", // 微信小程序 weixin: "scope.camera", // 微信小程序
alipay: "scope.camera", // 支付宝小程序 alipay: "scope.camera", // 支付宝小程序
android: "android.permission.CAMERA", // App-Android android: "android.permission.CAMERA", // App-Android
ios: "camera", // App-iOS简化处理实际需调用原生 API ios: "camera", // App-iOS简化处理实际需调用原生 API
h5: "camera", // H5 浏览器权限 h5: "camera", // H5 浏览器权限
}, },
location: { location: {
weixin: "scope.userLocation", weixin: "scope.userLocation",
alipay: "scope.userLocation", alipay: "scope.userLocation",
android: "android.permission.ACCESS_FINE_LOCATION", android: "android.permission.ACCESS_FINE_LOCATION",
ios: "location", ios: "location",
h5: "geolocation", h5: "geolocation",
}, },
microphone: { microphone: {
weixin: "scope.record", weixin: "scope.record",
alipay: "scope.record", alipay: "scope.record",
android: "android.permission.RECORD_AUDIO", android: "android.permission.RECORD_AUDIO",
ios: "microphone", ios: "microphone",
h5: "microphone", h5: "microphone",
}, },
album: { album: {
weixin: "scope.writePhotosAlbum", weixin: "scope.writePhotosAlbum",
alipay: "scope.album", alipay: "scope.album",
android: "android.permission.WRITE_EXTERNAL_STORAGE", android: "android.permission.WRITE_EXTERNAL_STORAGE",
ios: "photoLibrary", ios: "photoLibrary",
h5: "clipboard-write", // H5 相册权限支持有限 h5: "clipboard-write", // H5 相册权限支持有限
}, },
// 可根据需求扩展更多权限类型 // 可根据需求扩展更多权限类型
}; };
// 获取当前平台对应的权限名 // 获取当前平台对应的权限名
const getPlatformPermission = () => { const getPlatformPermission = () => {
if (typeof wx !== "undefined" && wx.getSetting) if (typeof wx !== "undefined" && wx.getSetting)
return permissionMap[permissionType]?.weixin; // 微信小程序 return permissionMap[permissionType]?.weixin; // 微信小程序
if (typeof my !== "undefined" && my.getSetting) if (typeof my !== "undefined" && my.getSetting)
return permissionMap[permissionType]?.alipay; // 支付宝小程序 return permissionMap[permissionType]?.alipay; // 支付宝小程序
if (platform === "android") return permissionMap[permissionType]?.android; // App-Android if (platform === "android") return permissionMap[permissionType]?.android; // App-Android
if (platform === "ios") return permissionMap[permissionType]?.ios; // App-iOS if (platform === "ios") return permissionMap[permissionType]?.ios; // App-iOS
return permissionMap[permissionType]?.h5; // H5 return permissionMap[permissionType]?.h5; // H5
}; };
const platformPermission = getPlatformPermission(); const platformPermission = getPlatformPermission();
if (!platformPermission) { if (!platformPermission) {
return reject(new Error(`不支持的权限类型: ${permissionType}`)); return reject(new Error(`不支持的权限类型: ${permissionType}`));
} }
// ----------------- 分平台处理 ----------------- // ----------------- 分平台处理 -----------------
// 1. 微信/支付宝小程序 // 1. 微信/支付宝小程序
if (typeof wx !== "undefined" && wx.getSetting) { if (typeof wx !== "undefined" && wx.getSetting) {
uni.getSetting({ uni.getSetting({
success: (res) => { success: (res) => {
const authSetting = res.authSetting || {}; const authSetting = res.authSetting || {};
const isGranted = authSetting[platformPermission] === true; const isGranted = authSetting[platformPermission] === true;
const status = isGranted const status = isGranted ?
? "granted" "granted" :
: authSetting[platformPermission] === false authSetting[platformPermission] === false ?
? "denied" "denied" :
: "undetermined"; "undetermined";
resolve({ granted: isGranted, status, platform: "weixin" }); resolve({
}, granted: isGranted,
fail: (err) => reject(err), status,
}); platform: "weixin"
} });
// 2. App-Android 平台 },
else if (platform === "android" && typeof plus !== "undefined") { fail: (err) => reject(err),
try { });
const Context = plus.android.importClass("android.content.Context"); }
const Activity = plus.android.runtimeMainActivity(); // 2. App-Android 平台
const PackageManager = plus.android.importClass( else if (platform === "android" && typeof plus !== "undefined") {
"android.content.pm.PackageManager" try {
); const Context = plus.android.importClass("android.content.Context");
const Activity = plus.android.runtimeMainActivity();
const PackageManager = plus.android.importClass(
"android.content.pm.PackageManager"
);
// 检查权限状态 // 检查权限状态
const granted = const granted =
Activity.checkSelfPermission(platformPermission) === Activity.checkSelfPermission(platformPermission) ===
PackageManager.PERMISSION_GRANTED; PackageManager.PERMISSION_GRANTED;
resolve({ resolve({
granted, granted,
status: granted ? "granted" : "denied", status: granted ? "granted" : "denied",
platform: "android", platform: "android",
}); });
} catch (err) { } catch (err) {
reject(err); reject(err);
} }
} }
// 3. App-iOS 平台 // 3. App-iOS 平台
else if (platform === "ios" && typeof plus !== "undefined") { else if (platform === "ios" && typeof plus !== "undefined") {
try { try {
// iOS 权限需通过原生 API 检查(以相机为例,其他权限类似) // iOS 权限需通过原生 API 检查(以相机为例,其他权限类似)
const result = { granted: false, status: "denied", platform: "ios" }; const result = {
granted: false,
status: "denied",
platform: "ios"
};
if (permissionType === "camera") { if (permissionType === "camera") {
// 相机权限检查iOS 需调用 AVCaptureDevice // 相机权限检查iOS 需调用 AVCaptureDevice
const AVCaptureDevice = plus.ios.importClass("AVCaptureDevice"); const AVCaptureDevice = plus.ios.importClass("AVCaptureDevice");
const authStatus = const authStatus =
AVCaptureDevice.authorizationStatusForMediaType("vide"); AVCaptureDevice.authorizationStatusForMediaType("vide");
// AVCaptureDeviceAuthorizationStatus 枚举值: // AVCaptureDeviceAuthorizationStatus 枚举值:
// 0: notDetermined未请求, 1: restricted受限制, 2: denied拒绝, 3: authorized授权 // 0: notDetermined未请求, 1: restricted受限制, 2: denied拒绝, 3: authorized授权
result.granted = authStatus === 3; result.granted = authStatus === 3;
result.status = result.status =
authStatus === 3 authStatus === 3 ?
? "granted" "granted" :
: authStatus === 0 authStatus === 0 ?
? "undetermined" "undetermined" :
: "denied"; "denied";
} }
// 可扩展其他 iOS 权限(如定位、麦克风等) // 可扩展其他 iOS 权限(如定位、麦克风等)
else if (permissionType === "location") { else if (permissionType === "location") {
const CLLocationManager = plus.ios.importClass("CLLocationManager"); const CLLocationManager = plus.ios.importClass("CLLocationManager");
const authStatus = CLLocationManager.authorizationStatus(); const authStatus = CLLocationManager.authorizationStatus();
// CLAuthorizationStatus 枚举值: // CLAuthorizationStatus 枚举值:
// 0: notDetermined, 1: restricted, 2: denied, 3: authorizedAlways, 4: authorizedWhenInUse // 0: notDetermined, 1: restricted, 2: denied, 3: authorizedAlways, 4: authorizedWhenInUse
result.granted = authStatus === 3 || authStatus === 4; result.granted = authStatus === 3 || authStatus === 4;
result.status = result.granted result.status = result.granted ?
? "granted" "granted" :
: authStatus === 0 authStatus === 0 ?
? "undetermined" "undetermined" :
: "denied"; "denied";
} }
resolve(result); resolve(result);
} catch (err) { } catch (err) {
reject(err); reject(err);
} }
} }
// 4. H5 平台(浏览器权限) // 4. H5 平台(浏览器权限)
else if (typeof navigator !== "undefined" && navigator.permissions) { else if (typeof navigator !== "undefined" && navigator.permissions) {
navigator.permissions navigator.permissions
.query({ name: platformPermission }) .query({
.then((permissionStatus) => { name: platformPermission
resolve({ })
granted: permissionStatus.state === "granted", .then((permissionStatus) => {
status: permissionStatus.state, // 'granted'|'denied'|'prompt' resolve({
platform: "h5", granted: permissionStatus.state === "granted",
}); status: permissionStatus.state, // 'granted'|'denied'|'prompt'
}) platform: "h5",
.catch((err) => reject(err)); });
} })
// 5. 其他平台(如百度小程序、字节跳动小程序等,可扩展) .catch((err) => reject(err));
else { }
reject(new Error(`当前平台不支持权限判断: ${platform}`)); // 5. 其他平台(如百度小程序、字节跳动小程序等,可扩展)
} else {
}); reject(new Error(`当前平台不支持权限判断: ${platform}`));
}
});
} }
/**
* 判断字符串是否为合法的 JSON 数组
* @param {string} str - 待判断的字符串
* @returns {boolean} true=是JSON数组字符串 / false=普通字符串/其他
*/
export function isJsonArrayString(str) {
// 1. 非字符串直接返回 false
if (typeof str !== 'string') {
return false;
}
// 2. 空字符串返回 false根据业务可调整
if (str.trim() === '') {
return false;
}
try {
// 3. 尝试解析 JSON
const parsed = JSON.parse(str);
// 4. 校验解析结果是否为数组
return Array.isArray(parsed);
} catch (e) {
// 解析失败(普通字符串/非法 JSON→ 返回 false
return false;
}
}