新增点餐首页和商品列表的轮播图
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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
823
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
681
utils/util.js
681
utils/util.js
@@ -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),补全为YYYYMMDD(19xx或20xx,默认19xx)
|
// 15位:第7-12位为出生日期(YYMMDD),补全为YYYYMMDD(19xx或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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user