first commit

This commit is contained in:
sexygoat
2026-01-22 16:43:48 +08:00
parent 36590e3cc0
commit 777265209a
15 changed files with 16425 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

5
babel.config.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
jsconfig.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

14795
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

52
package.json Normal file
View File

@@ -0,0 +1,52 @@
{
"name": "one-pipe-h5",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@vant/area-data": "^2.0.0",
"axios": "^1.9.0",
"core-js": "^3.8.3",
"crypto-js": "^4.2.0",
"i": "^0.3.7",
"js-base64": "^3.7.7",
"npm": "^11.4.1",
"parallax-js": "^3.1.0",
"vant": "^2.13.8",
"vue": "^2.6.14",
"vue-router": "^3.6.5"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

17
public/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

36
src/App.vue Normal file
View File

@@ -0,0 +1,36 @@
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {
name: "App"
};
</script>
<style>
body{
padding: 0;
margin: 0;
box-shadow: border-box;
}
.h5-container {
overflow-y: scroll; /* 或 auto */
scrollbar-width: thin; /* Firefox让它更细 */
scrollbar-color: transparent transparent; /* Firefox轨道和滑块都透明 */
}
/* Chrome / Safari / Edge */
.h5-container::-webkit-scrollbar {
width: 8px; /* 可以设置为 0如果你想完全隐藏 */
background-color: transparent; /* 滚动条轨道背景 */
}
.h5-container::-webkit-scrollbar-thumb {
background-color: transparent; /* 滚动条滑块 */
}
</style>

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

15
src/main.js Normal file
View File

@@ -0,0 +1,15 @@
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
Vue.config.productionTip = false;
import {Toast, Area, Popup} from "vant";
import "vant/lib/index.css";
Vue.use(Toast);
Vue.use(Area);
Vue.use(Popup);
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");

666
src/pages/404.vue Normal file
View File

@@ -0,0 +1,666 @@
<template>
<div class="c-4">
<section class="wrapper">
<div class="container">
<div id="scene" class="scene" data-hover-only="false">
<div class="circle" data-depth="1.2" />
<div class="one" data-depth="0.9">
<div class="content">
<span class="piece" />
<span class="piece" />
<span class="piece" />
</div>
</div>
<div class="two" data-depth="0.60">
<div class="content">
<span class="piece" />
<span class="piece" />
<span class="piece" />
</div>
</div>
<div class="three" data-depth="0.40">
<div class="content">
<span class="piece" />
<span class="piece" />
<span class="piece" />
</div>
</div>
<p class="p404" data-depth="0.50">暂无此产品</p>
<p class="p404" data-depth="0.10">暂无此产品</p>
</div>
<div class="text">
<article>
<!-- <p>此产品不存在</p> -->
<!-- <p></p> -->
<!-- <p>请点击按钮返回首页</p> -->
<!-- <button @click="enterHome">返回首页</button> -->
</article>
</div>
</div>
</section>
</div>
</template>
<script>
import Parallax from 'parallax-js'
export default {
name: 'H5404Page',
data() {
return {
parallaxInstance: null
}
},
created() {},
mounted() {
const scene = document.getElementById('scene')
if (scene) {
this.parallaxInstance = new Parallax(scene)
}
},
beforeDestroy() {
if (this.parallaxInstance) {
this.parallaxInstance.destroy()
this.parallaxInstance = null
}
},
methods: {
enterHome() {
this.$router.push({ name: 'H5' })
}
}
}
</script>
<style scoped>
.about {
position: fixed;
z-index: 10;
bottom: 10px;
right: 10px;
width: 40px;
height: 40px;
display: flex;
justify-content: flex-end;
align-items: flex-end;
transition: all 0.2s ease;
}
.about .bg_links {
width: 40px;
height: 40px;
border-radius: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(5px);
position: absolute;
}
.about .social {
opacity: 0;
right: 0;
bottom: 0;
}
.about:hover {
width: 105px;
height: 105px;
transition: all 0.6s cubic-bezier(0.64, 0.01, 0.07, 1.65);
}
.about:hover .logo {
opacity: 1;
transition: all 0.6s ease;
}
.about:hover .social {
opacity: 1;
}
.about:hover .social .icon {
opacity: 0.9;
}
.about:hover .social:hover {
background-size: 28px;
}
.about:hover .social:hover .icon {
background-size: 65%;
opacity: 1;
}
.about:hover .social.portfolio {
right: 0;
bottom: calc(100% - 40px);
transition: all 0.3s 0s cubic-bezier(0.64, 0.01, 0.07, 1.65);
}
.about:hover .social.portfolio .icon:hover {
background-color: #698fb7;
}
.about:hover .social.linkedin {
bottom: 0;
right: calc(100% - 40px);
transition: all 0.3s 0.25s cubic-bezier(0.64, 0.01, 0.07, 1.65);
}
.about:hover .social.linkedin .icon:hover {
background-color: #0077b5;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
ul,
li,
button,
a,
i,
input,
body {
margin: 0;
padding: 0;
list-style: none;
border: 0;
-webkit-tap-highlight-color: transparent;
text-decoration: none;
color: inherit;
}
h1:focus,
h2:focus,
h3:focus,
h4:focus,
h5:focus,
h6:focus,
p:focus,
ul:focus,
li:focus,
button:focus,
a:focus,
i:focus,
input:focus,
body:focus {
outline: 0;
}
.c-4 {
margin: 0;
padding: 0;
height: 100vh;
overflow: hidden;
background: #695681;
}
.logo {
position: fixed;
z-index: 5;
bottom: 10px;
right: 10px;
width: 40px;
height: 40px;
border-radius: 100%;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.1);
backdrop-filter: blur(5px);
}
.logo img {
width: 55%;
height: 55%;
transform: translateY(-1px);
opacity: 0.8;
}
nav .menu {
width: 100%;
height: 80px;
position: absolute;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 5%;
box-sizing: border-box;
z-index: 3;
}
nav .menu .website_name {
color: #695681;
font-weight: 600;
font-size: 20px;
letter-spacing: 1px;
background: white;
padding: 4px 8px;
border-radius: 2px;
opacity: 0.5;
transition: all 0.4s ease;
cursor: pointer;
}
nav .menu .website_name:hover {
opacity: 1;
}
nav .menu .menu_links {
transition: all 0.4s ease;
opacity: 0.5;
}
nav .menu .menu_links:hover {
opacity: 1;
}
@media screen and (max-width: 799px) {
nav .menu .menu_links {
display: none;
}
}
nav .menu .menu_links .link {
color: white;
text-transform: uppercase;
font-weight: 500;
margin-right: 50px;
letter-spacing: 2px;
position: relative;
transition: all 0.3s 0.2s ease;
}
nav .menu .menu_links .link:last-child {
margin-right: 0;
}
nav .menu .menu_links .link:before {
content: '';
position: absolute;
width: 0;
height: 4px;
background: linear-gradient(90deg, #ffedc0 0%, #ff9d87 100%);
bottom: -10px;
border-radius: 4px;
transition: all 0.4s cubic-bezier(0.82, 0.02, 0.13, 1.26);
left: 100%;
}
nav .menu .menu_links .link:hover {
opacity: 1;
color: #fb8a8a;
}
nav .menu .menu_links .link:hover:before {
width: 40px;
left: 0;
}
nav .menu .menu_icon {
width: 40px;
height: 40px;
position: relative;
display: none;
justify-content: center;
align-items: center;
cursor: pointer;
}
@media screen and (max-width: 799px) {
nav .menu .menu_icon {
display: flex;
}
}
nav .menu .menu_icon .icon {
width: 24px;
height: 2px;
background: white;
position: absolute;
}
nav .menu .menu_icon .icon:before,
nav .menu .menu_icon .icon:after {
content: '';
width: 100%;
height: 100%;
background: inherit;
position: absolute;
transition: all 0.3s cubic-bezier(0.49, 0.04, 0, 1.55);
}
nav .menu .menu_icon .icon:before {
transform: translateY(-8px);
}
nav .menu .menu_icon .icon:after {
transform: translateY(8px);
}
nav .menu .menu_icon:hover .icon {
background: #ffedc0;
}
nav .menu .menu_icon:hover .icon:before {
transform: translateY(-10px);
}
nav .menu .menu_icon:hover .icon:after {
transform: translateY(10px);
}
.wrapper {
display: grid;
grid-template-columns: 1fr;
justify-content: center;
align-items: center;
height: 100vh;
overflow-x: hidden;
}
.wrapper .container {
margin: 0 auto;
transition: all 0.4s ease;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.wrapper .container .scene {
position: absolute;
width: 100vw;
height: 100vh;
vertical-align: middle;
overflow: hidden;
}
.wrapper .container .one,
.wrapper .container .two,
.wrapper .container .three,
.wrapper .container .circle,
.wrapper .container .p404 {
width: 60%;
height: 60%;
top: 20% !important;
left: 20% !important;
min-width: 400px;
min-height: 400px;
}
.wrapper .container .one .content,
.wrapper .container .two .content,
.wrapper .container .three .content,
.wrapper .container .circle .content,
.wrapper .container .p404 .content {
width: 600px;
height: 600px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: content 0.8s cubic-bezier(1, 0.06, 0.25, 1) backwards;
}
@keyframes content {
0% {
width: 0;
}
}
.wrapper .container .one .content .piece,
.wrapper .container .two .content .piece,
.wrapper .container .three .content .piece,
.wrapper .container .circle .content .piece,
.wrapper .container .p404 .content .piece {
width: 200px;
height: 80px;
display: flex;
position: absolute;
border-radius: 80px;
z-index: 1;
animation: pieceLeft 8s cubic-bezier(1, 0.06, 0.25, 1) infinite both;
}
@keyframes pieceLeft {
50% {
left: 80%;
width: 10%;
}
}
@keyframes pieceRight {
50% {
right: 80%;
width: 10%;
}
}
@media screen and (max-width: 799px) {
.wrapper .container .one,
.wrapper .container .two,
.wrapper .container .three,
.wrapper .container .circle,
.wrapper .container .p404 {
width: 90%;
height: 90%;
top: 5% !important;
left: 5% !important;
min-width: 280px;
min-height: 280px;
}
}
@media screen and (max-height: 660px) {
.wrapper .container .one,
.wrapper .container .two,
.wrapper .container .three,
.wrapper .container .circle,
.wrapper .container .p404 {
min-width: 280px;
min-height: 280px;
width: 60%;
height: 60%;
top: 20% !important;
left: 20% !important;
}
}
.wrapper .container .text {
width: 60%;
height: 40%;
min-width: 400px;
min-height: 500px;
position: absolute;
margin: 40px 0;
animation: text 0.6s 1.8s ease backwards;
}
@keyframes text {
0% {
opacity: 0;
transform: translateY(40px);
}
}
@media screen and (max-width: 799px) {
.wrapper .container .text {
min-height: 400px;
height: 80%;
}
}
.wrapper .container .text article {
width: 500px;
position: absolute;
bottom: 80%;
z-index: 4;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
left: 50%;
transform: translateX(-50%);
}
@media screen and (max-width: 799px) {
.wrapper .container .text article {
width: 100%;
}
}
.wrapper .container .text article p {
color: white;
font-size: 18px;
letter-spacing: 0.6px;
margin-bottom: 20px;
text-shadow: 6px 6px 10px #32243e;
}
.wrapper .container .text article button {
height: 40px;
padding: 0 30px;
border-radius: 50px;
cursor: pointer;
box-shadow: 0px 15px 20px rgba(54, 24, 79, 0.5);
z-index: 3;
color: #695681;
background-color: white;
text-transform: uppercase;
font-weight: 600;
font-size: 12px;
transition: all 0.3s ease;
}
.wrapper .container .text article button:hover {
box-shadow: 0px 10px 10px -10px rgba(54, 24, 79, 0.5);
transform: translateY(5px);
background: #fb8a8a;
color: white;
}
.wrapper .container .p404 {
font-size: 100px;
font-weight: 700;
letter-spacing: 4px;
color: white;
display: flex !important;
justify-content: center;
align-items: center;
position: absolute;
z-index: 2;
animation: anime404 0.6s cubic-bezier(0.3, 0.8, 1, 1.05) both;
animation-delay: 1.2s;
}
@media screen and (max-width: 799px) {
.wrapper .container .p404 {
font-size: 50px;
}
}
@keyframes anime404 {
0% {
opacity: 0;
transform: scale(10) skew(20deg, 20deg);
}
}
.wrapper .container .p404:nth-of-type(2) {
color: #36184f;
z-index: 1;
animation-delay: 1s;
filter: blur(10px);
opacity: 0.8;
}
.wrapper .container .circle {
position: absolute;
}
.wrapper .container .circle:before {
content: '';
position: absolute;
width: 800px;
height: 800px;
background-color: rgba(54, 24, 79, 0.2);
border-radius: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow:
inset 5px 20px 40px rgba(54, 24, 79, 0.25),
inset 5px 0px 5px rgba(50, 36, 62, 0.3),
inset 5px 5px 20px rgba(50, 36, 62, 0.25),
2px 2px 5px rgba(255, 255, 255, 0.2);
animation: circle 0.8s cubic-bezier(1, 0.06, 0.25, 1) backwards;
}
@keyframes circle {
0% {
width: 0;
height: 0;
}
}
@media screen and (max-width: 799px) {
.wrapper .container .circle:before {
width: 400px;
height: 400px;
}
}
.wrapper .container .one .content:before {
content: '';
position: absolute;
width: 600px;
height: 600px;
background-color: rgba(54, 24, 79, 0.3);
border-radius: 100%;
box-shadow:
inset 5px 20px 40px rgba(54, 24, 79, 0.25),
inset 5px 0px 5px rgba(50, 36, 62, 0.3),
inset 5px 5px 20px rgba(50, 36, 62, 0.25),
2px 2px 5px rgba(255, 255, 255, 0.2);
animation: circle 0.8s 0.4s cubic-bezier(1, 0.06, 0.25, 1) backwards;
}
@media screen and (max-width: 799px) {
.wrapper .container .one .content:before {
width: 300px;
height: 300px;
}
}
.wrapper .container .one .content .piece {
background: linear-gradient(90deg, #8077ea 13.7%, #eb73ff 94.65%);
}
.wrapper .container .one .content .piece:nth-child(1) {
right: 15%;
top: 18%;
height: 30px;
width: 120px;
animation-delay: 0.5s;
animation-name: pieceRight;
}
.wrapper .container .one .content .piece:nth-child(2) {
left: 15%;
top: 45%;
width: 150px;
height: 50px;
animation-delay: 1s;
animation-name: pieceLeft;
}
.wrapper .container .one .content .piece:nth-child(3) {
left: 10%;
top: 75%;
height: 20px;
width: 70px;
animation-delay: 1.5s;
animation-name: pieceLeft;
}
.wrapper .container .two .content .piece {
background: linear-gradient(90deg, #ffedc0 0%, #ff9d87 100%);
}
.wrapper .container .two .content .piece:nth-child(1) {
left: 0%;
top: 25%;
height: 40px;
width: 120px;
animation-delay: 2s;
animation-name: pieceLeft;
}
.wrapper .container .two .content .piece:nth-child(2) {
right: 15%;
top: 35%;
width: 180px;
height: 50px;
animation-delay: 2.5s;
animation-name: pieceRight;
}
.wrapper .container .two .content .piece:nth-child(3) {
right: 10%;
top: 80%;
height: 20px;
width: 160px;
animation-delay: 3s;
animation-name: pieceRight;
}
.wrapper .container .three .content .piece {
background: #fb8a8a;
}
.wrapper .container .three .content .piece:nth-child(1) {
left: 25%;
top: 35%;
height: 20px;
width: 80px;
animation-name: pieceLeft;
animation-delay: 3.5s;
}
.wrapper .container .three .content .piece:nth-child(2) {
right: 10%;
top: 55%;
width: 140px;
height: 40px;
animation-name: pieceRight;
animation-delay: 4s;
}
.wrapper .container .three .content .piece:nth-child(3) {
left: 40%;
top: 68%;
height: 20px;
width: 80px;
animation-name: pieceLeft;
animation-delay: 4.5s;
}
</style>

425
src/pages/H5.vue Normal file
View File

@@ -0,0 +1,425 @@
<template>
<div class="h5-container">
<!-- topImg -->
<img
class="topImg"
:src="`https://h5.whjhft.com/static/${imgArr[0]}`"
v-show="imgArr[0]"
alt=""
/>
<!-- form表单 -->
<div class="form">
<div class="container">
<div class="wrapper">
<div class="form-item">
<label class="label">*身份证姓名</label>
<input
type="text"
v-model="form.cert_name"
placeholder="请输入身份证姓名(安全加密)"
@blur="validateName"
/>
</div>
<div class="form-item">
<label class="label">*联系电话</label>
<input
type="text"
v-model="form.contact_num"
placeholder="请输入联系电话(安全加密)"
@blur="validatePhone"
/>
</div>
<div class="form-item" v-show="need_identity === 1">
<label class="label">*身份证号码</label>
<input
type="text"
v-model="form.cert_no"
placeholder="未满18岁请勿领卡安全加密"
@blur="validateID"
/>
</div>
<div class="form-item">
<label class="label">*地区</label>
<input
type="text"
v-model="form.address"
placeholder="选择地区(安全加密)"
@focus="selectArea"
/>
</div>
<div class="area">
<van-popup v-model="show" position="bottom">
<van-area title="选择地区" :area-list="areaList" @confirm="confirm" @cancel="cancel"/>
</van-popup>
</div>
<!--<van-area title="标题" :area-list="areaList" value="110101" />-->
<div class="form-item">
<label class="label">*详细地址</label>
<input
type="text"
v-model="form.post_addr"
placeholder="街道/镇+村/小区/写字楼+门牌号"
@blur="validateAddress"
/>
</div>
<div class="tips">
*未满18周岁的因政策原因不发货请勿下单请保持电话畅通我们将随时可能与您联系
</div>
<div class="btn">
<button @click="add">立即领取包邮到家</button>
</div>
</div>
</div>
</div>
<!-- details_img -->
<img
:src="`https://h5.whjhft.com/static/${imgArr[1]}`"
v-show="imgArr[1]"
alt=""
/>
<!-- copyright_img -->
<img
:src="`https://h5.whjhft.com/static/${imgArr[2]}`"
v-show="imgArr[2]"
alt=""
/>
</div>
</template>
<script>
import axios from "axios";
import CryptoJS from "crypto-js";
import {areaList} from '@vant/area-data';
export default {
name: "H5Page",
data() {
return {
show: false,
areaList,
encrypted: "erPY%2B%2BgxM2AhOlGj3B%2FyiTu3jbfg82vQ2vcXhcjzvEx%2BCBOQdn3%2BENusPylgeE5dg8ohlNHVohvkZdWejWOL1g%3D%3D",
cryptoConfig: {
key: "1234567890123456", // 16位密钥AES-128
iv: "6543210987654321", // 16位初始向量
},
parseObj: {
goods_id: null,
agent_id: null,
},
need_identity: null,
imgArr: [],
base64: null,
form: {
cert_name: "",
contact_num: "",
address: "",
post_addr: "",
goods: [],
agent_id: "",
cert_no: "",
post_province_name: "",
post_city_name: "",
post_district_name: "",
source: 7,
source_type: 1,
},
errors: {
cert_name: '',
contact_num: '',
cert_no: '',
post_addr: ''
},
};
},
async created() {
await this.parseData();
},
methods: {
validateName() {
const name = this.form.cert_name.trim();
if (!/^[\u4e00-\u9fa5]{2,}$/.test(name)) {
this.errors.cert_name = '请输入至少两个中文字符的姓名';
this.$toast.fail('请输入至少两个中文字符的姓名');
} else {
this.errors.cert_name = '';
}
},
validatePhone() {
const phone = this.form.contact_num.trim();
if (!/^1[3-9]\d{9}$/.test(phone)) {
this.errors.contact_num = '请输入正确的11位手机号';
this.$toast.fail('请输入正确的11位手机号');
} else {
this.errors.contact_num = '';
}
},
validateID() {
const id = this.form.cert_no.trim();
if (!/^\d{17}[\dXx]$/.test(id)) {
this.errors.cert_no = '请输入正确的身份证号码';
this.$toast.fail('请输入正确的身份证号码');
} else {
this.errors.cert_no = '';
}
},
validateAddress() {
const addr = this.form.post_addr.trim();
if (addr.length < 6) {
this.errors.post_addr = '详细地址不能少于6个字';
this.$toast.fail('详细地址不能少于6个字');
} else {
this.errors.post_addr = '';
}
},
async add() {
// 提交前统一校验一次
this.validateName();
this.validatePhone();
if (this.need_identity === 1) {
this.validateID();
}
this.validateAddress();
// 如果没有错误才提交
const firstError = Object.values(this.errors).find(err => err);
if (firstError) {
return this.$toast.fail(firstError);
}
// 发送下单请求
const res = await axios({
url: 'https://h5.whjhft.com/api/v1/h5/lot/save',
method: 'POST',
data: this.form
});
if (res.data.status === 1) {
this.$toast.success("下单成功");
this.form = {
cert_name: "",
contact_num: "",
address: "",
post_addr: "",
goods: [],
agent_id: "",
cert_no: "",
post_province_name: "",
post_city_name: "",
post_district_name: "",
source: 7,
source_type: 1,
}
} else if (res.data.status === 0) {
this.$toast.fail(res.data.msg);
}
},
// 确定
confirm(value) {
this.form.address = value.map(item => item.name).join('/')
this.form.post_province_name = value[0].name
this.form.post_city_name = value[1].name
this.form.post_district_name = value[2].name
this.cancel()
},
// 取消
cancel() {
this.show = false
},
selectArea() {
this.show = true
},
// 解析URL参数
async parseData() {
// this.encrypted = window.location.href.split('=')[1]
const key = CryptoJS.enc.Utf8.parse(this.cryptoConfig.key);
const iv = CryptoJS.enc.Utf8.parse(this.cryptoConfig.iv);
const decrypted = CryptoJS.AES.decrypt(
decodeURIComponent(this.encrypted),
key,
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
}
);
this.parseObj = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));
this.form.goods = [this.parseObj.goods_id]
this.form.agent_id = this.parseObj.agent_id
// 加密成base64 获取当前图片
const base64 = btoa(
this.parseObj.agent_id + "_" + this.parseObj.goods_id
);
const {data} = await axios.get(
`https://h5.whjhft.com/api/v1/h5/lot?key=${base64}`
);
if (data.status === 0) {
await this.$router.push({name: "404"});
return;
}
this.imgArr = [
data.data.good.top_img,
data.data.good.details_img,
data.data.good.copyright_img,
];
document.title = data.data.good.good_name;
this.need_identity = data.data.good.need_identity;
},
},
};
</script>
<style scoped>
.h5-container {
width: 100%;
margin: 0 auto;
display: flex;
flex-direction: column;
.topImg {
width: 100%;
height: 300px;
}
img {
margin: -1px;
width: 100%;
height: auto;
object-fit: cover;
box-sizing: border-box;
}
.form {
width: 100%;
background-color: #1b0b0c;
padding: 40px 20px;
/* height: 500px; */
box-sizing: border-box;
.container {
border: 1px solid #ffffff;
border-radius: 10px;
height: 100%;
.wrapper {
padding: 10px;
.form-item {
background-color: white;
border-radius: 10px;
padding: 5px 10px;
display: flex;
align-items: center;
margin-bottom: 20px;
.label {
width: 90px;
/* border: 1px solid; */
}
input {
flex: 1;
border-radius: 10px;
padding: 10px;
border: none;
outline: none;
}
}
.tips{
color: white;
font-size: 13px;
line-height: 20px;
padding: 0 20px;
}
.btn {
display: flex;
justify-content: center;
align-items: center;
padding: 0 15px;
button {
width: 100%;
height: 50px;
background-color: #f46c6c;
color: white;
border-radius: 100px;
font-size: 15px;
border: none;
outline: none;
cursor: pointer;
padding: 10px;
box-shadow: inset 0 3px 0 0 hsla(0, 0%, 100%, 0.15),
inset 0 -3px 2px 0 rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
display: block;
margin-top: 20px;
margin-bottom: 20px;
/* 💥 动画部分 */
transition: all 0.3s ease;
/* 💥 无限伸缩动画 */
animation: pulse 1.5s ease-in-out infinite;
}
}
}
}
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.08);
}
100% {
transform: scale(1);
}
}
@media (min-width: 768px) {
.h5-container {
max-width: 410px;
.topImg {
width: 100%;
aspect-ratio: 1/1;
box-sizing: border-box;
}
}
img {
width: 100%;
height: auto;
margin: -1px;
object-fit: cover;
box-sizing: border-box;
}
}
.fade-slide-enter-active, .fade-slide-leave-active {
transition: all 0.3s ease;
}
.fade-slide-enter-from, .fade-slide-leave-to {
opacity: 0;
transform: translateY(10px);
}
.fade-slide-enter-to, .fade-slide-leave-from {
opacity: 1;
transform: translateY(0);
}
</style>

42
src/router/index.js Normal file
View File

@@ -0,0 +1,42 @@
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
import H5 from "@/pages/H5.vue";
import H5404Page from "@/pages/404.vue";
export const constantRoutes = [
{
path: "/h5",
name: "H5",
component: H5,
},
{
path: "/404",
name: "404",
component: H5404Page,
},
{
path: "/",
redirect: "/h5",
},
{
path: "*",
redirect: "/404"
},
];
const createRouter = () =>
new Router({
mode: "history", // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes,
});
const router = createRouter();
export default router;

326
test.vue Normal file
View File

@@ -0,0 +1,326 @@
<template>
<div>
<img class="imgs" :src="`https://h5.whjhft.com/static/${imgArr[0]}`" v-show="imgArr[0]">
<div class="content">
<van-cell-group>
<van-field :rules="[{ required: true, message: '请填写用户名' }]" v-model="formData.cert_name" label="收货人姓名" placeholder="请输入收货人姓名" />
</van-cell-group>
<van-cell-group>
<van-field v-model="formData.contact_num" label="收货人电话" placeholder="请输入收货人电话" />
</van-cell-group>
<van-cell-group>
<van-field v-model="formData.cert_no" label="身份证号" placeholder="请输入身份证号" v-show="need_identity===1"/>
</van-cell-group>
<van-cell-group>
<van-field readonly v-model="formData.post_province_name+formData.post_city_name+formData.post_district_name" label="地区" placeholder="请选择地区" @click="showPopup"/>
</van-cell-group>
<div class="popup">
<van-popup v-model="show" position="bottom" :style="{ height: '30%' }" >
<van-area title="选择地区" :area-list="areaList" @confirm="handleConfirm" @cancel="handleCancel"/>
</van-popup>
</div>
<van-cell-group>
<van-field v-model="formData.post_addr" label="详细地址" placeholder="请输入详细地址" />
</van-cell-group>
<button class="vbuttons" @click="submit">立即领取包邮到家</button>
</div>
<img class="imgs" :src="`https://h5.whjhft.com/static/${imgArr[1]}`" v-show="imgArr[1]" >
<img class="imgs" :src="`https://h5.whjhft.com/static/${imgArr[2]}`" v-show="imgArr[2]">
</div>
</template>
<script>
import areaData from 'china-area-data';
import { Base64 } from "js-base64";
import axios from "axios";
import { Toast } from 'vant';
import CryptoJS from 'crypto-js'; // 引入 CryptoJS
export default {
data() {
return {
need_identity:null,
base64:null,
show: false,
urlos: null,
id_goods: null,
id_agent: null,
imgArr: [],
encrypted: 'https://example.com/product?data=DiW0XlB6rznMFVtUopdDj6CyL%2FGYjEubPEyOZRCjmtzZco97wHy9bLPo%2BnZdqureW%2BynJnIR%2Biktz5HNWrvLOg%3D%3D',
areaList: {},
value: '',
formData: {
cert_name: "",
contact_num: "",
address: "",
addressOption: [],
post_addr: "",
goods: [],
agent_id: "",
cert_no: "",
post_province_name: "",
post_city_name: "",
post_district_name: "",
source: 7,
source_type: 1
},
// CryptoJS 解密配置(需与加密端一致)
cryptoConfig: {
key: '1234567890123456', // 16位密钥AES-128
iv: '6543210987654321' // 16位初始向量
}
};
},
async created() {
console.log(window.location.href);
// await this.Parse(this.encrypted);
this.formatAreaData();
// const result = window.location.href.split('=')[1]
const result = 'ffMngb0kTzCgTwjPAt7KY0yAtF4784R3VRxzhlVEM5PMZn4xXT4XvuVLXFkQqZyXVS5Cnsd3WRpKaQIQh%2BLn0Q%3D%3D'
await this.getLink(result)
const {data} = await axios.get(`https://h5.whjhft.com/api/v1/h5/lot?key=${this.base64}`);
console.log(data)
this.imgArr=[
data.data.good.top_img,
data.data.good.details_img,
data.data.good.copyright_img,
data.data.good.customize_img,
]
this.need_identity=data.data.good.need_identity
},
methods: {
getLink(encrypted){
console.log(encrypted)
const key = CryptoJS.enc.Utf8.parse('1234567890123456');
const iv = CryptoJS.enc.Utf8.parse('6543210987654321');
const decrypted = CryptoJS.AES.decrypt(decodeURIComponent(encrypted), key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
const result = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));
this.formData.goods=[
result.goods_id
]
this.formData.agent_id=result.agent_id
// console.log(btoa(result.agent_id+'_'+result.goods_id))
// console.log(btoa(result.agent_id+'_'+))
console.log(result.goods_id)
result.goods_id=53
this.base64=btoa(result.agent_id+'_'+result.goods_id)
},
showPopup() {
this.show = true;
},
async submit() {
// 提交逻辑保持不变
const res = await axios({
url: 'https://h5.whjhft.com/api/v1/h5/lot/save',
method: 'POST',
data: this.formData
});
if (res.data.status === 1) {
Toast.success("提交成功");
} else if (res.data.status === 0) {
Toast.fail(res.data.msg);
}
},
formatAreaData() {
// 地区数据格式化逻辑保持不变
const result = {
province_list: {},
city_list: {},
county_list: {}
};
if (areaData['86']) {
result.province_list = areaData['86'];
}
Object.keys(result.province_list).forEach(provinceCode => {
const cities = areaData[provinceCode] || {};
Object.keys(cities).forEach(cityCode => {
if (typeof cities[cityCode] === 'object') {
result.city_list[cityCode] = cityCode;
} else {
result.city_list[cityCode] = cities[cityCode];
}
});
});
Object.keys(result.city_list).forEach(cityCode => {
const counties = areaData[cityCode] || {};
Object.keys(counties).forEach(countyCode => {
result.county_list[countyCode] = counties[countyCode];
});
});
this.areaList = result;
console.log('格式化后的地区数据:', this.areaList);
},
handleConfirm(values) {
this.formData.post_province_name=values[0].name
this.formData.post_city_name=values[1].name
this.formData.post_district_name=values[2].name
this.formData.address=values[0].name+values[1].name+values[2].name
this.formData.addressOption=[
values[0].name,values[1].name,values[2].name
]
this.show = false;
},
handleCancel() {
this.show = false;
},
async Parse(urls) {
try {
console.log(urls);
const url = new URL(urls);
const dataParam = url.searchParams.get('data'); // 获取加密的data参数
console.log('原始加密参数:', dataParam);
// ====================== 核心修改:使用 CryptoJS 解密 ======================
// 1. Base64解码如果参数是Base64编码的需先解码
const base64Decoded = Base64.decode(dataParam);
// 2. AES-CBC解密需与加密端的密钥、IV、模式一致
const decryptedBytes = CryptoJS.AES.decrypt(
base64Decoded,
CryptoJS.enc.Utf8.parse(this.cryptoConfig.key), // 密钥
{
iv: CryptoJS.enc.Utf8.parse(this.cryptoConfig.iv), // 初始向量
mode: CryptoJS.mode.CBC, // 加密模式
padding: CryptoJS.pad.Pkcs7 // 填充方式
}
);
const decodedKey = decryptedBytes.toString(CryptoJS.enc.Utf8); // 转换为明文
// ====================== 修改结束 ======================
console.log('解密后数据:', decodedKey);
// 解析解密后的ID假设格式为 "agentId_goodsId"
const [id_agent, id_goods] = decodedKey.split('_');
this.id_goods = Number(id_goods);
this.id_agent = id_agent;
console.log('商品ID:', this.id_goods, '代理商ID:', this.id_agent);
// 加载商品图片逻辑保持不变
const res = await axios.get(url.origin + '/api/v1/h5/lot?key=' + dataParam);
this.imgArr = [
res.data.data.good.top_img,
res.data.data.good.customize_img,
res.data.data.good.details_img
];
} catch (error) {
console.error('解码/请求失败:', error);
Toast.fail('数据解析失败,请检查链接');
}
}
}
};
</script>
<style>
/* 新增自适应基础样式 */
html {
font-size: 50px; /* 375px宽度下的基准值1rem=50px */
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
/* 输入框相关自适应调整(优化原有样式) */
/* 其他原有样式保持不变 */
.imgs{
width: 100%;
height: auto;
}
.imgs{
width: 100%;
height: auto;
}
body{
max-width: 100vw;
overflow-x: hidden;
margin: 0 auto;
}
.content{
padding: 0.1rem;
margin: 5px;
border-radius: 15px;
}
.vbuttons {
border-radius: 11115px;
background-color: #f46c6c;
color: white;
font-size: 15px;
border: none;
outline: none;
cursor: pointer;
padding: 10px;
box-shadow: 0 2px 8px rgba(73, 147, 170, 0.2);
transition: all 0.3s ease;
display: block;
margin: 0 auto;
margin-top: 20px;
margin-bottom: 20px;
animation: heart 1s infinite;
}
.vbuttons:active {
transform: scale(0.98);
box-shadow: 0 1px 4px rgba(73, 147, 170, 0.4);
}
@keyframes heart {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1); }
}
@media screen and (min-width: 1280px) {
/* 限制页面最大宽度并居中 */
body {
max-width: 1280px;
margin: 10px auto;
overflow-x: hidden; /* 隐藏可能出现的横向滚动条 */
.van-picker{
max-width: 1280px !important;
margin: 0 auto;
}
.van-picker__cancel{
}
.van-picker__confirm{
}
.van-picker__title{
}
*{
font-size: 20px;
}
.vbuttons{
font-size: 22px;
}
}
/* 可选:调整大屏下的根字体大小(保持设计稿比例) */
html {
}
}
</style>

4
vue.config.js Normal file
View File

@@ -0,0 +1,4 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})