完成飞猪携程机
|
|
@ -7,7 +7,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"customPlaygroundType" : "local",
|
"customPlaygroundType" : "local",
|
||||||
"playground" : "standard",
|
"playground" : "custom",
|
||||||
"type" : "uni-app:app-android"
|
"type" : "uni-app:app-android"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
15
App.vue
|
|
@ -11,7 +11,10 @@ export default {
|
||||||
console.log('启动参数:', JSON.stringify(options))
|
console.log('启动参数:', JSON.stringify(options))
|
||||||
console.log('环境:', process.env.NODE_ENV)
|
console.log('环境:', process.env.NODE_ENV)
|
||||||
|
|
||||||
|
|
||||||
uni.setStorageSync('onNativeEventReceive', "no")
|
uni.setStorageSync('onNativeEventReceive', "no")
|
||||||
|
// 清除上次遗留的跳转指令,防止正常启动时自动跳转
|
||||||
|
uni.removeStorageSync('jumpTarget_url');
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
// 1. 获取并存储系统信息(只获取一次)
|
// 1. 获取并存储系统信息(只获取一次)
|
||||||
|
|
@ -91,15 +94,13 @@ export default {
|
||||||
}
|
}
|
||||||
} else if (event == "jump") {
|
} else if (event == "jump") {
|
||||||
if (data) {
|
if (data) {
|
||||||
let pages = getCurrentPages();
|
console.log('接收到跳转指令,已缓存目标地址:', data);
|
||||||
let currentPage = pages[pages.length - 1];
|
uni.setStorageSync('jumpTarget_url', data);
|
||||||
let currentUrl = currentPage.route;
|
// 强制重定向到首页,触发onShow获取用户信息后再跳转
|
||||||
if (currentUrl != data) {
|
uni.reLaunch({
|
||||||
uni.navigateTo({
|
url: '/pages/index/index'
|
||||||
url: '/' + data
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
|
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
|
||||||
this.globalData.recentNativeEvent = event
|
this.globalData.recentNativeEvent = event
|
||||||
this.globalData.recentNativeData = data
|
this.globalData.recentNativeData = data
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,530 @@
|
||||||
|
/************************************************************
|
||||||
|
** 请将全局样式拷贝到项目的全局 CSS 文件或者当前页面的顶部 **
|
||||||
|
** 否则页面将无法正常显示 **
|
||||||
|
************************************************************/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
|
||||||
|
'Droid Sans', 'Helvetica Neue', 'Microsoft Yahei', sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
view,
|
||||||
|
image,
|
||||||
|
text {
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-flex-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-justify-start {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-justify-end {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-justify-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-justify-around {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-justify-evenly {
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-items-start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-items-end {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-items-baseline {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-items-stretch {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-self-start {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-self-end {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-self-center {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-self-baseline {
|
||||||
|
align-self: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-self-stretch {
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-flex-1 {
|
||||||
|
flex: 1 1 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-flex-auto {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-grow-0 {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-shrink {
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-shrink-0 {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-relative {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-2 {
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-2 {
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-4 {
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-4 {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-6 {
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-6 {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-8 {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-8 {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-10 {
|
||||||
|
margin-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-10 {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-12 {
|
||||||
|
margin-left: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-12 {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-14 {
|
||||||
|
margin-left: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-14 {
|
||||||
|
margin-top: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-16 {
|
||||||
|
margin-left: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-16 {
|
||||||
|
margin-top: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-18 {
|
||||||
|
margin-left: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-18 {
|
||||||
|
margin-top: 36rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-20 {
|
||||||
|
margin-left: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-20 {
|
||||||
|
margin-top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-22 {
|
||||||
|
margin-left: 44rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-22 {
|
||||||
|
margin-top: 44rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-24 {
|
||||||
|
margin-left: 48rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-24 {
|
||||||
|
margin-top: 48rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-26 {
|
||||||
|
margin-left: 52rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-26 {
|
||||||
|
margin-top: 52rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-28 {
|
||||||
|
margin-left: 56rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-28 {
|
||||||
|
margin-top: 56rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-30 {
|
||||||
|
margin-left: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-30 {
|
||||||
|
margin-top: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-32 {
|
||||||
|
margin-left: 64rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-32 {
|
||||||
|
margin-top: 64rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-34 {
|
||||||
|
margin-left: 68rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-34 {
|
||||||
|
margin-top: 68rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-36 {
|
||||||
|
margin-left: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-36 {
|
||||||
|
margin-top: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-38 {
|
||||||
|
margin-left: 76rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-38 {
|
||||||
|
margin-top: 76rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-40 {
|
||||||
|
margin-left: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-40 {
|
||||||
|
margin-top: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-42 {
|
||||||
|
margin-left: 84rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-42 {
|
||||||
|
margin-top: 84rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-44 {
|
||||||
|
margin-left: 88rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-44 {
|
||||||
|
margin-top: 88rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-46 {
|
||||||
|
margin-left: 92rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-46 {
|
||||||
|
margin-top: 92rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-48 {
|
||||||
|
margin-left: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-48 {
|
||||||
|
margin-top: 96rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-50 {
|
||||||
|
margin-left: 100rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-50 {
|
||||||
|
margin-top: 100rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-52 {
|
||||||
|
margin-left: 104rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-52 {
|
||||||
|
margin-top: 104rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-54 {
|
||||||
|
margin-left: 108rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-54 {
|
||||||
|
margin-top: 108rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-56 {
|
||||||
|
margin-left: 112rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-56 {
|
||||||
|
margin-top: 112rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-58 {
|
||||||
|
margin-left: 116rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-58 {
|
||||||
|
margin-top: 116rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-60 {
|
||||||
|
margin-left: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-60 {
|
||||||
|
margin-top: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-62 {
|
||||||
|
margin-left: 124rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-62 {
|
||||||
|
margin-top: 124rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-64 {
|
||||||
|
margin-left: 128rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-64 {
|
||||||
|
margin-top: 128rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-66 {
|
||||||
|
margin-left: 132rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-66 {
|
||||||
|
margin-top: 132rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-68 {
|
||||||
|
margin-left: 136rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-68 {
|
||||||
|
margin-top: 136rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-70 {
|
||||||
|
margin-left: 140rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-70 {
|
||||||
|
margin-top: 140rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-72 {
|
||||||
|
margin-left: 144rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-72 {
|
||||||
|
margin-top: 144rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-74 {
|
||||||
|
margin-left: 148rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-74 {
|
||||||
|
margin-top: 148rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-76 {
|
||||||
|
margin-left: 152rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-76 {
|
||||||
|
margin-top: 152rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-78 {
|
||||||
|
margin-left: 156rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-78 {
|
||||||
|
margin-top: 156rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-80 {
|
||||||
|
margin-left: 160rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-80 {
|
||||||
|
margin-top: 160rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-82 {
|
||||||
|
margin-left: 164rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-82 {
|
||||||
|
margin-top: 164rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-84 {
|
||||||
|
margin-left: 168rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-84 {
|
||||||
|
margin-top: 168rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-86 {
|
||||||
|
margin-left: 172rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-86 {
|
||||||
|
margin-top: 172rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-88 {
|
||||||
|
margin-left: 176rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-88 {
|
||||||
|
margin-top: 176rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-90 {
|
||||||
|
margin-left: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-90 {
|
||||||
|
margin-top: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-92 {
|
||||||
|
margin-left: 184rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-92 {
|
||||||
|
margin-top: 184rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-94 {
|
||||||
|
margin-left: 188rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-94 {
|
||||||
|
margin-top: 188rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-96 {
|
||||||
|
margin-left: 192rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-96 {
|
||||||
|
margin-top: 192rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-98 {
|
||||||
|
margin-left: 196rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-98 {
|
||||||
|
margin-top: 196rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-ml-100 {
|
||||||
|
margin-left: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codefun-mt-100 {
|
||||||
|
margin-top: 200rpx;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<!-- 底部弹出层 -->
|
<!-- 底部弹出层 -->
|
||||||
<uni-popup ref="timepopup" type="bottom">
|
<uni-popup ref="timepopup" type="bottom" :safe-area="false">
|
||||||
<!-- 账单分类 -->
|
<!-- 账单分类 -->
|
||||||
<view v-if="data.popupType == 'billClassify'" class="bill-classify-box">
|
<view v-if="data.popupType == 'billClassify'" class="bill-classify-box">
|
||||||
<view class="title-box">
|
<view class="title-box">
|
||||||
|
|
@ -188,6 +188,9 @@ defineExpose({
|
||||||
@import "@/common/specify-style.less";
|
@import "@/common/specify-style.less";
|
||||||
|
|
||||||
.bill-classify-box {
|
.bill-classify-box {
|
||||||
|
padding-bottom: calc(32rpx + constant(safe-area-inset-bottom)) !important;
|
||||||
|
padding-bottom: calc(32rpx + env(safe-area-inset-bottom)) !important;
|
||||||
|
|
||||||
|
|
||||||
.tag-content {
|
.tag-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<view style="width: 100%;" :style="{ height: `calc(${data.statusBarHeight}px + 88rpx)` }"></view>
|
<view style="width: 100%;" :style="{ height: `calc(${data.statusBarHeight}px + 88rpx)` }">
|
||||||
|
<slot name="statusBar"></slot>
|
||||||
|
</view>
|
||||||
<view class="nav-bar-container" :style="{ backgroundColor: bgColor, zIndex: zIndex }">
|
<view class="nav-bar-container" :style="{ backgroundColor: bgColor, zIndex: zIndex }">
|
||||||
<view class="status-placeholder" :style="{ height: `${data.statusBarHeight}px` }"></view>
|
<view class="status-placeholder" :style="{ height: `${data.statusBarHeight}px` }"></view>
|
||||||
<view style="width: 100%;height: 88rpx;" @click="openPopup">
|
<view style="width: 100%;height: 88rpx;" @click="openPopup">
|
||||||
|
|
|
||||||
2
main.js
|
|
@ -27,7 +27,7 @@ export function createApp() {
|
||||||
const systemInfo = uni.getStorageSync('systemInfo') || {}
|
const systemInfo = uni.getStorageSync('systemInfo') || {}
|
||||||
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
|
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
|
||||||
app.config.globalProperties.$systemInfo = systemInfo
|
app.config.globalProperties.$systemInfo = systemInfo
|
||||||
uni.setStorageSync('version', '1.0.1')
|
uni.setStorageSync('version', '1.0.1.sp5')
|
||||||
app.config.globalProperties.$version = uni.getStorageSync('version')
|
app.config.globalProperties.$version = uni.getStorageSync('version')
|
||||||
|
|
||||||
app.use(globalMethods);
|
app.use(globalMethods);
|
||||||
|
|
|
||||||
39
pages.json
|
|
@ -9,9 +9,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subPackages": [{
|
"subPackages": [
|
||||||
|
{
|
||||||
"root": "pages/balance",
|
"root": "pages/balance",
|
||||||
"pages": [{
|
"pages": [
|
||||||
|
{
|
||||||
"path": "index",
|
"path": "index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "余额页面",
|
"navigationBarTitleText": "余额页面",
|
||||||
|
|
@ -30,7 +32,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/bill",
|
"root": "pages/bill",
|
||||||
"pages": [{
|
"pages": [
|
||||||
|
{
|
||||||
"path": "bill-list/bill-list",
|
"path": "bill-list/bill-list",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "账单列表页面",
|
"navigationBarTitleText": "账单列表页面",
|
||||||
|
|
@ -55,7 +58,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/ant-credit-pay",
|
"root": "pages/ant-credit-pay",
|
||||||
"pages": [{
|
"pages": [
|
||||||
|
{
|
||||||
"path": "index",
|
"path": "index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "花呗首页",
|
"navigationBarTitleText": "花呗首页",
|
||||||
|
|
@ -73,7 +77,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/finance-management",
|
"root": "pages/finance-management",
|
||||||
"pages": [{
|
"pages": [
|
||||||
|
{
|
||||||
"path": "index",
|
"path": "index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "理财首页",
|
"navigationBarTitleText": "理财首页",
|
||||||
|
|
@ -98,7 +103,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/other",
|
"root": "pages/other",
|
||||||
"pages": [{
|
"pages": [
|
||||||
|
{
|
||||||
"path": "/video-group-chat/video-group-chat",
|
"path": "/video-group-chat/video-group-chat",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "视频群聊",
|
"navigationBarTitleText": "视频群聊",
|
||||||
|
|
@ -145,17 +151,32 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tickets-app/index",
|
"path": "tickets-app/index",
|
||||||
"style" :
|
"style": {
|
||||||
{
|
|
||||||
"navigationBarTitleText": "选择机票/火车票平台",
|
"navigationBarTitleText": "选择机票/火车票平台",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "air-tickets/fliggy-air-tickets/fliggy-air-tickets",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "飞猪飞机票",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "air-tickets/ctrip-air-tickets/ctrip-air-tickets",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "携程飞机票",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTextStyle": "white"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/common",
|
"root": "pages/common",
|
||||||
"pages": [{
|
"pages": [
|
||||||
|
{
|
||||||
"path": "hot-icon/hot-icon",
|
"path": "hot-icon/hot-icon",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "热门图标",
|
"navigationBarTitleText": "热门图标",
|
||||||
|
|
|
||||||
|
|
@ -670,7 +670,7 @@ const goBack = () => {
|
||||||
|
|
||||||
.overdue-info {
|
.overdue-info {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 87%;
|
||||||
margin: 0 48rpx;
|
margin: 0 48rpx;
|
||||||
background: linear-gradient(180deg, #FFF5F4 0%, #FFFBF7 100%);
|
background: linear-gradient(180deg, #FFF5F4 0%, #FFFBF7 100%);
|
||||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||||
|
|
@ -707,6 +707,7 @@ const goBack = () => {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin: 0 90rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,19 @@
|
||||||
@click="onClickItemInfo(item)">
|
@click="onClickItemInfo(item)">
|
||||||
</image>
|
</image>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 条形码 -->
|
||||||
|
<view v-if="item.key == 'merchantOrderNumber' && billData.merchantOption.isShowBarcode"
|
||||||
|
style="background-color: #FFFFFF;padding:0 2rem;">
|
||||||
|
<view class="barcode-box"
|
||||||
|
style="display: flex; justify-content: center;flex-direction: column; align-items: center;">
|
||||||
|
<l-barcode class="barcode" :lineLength="50" :font-size="12" text-align="center"
|
||||||
|
text-position="bottom" color="#1A1A1A" :text="item.value"
|
||||||
|
:displayValue='false'></l-barcode>
|
||||||
|
<text style="font-size: 24rpx;margin-top: -10rpx; z-index: 1;color: #969696;">{{
|
||||||
|
item.value.slice(0,
|
||||||
|
4) }}******点击查看订单号</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
<view v-if="item.type == 'link'" class="info-item-link">
|
<view v-if="item.type == 'link'" class="info-item-link">
|
||||||
<view class="img-box" @click="onClickItemInfo(item, 'uploadImage')">
|
<view class="img-box" @click="onClickItemInfo(item, 'uploadImage')">
|
||||||
<image class="img w100 h100" :src="item.value.imgUrl || defaultImage"></image>
|
<image class="img w100 h100" :src="item.value.imgUrl || defaultImage"></image>
|
||||||
|
|
@ -389,6 +402,13 @@
|
||||||
showTypeIds: [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13],
|
showTypeIds: [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13],
|
||||||
key: 'countInAndOut',
|
key: 'countInAndOut',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "商家订单号转换条形码",
|
||||||
|
"isSwitch": true,
|
||||||
|
showTypeIds: [1, 2, 3],
|
||||||
|
key: 'isShowBarcode',
|
||||||
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -439,6 +459,7 @@
|
||||||
bottomIcons: [0, 1, 2, 3],
|
bottomIcons: [0, 1, 2, 3],
|
||||||
merchantOption: {
|
merchantOption: {
|
||||||
serviceDetail: false,
|
serviceDetail: false,
|
||||||
|
isShowBarcode: false,
|
||||||
serviceDetailInfo: {
|
serviceDetailInfo: {
|
||||||
imgUrl: '',
|
imgUrl: '',
|
||||||
rightText: 0,
|
rightText: 0,
|
||||||
|
|
@ -507,7 +528,7 @@
|
||||||
data.type = option.type
|
data.type = option.type
|
||||||
data.navBar.title = '新增余额明细'
|
data.navBar.title = '新增余额明细'
|
||||||
}
|
}
|
||||||
if (option.id && isEdit) {
|
if (option.id && option.isEdit) {
|
||||||
console.log('编辑页面')
|
console.log('编辑页面')
|
||||||
// 获取当前id账单
|
// 获取当前id账单
|
||||||
const existingBill = JSON.parse(JSON.stringify(getBillList())).find(b => b.id === option.id)
|
const existingBill = JSON.parse(JSON.stringify(getBillList())).find(b => b.id === option.id)
|
||||||
|
|
@ -527,7 +548,27 @@
|
||||||
|
|
||||||
// 等待监听处理selectId更改,然后覆盖itemInfoList
|
// 等待监听处理selectId更改,然后覆盖itemInfoList
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
billData.value.itemInfoList = JSON.parse(JSON.stringify(existingBill.itemInfoList))
|
let itemInfoList = JSON.parse(JSON.stringify(existingBill.itemInfoList))
|
||||||
|
// 检查是否有缺失的字段(如orderNumber, merchantOrderNumber)并补充
|
||||||
|
const template = classifyTabBar.value.find(item => item.selectId == existingBill.selectId)
|
||||||
|
if (template && template.itemInfoList) {
|
||||||
|
const fieldsToCheck = ['orderNumber', 'merchantOrderNumber']
|
||||||
|
fieldsToCheck.forEach(key => {
|
||||||
|
// 查找当前数据中是否存在该字段
|
||||||
|
const exists = itemInfoList.some(item => item.key === key)
|
||||||
|
// 查找模板中是否定义了该字段
|
||||||
|
const templateItem = template.itemInfoList.find(item => item.key === key)
|
||||||
|
|
||||||
|
if (!exists && templateItem) {
|
||||||
|
// 如果当前数据缺失但模板中有定义,则补全
|
||||||
|
const newItem = JSON.parse(JSON.stringify(templateItem))
|
||||||
|
// 生成随机值
|
||||||
|
newItem.value = randomUtil.randomOrderNumber(28)
|
||||||
|
itemInfoList.push(newItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
billData.value.itemInfoList = itemInfoList
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -720,6 +761,8 @@
|
||||||
item.value = randomUtil.randomOrderNumber(10)
|
item.value = randomUtil.randomOrderNumber(10)
|
||||||
} else if (item.key == 'receiverFullName') {
|
} else if (item.key == 'receiverFullName') {
|
||||||
item.value = `${billData.value.name}有限公司`
|
item.value = `${billData.value.name}有限公司`
|
||||||
|
} else if (item.key == 'merchantOrderNumber') {
|
||||||
|
item.value = randomUtil.randomOrderNumber(28)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -1163,6 +1206,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 条形码
|
||||||
|
.barcode-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
|
||||||
|
.barcode {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-item-box {
|
.info-item-box {
|
||||||
|
|
@ -1182,8 +1236,8 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: 20px;
|
flex: 0 1 auto;
|
||||||
max-width: 100%;
|
min-width: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,16 @@
|
||||||
</view>
|
</view>
|
||||||
<!-- 详情信息列表 -->
|
<!-- 详情信息列表 -->
|
||||||
<view class="detail-info-container">
|
<view class="detail-info-container">
|
||||||
<template v-for="item in billData.itemInfoList" :key="item.id">
|
<template v-for="item in visibleItemInfoList" :key="item.id">
|
||||||
<view class="info-item-box" :class="{ 'bottom-border': item.key == 'relatedRecord' }"
|
<view class="info-item-box" :class="{ 'bottom-border': item.key == 'relatedRecord' }">
|
||||||
v-if="item.key != 'paymentReward' || (item.key == 'paymentReward' && billData.merchantOption.payReward)">
|
|
||||||
<view class="item-label">
|
<view class="item-label">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</view>
|
</view>
|
||||||
<view v-if="item.type != 'link' && item.key != 'paymentReward'" class="info-item-input">
|
<view v-if="item.type != 'link' && item.key != 'paymentReward'" class="info-item-input">
|
||||||
<!-- 隐藏的text用于测量宽度 -->
|
<!-- 隐藏的text用于测量宽度 -->
|
||||||
<text class="text-measure">{{ item.value }}</text>
|
<text v-if="item.key == 'merchantOrderNumber' && billData.merchantOption.isShowBarcode"
|
||||||
|
class="text-measure">商家可扫码退款或查询交易</text>
|
||||||
|
<text v-else class="text-measure">{{ item.value }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="item.key == 'paymentReward'" class="payment-reward-box">
|
<view v-if="item.key == 'paymentReward'" class="payment-reward-box">
|
||||||
<view class="payment-reward flex-align-center">
|
<view class="payment-reward flex-align-center">
|
||||||
|
|
@ -57,6 +58,20 @@
|
||||||
<text>立即领取{{ item.value }}积分</text>
|
<text>立即领取{{ item.value }}积分</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<!-- 条形码 -->
|
||||||
|
<view v-if="item.key == 'merchantOrderNumber' && billData.merchantOption.isShowBarcode"
|
||||||
|
style="background-color: #FFFFFF;padding:0 2rem;">
|
||||||
|
<view class="barcode-box"
|
||||||
|
style="display: flex; justify-content: center;flex-direction: column; align-items: center;">
|
||||||
|
<l-barcode class="barcode" :lineLength="50" :font-size="12" text-align="center"
|
||||||
|
text-position="bottom" color="#1A1A1A" :text="item.value"
|
||||||
|
:displayValue='false'></l-barcode>
|
||||||
|
<text style="font-size: 24rpx;margin-top: -10rpx; z-index: 1;color: #969696;">{{
|
||||||
|
item.value.slice(0,
|
||||||
|
4) }}******点击查看订单号</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="item.type == 'link'" class="info-item-link">
|
<view v-if="item.type == 'link'" class="info-item-link">
|
||||||
<view class="img-box">
|
<view class="img-box">
|
||||||
|
|
@ -148,8 +163,8 @@
|
||||||
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<view class="more-box">
|
<view v-if="!data.isMoreShow" class="more-box">
|
||||||
<text>更多</text>
|
<text @click="data.isMoreShow = !data.isMoreShow">更多</text>
|
||||||
<image class="icon" src="/static/image/common/down-grey.png"></image>
|
<image class="icon" src="/static/image/common/down-grey.png"></image>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -231,11 +246,14 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import navBar from '@/components/nav-bar/nav-bar.vue'
|
import navBar from '@/components/nav-bar/nav-bar.vue'
|
||||||
import billManagementPopup from '@/components/bill-management-popup/bill-management-popup.vue'
|
import billManagementPopup from '@/components/bill-management-popup/bill-management-popup.vue'
|
||||||
|
import addBillJson from '@/static/json/add-bill.json'
|
||||||
|
const {
|
||||||
|
billBottomIconList,
|
||||||
|
classifyTabBar
|
||||||
|
} = addBillJson
|
||||||
import {
|
import {
|
||||||
billBottomIconList
|
util,
|
||||||
} from '@/static/json/add-bill.json'
|
randomUtil
|
||||||
import {
|
|
||||||
util
|
|
||||||
} from '@/utils/common.js'
|
} from '@/utils/common.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -243,7 +261,8 @@ import {
|
||||||
toRefs,
|
toRefs,
|
||||||
ref,
|
ref,
|
||||||
nextTick,
|
nextTick,
|
||||||
getCurrentInstance
|
getCurrentInstance,
|
||||||
|
computed
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import {
|
import {
|
||||||
onLoad,
|
onLoad,
|
||||||
|
|
@ -281,6 +300,7 @@ const data = reactive({
|
||||||
buttonGroup: buttonGroup
|
buttonGroup: buttonGroup
|
||||||
},
|
},
|
||||||
billId: "",
|
billId: "",
|
||||||
|
isMoreShow: false,
|
||||||
// 账单数据
|
// 账单数据
|
||||||
billData: {
|
billData: {
|
||||||
id: "",
|
id: "",
|
||||||
|
|
@ -320,6 +340,23 @@ let {
|
||||||
billData
|
billData
|
||||||
} = toRefs(data)
|
} = toRefs(data)
|
||||||
|
|
||||||
|
const visibleItemInfoList = computed(() => {
|
||||||
|
if (!billData.value.itemInfoList) return []
|
||||||
|
return billData.value.itemInfoList.filter(item => {
|
||||||
|
// Payment Reward Logic
|
||||||
|
if (item.key == 'paymentReward' && !billData.value.merchantOption.payReward) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show More Logic
|
||||||
|
if (item.isMore && !data.isMoreShow) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
getBillData(data.billId)
|
getBillData(data.billId)
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
|
|
@ -346,6 +383,7 @@ onLoad((option) => {
|
||||||
function getBillData(id) {
|
function getBillData(id) {
|
||||||
// 获取当前id账单
|
// 获取当前id账单
|
||||||
const existingBill = getBillList().find(b => b.id === id)
|
const existingBill = getBillList().find(b => b.id === id)
|
||||||
|
console.log("existingBill", existingBill)
|
||||||
if (existingBill) {
|
if (existingBill) {
|
||||||
//表单赋值
|
//表单赋值
|
||||||
billData.value.id = existingBill.id
|
billData.value.id = existingBill.id
|
||||||
|
|
@ -362,7 +400,29 @@ function getBillData(id) {
|
||||||
|
|
||||||
// 等待监听处理selectId更改,然后覆盖itemInfoList
|
// 等待监听处理selectId更改,然后覆盖itemInfoList
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
billData.value.itemInfoList = JSON.parse(JSON.stringify(existingBill.itemInfoList))
|
let itemInfoList = JSON.parse(JSON.stringify(existingBill.itemInfoList))
|
||||||
|
// 检查是否有缺失的字段(如orderNumber, merchantOrderNumber)并补充
|
||||||
|
const template = classifyTabBar.find(item => item.selectId == existingBill.selectId)
|
||||||
|
if (template && template.itemInfoList) {
|
||||||
|
const fieldsToCheck = ['orderNumber', 'merchantOrderNumber']
|
||||||
|
fieldsToCheck.forEach(key => {
|
||||||
|
// 查找当前数据中是否存在该字段
|
||||||
|
const exists = itemInfoList.some(item => item.key === key)
|
||||||
|
// 查找模板中是否定义了该字段
|
||||||
|
const templateItem = template.itemInfoList.find(item => item.key === key)
|
||||||
|
|
||||||
|
if (!exists && templateItem) {
|
||||||
|
// 如果当前数据缺失但模板中有定义,则补全
|
||||||
|
const newItem = JSON.parse(JSON.stringify(templateItem))
|
||||||
|
// 生成随机值
|
||||||
|
newItem.value = randomUtil.randomOrderNumber(28)
|
||||||
|
itemInfoList.push(newItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
billData.value.itemInfoList = itemInfoList
|
||||||
|
// Edit mode
|
||||||
|
updateBill(billData.value.id, billData.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +477,6 @@ const handleBillManagementConfirm = (data) => {
|
||||||
console.log(data)
|
console.log(data)
|
||||||
if (data.type == 'billClassify') {
|
if (data.type == 'billClassify') {
|
||||||
billData.value.merchantOption.billClassify = data.value
|
billData.value.merchantOption.billClassify = data.value
|
||||||
billData.value.selectId = data.id
|
|
||||||
} else if (data.type == 'tagAndNote') {
|
} else if (data.type == 'tagAndNote') {
|
||||||
billData.value.merchantOption.tag = data.tags
|
billData.value.merchantOption.tag = data.tags
|
||||||
billData.value.merchantOption.note = data.note
|
billData.value.merchantOption.note = data.note
|
||||||
|
|
@ -651,6 +710,17 @@ page {
|
||||||
line-height: 32rpx;
|
line-height: 32rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 条形码
|
||||||
|
.barcode-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
|
||||||
|
.barcode {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-item-box {
|
.info-item-box {
|
||||||
|
|
@ -675,19 +745,19 @@ page {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: 20px;
|
flex: 1;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
width: auto;
|
width: auto;
|
||||||
overflow: hidden;
|
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
|
|
||||||
/* 隐藏的测量元素 */
|
/* 隐藏的测量元素 */
|
||||||
.text-measure {
|
.text-measure {
|
||||||
white-space: break-spaces;
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
width: auto;
|
width: 100%;
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,8 @@
|
||||||
<view class="group-box">
|
<view class="group-box">
|
||||||
<image class="title-img" src="/static/image/index/shipingjiaocheng.png"></image>
|
<image class="title-img" src="/static/image/index/shipingjiaocheng.png"></image>
|
||||||
<view class="video-help-box">
|
<view class="video-help-box">
|
||||||
<view class="video-help-item" v-for="item in videoHelpList" :key="item.id"
|
<view class="video-help-item" :style="{ width: (windowWidth - 50) / 4 + 'px' }"
|
||||||
@click="clickVideoHelp(item)">
|
v-for="item in videoHelpList" :key="item.id" @click="clickVideoHelp(item)">
|
||||||
<image class="video-help-img" :src="item.icon"></image>
|
<image class="video-help-img" :src="item.icon"></image>
|
||||||
<text class="video-help-title">{{ item.text }}</text>
|
<text class="video-help-title">{{ item.text }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -115,8 +115,8 @@
|
||||||
<view class="group-box">
|
<view class="group-box">
|
||||||
<image class="title-img" src="/static/image/index/qita.png"></image>
|
<image class="title-img" src="/static/image/index/qita.png"></image>
|
||||||
<view class="video-help-box">
|
<view class="video-help-box">
|
||||||
<view class="video-help-item" v-for="item in otherList" :key="item.id"
|
<view class="video-help-item" :style="{ width: (windowWidth - 50) / 4 + 'px' }"
|
||||||
@click="clickMenu(item)">
|
v-for="item in otherList" :key="item.id" @click="clickMenu(item)">
|
||||||
<image class="video-help-img" :src="item.icon"></image>
|
<image class="video-help-img" :src="item.icon"></image>
|
||||||
<text class="video-help-title">{{ item.name }}</text>
|
<text class="video-help-title">{{ item.name }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -337,6 +337,16 @@ const fetchUserData = async () => {
|
||||||
|
|
||||||
// 刷新页面数据
|
// 刷新页面数据
|
||||||
setUserData()
|
setUserData()
|
||||||
|
|
||||||
|
// 检查是否有延迟跳转
|
||||||
|
const jumpTarget = uni.getStorageSync('jumpTarget_url');
|
||||||
|
if (jumpTarget) {
|
||||||
|
console.log('执行延迟跳转:', jumpTarget);
|
||||||
|
uni.removeStorageSync('jumpTarget_url');
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/' + jumpTarget
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取用户数据异常:', error)
|
console.error('获取用户数据异常:', error)
|
||||||
}
|
}
|
||||||
|
|
@ -399,11 +409,13 @@ const setUserData = () => {
|
||||||
// 启动走马灯
|
// 启动走马灯
|
||||||
startMarquee();
|
startMarquee();
|
||||||
data.videoHelpList = configData.config['client.uniapp.alipay.video_help'] || []
|
data.videoHelpList = configData.config['client.uniapp.alipay.video_help'] || []
|
||||||
|
// data.videoHelpList.push(configData.config['client.uniapp.alipay.video_help'][0])
|
||||||
data.qqgroup = configData.config['client.uniapp.qqgroup'] || {
|
data.qqgroup = configData.config['client.uniapp.qqgroup'] || {
|
||||||
enable: false,
|
enable: false,
|
||||||
number: "",
|
number: "",
|
||||||
text: ""
|
text: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
data.noticeInfo = {
|
data.noticeInfo = {
|
||||||
text: '加载中...',
|
text: '加载中...',
|
||||||
|
|
@ -833,14 +845,20 @@ onUnload(() => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
padding: 24rpx 32rpx;
|
padding: 24rpx 0;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
margin-top: 16rpx;
|
margin-top: 16rpx;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-top: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-help-item {
|
.video-help-item {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
width: 25%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-help-img {
|
.video-help-img {
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,86 @@
|
||||||
{
|
{
|
||||||
"orderInfo": {
|
"orderInfo": {
|
||||||
"price": "714",
|
"price": "13684",
|
||||||
"orderNo": "203012348597",
|
"orderNo": "203012348597",
|
||||||
"orderTime": "2025-07-29 06:55"
|
"orderTime": "2026-01-01 12:05:53",
|
||||||
|
"transactionNo": "202601100062596659666952BB"
|
||||||
},
|
},
|
||||||
"flightInfo": {
|
"flightInfo": {
|
||||||
"airline": "南方航空",
|
"airline": "南方航空",
|
||||||
"airlineCode": "CZ",
|
"airlineCode": "CZ",
|
||||||
"flightNumber": "CZ2355",
|
"flightNumber": "CZ2355",
|
||||||
"date": "2025-03-04",
|
"date": "2026-01-20",
|
||||||
"startTime": "08:00",
|
"startTime": "08:00",
|
||||||
"endTime": "10:00",
|
"endTime": "10:05",
|
||||||
"startCity": "武汉",
|
"startCity": "上海",
|
||||||
"endCity": "北京",
|
"endCity": "北京",
|
||||||
"startAirport": "天河机场T3",
|
"startAirport": "虹桥机场T2",
|
||||||
"endAirport": "北京大兴机场",
|
"endAirport": "首都机场",
|
||||||
"duration": "2时5分",
|
"duration": "2时05分",
|
||||||
"aircraftType": "737-800",
|
"aircraftType": "737-800",
|
||||||
"seatCategory": "头等舱",
|
"seatCategory": "头等舱",
|
||||||
"onTimeRate": "100%",
|
"onTimeRate": "99%",
|
||||||
"meal": "正餐",
|
"meal": "正餐",
|
||||||
"luggageCheckInQuota": "含20kg免费托运行李"
|
"luggageCheckInQuota": "含50kg免费托运行李"
|
||||||
},
|
},
|
||||||
"ticketNumber": "12345678901",
|
"ticketNumber": "12345678901",
|
||||||
"passengersInfo": [
|
"passengersInfo": [
|
||||||
{
|
{
|
||||||
"name": "张三",
|
"name": "迪丽热巴",
|
||||||
"idType": "身份证",
|
"idType": "身份证",
|
||||||
"idNumber": "123456789012345678",
|
"idNumber": "1234************568",
|
||||||
"ticketNo": "12345678901"
|
"ticketNo": "12345678901"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "张三",
|
"name": "古力娜扎",
|
||||||
"idType": "身份证",
|
"idType": "身份证",
|
||||||
"idNumber": "123456789012345678",
|
"idNumber": "1234************568",
|
||||||
"ticketNo": "12345678901"
|
"ticketNo": "12345678901"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"fligggyOrderInfo": {
|
||||||
|
"serviceProvider": "北京遨游国际航空服务有限公司",
|
||||||
|
"flyingPigMileage": "起飞7天后可领168飞猪里程",
|
||||||
|
"transactionSerialNumber": "202601100062596659666952BB",
|
||||||
|
"tips": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"content": "伤残军人/警察优惠购票公告"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"content": "充电宝限制携带提醒"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"content": "防诈骗提醒"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4",
|
||||||
|
"content": "乘机文明提醒"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5",
|
||||||
|
"content": "中国南方航空运输总则"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ctripOrderInfo": {
|
||||||
|
"tips": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"content": "乘机行李规定"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"content": "防诈骗提醒"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"content": "乘机文明提醒"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"points": "888",
|
||||||
|
"level": 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,10 +130,10 @@
|
||||||
<text class="label">姓名</text>
|
<text class="label">姓名</text>
|
||||||
<input class="input" v-model="passenger.name" />
|
<input class="input" v-model="passenger.name" />
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="form-item">
|
<view class="form-item">
|
||||||
<text class="label">证件类型</text>
|
<text class="label">证件类型</text>
|
||||||
<input class="input" v-model="passenger.idType" />
|
<input class="input" v-model="passenger.idType" />
|
||||||
</view> -->
|
</view>
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="label">身份证号</text>
|
<text class="label">身份证号</text>
|
||||||
<input class="input" v-model="passenger.idNumber" />
|
<input class="input" v-model="passenger.idNumber" />
|
||||||
|
|
@ -151,6 +151,104 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 飞猪其他信息 -->
|
||||||
|
<view class="section-container" v-if="data.storageKey === 'fliggyAirTicketsInfo'">
|
||||||
|
<view class="section-header" @click="toggleSection('fligggyOrderInfo')">
|
||||||
|
<text class="section-title">飞猪订单信息</text>
|
||||||
|
<uni-icons :type="collapsed.fligggyOrderInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-show="!collapsed.fligggyOrderInfo">
|
||||||
|
<view class="card">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">服务方</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.fligggyOrderInfo.serviceProvider" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">飞猪里程</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.fligggyOrderInfo.flyingPigMileage" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">交易流水号</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.fligggyOrderInfo.transactionSerialNumber" />
|
||||||
|
</view>
|
||||||
|
<view class="tips-container">
|
||||||
|
<view class="tips-header">
|
||||||
|
<text class="label">订单提示</text>
|
||||||
|
<view class="add-tip-btn" @click="addFliggyTip">
|
||||||
|
<uni-icons type="plusempty" size="16" color="#1677FF"></uni-icons>
|
||||||
|
<text style="color: #1677FF; font-size: 26rpx; margin-left: 4rpx;">添加</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="tip-list">
|
||||||
|
<view v-for="(tip, index) in ticketsInfo.fligggyOrderInfo.tips" :key="tip.id"
|
||||||
|
class="tip-item"
|
||||||
|
:class="{ 'sort-active': dragInfo.listType === 'fliggy' && dragInfo.index === index }"
|
||||||
|
:style="{ transform: dragInfo.listType === 'fliggy' && dragInfo.index === index ? `translateY(${dragInfo.offsetY}px)` : '', zIndex: dragInfo.listType === 'fliggy' && dragInfo.index === index ? 99 : 1 }">
|
||||||
|
<input class="tip-input" v-model="tip.content" placeholder="请输入提示内容" />
|
||||||
|
<view class="tip-actions">
|
||||||
|
<uni-icons class="drag-handle" type="bars" size="20" color="#999"
|
||||||
|
@touchstart.stop="onDragStart($event, index, 'fliggy')"
|
||||||
|
@touchmove.stop.prevent="onDragMove" @touchend.stop="onDragEnd"></uni-icons>
|
||||||
|
<uni-icons type="trash" size="18" color="#FF4D4F"
|
||||||
|
@click="removeFliggyTip(index)"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 携程其他信息 -->
|
||||||
|
<view class="section-container" v-if="data.storageKey === 'ctripAirTicketsInfo'">
|
||||||
|
<view class="section-header" @click="toggleSection('ctripOrderInfo')">
|
||||||
|
<text class="section-title">携程订单信息</text>
|
||||||
|
<uni-icons :type="collapsed.ctripOrderInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-show="!collapsed.ctripOrderInfo">
|
||||||
|
<view class="card">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">订单积分</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.ctripOrderInfo.points" />
|
||||||
|
</view>
|
||||||
|
<picker mode="selector" :range="ctripLevelList" range-key="label" @change="onLevelChange">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">等级</text>
|
||||||
|
<view class="input"
|
||||||
|
:style="{ color: !ticketsInfo.ctripOrderInfo.level ? '#999' : '#333' }">
|
||||||
|
{{ getLevelLabel(ticketsInfo.ctripOrderInfo.level) || '请选择等级' }}</view>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<view class="tips-container">
|
||||||
|
<view class="tips-header">
|
||||||
|
<text class="label">订单提示</text>
|
||||||
|
<view class="add-tip-btn" @click="addTip">
|
||||||
|
<uni-icons type="plusempty" size="16" color="#1677FF"></uni-icons>
|
||||||
|
<text style="color: #1677FF; font-size: 26rpx; margin-left: 4rpx;">添加</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="tip-list">
|
||||||
|
<view v-for="(tip, index) in ticketsInfo.ctripOrderInfo.tips" :key="tip.id"
|
||||||
|
class="tip-item"
|
||||||
|
:class="{ 'sort-active': dragInfo.listType === 'ctrip' && dragInfo.index === index }"
|
||||||
|
:style="{ transform: dragInfo.listType === 'ctrip' && dragInfo.index === index ? `translateY(${dragInfo.offsetY}px)` : '', zIndex: dragInfo.listType === 'ctrip' && dragInfo.index === index ? 99 : 1 }">
|
||||||
|
<input class="tip-input" v-model="tip.content" placeholder="请输入提示内容" />
|
||||||
|
<view class="tip-actions">
|
||||||
|
<uni-icons class="drag-handle" type="bars" size="20" color="#999"
|
||||||
|
@touchstart.stop="onDragStart($event, index, 'ctrip')"
|
||||||
|
@touchmove.stop.prevent="onDragMove" @touchend.stop="onDragEnd"></uni-icons>
|
||||||
|
<uni-icons type="trash" size="18" color="#FF4D4F"
|
||||||
|
@click="removeTip(index)"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="placeholder"></view>
|
<view class="placeholder"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -159,6 +257,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
||||||
import { reactive, toRefs, onMounted } from 'vue';
|
import { reactive, toRefs, onMounted } from 'vue';
|
||||||
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
|
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
|
||||||
import airlineJson from '@/static/json/air-line.json';
|
import airlineJson from '@/static/json/air-line.json';
|
||||||
|
|
||||||
|
|
@ -170,10 +269,18 @@ const data = reactive({
|
||||||
orderInfo: false,
|
orderInfo: false,
|
||||||
flightInfo: false,
|
flightInfo: false,
|
||||||
passengersInfo: false
|
passengersInfo: false
|
||||||
|
},
|
||||||
|
storageKey: 'airTicketsInfo',
|
||||||
|
dragInfo: {
|
||||||
|
index: -1,
|
||||||
|
listType: '', // 'ctrip' or 'fliggy'
|
||||||
|
startY: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
itemHeight: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { ticketsInfo, collapsed } = toRefs(data)
|
const { ticketsInfo, collapsed, dragInfo } = toRefs(data)
|
||||||
|
|
||||||
const onAirlineChange = (e) => {
|
const onAirlineChange = (e) => {
|
||||||
const index = e.detail.value;
|
const index = e.detail.value;
|
||||||
|
|
@ -184,11 +291,176 @@ const onAirlineChange = (e) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
//携程会员等级
|
||||||
const stored = uni.getStorageSync('airTicketsInfo')
|
const ctripLevelList = [
|
||||||
|
{
|
||||||
|
label: "白银",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "黄金",
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "铂金",
|
||||||
|
value: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "钻石",
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const onLevelChange = (e) => {
|
||||||
|
const index = e.detail.value;
|
||||||
|
ticketsInfo.value.ctripOrderInfo.level = ctripLevelList[index].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLevelLabel = (value) => {
|
||||||
|
const item = ctripLevelList.find(i => i.value == value);
|
||||||
|
return item ? item.label : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const addTip = () => {
|
||||||
|
if (!ticketsInfo.value.ctripOrderInfo.tips) {
|
||||||
|
ticketsInfo.value.ctripOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
ticketsInfo.value.ctripOrderInfo.tips.push({
|
||||||
|
id: Date.now().toString(),
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTip = (index) => {
|
||||||
|
ticketsInfo.value.ctripOrderInfo.tips.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addFliggyTip = () => {
|
||||||
|
if (!ticketsInfo.value.fligggyOrderInfo.tips) {
|
||||||
|
ticketsInfo.value.fligggyOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
ticketsInfo.value.fligggyOrderInfo.tips.push({
|
||||||
|
id: Date.now().toString(),
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeFliggyTip = (index) => {
|
||||||
|
ticketsInfo.value.fligggyOrderInfo.tips.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Drag Sorting Logic
|
||||||
|
let dragTimer = null
|
||||||
|
const onDragStart = (e, index, type) => {
|
||||||
|
const touch = e.touches[0]
|
||||||
|
// Get item height first - assuming fixed height or query first item
|
||||||
|
// Better to query the specific item height
|
||||||
|
// For simplicity, let's assume item height is consistent or query dynamically
|
||||||
|
const query = uni.createSelectorQuery().in(this)
|
||||||
|
query.selectAll('.tip-item').boundingClientRect(data => {
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const itemRect = data[index]
|
||||||
|
dragInfo.value.itemHeight = itemRect.height
|
||||||
|
dragInfo.value.index = index
|
||||||
|
dragInfo.value.listType = type
|
||||||
|
dragInfo.value.startY = touch.clientY
|
||||||
|
dragInfo.value.offsetY = 0
|
||||||
|
}
|
||||||
|
}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragMove = (e) => {
|
||||||
|
if (dragInfo.value.index === -1) return
|
||||||
|
const touch = e.touches[0]
|
||||||
|
const deltaY = touch.clientY - dragInfo.value.startY
|
||||||
|
dragInfo.value.offsetY = deltaY
|
||||||
|
|
||||||
|
// Calculate if we moved enough to swap
|
||||||
|
const itemHeight = dragInfo.value.itemHeight || 50 // fallback height
|
||||||
|
const moveSteps = Math.round(deltaY / itemHeight)
|
||||||
|
|
||||||
|
if (moveSteps !== 0) {
|
||||||
|
const newIndex = dragInfo.value.index + moveSteps
|
||||||
|
let tips = []
|
||||||
|
if (dragInfo.value.listType === 'ctrip') {
|
||||||
|
tips = ticketsInfo.value.ctripOrderInfo.tips
|
||||||
|
} else if (dragInfo.value.listType === 'fliggy') {
|
||||||
|
tips = ticketsInfo.value.fligggyOrderInfo.tips
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex >= 0 && newIndex < tips.length) {
|
||||||
|
// Debounce swap or swap immediately if safe?
|
||||||
|
// Immediate swap might feel jittery if not careful
|
||||||
|
// Let's swap data and update startY to reflect new position
|
||||||
|
// Logic: Swap data, reset offset (because the item moved in DOM)
|
||||||
|
|
||||||
|
// Simple swap
|
||||||
|
const temp = tips[dragInfo.value.index]
|
||||||
|
tips[dragInfo.value.index] = tips[newIndex]
|
||||||
|
tips[newIndex] = temp
|
||||||
|
|
||||||
|
// Update current index to new index
|
||||||
|
dragInfo.value.index = newIndex
|
||||||
|
// Reset startY to current touch position relative to new item position
|
||||||
|
dragInfo.value.startY = touch.clientY
|
||||||
|
dragInfo.value.offsetY = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragEnd = () => {
|
||||||
|
dragInfo.value.index = -1
|
||||||
|
dragInfo.value.listType = ''
|
||||||
|
dragInfo.value.offsetY = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度合并函数
|
||||||
|
const deepMerge = (target, source) => {
|
||||||
|
for (const key in source) {
|
||||||
|
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
||||||
|
if (!target[key] || typeof target[key] !== 'object') {
|
||||||
|
target[key] = {};
|
||||||
|
}
|
||||||
|
deepMerge(target[key], source[key]);
|
||||||
|
} else {
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad((options) => {
|
||||||
|
console.log('options', options)
|
||||||
|
if (options.storageKey) {
|
||||||
|
data.storageKey = options.storageKey
|
||||||
|
}
|
||||||
|
const stored = uni.getStorageSync(data.storageKey)
|
||||||
if (stored) {
|
if (stored) {
|
||||||
// Deep merge or just assign if structure matches
|
// 使用默认数据作为基础,将存储的数据合并进去
|
||||||
Object.assign(data.ticketsInfo, stored)
|
// 这样可以确保新增的默认字段不会被旧的存储数据覆盖丢失
|
||||||
|
// 注意:这里 data.ticketsInfo 已经是 reactive 的 defualtData 的副本
|
||||||
|
// 但为了保险,我们重新基于 defaultData 做合并,或者直接对 ticketsInfo 做深度合并
|
||||||
|
// 鉴于 ticketsInfo 已经在 setup 中被初始化为 defaultData 的深拷贝
|
||||||
|
// 我们直接将 stored merge 进 ticketsInfo 即可,但要用 deepMerge
|
||||||
|
|
||||||
|
// 重新从 defaultData 获取一份纯净的基础数据,以防万一
|
||||||
|
const baseData = JSON.parse(JSON.stringify(defualtData));
|
||||||
|
const mergedData = deepMerge(baseData, stored);
|
||||||
|
|
||||||
|
// 将合并后的数据赋值给 reactive 对象
|
||||||
|
//由于 ticketsInfo 是 reactive 的属性,我们可以直接 Object.assign 或逐个属性赋值
|
||||||
|
Object.assign(data.ticketsInfo, mergedData)
|
||||||
|
|
||||||
|
// 再次检查 tips 是否存在,如果不存在(比如 stored 是旧数据且 defaultData 也没这个字段 - 虽然 defaultData 应该有),初始化为空数组
|
||||||
|
if (data.storageKey === 'fligggyAirTicketsInfo' && !data.ticketsInfo.fligggyOrderInfo.tips) {
|
||||||
|
data.ticketsInfo.fligggyOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
if (data.storageKey === 'ctripAirTicketsInfo' && !data.ticketsInfo.ctripOrderInfo.tips) {
|
||||||
|
data.ticketsInfo.ctripOrderInfo.tips = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -212,9 +484,16 @@ const handleRightButtonClick = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (data.storageKey === 'ctripAirTicketsInfo') {
|
||||||
|
delete data.ticketsInfo.fligggyOrderInfo
|
||||||
|
} else if (data.storageKey === 'fligggyAirTicketsInfo') {
|
||||||
|
delete data.ticketsInfo.ctripOrderInfo
|
||||||
|
} else {
|
||||||
|
delete data.ticketsInfo.fligggyOrderInfo
|
||||||
|
delete data.ticketsInfo.ctripOrderInfo
|
||||||
|
}
|
||||||
console.log('data.ticketsInfo', data.ticketsInfo)
|
console.log('data.ticketsInfo', data.ticketsInfo)
|
||||||
uni.setStorageSync('airTicketsInfo', data.ticketsInfo)
|
uni.setStorageSync(data.storageKey, data.ticketsInfo)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
|
|
@ -427,4 +706,47 @@ page {
|
||||||
.placeholder {
|
.placeholder {
|
||||||
height: 60rpx;
|
height: 60rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tips-container {
|
||||||
|
padding: 24rpx 0;
|
||||||
|
border-bottom: 1rpx solid #F5F5F5;
|
||||||
|
|
||||||
|
.tips-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-list {
|
||||||
|
.tip-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
padding: 12rpx 16rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
.tip-input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-active {
|
||||||
|
background-color: #E6F2FF;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.1s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
|
<!-- 水印 -->
|
||||||
|
<view v-if="$isVip()">
|
||||||
|
<watermark dark="light" />
|
||||||
|
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
|
||||||
|
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
|
||||||
|
</liu-drag-button>
|
||||||
|
</view>
|
||||||
<view class="page-container">
|
<view class="page-container">
|
||||||
<!-- Main Content Wrapper (Standard Flow) -->
|
<!-- Main Content Wrapper (Standard Flow) -->
|
||||||
<view class="main-content">
|
<view class="main-content">
|
||||||
<!-- Header Background (Inside scroll flow) -->
|
<!-- Header Background (Inside scroll flow) -->
|
||||||
<view class="header-bg">
|
<view class="header-bg">
|
||||||
<NavBar isBack :bgColor="data.navBar.bgColor" :textColor="data.navBar.textColor"
|
<NavBar isBack :bgColor="data.navBar.bgColor" :textColor="data.navBar.textColor"
|
||||||
:buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton">
|
:buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton"
|
||||||
|
tipLayerType="qunar-air-tickets-tip" isTipLayer tipLayerText="修改机票信息">
|
||||||
<template v-slot:right>
|
<template v-slot:right>
|
||||||
<view class="title-right flex flex-align-center">
|
<view class="title-right flex flex-align-center">
|
||||||
<view class="item flex flex-align-center flex-column">
|
<view class="item flex flex-align-center flex-column">
|
||||||
|
|
@ -71,12 +79,12 @@
|
||||||
|
|
||||||
<view class="flight-time-row">
|
<view class="flight-time-row">
|
||||||
<view style="align-items: flex-end;" class="flex">
|
<view style="align-items: flex-end;" class="flex">
|
||||||
<view class="time-col">
|
<view class="time-col" @click="util.goPage('/pages/other/air-tickets/edit/edit')">
|
||||||
<text class="time-big alipay-font">{{ ticketData.flightInfo.startTime }}</text>
|
<text class="time-big alipay-font">{{ ticketData.flightInfo.startTime }}</text>
|
||||||
<text class="airport-text">{{ ticketData.flightInfo.startAirport }} </text>
|
<text class="airport-text">{{ ticketData.flightInfo.startAirport }} </text>
|
||||||
</view>
|
</view>
|
||||||
<image src="/static/image/other/air-tickets/qunar/location.png" mode="widthFix"
|
<image src="/static/image/other/air-tickets/qunar/location.png"
|
||||||
style="width: 16rpx; height: 16rpx;"></image>
|
style="width: 16rpx; height: 16rpx;flex-shrink: 0;margin-bottom: 4rpx;"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="arrow-col">
|
<view class="arrow-col">
|
||||||
|
|
@ -98,33 +106,34 @@
|
||||||
<text class="duration-text">{{ ticketData.flightInfo.duration }}</text>
|
<text class="duration-text">{{ ticketData.flightInfo.duration }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view style="margin-right: 44rpx;align-items: flex-end;" class="flex">
|
<view style="margin-right: 44rpx;align-items: flex-end;" class="flex">
|
||||||
<view class="time-col align-right">
|
<view class="time-col align-right"
|
||||||
|
@click="util.goPage('/pages/other/air-tickets/edit/edit')">
|
||||||
<text class="time-big alipay-font">{{ ticketData.flightInfo.endTime }}</text>
|
<text class="time-big alipay-font">{{ ticketData.flightInfo.endTime }}</text>
|
||||||
<text class="airport-text">{{ ticketData.flightInfo.endAirport }}</text>
|
<text class="airport-text">{{ ticketData.flightInfo.endAirport }}</text>
|
||||||
</view>
|
</view>
|
||||||
<image src="/static/image/other/air-tickets/qunar/location.png" mode="widthFix"
|
<image src="/static/image/other/air-tickets/qunar/location.png"
|
||||||
style="width: 16rpx; height: 16rpx;"></image>
|
style="width: 16rpx; height: 16rpx;flex-shrink: 0;margin-bottom: 4rpx;"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="flight-detail-row">
|
<view class="flight-detail-row">
|
||||||
<image src="/static/image/other/air-tickets/qunar/flight-number.png" mode="widthFix"
|
<image class="item" src="/static/image/other/air-tickets/qunar/flight-number.png"
|
||||||
|
mode="widthFix" style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
||||||
|
<text class="detail-item item">{{ ticketData.flightInfo.aircraftType }}</text>
|
||||||
|
<text class="detail-divider item">|</text>
|
||||||
|
<image class="item" src="/static/image/other/air-tickets/qunar/seat-category.png"
|
||||||
|
mode="widthFix" style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
||||||
|
<text class="detail-item item">{{ ticketData.flightInfo.seatCategory }}</text>
|
||||||
|
<text class="detail-divider item">|</text>
|
||||||
|
<image class="item" src="/static/image/other/air-tickets/qunar/on-time.png"
|
||||||
|
mode="widthFix" style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
||||||
|
<text class="detail-item item">准点率{{ ticketData.flightInfo.onTimeRate }}</text>
|
||||||
|
<text class="detail-divider item">|</text>
|
||||||
|
<image class="item" src="/static/image/other/air-tickets/qunar/meal.png" mode="widthFix"
|
||||||
style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
||||||
<text class="detail-item">{{ ticketData.flightInfo.aircraftType }}</text>
|
<text class="detail-item item">{{ ticketData.flightInfo.meal }}</text>
|
||||||
<text class="detail-divider">|</text>
|
|
||||||
<image src="/static/image/other/air-tickets/qunar/seat-category.png" mode="widthFix"
|
|
||||||
style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
|
||||||
<text class="detail-item">{{ ticketData.flightInfo.seatCategory }}</text>
|
|
||||||
<text class="detail-divider">|</text>
|
|
||||||
<image src="/static/image/other/air-tickets/qunar/on-time.png" mode="widthFix"
|
|
||||||
style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
|
||||||
<text class="detail-item">准点率{{ ticketData.flightInfo.onTimeRate }}</text>
|
|
||||||
<text class="detail-divider">|</text>
|
|
||||||
<image src="/static/image/other/air-tickets/qunar/meal.png" mode="widthFix"
|
|
||||||
style="width: 24rpx; height: 24rpx;margin-right: 4rpx;"></image>
|
|
||||||
<text class="detail-item">{{ ticketData.flightInfo.meal }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="tips-box">
|
<view class="tips-box">
|
||||||
<view class="tips-row flex flex-justify-between">
|
<view class="tips-row flex flex-justify-between">
|
||||||
|
|
@ -180,7 +189,8 @@
|
||||||
<view class="row-label" v-if="i === 0">乘机人</view>
|
<view class="row-label" v-if="i === 0">乘机人</view>
|
||||||
<view class="row-label-placeholder" v-else></view>
|
<view class="row-label-placeholder" v-else></view>
|
||||||
|
|
||||||
<view class="row-content">
|
<view class="row-content"
|
||||||
|
@click="util.goPage('/pages/other/air-tickets/edit/edit')">
|
||||||
<view class="p-name">{{ p.name }}</view>
|
<view class="p-name">{{ p.name }}</view>
|
||||||
<view class="p-sub">{{ p.idType }}: {{ p.idNumber }}</view>
|
<view class="p-sub">{{ p.idType }}: {{ p.idNumber }}</view>
|
||||||
<view class="p-sub">
|
<view class="p-sub">
|
||||||
|
|
@ -236,25 +246,27 @@
|
||||||
<view class="product-item">
|
<view class="product-item">
|
||||||
<view class="pi-top">
|
<view class="pi-top">
|
||||||
<text class="pi-title">接送机立减券 (8... x2</text>
|
<text class="pi-title">接送机立减券 (8... x2</text>
|
||||||
<uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
||||||
</view>
|
</view>
|
||||||
|
<uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
<!-- Item 2 -->
|
<!-- Item 2 -->
|
||||||
<view class="product-item">
|
<view class="product-item">
|
||||||
<view class="pi-top">
|
<view class="pi-top">
|
||||||
<text class="pi-title">度假券 x2</text>
|
<text class="pi-title">度假券 x2</text>
|
||||||
<uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
||||||
</view>
|
|
||||||
<text class="pi-sub">跟团游享<text style="color: #EF7D12;">9.7折</text></text>
|
<text class="pi-sub">跟团游享<text style="color: #EF7D12;">9.7折</text></text>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
<!-- Item 3 -->
|
<!-- Item 3 -->
|
||||||
<view class="product-item">
|
<view class="product-item">
|
||||||
<view class="pi-top">
|
<view class="pi-top">
|
||||||
<text class="pi-title">租车券 x1</text>
|
<text class="pi-title">租车券 x1</text>
|
||||||
<uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
|
||||||
</view>
|
|
||||||
<text class="pi-sub">租车享<text style="color: #EF7D12;">85折</text></text>
|
<text class="pi-sub">租车享<text style="color: #EF7D12;">85折</text></text>
|
||||||
</view>
|
</view>
|
||||||
|
<uni-icons type="right" size="12" color="#ccc"></uni-icons>
|
||||||
|
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="show-more-row">
|
<view class="show-more-row">
|
||||||
|
|
@ -382,6 +394,13 @@ onShow(() => {
|
||||||
if (stored) {
|
if (stored) {
|
||||||
Object.assign(data.ticketData, stored);
|
Object.assign(data.ticketData, stored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
util.setAndroidSystemBarColor('#ffffff')
|
||||||
|
setTimeout(() => {
|
||||||
|
plus.navigator.setStatusBarStyle("light");
|
||||||
|
}, 500)
|
||||||
|
// #endif
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
@ -569,7 +588,7 @@ onMounted(() => {
|
||||||
|
|
||||||
.flight-header {
|
.flight-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
// align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
|
|
@ -718,9 +737,14 @@ onMounted(() => {
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
color: #626262;
|
color: #626262;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
gap: 8rpx;
|
|
||||||
|
|
||||||
|
.item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-divider {
|
.detail-divider {
|
||||||
color: #D8D8D8;
|
color: #D8D8D8;
|
||||||
|
|
@ -738,16 +762,13 @@ onMounted(() => {
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
padding: 12rpx;
|
padding: 12rpx;
|
||||||
|
|
||||||
|
|
||||||
image {
|
|
||||||
margin-top: 6rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
color: #626262;
|
color: #626262;
|
||||||
line-height: 32rpx;
|
line-height: 32rpx;
|
||||||
margin-right: 62rpx;
|
margin-right: 62rpx;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -759,7 +780,7 @@ onMounted(() => {
|
||||||
.action-btn {
|
.action-btn {
|
||||||
// flex: 1;
|
// flex: 1;
|
||||||
width: calc(50% - 10rpx);
|
width: calc(50% - 10rpx);
|
||||||
border: 1px solid #35D5E4;
|
border: 1.5rpx solid #35D5E4;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
padding: 20rpx 0;
|
padding: 20rpx 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -862,6 +883,7 @@ onMounted(() => {
|
||||||
width: 154rpx;
|
width: 154rpx;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
|
line-height: 30rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row-label-placeholder {
|
.row-label-placeholder {
|
||||||
|
|
@ -993,15 +1015,17 @@ onMounted(() => {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
height: 88rpx;
|
height: 88rpx;
|
||||||
background-color: #F7F8FA;
|
background-color: #F7F8FA;
|
||||||
padding: 20rpx;
|
padding: 0 20rpx;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
.pi-top {
|
.pi-top {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
|
|
@ -1071,7 +1095,7 @@ onMounted(() => {
|
||||||
line-height: 52rpx;
|
line-height: 52rpx;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1rpx solid #C2C2C2;
|
border: 0.5px solid #C2C2C2;
|
||||||
border-radius: 26rpx;
|
border-radius: 26rpx;
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: #626262;
|
color: #626262;
|
||||||
|
|
@ -1161,6 +1185,7 @@ onMounted(() => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding-bottom: 148rpx;
|
||||||
padding-bottom: calc(148rpx + constant(safe-area-inset-bottom));
|
padding-bottom: calc(148rpx + constant(safe-area-inset-bottom));
|
||||||
padding-bottom: calc(148rpx + env(safe-area-inset-bottom));
|
padding-bottom: calc(148rpx + env(safe-area-inset-bottom));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -416,7 +416,15 @@ function saveImage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad((option) => { })
|
onLoad((option) => {
|
||||||
|
// 进入工资单页面埋点
|
||||||
|
proxy.$apiUserEvent('all', {
|
||||||
|
type: 'event',
|
||||||
|
key: 'paystub',
|
||||||
|
prefix: '.uni.other.',
|
||||||
|
value: "工资单"
|
||||||
|
})
|
||||||
|
})
|
||||||
onReady(() => {
|
onReady(() => {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,13 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import NavBar from '@/components/nav-bar/nav-bar.vue';
|
import NavBar from '@/components/nav-bar/nav-bar.vue';
|
||||||
import { reactive, toRefs } from 'vue';
|
import { reactive, toRefs, getCurrentInstance } from 'vue';
|
||||||
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
import { util } from '@/utils/common.js'; // Assuming util exists for navigation, similar to previous tasks
|
import { util } from '@/utils/common.js'; // Assuming util exists for navigation, similar to previous tasks
|
||||||
|
const {
|
||||||
|
appContext,
|
||||||
|
proxy
|
||||||
|
} = getCurrentInstance();
|
||||||
|
|
||||||
const appList = [
|
const appList = [
|
||||||
{
|
{
|
||||||
|
|
@ -43,14 +48,14 @@ const appList = [
|
||||||
name: '飞猪APP',
|
name: '飞猪APP',
|
||||||
logo: '/static/image/other/tickets-app/fliggy-logo.png',
|
logo: '/static/image/other/tickets-app/fliggy-logo.png',
|
||||||
bgImage: '/static/image/other/tickets-app/fliggy-bg.png',
|
bgImage: '/static/image/other/tickets-app/fliggy-bg.png',
|
||||||
path: '',
|
path: '/pages/other/air-tickets/fliggy-air-tickets/fliggy-air-tickets',
|
||||||
isHot: true
|
isHot: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '携程APP',
|
name: '携程APP',
|
||||||
logo: '/static/image/other/tickets-app/trip-com-logo.png',
|
logo: '/static/image/other/tickets-app/trip-com-logo.png',
|
||||||
bgImage: '/static/image/other/tickets-app/trip-com-bg.png',
|
bgImage: '/static/image/other/tickets-app/trip-com-bg.png',
|
||||||
path: '',
|
path: '/pages/other/air-tickets/ctrip-air-tickets/ctrip-air-tickets',
|
||||||
isHot: false
|
isHot: false
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|
@ -62,6 +67,16 @@ const appList = [
|
||||||
// }
|
// }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
onLoad((option) => {
|
||||||
|
// 进入机票页面埋点
|
||||||
|
proxy.$apiUserEvent('all', {
|
||||||
|
type: 'event',
|
||||||
|
key: 'ticket',
|
||||||
|
prefix: '.uni.other.',
|
||||||
|
value: "机票"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const handleItemClick = (item) => {
|
const handleItemClick = (item) => {
|
||||||
if (item.path) {
|
if (item.path) {
|
||||||
util.goPage(item.path)
|
util.goPage(item.path)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
|
<!-- 水印 -->
|
||||||
|
<view v-if="$isVip()">
|
||||||
|
<watermark dark="light" />
|
||||||
|
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
|
||||||
|
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
|
||||||
|
</liu-drag-button>
|
||||||
|
</view>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<!-- 顶部背景 -->
|
<!-- 顶部背景 -->
|
||||||
<view class="header-bg"></view>
|
<view class="header-bg"></view>
|
||||||
|
|
@ -26,14 +33,14 @@
|
||||||
<!-- 车票卡片 -->
|
<!-- 车票卡片 -->
|
||||||
<view class="ticket-card">
|
<view class="ticket-card">
|
||||||
<view class="ticket-main-info">
|
<view class="ticket-main-info">
|
||||||
<view class="station-time-box">
|
<view class="station-time-box" @click="goEdit">
|
||||||
<text class="time-text">{{ departureTimeDisplay }}</text>
|
<text class="time-text">{{ departureTimeDisplay }}</text>
|
||||||
<view class="station-text">{{ ticketsInfo.ticketInfo.departureStation }}
|
<view class="station-text">{{ ticketsInfo.ticketInfo.departureStation }}
|
||||||
<uni-icons type="forward" size="10" color="#767676"></uni-icons>
|
<uni-icons type="forward" size="10" color="#767676"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="train-info-box">
|
<view class="train-info-box" @click="goEdit">
|
||||||
<view class="train-no">
|
<view class="train-no">
|
||||||
<text style="line-height: 26rpx;">{{ ticketsInfo.ticketInfo.trainNo }}</text>
|
<text style="line-height: 26rpx;">{{ ticketsInfo.ticketInfo.trainNo }}</text>
|
||||||
<uni-icons type="arrowright" size="10" color="#979797"></uni-icons>
|
<uni-icons type="arrowright" size="10" color="#979797"></uni-icons>
|
||||||
|
|
@ -45,7 +52,7 @@
|
||||||
<text class="duration-text">历时{{ ticketsInfo.ticketInfo.duration }}</text>
|
<text class="duration-text">历时{{ ticketsInfo.ticketInfo.duration }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="station-time-box" style="align-items: flex-start;">
|
<view class="station-time-box" style="align-items: flex-start;" @click="goEdit">
|
||||||
<view class="time-row">
|
<view class="time-row">
|
||||||
<text class="time-text">{{ arrivalTimeDisplay }}</text>
|
<text class="time-text">{{ arrivalTimeDisplay }}</text>
|
||||||
<text v-if="dayDiff > 0" class="day-badge">+{{ dayDiff }}</text>
|
<text v-if="dayDiff > 0" class="day-badge">+{{ dayDiff }}</text>
|
||||||
|
|
@ -86,7 +93,7 @@
|
||||||
|
|
||||||
<!-- 乘客卡片 -->
|
<!-- 乘客卡片 -->
|
||||||
<view class="passenger-card-box" v-for="(passenger, index) in ticketsInfo.passengerList" :key="index">
|
<view class="passenger-card-box" v-for="(passenger, index) in ticketsInfo.passengerList" :key="index">
|
||||||
<view class="passenger-card">
|
<view class="passenger-card" @click="goEdit">
|
||||||
<view class="passenger-header">
|
<view class="passenger-header">
|
||||||
<view class="name-box">
|
<view class="name-box">
|
||||||
<text class="passenger-name">{{ passenger.name }}</text>
|
<text class="passenger-name">{{ passenger.name }}</text>
|
||||||
|
|
@ -194,7 +201,8 @@ import {
|
||||||
ref,
|
ref,
|
||||||
reactive,
|
reactive,
|
||||||
toRefs,
|
toRefs,
|
||||||
computed
|
computed,
|
||||||
|
getCurrentInstance
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import {
|
import {
|
||||||
onLoad,
|
onLoad,
|
||||||
|
|
@ -202,14 +210,22 @@ import {
|
||||||
} from '@dcloudio/uni-app';
|
} from '@dcloudio/uni-app';
|
||||||
|
|
||||||
import { util } from '@/utils/common.js';
|
import { util } from '@/utils/common.js';
|
||||||
|
const {
|
||||||
|
appContext,
|
||||||
|
proxy
|
||||||
|
} = getCurrentInstance();
|
||||||
|
|
||||||
const buttonGroup = [{
|
const buttonGroup = [{
|
||||||
name: "编辑车票信息",
|
name: "编辑车票信息",
|
||||||
click: () => {
|
click: () => {
|
||||||
util.goPage('/pages/other/train-tickets/edit/edit')
|
goEdit()
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
function goEdit() {
|
||||||
|
util.goPage('/pages/other/train-tickets/edit/edit')
|
||||||
|
}
|
||||||
|
|
||||||
const ticketType = [
|
const ticketType = [
|
||||||
{
|
{
|
||||||
label: '成人票',
|
label: '成人票',
|
||||||
|
|
@ -348,6 +364,14 @@ const calculateNightCount = () => {
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
const sys = uni.getSystemInfoSync();
|
const sys = uni.getSystemInfoSync();
|
||||||
statusBarHeight.value = sys.statusBarHeight || 20;
|
statusBarHeight.value = sys.statusBarHeight || 20;
|
||||||
|
|
||||||
|
// 进入高铁票页面埋点
|
||||||
|
proxy.$apiUserEvent('all', {
|
||||||
|
type: 'event',
|
||||||
|
key: 'train_ticket',
|
||||||
|
prefix: '.uni.other.',
|
||||||
|
value: "高铁票"
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
|
|
@ -867,6 +891,7 @@ const handleAction = (action) => {
|
||||||
color: #4495F0;
|
color: #4495F0;
|
||||||
margin-right: 44rpx;
|
margin-right: 44rpx;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-range {
|
.date-range {
|
||||||
|
|
@ -884,12 +909,14 @@ const handleAction = (action) => {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #4495F0;
|
color: #4495F0;
|
||||||
border-bottom: 1px solid #4495F0;
|
border-bottom: 1px solid #4495F0;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-label {
|
.date-label {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #1A1A1A;
|
color: #1A1A1A;
|
||||||
margin: 0 10rpx;
|
margin: 0 10rpx;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.night-count {
|
.night-count {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- 水印 -->
|
<!-- 水印 -->
|
||||||
<view v-if="$isVip()">
|
<view v-if="$isVip()">
|
||||||
<watermark :dark="data.dark" />
|
<watermark dark="light" />
|
||||||
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
|
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
|
||||||
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
|
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
|
||||||
</liu-drag-button>
|
</liu-drag-button>
|
||||||
|
|
@ -37,7 +37,8 @@
|
||||||
@touchend="handleTouchEnd($event, index)" @click.stop="changeVideoOrImage(item, index)">
|
@touchend="handleTouchEnd($event, index)" @click.stop="changeVideoOrImage(item, index)">
|
||||||
<image v-if="item.preview" class="video-preview" :src="item.preview" mode="aspectFill"></image>
|
<image v-if="item.preview" class="video-preview" :src="item.preview" mode="aspectFill"></image>
|
||||||
<DomVideoPlayer v-else-if="item.videoUrl" :ref="`videoPlayer${index}`" class="video-preview"
|
<DomVideoPlayer v-else-if="item.videoUrl" :ref="`videoPlayer${index}`" class="video-preview"
|
||||||
:src="item.videoUrl" objectFit="cover" autoplay loop muted :isLoading="true" />
|
:src="item.videoUrl" objectFit="cover" autoplay loop muted :controls="false"
|
||||||
|
:isLoading="true" />
|
||||||
<view v-else class="video-preview" style="background-color: #F3F3F3;"></view>
|
<view v-else class="video-preview" style="background-color: #F3F3F3;"></view>
|
||||||
<view class="video-overlay" @click.stop="changeIconType(item, index)">
|
<view class="video-overlay" @click.stop="changeIconType(item, index)">
|
||||||
<image class="mute-icon" v-if="item.iconType > 0"
|
<image class="mute-icon" v-if="item.iconType > 0"
|
||||||
|
|
@ -141,9 +142,13 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import NavBar from "@/components/nav-bar/nav-bar.vue"
|
import NavBar from "@/components/nav-bar/nav-bar.vue"
|
||||||
|
|
||||||
import { ref, toRefs, onMounted, onUnmounted, reactive, computed } from 'vue'
|
import { ref, toRefs, onMounted, onUnmounted, reactive, computed, getCurrentInstance } from 'vue'
|
||||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
||||||
import { util } from '@/utils/common.js'
|
import { util } from '@/utils/common.js'
|
||||||
|
const {
|
||||||
|
appContext,
|
||||||
|
proxy
|
||||||
|
} = getCurrentInstance();
|
||||||
|
|
||||||
const buttonGroup = [
|
const buttonGroup = [
|
||||||
{
|
{
|
||||||
|
|
@ -168,9 +173,12 @@ const data = reactive({
|
||||||
mainVideoIndex: 0,
|
mainVideoIndex: 0,
|
||||||
timeText: '125:22',
|
timeText: '125:22',
|
||||||
videoList: [
|
videoList: [
|
||||||
{ preview: '/static/image/other/video-call/defualt/video-img1.png', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
{ preview: '/static/image/other/video-call/defualt/voice_chat_1.jpg', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
||||||
{ preview: '/static/image/other/video-call/defualt/video-img2.png', videoUrl: '', savedVideoUrl: '', iconType: 1 },
|
{ preview: '/static/image/other/video-call/defualt/voice_chat_2.jpg', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
||||||
{ preview: '/static/image/other/video-call/defualt/video-img3.png', videoUrl: '', savedVideoUrl: '', iconType: 2 }
|
{ preview: '/static/image/other/video-call/defualt/voice_chat_3.jpg', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
||||||
|
{ preview: '/static/image/other/video-call/defualt/voice_chat_4.jpg', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
||||||
|
{ preview: '/static/image/other/video-call/defualt/voice_chat_5.jpg', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
||||||
|
{ preview: '/static/image/other/video-call/defualt/voice_chat_me.jpg', videoUrl: '', savedVideoUrl: '', iconType: 1 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
videoDataBackup: null, // 编辑模式备份
|
videoDataBackup: null, // 编辑模式备份
|
||||||
|
|
@ -260,6 +268,14 @@ onLoad(() => {
|
||||||
}
|
}
|
||||||
data.videoData = videoDataNew
|
data.videoData = videoDataNew
|
||||||
console.log('videoData2', data.videoData)
|
console.log('videoData2', data.videoData)
|
||||||
|
|
||||||
|
// 进入视频群聊页面埋点
|
||||||
|
proxy.$apiUserEvent('all', {
|
||||||
|
type: 'event',
|
||||||
|
key: 'voice_chat',
|
||||||
|
prefix: '.uni.other.',
|
||||||
|
value: "视频群聊"
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
|
|
@ -502,16 +518,16 @@ const chooseVideo = () => {
|
||||||
preview: '',
|
preview: '',
|
||||||
videoUrl: videoUrl,
|
videoUrl: videoUrl,
|
||||||
savedVideoUrl: savedVideoUrl,
|
savedVideoUrl: savedVideoUrl,
|
||||||
iconType: 1
|
iconType: 0
|
||||||
}
|
}
|
||||||
console.log('✅ 已替换索引', data.currentEditIndex, '的视频')
|
console.log('✅ 已替换索引', data.currentEditIndex, '的视频')
|
||||||
} else {
|
} else {
|
||||||
// 新增模式:添加新项
|
// 新增模式:添加新项
|
||||||
data.videoData.videoList.push({
|
data.videoData.videoList.unshift({
|
||||||
preview: '',
|
preview: '',
|
||||||
videoUrl: videoUrl,
|
videoUrl: videoUrl,
|
||||||
savedVideoUrl: savedVideoUrl,
|
savedVideoUrl: savedVideoUrl,
|
||||||
iconType: 1
|
iconType: 0
|
||||||
})
|
})
|
||||||
console.log('✅ 已添加新视频')
|
console.log('✅ 已添加新视频')
|
||||||
}
|
}
|
||||||
|
|
@ -560,16 +576,16 @@ const chooseImage = () => {
|
||||||
preview: res.tempFilePaths[0],
|
preview: res.tempFilePaths[0],
|
||||||
videoUrl: '',
|
videoUrl: '',
|
||||||
savedVideoUrl: '',
|
savedVideoUrl: '',
|
||||||
iconType: 1
|
iconType: 0
|
||||||
}
|
}
|
||||||
console.log('✅ 已替换索引', data.currentEditIndex, '的图片')
|
console.log('✅ 已替换索引', data.currentEditIndex, '的图片')
|
||||||
} else {
|
} else {
|
||||||
// 新增模式:添加新项
|
// 新增模式:添加新项
|
||||||
data.videoData.videoList.push(...res.tempFilePaths.map((item) => ({
|
data.videoData.videoList.unshift(...res.tempFilePaths.map((item) => ({
|
||||||
preview: item,
|
preview: item,
|
||||||
videoUrl: '',
|
videoUrl: '',
|
||||||
savedVideoUrl: '',
|
savedVideoUrl: '',
|
||||||
iconType: 1
|
iconType: 0
|
||||||
})))
|
})))
|
||||||
console.log('✅ 已添加', res.tempFilePaths.length, '张图片')
|
console.log('✅ 已添加', res.tempFilePaths.length, '张图片')
|
||||||
}
|
}
|
||||||
|
|
@ -722,6 +738,21 @@ const handleTouchEnd = (event, index) => {
|
||||||
list[data.dragState.draggingIndex] = list[targetIndex]
|
list[data.dragState.draggingIndex] = list[targetIndex]
|
||||||
list[targetIndex] = temp
|
list[targetIndex] = temp
|
||||||
|
|
||||||
|
// 判断是否涉及最后一个元素的交换
|
||||||
|
const lastIndex = list.length - 1
|
||||||
|
if (data.dragState.draggingIndex === lastIndex || targetIndex === lastIndex) {
|
||||||
|
// 确保最后一个位置是 iconType 1,其他位置是 iconType 0
|
||||||
|
// list[lastIndex].iconType = 1
|
||||||
|
// 另一个交换的位置设为 0
|
||||||
|
const otherIndex = data.dragState.draggingIndex === lastIndex ? targetIndex : data.dragState.draggingIndex
|
||||||
|
if (list[otherIndex].iconType == 1) {
|
||||||
|
list[otherIndex].iconType = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到本地存储
|
||||||
|
uni.setStorageSync('videoData', data.videoData)
|
||||||
|
|
||||||
console.log('✅ 交换成功!')
|
console.log('✅ 交换成功!')
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ 目标索引无效,不交换 - 原因:', targetIndex < 0 ? '索引<0' : targetIndex >= videoData.value.videoList.length ? '索引超出' : '索引相同')
|
console.log('❌ 目标索引无效,不交换 - 原因:', targetIndex < 0 ? '索引<0' : targetIndex >= videoData.value.videoList.length ? '索引超出' : '索引相同')
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 766 B |
|
After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 233 B |
|
After Width: | Height: | Size: 285 B |
|
After Width: | Height: | Size: 934 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 556 B |
|
After Width: | Height: | Size: 873 B |
|
After Width: | Height: | Size: 668 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 946 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 726 B |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 323 B |
|
After Width: | Height: | Size: 219 B |
|
After Width: | Height: | Size: 598 B |
|
After Width: | Height: | Size: 551 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 617 B |
|
After Width: | Height: | Size: 705 B |
|
After Width: | Height: | Size: 506 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 382 B |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 179 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 774 B |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 873 B |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
|
@ -55,6 +55,24 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "receiverFullName",
|
"key": "receiverFullName",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -111,6 +129,15 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "receiverFullName",
|
"key": "receiverFullName",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -176,6 +203,24 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "clearingOrganization",
|
"key": "clearingOrganization",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -223,6 +268,15 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "paymentReward",
|
"key": "paymentReward",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -307,6 +361,15 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "counterAccount",
|
"key": "counterAccount",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -347,6 +410,15 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "counterAccount",
|
"key": "counterAccount",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -389,6 +461,15 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "productDescription",
|
"key": "productDescription",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -439,6 +520,24 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "productDescription",
|
"key": "productDescription",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -487,6 +586,24 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "receiverFullName",
|
"key": "receiverFullName",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -596,6 +713,24 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "tradeDetail",
|
"key": "tradeDetail",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
@ -652,6 +787,24 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"key": "receiverFullName",
|
"key": "receiverFullName",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "orderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "商家订单号",
|
||||||
|
"value": "",
|
||||||
|
"type": "number",
|
||||||
|
"focus": false,
|
||||||
|
"key": "merchantOrderNumber",
|
||||||
|
"required": true,
|
||||||
|
"isMore": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"defaultBottomIcons": [
|
"defaultBottomIcons": [
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
## 0.0.7(2025-08-20)
|
||||||
|
- fix: 更新文档
|
||||||
|
## 0.0.6(2025-05-24)
|
||||||
|
- feat: 兼容uniapp x 鸿蒙next
|
||||||
|
## 0.0.5(2025-02-09)
|
||||||
|
- chore: 优化TS提示
|
||||||
|
## 0.0.4(2025-02-09)
|
||||||
|
- chore: 更新文档
|
||||||
|
## 0.0.3(2025-01-17)
|
||||||
|
- feat: 兼容uniapp x 微信小程序
|
||||||
|
## 0.0.2(2024-09-10)
|
||||||
|
- fix: 修复微信小程序不显示的问题
|
||||||
|
## 0.0.1(2024-01-19)
|
||||||
|
- 首次上传
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
// 导入所有条形码
|
||||||
|
// @ts-nocheck
|
||||||
|
import barcodes from './jsbarcode/barcodes/';
|
||||||
|
import fixOptions from './jsbarcode/help/fixOptions';
|
||||||
|
import merge from './jsbarcode/help/merge';
|
||||||
|
import defaultOptions from './jsbarcode/options/defaults';
|
||||||
|
import { InvalidInputException } from './jsbarcode/exceptions/exceptions';
|
||||||
|
import linearizeEncodings from './jsbarcode/help/linearizeEncodings';
|
||||||
|
import { calculateEncodingAttributes, getTotalWidthOfEncodings, getMaximumHeightOfEncodings } from "./jsbarcode/renderers/shared";
|
||||||
|
import { LBarcodeOptions, EncodeResult } from './type'
|
||||||
|
|
||||||
|
function autoSelectBarcode() : string {
|
||||||
|
// 如果有 CODE128,则使用它
|
||||||
|
if (barcodes["CODE128"]) {
|
||||||
|
return "CODE128";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则,使用第一个(或唯一)条形码
|
||||||
|
return Object.keys(barcodes)[0];
|
||||||
|
}
|
||||||
|
// encode() 处理编码器调用并生成要呈现的二进制数据
|
||||||
|
function encode(text : string, Encoder : any, options : LBarcodeOptions) : any {
|
||||||
|
// 确保文本是字符串
|
||||||
|
text = "" + text;
|
||||||
|
|
||||||
|
let encoder = new Encoder(text, options);
|
||||||
|
|
||||||
|
// 如果输入对编码器无效,则抛出错误。如果已设置有效回调选项,则调用该选项
|
||||||
|
if (!encoder.valid()) {
|
||||||
|
throw new InvalidInputException(encoder.constructor.name, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为要呈现的二进制数据发出请求(和其他信息)
|
||||||
|
let encoded = encoder.encode();
|
||||||
|
|
||||||
|
// 编码可以像 [1-1, 1-2], 2, [3-1, 3-2] 一样嵌套
|
||||||
|
// 将其转换为 [1-1, 1-2, 2, 3-1, 3-2]
|
||||||
|
encoded = linearizeEncodings(encoded);
|
||||||
|
|
||||||
|
// 合并
|
||||||
|
for (let i = 0; i< encoded.length; i++) {
|
||||||
|
encoded[i].options = merge(options, encoded[i].options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let timer = null
|
||||||
|
function prepareCanvas(element : any, encodings : any, options : LBarcodeOptions) {
|
||||||
|
const ctx = element.getContext('2d');
|
||||||
|
ctx.save();
|
||||||
|
calculateEncodingAttributes(encodings, options, ctx);
|
||||||
|
const totalWidth = getTotalWidthOfEncodings(encodings);
|
||||||
|
const maxHeight = getMaximumHeightOfEncodings(encodings);
|
||||||
|
const width = totalWidth + options.marginLeft + options.marginRight
|
||||||
|
// 为避免报黄 Multiple readback operations using getImageData
|
||||||
|
element.width = width;
|
||||||
|
element.height = maxHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCanvasBarcode(element : any, options : LBarcodeOptions, encoding : EncodeResult) {
|
||||||
|
// Get the canvas context
|
||||||
|
const ctx = element.getContext("2d");
|
||||||
|
const binary = encoding.data;
|
||||||
|
// Creates the barcode out of the encoded binary
|
||||||
|
let yFrom = 0;
|
||||||
|
if (options.textPosition == "top") {
|
||||||
|
yFrom = options.marginTop + options.fontSize + options.textMargin;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
yFrom = options.marginTop;
|
||||||
|
}
|
||||||
|
ctx.fillStyle = options.lineColor;
|
||||||
|
for (let b = 0; b < binary.length; b++) {
|
||||||
|
let x = b * options.width + encoding.barcodePadding;
|
||||||
|
if (binary[b] === "1") {
|
||||||
|
ctx.fillRect(x, yFrom, options.width, options.height);
|
||||||
|
}
|
||||||
|
else if (binary[b]) {
|
||||||
|
ctx.fillRect(x, yFrom, options.width, options.height * parseInt(binary[b]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function drawCanvasText(element : any, options : LBarcodeOptions, encoding : EncodeResult) {
|
||||||
|
// Get the canvas context
|
||||||
|
const ctx = element.getContext("2d");
|
||||||
|
|
||||||
|
const font = `${options.fontOptions} ${options.fontSize}px ${options.font}`.trim();
|
||||||
|
|
||||||
|
// Draw the text if displayValue is set
|
||||||
|
if (options.displayValue) {
|
||||||
|
let x = 0, y = 0;
|
||||||
|
|
||||||
|
if (options.textPosition == "top") {
|
||||||
|
y = options.marginTop + options.fontSize - options.textMargin;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y = options.height + options.textMargin + options.marginTop + options.fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.font = font;
|
||||||
|
|
||||||
|
// Draw the text in the correct X depending on the textAlign option
|
||||||
|
if (options.textAlign == "left" || encoding.barcodePadding > 0) {
|
||||||
|
x = 0;
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
}
|
||||||
|
else if (options.textAlign == "right") {
|
||||||
|
x = encoding.width - 1;
|
||||||
|
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
}
|
||||||
|
// In all other cases, center the text
|
||||||
|
else {
|
||||||
|
x = encoding.width / 2;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
}
|
||||||
|
ctx.fillText(encoding.text, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function moveCanvasDrawing(element : any, encoding: EncodeResult) {
|
||||||
|
const ctx = element.getContext("2d");
|
||||||
|
ctx.translate(encoding.width, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CanvasRenderer(element : any, text : string, options : LBarcodeOptions, cb: ()=> void) {
|
||||||
|
if (typeof element === "undefined") {
|
||||||
|
throw Error("No element to render on was provided.");
|
||||||
|
}
|
||||||
|
if (text) {
|
||||||
|
let newOptions = { ...defaultOptions, text };
|
||||||
|
for (let key in options) {
|
||||||
|
const value = options[key]
|
||||||
|
if (value || typeof value == 'boolean') {
|
||||||
|
newOptions[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newOptions = fixOptions(newOptions);
|
||||||
|
if (!newOptions.format || newOptions.format == "auto") {
|
||||||
|
newOptions.format = autoSelectBarcode();
|
||||||
|
}
|
||||||
|
if (!element.getContext) {
|
||||||
|
throw new Error('不存在 getContext');
|
||||||
|
}
|
||||||
|
const ctx = element.getContext('2d');
|
||||||
|
const Encoder = barcodes[newOptions.format.toUpperCase()];
|
||||||
|
const encodings = encode(text, Encoder, newOptions);
|
||||||
|
prepareCanvas(element, encodings, newOptions)
|
||||||
|
// Paint the canvas
|
||||||
|
// clearTimeout(timer)
|
||||||
|
setTimeout(() => {
|
||||||
|
ctx.clearRect(0, 0, element.width, element.height);
|
||||||
|
if (newOptions.background) {
|
||||||
|
ctx.fillStyle = newOptions.background;
|
||||||
|
ctx.fillRect(0, 0, element.width, element.height);
|
||||||
|
}
|
||||||
|
ctx.translate(newOptions.marginLeft, 0);
|
||||||
|
// render
|
||||||
|
for (let i = 0; i < encodings.length; i++) {
|
||||||
|
const encodingOptions = merge(newOptions, encodings[i].options);
|
||||||
|
|
||||||
|
drawCanvasBarcode(element, encodingOptions, encodings[i]);
|
||||||
|
drawCanvasText(element, encodingOptions, encodings[i]);
|
||||||
|
|
||||||
|
moveCanvasDrawing(element, encodings[i]);
|
||||||
|
}
|
||||||
|
// render end
|
||||||
|
ctx.restore();
|
||||||
|
if (ctx.draw) {
|
||||||
|
ctx.draw(false, cb)
|
||||||
|
} else {
|
||||||
|
cb && cb()
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
// Import all the barcodes
|
||||||
|
import barcodes from './barcodes/';
|
||||||
|
// Help functions
|
||||||
|
import merge from './help/merge.js';
|
||||||
|
import linearizeEncodings from './help/linearizeEncodings.js';
|
||||||
|
import fixOptions from './help/fixOptions.js';
|
||||||
|
import getRenderProperties from './help/getRenderProperties.js';
|
||||||
|
import optionsFromStrings from './help/optionsFromStrings.js';
|
||||||
|
|
||||||
|
// Exceptions
|
||||||
|
import ErrorHandler from './exceptions/ErrorHandler.js';
|
||||||
|
import { InvalidInputException, NoElementException } from './exceptions/exceptions.js';
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
import defaults from './options/defaults.js';
|
||||||
|
|
||||||
|
// The protype of the object returned from the JsBarcode() call
|
||||||
|
let API = function () { };
|
||||||
|
|
||||||
|
// The first call of the library API
|
||||||
|
// Will return an object with all barcodes calls and the data that is used
|
||||||
|
// by the renderers
|
||||||
|
let JsBarcode = function (element, text, options) {
|
||||||
|
console.log('1111111')
|
||||||
|
var api = new API();
|
||||||
|
|
||||||
|
if (typeof element === "undefined") {
|
||||||
|
throw Error("No element to render on was provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variables that will be pased through the API calls
|
||||||
|
api._renderProperties = getRenderProperties(element);
|
||||||
|
api._encodings = [];
|
||||||
|
api._options = defaults;
|
||||||
|
api._errorHandler = new ErrorHandler(api);
|
||||||
|
|
||||||
|
// If text is set, use the simple syntax (render the barcode directly)
|
||||||
|
if (typeof text !== "undefined") {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (!options.format || options.format == "auto") {
|
||||||
|
options.format = autoSelectBarcode();
|
||||||
|
}
|
||||||
|
api.options(options)[options.format](text, options).render();
|
||||||
|
}
|
||||||
|
|
||||||
|
return api;
|
||||||
|
};
|
||||||
|
|
||||||
|
// To make tests work TODO: remove
|
||||||
|
JsBarcode.getModule = function (name) {
|
||||||
|
return barcodes[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register all barcodes
|
||||||
|
for (var name in barcodes) {
|
||||||
|
if (barcodes.hasOwnProperty(name)) { // Security check if the propery is a prototype property
|
||||||
|
registerBarcode(barcodes, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function registerBarcode(barcodes, name) {
|
||||||
|
// console.log('name', name)
|
||||||
|
API.prototype[name] =
|
||||||
|
API.prototype[name.toUpperCase()] =
|
||||||
|
API.prototype[name.toLowerCase()] =
|
||||||
|
function (text, options) {
|
||||||
|
|
||||||
|
var api = this;
|
||||||
|
return api._errorHandler.wrapBarcodeCall(function () {
|
||||||
|
// Ensure text is options.text
|
||||||
|
options.text = typeof options.text === 'undefined' ? undefined : '' + options.text;
|
||||||
|
|
||||||
|
var newOptions = merge(api._options, options);
|
||||||
|
newOptions = optionsFromStrings(newOptions);
|
||||||
|
// console.log('newOptions:::2', newOptions)
|
||||||
|
var Encoder = barcodes[name];
|
||||||
|
var encoded = encode(text, Encoder, newOptions);
|
||||||
|
api._encodings.push(encoded);
|
||||||
|
|
||||||
|
return api;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode() handles the Encoder call and builds the binary string to be rendered
|
||||||
|
function encode(text, Encoder, options) {
|
||||||
|
// Ensure that text is a string
|
||||||
|
text = "" + text;
|
||||||
|
|
||||||
|
var encoder = new Encoder(text, options);
|
||||||
|
|
||||||
|
// If the input is not valid for the encoder, throw error.
|
||||||
|
// If the valid callback option is set, call it instead of throwing error
|
||||||
|
if (!encoder.valid()) {
|
||||||
|
throw new InvalidInputException(encoder.constructor.name, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a request for the binary data (and other infromation) that should be rendered
|
||||||
|
var encoded = encoder.encode();
|
||||||
|
|
||||||
|
// Encodings can be nestled like [[1-1, 1-2], 2, [3-1, 3-2]
|
||||||
|
// Convert to [1-1, 1-2, 2, 3-1, 3-2]
|
||||||
|
encoded = linearizeEncodings(encoded);
|
||||||
|
|
||||||
|
// Merge
|
||||||
|
for (let i = 0; i < encoded.length; i++) {
|
||||||
|
encoded[i].options = merge(options, encoded[i].options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoSelectBarcode() {
|
||||||
|
// If CODE128 exists. Use it
|
||||||
|
if (barcodes["CODE128"]) {
|
||||||
|
return "CODE128";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else, take the first (probably only) barcode
|
||||||
|
return Object.keys(barcodes)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets global encoder options
|
||||||
|
// Added to the api by the JsBarcode function
|
||||||
|
API.prototype.options = function (options) {
|
||||||
|
this._options = merge(this._options, options);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Will create a blank space (usually in between barcodes)
|
||||||
|
API.prototype.blank = function (size) {
|
||||||
|
// const zeroes = new Array(size + 1).join("0");
|
||||||
|
// this._encodings.push({ data: zeroes });
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize JsBarcode on all HTML elements defined.
|
||||||
|
API.prototype.init = function () {
|
||||||
|
// Should do nothing if no elements where found
|
||||||
|
// if (!this._renderProperties) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Make sure renderProperies is an array
|
||||||
|
// if (!Array.isArray(this._renderProperties)) {
|
||||||
|
// this._renderProperties = [this._renderProperties];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var renderProperty;
|
||||||
|
// for (let i in this._renderProperties) {
|
||||||
|
// renderProperty = this._renderProperties[i];
|
||||||
|
// var options = merge(this._options, renderProperty.options);
|
||||||
|
|
||||||
|
// if (options.format == "auto") {
|
||||||
|
// options.format = autoSelectBarcode();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// this._errorHandler.wrapBarcodeCall(function () {
|
||||||
|
// var text = options.value;
|
||||||
|
// var Encoder = barcodes[options.format.toUpperCase()];
|
||||||
|
// var encoded = encode(text, Encoder, options);
|
||||||
|
// render(renderProperty, encoded, options);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The render API call. Calls the real render function.
|
||||||
|
API.prototype.render = function () {
|
||||||
|
// if (!this._renderProperties) {
|
||||||
|
// throw new NoElementException();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (Array.isArray(this._renderProperties)) {
|
||||||
|
// for (var i = 0; i < this._renderProperties.length; i++) {
|
||||||
|
// render(this._renderProperties[i], this._encodings, this._options);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// render(this._renderProperties, this._encodings, this._options);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype._defaults = defaults;
|
||||||
|
|
||||||
|
// Prepares the encodings and calls the renderer
|
||||||
|
function render(renderProperties, encodings, options) {
|
||||||
|
encodings = linearizeEncodings(encodings);
|
||||||
|
|
||||||
|
for (let i = 0; i < encodings.length; i++) {
|
||||||
|
encodings[i].options = merge(options, encodings[i].options);
|
||||||
|
fixOptions(encodings[i].options);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixOptions(options);
|
||||||
|
|
||||||
|
var Renderer = renderProperties.renderer;
|
||||||
|
var renderer = new Renderer(renderProperties.element, encodings, options);
|
||||||
|
renderer.render();
|
||||||
|
|
||||||
|
if (renderProperties.afterRender) {
|
||||||
|
renderProperties.afterRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { JsBarcode };
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
class Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
this.data = data;
|
||||||
|
this.text = options.text || data;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Barcode;
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
import Barcode from "../Barcode.js";
|
||||||
|
import { SHIFT, SET_A, SET_B, MODULO, STOP, FNC1, SET_BY_CODE, SWAP, BARS } from './constants';
|
||||||
|
|
||||||
|
// This is the master class,
|
||||||
|
// it does require the start code to be included in the string
|
||||||
|
class CODE128 extends Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data.substring(1), options);
|
||||||
|
|
||||||
|
// Get array of ascii codes from data
|
||||||
|
this.bytes = data.split('')
|
||||||
|
.map(char => char.charCodeAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
// ASCII value ranges 0-127, 200-211
|
||||||
|
return /^[\x00-\x7F\xC8-\xD3]+$/.test(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The public encoding function
|
||||||
|
encode() {
|
||||||
|
const bytes = this.bytes;
|
||||||
|
// Remove the start code from the bytes and set its index
|
||||||
|
const startIndex = bytes.shift() - 105;
|
||||||
|
// Get start set by index
|
||||||
|
const startSet = SET_BY_CODE[startIndex];
|
||||||
|
|
||||||
|
if (startSet === undefined) {
|
||||||
|
throw new RangeError('The encoding does not start with a start character.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.shouldEncodeAsEan128() === true) {
|
||||||
|
bytes.unshift(FNC1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start encode with the right type
|
||||||
|
const encodingResult = CODE128.next(bytes, 1, startSet);
|
||||||
|
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
this.text === this.data
|
||||||
|
? this.text.replace(/[^\x20-\x7E]/g, '')
|
||||||
|
: this.text,
|
||||||
|
data:
|
||||||
|
// Add the start bits
|
||||||
|
CODE128.getBar(startIndex) +
|
||||||
|
// Add the encoded bits
|
||||||
|
encodingResult.result +
|
||||||
|
// Add the checksum
|
||||||
|
CODE128.getBar((encodingResult.checksum + startIndex) % MODULO) +
|
||||||
|
// Add the end bits
|
||||||
|
CODE128.getBar(STOP)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// GS1-128/EAN-128
|
||||||
|
shouldEncodeAsEan128() {
|
||||||
|
let isEAN128 = this.options.ean128 || false;
|
||||||
|
if (typeof isEAN128 === 'string') {
|
||||||
|
isEAN128 = isEAN128.toLowerCase() === 'true';
|
||||||
|
}
|
||||||
|
return isEAN128;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a bar symbol by index
|
||||||
|
static getBar(index) {
|
||||||
|
return BARS[index] ? BARS[index].toString() : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correct an index by a set and shift it from the bytes array
|
||||||
|
static correctIndex(bytes, set) {
|
||||||
|
if (set === SET_A) {
|
||||||
|
const charCode = bytes.shift();
|
||||||
|
return charCode < 32 ? charCode + 64 : charCode - 32;
|
||||||
|
} else if (set === SET_B) {
|
||||||
|
return bytes.shift() - 32;
|
||||||
|
} else {
|
||||||
|
return (bytes.shift() - 48) * 10 + bytes.shift() - 48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static next(bytes, pos, set) {
|
||||||
|
if (!bytes.length) {
|
||||||
|
return { result: '', checksum: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextCode, index;
|
||||||
|
|
||||||
|
// Special characters
|
||||||
|
if (bytes[0] >= 200) {
|
||||||
|
index = bytes.shift() - 105;
|
||||||
|
const nextSet = SWAP[index];
|
||||||
|
|
||||||
|
// Swap to other set
|
||||||
|
if (nextSet !== undefined) {
|
||||||
|
nextCode = CODE128.next(bytes, pos + 1, nextSet);
|
||||||
|
}
|
||||||
|
// Continue on current set but encode a special character
|
||||||
|
else {
|
||||||
|
// Shift
|
||||||
|
if ((set === SET_A || set === SET_B) && index === SHIFT) {
|
||||||
|
// Convert the next character so that is encoded correctly
|
||||||
|
bytes[0] = (set === SET_A)
|
||||||
|
? bytes[0] > 95 ? bytes[0] - 96 : bytes[0]
|
||||||
|
: bytes[0] < 32 ? bytes[0] + 96 : bytes[0];
|
||||||
|
}
|
||||||
|
nextCode = CODE128.next(bytes, pos + 1, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Continue encoding
|
||||||
|
else {
|
||||||
|
index = CODE128.correctIndex(bytes, set);
|
||||||
|
nextCode = CODE128.next(bytes, pos + 1, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the correct binary encoding and calculate the weight
|
||||||
|
const enc = CODE128.getBar(index);
|
||||||
|
const weight = index * pos;
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: enc + nextCode.result,
|
||||||
|
checksum: weight + nextCode.checksum
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CODE128;
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import CODE128 from './CODE128.js';
|
||||||
|
import { A_START_CHAR, A_CHARS } from './constants';
|
||||||
|
|
||||||
|
class CODE128A extends CODE128 {
|
||||||
|
constructor(string, options) {
|
||||||
|
super(A_START_CHAR + string, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return (new RegExp(`^${A_CHARS}+$`)).test(this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CODE128A;
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import CODE128 from './CODE128.js';
|
||||||
|
import { B_START_CHAR, B_CHARS } from './constants';
|
||||||
|
|
||||||
|
class CODE128B extends CODE128 {
|
||||||
|
constructor(string, options) {
|
||||||
|
super(B_START_CHAR + string, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return (new RegExp(`^${B_CHARS}+$`)).test(this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CODE128B;
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import CODE128 from './CODE128.js';
|
||||||
|
import { C_START_CHAR, C_CHARS } from './constants';
|
||||||
|
|
||||||
|
class CODE128C extends CODE128 {
|
||||||
|
constructor(string, options) {
|
||||||
|
super(C_START_CHAR + string, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return (new RegExp(`^${C_CHARS}+$`)).test(this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CODE128C;
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import CODE128 from './CODE128';
|
||||||
|
import autoSelectModes from './auto';
|
||||||
|
|
||||||
|
class CODE128AUTO extends CODE128 {
|
||||||
|
constructor(data, options) {
|
||||||
|
// ASCII value ranges 0-127, 200-211
|
||||||
|
if (/^[\x00-\x7F\xC8-\xD3]+$/.test(data)) {
|
||||||
|
super(autoSelectModes(data), options);
|
||||||
|
} else {
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CODE128AUTO;
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { A_START_CHAR, B_START_CHAR, C_START_CHAR, A_CHARS, B_CHARS, C_CHARS } from './constants';
|
||||||
|
|
||||||
|
// Match Set functions
|
||||||
|
const matchSetALength = (string) => string.match(new RegExp(`^${A_CHARS}*`))[0].length;
|
||||||
|
const matchSetBLength = (string) => string.match(new RegExp(`^${B_CHARS}*`))[0].length;
|
||||||
|
const matchSetC = (string) => string.match(new RegExp(`^${C_CHARS}*`))[0];
|
||||||
|
|
||||||
|
// CODE128A or CODE128B
|
||||||
|
function autoSelectFromAB(string, isA) {
|
||||||
|
const ranges = isA ? A_CHARS : B_CHARS;
|
||||||
|
const untilC = string.match(new RegExp(`^(${ranges}+?)(([0-9]{2}){2,})([^0-9]|$)`));
|
||||||
|
|
||||||
|
if (untilC) {
|
||||||
|
return (
|
||||||
|
untilC[1] +
|
||||||
|
String.fromCharCode(204) +
|
||||||
|
autoSelectFromC(string.substring(untilC[1].length))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const chars = string.match(new RegExp(`^${ranges}+`))[0];
|
||||||
|
|
||||||
|
if (chars.length === string.length) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
chars +
|
||||||
|
String.fromCharCode(isA ? 205 : 206) +
|
||||||
|
autoSelectFromAB(string.substring(chars.length), !isA)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CODE128C
|
||||||
|
function autoSelectFromC(string) {
|
||||||
|
const cMatch = matchSetC(string);
|
||||||
|
const length = cMatch.length;
|
||||||
|
|
||||||
|
if (length === string.length) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
string = string.substring(length);
|
||||||
|
|
||||||
|
// Select A/B depending on the longest match
|
||||||
|
const isA = matchSetALength(string) >= matchSetBLength(string);
|
||||||
|
return cMatch + String.fromCharCode(isA ? 206 : 205) + autoSelectFromAB(string, isA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect Code Set (A, B or C) and format the string
|
||||||
|
export default (string) => {
|
||||||
|
let newString;
|
||||||
|
const cLength = matchSetC(string).length;
|
||||||
|
|
||||||
|
// Select 128C if the string start with enough digits
|
||||||
|
if (cLength >= 2) {
|
||||||
|
newString = C_START_CHAR + autoSelectFromC(string);
|
||||||
|
} else {
|
||||||
|
// Select A/B depending on the longest match
|
||||||
|
const isA = matchSetALength(string) > matchSetBLength(string);
|
||||||
|
newString = (isA ? A_START_CHAR : B_START_CHAR) + autoSelectFromAB(string, isA);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newString.replace(
|
||||||
|
/[\xCD\xCE]([^])[\xCD\xCE]/, // Any sequence between 205 and 206 characters
|
||||||
|
(match, char) => String.fromCharCode(203) + char
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// constants for internal usage
|
||||||
|
export const SET_A = 0;
|
||||||
|
export const SET_B = 1;
|
||||||
|
export const SET_C = 2;
|
||||||
|
|
||||||
|
// Special characters
|
||||||
|
export const SHIFT = 98;
|
||||||
|
export const START_A = 103;
|
||||||
|
export const START_B = 104;
|
||||||
|
export const START_C = 105;
|
||||||
|
export const MODULO = 103;
|
||||||
|
export const STOP = 106;
|
||||||
|
export const FNC1 = 207;
|
||||||
|
|
||||||
|
// Get set by start code
|
||||||
|
export const SET_BY_CODE = {
|
||||||
|
[START_A]: SET_A,
|
||||||
|
[START_B]: SET_B,
|
||||||
|
[START_C]: SET_C,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get next set by code
|
||||||
|
export const SWAP = {
|
||||||
|
101: SET_A,
|
||||||
|
100: SET_B,
|
||||||
|
99: SET_C,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const A_START_CHAR = String.fromCharCode(208); // START_A + 105
|
||||||
|
export const B_START_CHAR = String.fromCharCode(209); // START_B + 105
|
||||||
|
export const C_START_CHAR = String.fromCharCode(210); // START_C + 105
|
||||||
|
|
||||||
|
// 128A (Code Set A)
|
||||||
|
// ASCII characters 00 to 95 (0–9, A–Z and control codes), special characters, and FNC 1–4
|
||||||
|
export const A_CHARS = "[\x00-\x5F\xC8-\xCF]";
|
||||||
|
|
||||||
|
// 128B (Code Set B)
|
||||||
|
// ASCII characters 32 to 127 (0–9, A–Z, a–z), special characters, and FNC 1–4
|
||||||
|
export const B_CHARS = "[\x20-\x7F\xC8-\xCF]";
|
||||||
|
|
||||||
|
// 128C (Code Set C)
|
||||||
|
// 00–99 (encodes two digits with a single code point) and FNC1
|
||||||
|
export const C_CHARS = "(\xCF*[0-9]{2}\xCF*)";
|
||||||
|
|
||||||
|
// CODE128 includes 107 symbols:
|
||||||
|
// 103 data symbols, 3 start symbols (A, B and C), and 1 stop symbol (the last one)
|
||||||
|
// Each symbol consist of three black bars (1) and three white spaces (0).
|
||||||
|
export const BARS = [
|
||||||
|
11011001100, 11001101100, 11001100110, 10010011000, 10010001100,
|
||||||
|
10001001100, 10011001000, 10011000100, 10001100100, 11001001000,
|
||||||
|
11001000100, 11000100100, 10110011100, 10011011100, 10011001110,
|
||||||
|
10111001100, 10011101100, 10011100110, 11001110010, 11001011100,
|
||||||
|
11001001110, 11011100100, 11001110100, 11101101110, 11101001100,
|
||||||
|
11100101100, 11100100110, 11101100100, 11100110100, 11100110010,
|
||||||
|
11011011000, 11011000110, 11000110110, 10100011000, 10001011000,
|
||||||
|
10001000110, 10110001000, 10001101000, 10001100010, 11010001000,
|
||||||
|
11000101000, 11000100010, 10110111000, 10110001110, 10001101110,
|
||||||
|
10111011000, 10111000110, 10001110110, 11101110110, 11010001110,
|
||||||
|
11000101110, 11011101000, 11011100010, 11011101110, 11101011000,
|
||||||
|
11101000110, 11100010110, 11101101000, 11101100010, 11100011010,
|
||||||
|
11101111010, 11001000010, 11110001010, 10100110000, 10100001100,
|
||||||
|
10010110000, 10010000110, 10000101100, 10000100110, 10110010000,
|
||||||
|
10110000100, 10011010000, 10011000010, 10000110100, 10000110010,
|
||||||
|
11000010010, 11001010000, 11110111010, 11000010100, 10001111010,
|
||||||
|
10100111100, 10010111100, 10010011110, 10111100100, 10011110100,
|
||||||
|
10011110010, 11110100100, 11110010100, 11110010010, 11011011110,
|
||||||
|
11011110110, 11110110110, 10101111000, 10100011110, 10001011110,
|
||||||
|
10111101000, 10111100010, 11110101000, 11110100010, 10111011110,
|
||||||
|
10111101110, 11101011110, 11110101110, 11010000100, 11010010000,
|
||||||
|
11010011100, 1100011101011
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
import CODE128 from './CODE128_AUTO.js';
|
||||||
|
import CODE128A from './CODE128A.js';
|
||||||
|
import CODE128B from './CODE128B.js';
|
||||||
|
import CODE128C from './CODE128C.js';
|
||||||
|
|
||||||
|
export { CODE128, CODE128A, CODE128B, CODE128C };
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/Code_39#Encoding
|
||||||
|
|
||||||
|
import Barcode from "../Barcode.js";
|
||||||
|
|
||||||
|
class CODE39 extends Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
data = data.toUpperCase();
|
||||||
|
|
||||||
|
// Calculate mod43 checksum if enabled
|
||||||
|
if (options.mod43) {
|
||||||
|
data += getCharacter(mod43checksum(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
// First character is always a *
|
||||||
|
var result = getEncoding("*");
|
||||||
|
|
||||||
|
// Take every character and add the binary representation to the result
|
||||||
|
for (let i = 0; i < this.data.length; i++) {
|
||||||
|
result += getEncoding(this.data[i]) + "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last character is always a *
|
||||||
|
result += getEncoding("*");
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: result,
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.data.search(/^[0-9A-Z\-\.\ \$\/\+\%]+$/) !== -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// All characters. The position in the array is the (checksum) value
|
||||||
|
var characters = [
|
||||||
|
"0", "1", "2", "3",
|
||||||
|
"4", "5", "6", "7",
|
||||||
|
"8", "9", "A", "B",
|
||||||
|
"C", "D", "E", "F",
|
||||||
|
"G", "H", "I", "J",
|
||||||
|
"K", "L", "M", "N",
|
||||||
|
"O", "P", "Q", "R",
|
||||||
|
"S", "T", "U", "V",
|
||||||
|
"W", "X", "Y", "Z",
|
||||||
|
"-", ".", " ", "$",
|
||||||
|
"/", "+", "%", "*"
|
||||||
|
];
|
||||||
|
|
||||||
|
// The decimal representation of the characters, is converted to the
|
||||||
|
// corresponding binary with the getEncoding function
|
||||||
|
var encodings = [
|
||||||
|
20957, 29783, 23639, 30485,
|
||||||
|
20951, 29813, 23669, 20855,
|
||||||
|
29789, 23645, 29975, 23831,
|
||||||
|
30533, 22295, 30149, 24005,
|
||||||
|
21623, 29981, 23837, 22301,
|
||||||
|
30023, 23879, 30545, 22343,
|
||||||
|
30161, 24017, 21959, 30065,
|
||||||
|
23921, 22385, 29015, 18263,
|
||||||
|
29141, 17879, 29045, 18293,
|
||||||
|
17783, 29021, 18269, 17477,
|
||||||
|
17489, 17681, 20753, 35770
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get the binary representation of a character by converting the encodings
|
||||||
|
// from decimal to binary
|
||||||
|
function getEncoding(character) {
|
||||||
|
return getBinary(characterValue(character));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBinary(characterValue) {
|
||||||
|
return encodings[characterValue].toString(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCharacter(characterValue) {
|
||||||
|
return characters[characterValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
function characterValue(character) {
|
||||||
|
return characters.indexOf(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mod43checksum(data) {
|
||||||
|
var checksum = 0;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
checksum += characterValue(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = checksum % 43;
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { CODE39 };
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { SIDE_BIN, MIDDLE_BIN } from './constants';
|
||||||
|
import encode from './encoder';
|
||||||
|
import Barcode from '../Barcode';
|
||||||
|
|
||||||
|
// Base class for EAN8 & EAN13
|
||||||
|
class EAN extends Barcode {
|
||||||
|
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data, options);
|
||||||
|
|
||||||
|
// Make sure the font is not bigger than the space between the guard bars
|
||||||
|
this.fontSize = !options.flat && options.fontSize > options.width * 10
|
||||||
|
? options.width * 10
|
||||||
|
: options.fontSize;
|
||||||
|
|
||||||
|
// Make the guard bars go down half the way of the text
|
||||||
|
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
return this.options.flat
|
||||||
|
? this.encodeFlat()
|
||||||
|
: this.encodeGuarded();
|
||||||
|
}
|
||||||
|
|
||||||
|
leftText(from, to) {
|
||||||
|
return this.text.substr(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftEncode(data, structure) {
|
||||||
|
return encode(data, structure);
|
||||||
|
}
|
||||||
|
|
||||||
|
rightText(from, to) {
|
||||||
|
return this.text.substr(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
rightEncode(data, structure) {
|
||||||
|
return encode(data, structure);
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeGuarded() {
|
||||||
|
const textOptions = { fontSize: this.fontSize };
|
||||||
|
const guardOptions = { height: this.guardHeight };
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ data: SIDE_BIN, options: guardOptions },
|
||||||
|
{ data: this.leftEncode(), text: this.leftText(), options: textOptions },
|
||||||
|
{ data: MIDDLE_BIN, options: guardOptions },
|
||||||
|
{ data: this.rightEncode(), text: this.rightText(), options: textOptions },
|
||||||
|
{ data: SIDE_BIN, options: guardOptions },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeFlat() {
|
||||||
|
const data = [
|
||||||
|
SIDE_BIN,
|
||||||
|
this.leftEncode(),
|
||||||
|
MIDDLE_BIN,
|
||||||
|
this.rightEncode(),
|
||||||
|
SIDE_BIN
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data.join(''),
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EAN;
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Binary_encoding_of_data_digits_into_EAN-13_barcode
|
||||||
|
|
||||||
|
import { EAN13_STRUCTURE } from './constants';
|
||||||
|
import EAN from './EAN';
|
||||||
|
|
||||||
|
// Calculate the checksum digit
|
||||||
|
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Calculation_of_checksum_digit
|
||||||
|
const checksum = (number) => {
|
||||||
|
const res = number
|
||||||
|
.substr(0, 12)
|
||||||
|
.split('')
|
||||||
|
.map((n) => +n)
|
||||||
|
.reduce((sum, a, idx) => (
|
||||||
|
idx % 2 ? sum + a * 3 : sum + a
|
||||||
|
), 0);
|
||||||
|
|
||||||
|
return (10 - (res % 10)) % 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EAN13 extends EAN {
|
||||||
|
|
||||||
|
constructor(data, options) {
|
||||||
|
// Add checksum if it does not exist
|
||||||
|
if (data.search(/^[0-9]{12}$/) !== -1) {
|
||||||
|
data += checksum(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
super(data, options);
|
||||||
|
|
||||||
|
// Adds a last character to the end of the barcode
|
||||||
|
this.lastChar = options.lastChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return (
|
||||||
|
this.data.search(/^[0-9]{13}$/) !== -1 &&
|
||||||
|
+this.data[12] === checksum(this.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftText() {
|
||||||
|
return super.leftText(1, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftEncode() {
|
||||||
|
const data = this.data.substr(1, 6);
|
||||||
|
const structure = EAN13_STRUCTURE[this.data[0]];
|
||||||
|
return super.leftEncode(data, structure);
|
||||||
|
}
|
||||||
|
|
||||||
|
rightText() {
|
||||||
|
return super.rightText(7, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
rightEncode() {
|
||||||
|
const data = this.data.substr(7, 6);
|
||||||
|
return super.rightEncode(data, 'RRRRRR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "standard" way of printing EAN13 barcodes with guard bars
|
||||||
|
encodeGuarded() {
|
||||||
|
const data = super.encodeGuarded();
|
||||||
|
// Extend data with left digit & last character
|
||||||
|
if (this.options.displayValue) {
|
||||||
|
data.unshift({
|
||||||
|
data: '000000000000',
|
||||||
|
text: this.text.substr(0, 1),
|
||||||
|
options: { textAlign: 'left', fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.lastChar) {
|
||||||
|
data.push({
|
||||||
|
data: '00'
|
||||||
|
});
|
||||||
|
data.push({
|
||||||
|
data: '00000',
|
||||||
|
text: this.options.lastChar,
|
||||||
|
options: { fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EAN13;
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/EAN_2#Encoding
|
||||||
|
|
||||||
|
import { EAN2_STRUCTURE } from './constants';
|
||||||
|
import encode from './encoder';
|
||||||
|
import Barcode from '../Barcode';
|
||||||
|
|
||||||
|
class EAN2 extends Barcode {
|
||||||
|
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.data.search(/^[0-9]{2}$/) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
// Choose the structure based on the number mod 4
|
||||||
|
const structure = EAN2_STRUCTURE[parseInt(this.data) % 4];
|
||||||
|
return {
|
||||||
|
// Start bits + Encode the two digits with 01 in between
|
||||||
|
data: '1011' + encode(this.data, structure, '01'),
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EAN2;
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/EAN_5#Encoding
|
||||||
|
|
||||||
|
import { EAN5_STRUCTURE } from './constants';
|
||||||
|
import encode from './encoder';
|
||||||
|
import Barcode from '../Barcode';
|
||||||
|
|
||||||
|
const checksum = (data) => {
|
||||||
|
const result = data
|
||||||
|
.split('')
|
||||||
|
.map(n => +n)
|
||||||
|
.reduce((sum, a, idx) => {
|
||||||
|
return idx % 2
|
||||||
|
? sum + a * 9
|
||||||
|
: sum + a * 3;
|
||||||
|
}, 0);
|
||||||
|
return result % 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EAN5 extends Barcode {
|
||||||
|
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.data.search(/^[0-9]{5}$/) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
const structure = EAN5_STRUCTURE[checksum(this.data)];
|
||||||
|
return {
|
||||||
|
data: '1011' + encode(this.data, structure, '01'),
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EAN5;
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// http://www.barcodeisland.com/ean8.phtml
|
||||||
|
|
||||||
|
import EAN from './EAN';
|
||||||
|
|
||||||
|
// Calculate the checksum digit
|
||||||
|
const checksum = (number) => {
|
||||||
|
const res = number
|
||||||
|
.substr(0, 7)
|
||||||
|
.split('')
|
||||||
|
.map((n) => +n)
|
||||||
|
.reduce((sum, a, idx) => (
|
||||||
|
idx % 2 ? sum + a : sum + a * 3
|
||||||
|
), 0);
|
||||||
|
|
||||||
|
return (10 - (res % 10)) % 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EAN8 extends EAN {
|
||||||
|
|
||||||
|
constructor(data, options) {
|
||||||
|
// Add checksum if it does not exist
|
||||||
|
if (data.search(/^[0-9]{7}$/) !== -1) {
|
||||||
|
data += checksum(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return (
|
||||||
|
this.data.search(/^[0-9]{8}$/) !== -1 &&
|
||||||
|
+this.data[7] === checksum(this.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftText() {
|
||||||
|
return super.leftText(0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftEncode() {
|
||||||
|
const data = this.data.substr(0, 4);
|
||||||
|
return super.leftEncode(data, 'LLLL');
|
||||||
|
}
|
||||||
|
|
||||||
|
rightText() {
|
||||||
|
return super.rightText(4, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
rightEncode() {
|
||||||
|
const data = this.data.substr(4, 4);
|
||||||
|
return super.rightEncode(data, 'RRRR');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EAN8;
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding
|
||||||
|
|
||||||
|
import encode from './encoder';
|
||||||
|
import Barcode from "../Barcode.js";
|
||||||
|
|
||||||
|
class UPC extends Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
// Add checksum if it does not exist
|
||||||
|
if (data.search(/^[0-9]{11}$/) !== -1) {
|
||||||
|
data += checksum(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
super(data, options);
|
||||||
|
|
||||||
|
this.displayValue = options.displayValue;
|
||||||
|
|
||||||
|
// Make sure the font is not bigger than the space between the guard bars
|
||||||
|
if (options.fontSize > options.width * 10) {
|
||||||
|
this.fontSize = options.width * 10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.fontSize = options.fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the guard bars go down half the way of the text
|
||||||
|
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.data.search(/^[0-9]{12}$/) !== -1 &&
|
||||||
|
this.data[11] == checksum(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
if (this.options.flat) {
|
||||||
|
return this.flatEncoding();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.guardedEncoding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flatEncoding() {
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
result += "101";
|
||||||
|
result += encode(this.data.substr(0, 6), "LLLLLL");
|
||||||
|
result += "01010";
|
||||||
|
result += encode(this.data.substr(6, 6), "RRRRRR");
|
||||||
|
result += "101";
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: result,
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
guardedEncoding() {
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
// Add the first digit
|
||||||
|
if (this.displayValue) {
|
||||||
|
result.push({
|
||||||
|
data: "00000000",
|
||||||
|
text: this.text.substr(0, 1),
|
||||||
|
options: { textAlign: "left", fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the guard bars
|
||||||
|
result.push({
|
||||||
|
data: "101" + encode(this.data[0], "L"),
|
||||||
|
options: { height: this.guardHeight }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the left side
|
||||||
|
result.push({
|
||||||
|
data: encode(this.data.substr(1, 5), "LLLLL"),
|
||||||
|
text: this.text.substr(1, 5),
|
||||||
|
options: { fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the middle bits
|
||||||
|
result.push({
|
||||||
|
data: "01010",
|
||||||
|
options: { height: this.guardHeight }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the right side
|
||||||
|
result.push({
|
||||||
|
data: encode(this.data.substr(6, 5), "RRRRR"),
|
||||||
|
text: this.text.substr(6, 5),
|
||||||
|
options: { fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the end bits
|
||||||
|
result.push({
|
||||||
|
data: encode(this.data[11], "R") + "101",
|
||||||
|
options: { height: this.guardHeight }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the last digit
|
||||||
|
if (this.displayValue) {
|
||||||
|
result.push({
|
||||||
|
data: "00000000",
|
||||||
|
text: this.text.substr(11, 1),
|
||||||
|
options: { textAlign: "right", fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calulate the checksum digit
|
||||||
|
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Calculation_of_checksum_digit
|
||||||
|
export function checksum(number) {
|
||||||
|
var result = 0;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
for (i = 1; i < 11; i += 2) {
|
||||||
|
result += parseInt(number[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 11; i += 2) {
|
||||||
|
result += parseInt(number[i]) * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (10 - (result % 10)) % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UPC;
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
// Encoding documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding
|
||||||
|
//
|
||||||
|
// UPC-E documentation:
|
||||||
|
// https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E
|
||||||
|
|
||||||
|
import encode from './encoder';
|
||||||
|
import Barcode from "../Barcode.js";
|
||||||
|
import { checksum } from './UPC.js';
|
||||||
|
|
||||||
|
const EXPANSIONS = [
|
||||||
|
"XX00000XXX",
|
||||||
|
"XX10000XXX",
|
||||||
|
"XX20000XXX",
|
||||||
|
"XXX00000XX",
|
||||||
|
"XXXX00000X",
|
||||||
|
"XXXXX00005",
|
||||||
|
"XXXXX00006",
|
||||||
|
"XXXXX00007",
|
||||||
|
"XXXXX00008",
|
||||||
|
"XXXXX00009"
|
||||||
|
];
|
||||||
|
|
||||||
|
const PARITIES = [
|
||||||
|
["EEEOOO", "OOOEEE"],
|
||||||
|
["EEOEOO", "OOEOEE"],
|
||||||
|
["EEOOEO", "OOEEOE"],
|
||||||
|
["EEOOOE", "OOEEEO"],
|
||||||
|
["EOEEOO", "OEOOEE"],
|
||||||
|
["EOOEEO", "OEEOOE"],
|
||||||
|
["EOOOEE", "OEEEOO"],
|
||||||
|
["EOEOEO", "OEOEOE"],
|
||||||
|
["EOEOOE", "OEOEEO"],
|
||||||
|
["EOOEOE", "OEEOEO"]
|
||||||
|
];
|
||||||
|
|
||||||
|
class UPCE extends Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
// Code may be 6 or 8 digits;
|
||||||
|
// A 7 digit code is ambiguous as to whether the extra digit
|
||||||
|
// is a UPC-A check or number system digit.
|
||||||
|
super(data, options);
|
||||||
|
this.isValid = false;
|
||||||
|
if (data.search(/^[0-9]{6}$/) !== -1) {
|
||||||
|
this.middleDigits = data;
|
||||||
|
this.upcA = expandToUPCA(data, "0");
|
||||||
|
this.text = options.text ||
|
||||||
|
`${this.upcA[0]}${data}${this.upcA[this.upcA.length - 1]}`;
|
||||||
|
this.isValid = true;
|
||||||
|
}
|
||||||
|
else if (data.search(/^[01][0-9]{7}$/) !== -1) {
|
||||||
|
this.middleDigits = data.substring(1, data.length - 1);
|
||||||
|
this.upcA = expandToUPCA(this.middleDigits, data[0]);
|
||||||
|
|
||||||
|
if (this.upcA[this.upcA.length - 1] === data[data.length - 1]) {
|
||||||
|
this.isValid = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// checksum mismatch
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displayValue = options.displayValue;
|
||||||
|
|
||||||
|
// Make sure the font is not bigger than the space between the guard bars
|
||||||
|
if (options.fontSize > options.width * 10) {
|
||||||
|
this.fontSize = options.width * 10;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.fontSize = options.fontSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the guard bars go down half the way of the text
|
||||||
|
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
if (this.options.flat) {
|
||||||
|
return this.flatEncoding();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.guardedEncoding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flatEncoding() {
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
result += "101";
|
||||||
|
result += this.encodeMiddleDigits();
|
||||||
|
result += "010101";
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: result,
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
guardedEncoding() {
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
// Add the UPC-A number system digit beneath the quiet zone
|
||||||
|
if (this.displayValue) {
|
||||||
|
result.push({
|
||||||
|
data: "00000000",
|
||||||
|
text: this.text[0],
|
||||||
|
options: { textAlign: "left", fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the guard bars
|
||||||
|
result.push({
|
||||||
|
data: "101",
|
||||||
|
options: { height: this.guardHeight }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the 6 UPC-E digits
|
||||||
|
result.push({
|
||||||
|
data: this.encodeMiddleDigits(),
|
||||||
|
text: this.text.substring(1, 7),
|
||||||
|
options: { fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the end bits
|
||||||
|
result.push({
|
||||||
|
data: "010101",
|
||||||
|
options: { height: this.guardHeight }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the UPC-A check digit beneath the quiet zone
|
||||||
|
if (this.displayValue) {
|
||||||
|
result.push({
|
||||||
|
data: "00000000",
|
||||||
|
text: this.text[7],
|
||||||
|
options: { textAlign: "right", fontSize: this.fontSize }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeMiddleDigits() {
|
||||||
|
const numberSystem = this.upcA[0];
|
||||||
|
const checkDigit = this.upcA[this.upcA.length - 1];
|
||||||
|
const parity = PARITIES[parseInt(checkDigit)][parseInt(numberSystem)];
|
||||||
|
return encode(this.middleDigits, parity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandToUPCA(middleDigits, numberSystem) {
|
||||||
|
const lastUpcE = parseInt(middleDigits[middleDigits.length - 1]);
|
||||||
|
const expansion = EXPANSIONS[lastUpcE];
|
||||||
|
|
||||||
|
let result = "";
|
||||||
|
let digitIndex = 0;
|
||||||
|
for (let i = 0; i < expansion.length; i++) {
|
||||||
|
let c = expansion[i];
|
||||||
|
if (c === 'X') {
|
||||||
|
result += middleDigits[digitIndex++];
|
||||||
|
} else {
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = `${numberSystem}${result}`;
|
||||||
|
return `${result}${checksum(result)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UPCE;
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Standard start end and middle bits
|
||||||
|
export const SIDE_BIN = '101';
|
||||||
|
export const MIDDLE_BIN = '01010';
|
||||||
|
|
||||||
|
export const BINARIES = {
|
||||||
|
'L': [ // The L (left) type of encoding
|
||||||
|
'0001101', '0011001', '0010011', '0111101', '0100011',
|
||||||
|
'0110001', '0101111', '0111011', '0110111', '0001011'
|
||||||
|
],
|
||||||
|
'G': [ // The G type of encoding
|
||||||
|
'0100111', '0110011', '0011011', '0100001', '0011101',
|
||||||
|
'0111001', '0000101', '0010001', '0001001', '0010111'
|
||||||
|
],
|
||||||
|
'R': [ // The R (right) type of encoding
|
||||||
|
'1110010', '1100110', '1101100', '1000010', '1011100',
|
||||||
|
'1001110', '1010000', '1000100', '1001000', '1110100'
|
||||||
|
],
|
||||||
|
'O': [ // The O (odd) encoding for UPC-E
|
||||||
|
'0001101', '0011001', '0010011', '0111101', '0100011',
|
||||||
|
'0110001', '0101111', '0111011', '0110111', '0001011'
|
||||||
|
],
|
||||||
|
'E': [ // The E (even) encoding for UPC-E
|
||||||
|
'0100111', '0110011', '0011011', '0100001', '0011101',
|
||||||
|
'0111001', '0000101', '0010001', '0001001', '0010111'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define the EAN-2 structure
|
||||||
|
export const EAN2_STRUCTURE = ['LL', 'LG', 'GL', 'GG'];
|
||||||
|
|
||||||
|
// Define the EAN-5 structure
|
||||||
|
export const EAN5_STRUCTURE = [
|
||||||
|
'GGLLL', 'GLGLL', 'GLLGL', 'GLLLG', 'LGGLL',
|
||||||
|
'LLGGL', 'LLLGG', 'LGLGL', 'LGLLG', 'LLGLG'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Define the EAN-13 structure
|
||||||
|
export const EAN13_STRUCTURE = [
|
||||||
|
'LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
|
||||||
|
'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { BINARIES } from './constants';
|
||||||
|
|
||||||
|
// Encode data string
|
||||||
|
const encode = (data, structure, separator) => {
|
||||||
|
let encoded = data
|
||||||
|
.split('')
|
||||||
|
.map((val, idx) => BINARIES[structure[idx]])
|
||||||
|
.map((val, idx) => val ? val[data[idx]] : '');
|
||||||
|
|
||||||
|
if (separator) {
|
||||||
|
const last = data.length - 1;
|
||||||
|
encoded = encoded.map((val, idx) => (
|
||||||
|
idx < last ? val + separator : val
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
export default encode;
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import EAN13 from './EAN13';
|
||||||
|
import EAN8 from './EAN8';
|
||||||
|
import EAN5 from './EAN5';
|
||||||
|
import EAN2 from './EAN2';
|
||||||
|
import UPC from './UPC';
|
||||||
|
import UPCE from './UPCE';
|
||||||
|
|
||||||
|
export { EAN13, EAN8, EAN5, EAN2, UPC, UPCE };
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import Barcode from "../Barcode.js";
|
||||||
|
|
||||||
|
class GenericBarcode extends Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data, options); // Sets this.data and this.text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the corresponding binary numbers for the data provided
|
||||||
|
encode() {
|
||||||
|
return {
|
||||||
|
data: "10101010101010101010101010101010101010101",
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resturn true/false if the string provided is valid for this encoder
|
||||||
|
valid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { GenericBarcode };
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { START_BIN, END_BIN, BINARIES } from './constants';
|
||||||
|
import Barcode from '../Barcode';
|
||||||
|
|
||||||
|
class ITF extends Barcode {
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.data.search(/^([0-9]{2})+$/) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
// Calculate all the digit pairs
|
||||||
|
const encoded = this.data
|
||||||
|
.match(/.{2}/g)
|
||||||
|
.map(pair => this.encodePair(pair))
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: START_BIN + encoded + END_BIN,
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the data of a number pair
|
||||||
|
encodePair(pair) {
|
||||||
|
const second = BINARIES[pair[1]];
|
||||||
|
|
||||||
|
return BINARIES[pair[0]]
|
||||||
|
.split('')
|
||||||
|
.map((first, idx) => (
|
||||||
|
(first === '1' ? '111' : '1') +
|
||||||
|
(second[idx] === '1' ? '000' : '0')
|
||||||
|
))
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ITF;
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import ITF from './ITF';
|
||||||
|
|
||||||
|
// Calculate the checksum digit
|
||||||
|
const checksum = (data) => {
|
||||||
|
const res = data
|
||||||
|
.substr(0, 13)
|
||||||
|
.split('')
|
||||||
|
.map(num => parseInt(num, 10))
|
||||||
|
.reduce((sum, n, idx) => sum + (n * (3 - (idx % 2) * 2)), 0);
|
||||||
|
|
||||||
|
return Math.ceil(res / 10) * 10 - res;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ITF14 extends ITF {
|
||||||
|
|
||||||
|
constructor(data, options) {
|
||||||
|
// Add checksum if it does not exist
|
||||||
|
if (data.search(/^[0-9]{13}$/) !== -1) {
|
||||||
|
data += checksum(data);
|
||||||
|
}
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return (
|
||||||
|
this.data.search(/^[0-9]{14}$/) !== -1 &&
|
||||||
|
+this.data[13] === checksum(this.data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ITF14;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const START_BIN = '1010';
|
||||||
|
export const END_BIN = '11101';
|
||||||
|
|
||||||
|
export const BINARIES = [
|
||||||
|
'00110', '10001', '01001', '11000', '00101',
|
||||||
|
'10100', '01100', '00011', '10010', '01010',
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
import ITF from './ITF';
|
||||||
|
import ITF14 from './ITF14';
|
||||||
|
|
||||||
|
export { ITF, ITF14 };
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Encoding documentation
|
||||||
|
// https://en.wikipedia.org/wiki/MSI_Barcode#Character_set_and_binary_lookup
|
||||||
|
|
||||||
|
import Barcode from "../Barcode.js";
|
||||||
|
|
||||||
|
class MSI extends Barcode {
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
encode() {
|
||||||
|
// Start bits
|
||||||
|
var ret = "110";
|
||||||
|
|
||||||
|
for (var i = 0; i < this.data.length; i++) {
|
||||||
|
// Convert the character to binary (always 4 binary digits)
|
||||||
|
var digit = parseInt(this.data[i]);
|
||||||
|
var bin = digit.toString(2);
|
||||||
|
bin = addZeroes(bin, 4 - bin.length);
|
||||||
|
|
||||||
|
// Add 100 for every zero and 110 for every 1
|
||||||
|
for (var b = 0; b < bin.length; b++) {
|
||||||
|
ret += bin[b] == "0" ? "100" : "110";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// End bits
|
||||||
|
ret += "1001";
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: ret,
|
||||||
|
text: this.text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
valid() {
|
||||||
|
return this.data.search(/^[0-9]+$/) !== -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addZeroes(number, n) {
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
number = "0" + number;
|
||||||
|
}
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MSI;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import MSI from './MSI.js';
|
||||||
|
import { mod10 } from './checksums.js';
|
||||||
|
|
||||||
|
class MSI10 extends MSI {
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data + mod10(data), options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MSI10;
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import MSI from './MSI.js';
|
||||||
|
import { mod10 } from './checksums.js';
|
||||||
|
|
||||||
|
class MSI1010 extends MSI {
|
||||||
|
constructor(data, options) {
|
||||||
|
data += mod10(data);
|
||||||
|
data += mod10(data);
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MSI1010;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import MSI from './MSI.js';
|
||||||
|
import { mod11 } from './checksums.js';
|
||||||
|
|
||||||
|
class MSI11 extends MSI {
|
||||||
|
constructor(data, options) {
|
||||||
|
super(data + mod11(data), options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MSI11;
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import MSI from './MSI.js';
|
||||||
|
import { mod10, mod11 } from './checksums.js';
|
||||||
|
|
||||||
|
class MSI1110 extends MSI {
|
||||||
|
constructor(data, options) {
|
||||||
|
data += mod11(data);
|
||||||
|
data += mod10(data);
|
||||||
|
super(data, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MSI1110;
|
||||||