first
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(git add:*)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
94
.gitignore
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# uni-app
|
||||||
|
unpackage/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
594
App.vue
Normal file
@@ -0,0 +1,594 @@
|
|||||||
|
<script>
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
// 判断是否是微信环境
|
||||||
|
function isWeChat() {
|
||||||
|
var ua = window.navigator.userAgent.toLowerCase();
|
||||||
|
if (ua.match(/micromessenger/i) == 'micromessenger') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
onLaunch: function() {
|
||||||
|
console.log('App Launch')
|
||||||
|
|
||||||
|
// 初始化用户登录状态
|
||||||
|
userStore.actions.initLoginState();
|
||||||
|
// 延迟检查登录状态,确保页面加载完成
|
||||||
|
// setTimeout(() => {
|
||||||
|
// this.checkLoginStatus();
|
||||||
|
// }, 100);
|
||||||
|
},
|
||||||
|
onShow: function() {
|
||||||
|
console.log('App Show')
|
||||||
|
// 在应用显示时检查登录状态
|
||||||
|
// this.checkLoginStatus();
|
||||||
|
|
||||||
|
// 判断是否是微信环境 当前不在error页面
|
||||||
|
// if (!isWeChat() && window.location.href.indexOf('error') === -1) {
|
||||||
|
// // 跳转到自定义的错误页面
|
||||||
|
// setTimeout(() => {
|
||||||
|
// window.location.href = 'https://m1.whjhft.com/singer-card/pages/error/error';
|
||||||
|
// }, 500);
|
||||||
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
onHide: function() {
|
||||||
|
console.log('App Hide')
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// uni-app 页面卸载
|
||||||
|
this.handleExit();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 触发退出逻辑
|
||||||
|
async handleExit() {
|
||||||
|
await userStore.actions.logout()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查登录状态和路由拦截
|
||||||
|
checkLoginStatus() {
|
||||||
|
try {
|
||||||
|
// 获取当前页面路径
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
if (!pages || pages.length === 0) {
|
||||||
|
console.log('页面栈为空,跳过路由检查');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPage = pages[pages.length - 1];
|
||||||
|
const currentRoute = '/' + currentPage.route;
|
||||||
|
|
||||||
|
console.log('当前路由:', currentRoute);
|
||||||
|
|
||||||
|
// 获取登录状态(通过device_id判断)
|
||||||
|
const deviceId = uni.getStorageSync('device_id');
|
||||||
|
const isLoggedInStorage = uni.getStorageSync('isLoggedIn');
|
||||||
|
const isLoggedIn = !!deviceId && !!isLoggedInStorage;
|
||||||
|
|
||||||
|
console.log('登录状态:', isLoggedIn, '设备ID:', deviceId);
|
||||||
|
|
||||||
|
// 如果已登录但在登录页面,跳转到首页
|
||||||
|
if (isLoggedIn && currentRoute === '/pages/login/login') {
|
||||||
|
uni.showToast({
|
||||||
|
title: "已登录但在登录页面,跳转到首页",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
uni.setTimeout(() => {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/index/index',
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('跳转到首页失败:', err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未登录且不在登录页面,跳转到登录页
|
||||||
|
if (!isLoggedIn && currentRoute !== '/pages/login/login') {
|
||||||
|
console.log('未登录,跳转到登录页');
|
||||||
|
uni.showToast({
|
||||||
|
title: "未登录,跳转到登录页",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
uni.setTimeout(() => {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/login/login',
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('跳转到登录页失败:', err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('路由检查通过,无需跳转');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('路由检查出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
|
||||||
|
@import "uview-plus/index.scss";
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Apple Design System - Variables
|
||||||
|
================================ */
|
||||||
|
:root {
|
||||||
|
/* Apple Color Palette */
|
||||||
|
--apple-blue: #ff6800;
|
||||||
|
--apple-green: #34C759;
|
||||||
|
--apple-orange: #FF9500;
|
||||||
|
--apple-red: #FF3B30;
|
||||||
|
--apple-purple: #AF52DE;
|
||||||
|
--apple-pink: #FF2D92;
|
||||||
|
--apple-indigo: #5856D6;
|
||||||
|
--apple-teal: #5AC8FA;
|
||||||
|
|
||||||
|
/* Neutral Colors */
|
||||||
|
--system-gray: #8E8E93;
|
||||||
|
--system-gray-2: #AEAEB2;
|
||||||
|
--system-gray-3: #C7C7CC;
|
||||||
|
--system-gray-4: #D1D1D6;
|
||||||
|
--system-gray-5: #E5E5EA;
|
||||||
|
--system-gray-6: #F2F2F7;
|
||||||
|
|
||||||
|
/* Background Colors */
|
||||||
|
--background-primary: #FFFFFF;
|
||||||
|
--background-secondary: #F2F2F7;
|
||||||
|
--background-tertiary: #FFFFFF;
|
||||||
|
--background-grouped: #F2F2F7;
|
||||||
|
|
||||||
|
/* Text Colors */
|
||||||
|
--text-primary: #000000;
|
||||||
|
--text-secondary: #3C3C43;
|
||||||
|
--text-tertiary: #3C3C4399;
|
||||||
|
--text-quaternary: #3C3C4366;
|
||||||
|
|
||||||
|
/* Shadow System */
|
||||||
|
--shadow-small: 0 2rpx 4rpx rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-medium: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
--shadow-large: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
|
||||||
|
--shadow-extra-large: 0 16rpx 48rpx rgba(0, 0, 0, 0.16);
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
--radius-small: 8rpx;
|
||||||
|
--radius-medium: 16rpx;
|
||||||
|
--radius-large: 24rpx;
|
||||||
|
--radius-extra-large: 32rpx;
|
||||||
|
|
||||||
|
/* Spacing System (8pt grid) */
|
||||||
|
--space-xs: 8rpx;
|
||||||
|
--space-sm: 16rpx;
|
||||||
|
--space-md: 24rpx;
|
||||||
|
--space-lg: 32rpx;
|
||||||
|
--space-xl: 40rpx;
|
||||||
|
--space-2xl: 48rpx;
|
||||||
|
|
||||||
|
/* Animation */
|
||||||
|
--transition-fast: 0.2s ease-out;
|
||||||
|
--transition-normal: 0.3s ease-out;
|
||||||
|
--transition-slow: 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Global Reset & Base Styles
|
||||||
|
================================ */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "MiSans", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
page {
|
||||||
|
color: var(--text-primary);
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Layout Components
|
||||||
|
================================ */
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 30rpx 20rpx;
|
||||||
|
max-width: 750rpx;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--background-primary);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
border: 1rpx solid var(--system-gray-5);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-elevated {
|
||||||
|
box-shadow: var(--shadow-large);
|
||||||
|
transform: translateY(-4rpx);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-interactive {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Typography System
|
||||||
|
================================ */
|
||||||
|
.title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 34rpx;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--text-primary);
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.caption {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Spacing Utilities
|
||||||
|
================================ */
|
||||||
|
.mt-xs {
|
||||||
|
margin-top: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-sm {
|
||||||
|
margin-top: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-md {
|
||||||
|
margin-top: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-lg {
|
||||||
|
margin-top: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-xl {
|
||||||
|
margin-top: var(--space-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-30 {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 保持兼容性 */
|
||||||
|
.mt-20 {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 保持兼容性 */
|
||||||
|
|
||||||
|
.mb-xs {
|
||||||
|
margin-bottom: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-sm {
|
||||||
|
margin-bottom: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-md {
|
||||||
|
margin-bottom: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-lg {
|
||||||
|
margin-bottom: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-xl {
|
||||||
|
margin-bottom: var(--space-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-xs {
|
||||||
|
padding: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-sm {
|
||||||
|
padding: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-md {
|
||||||
|
padding: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-lg {
|
||||||
|
padding: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-xl {
|
||||||
|
padding: var(--space-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Flexbox Layout Utilities
|
||||||
|
================================ */
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-g8 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-g16 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-g20 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
/* 保持兼容性 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-g24 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-sb {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row-center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col-g8 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col-g16 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col-g20 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
/* 保持兼容性 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col-g24 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col-center-g20 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
/* 保持兼容性 */
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Sizing Utilities
|
||||||
|
================================ */
|
||||||
|
.w-100 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-100 {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Interactive Elements
|
||||||
|
================================ */
|
||||||
|
.interactive {
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-apple {
|
||||||
|
background: var(--apple-blue);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-medium);
|
||||||
|
padding: var(--space-sm) var(--space-md);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 28rpx;
|
||||||
|
transition: all var(--transition-normal);
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Tag Styles
|
||||||
|
================================ */
|
||||||
|
.tag-apple {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--space-xs) var(--space-sm);
|
||||||
|
background: var(--system-gray-6);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border-radius: var(--radius-small);
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
|
||||||
|
&.tag-success {
|
||||||
|
background: rgba(52, 199, 89, 0.1);
|
||||||
|
color: var(--apple-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tag-primary {
|
||||||
|
background: rgba(0, 122, 255, 0.1);
|
||||||
|
color: var(--apple-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tag-warning {
|
||||||
|
background: rgba(255, 149, 0, 0.1);
|
||||||
|
color: var(--apple-orange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Progress Bar
|
||||||
|
================================ */
|
||||||
|
.progress-apple {
|
||||||
|
width: 100%;
|
||||||
|
height: 8rpx;
|
||||||
|
background: var(--system-gray-5);
|
||||||
|
border-radius: var(--radius-small);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, var(--apple-blue), var(--apple-teal));
|
||||||
|
border-radius: var(--radius-small);
|
||||||
|
transition: width var(--transition-normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Simple & Clean Button Styles
|
||||||
|
================================ */
|
||||||
|
.btn-apple {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: var(--system-gray-6);
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
/* 按钮变体 */
|
||||||
|
&.btn-primary {
|
||||||
|
background: var(--apple-blue);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-success {
|
||||||
|
background: var(--apple-green);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-warning {
|
||||||
|
background: var(--apple-orange);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background: var(--apple-red);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-secondary {
|
||||||
|
background: var(--system-gray-4);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 小号按钮 */
|
||||||
|
&.btn-mini {
|
||||||
|
min-height: 40rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大号按钮 */
|
||||||
|
&.btn-large {
|
||||||
|
min-height: 64rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.btn-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮组 */
|
||||||
|
.btn-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.btn-apple {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-group-vertical {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
220
api/request.js
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
/**
|
||||||
|
* 统一请求拦截器
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
|
||||||
|
// 基础配置
|
||||||
|
const BASE_CONFIG = {
|
||||||
|
timeout: 30000, // 30秒超时
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建请求实例
|
||||||
|
*/
|
||||||
|
class HttpRequest {
|
||||||
|
constructor() {
|
||||||
|
this.config = {
|
||||||
|
...BASE_CONFIG
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应拦截器 - 处理Cookie和错误
|
||||||
|
* @param {Object} response 响应对象
|
||||||
|
* @returns {Promise} 处理后的响应
|
||||||
|
*/
|
||||||
|
async interceptResponse(response) {
|
||||||
|
try {
|
||||||
|
if (response.data.system_result_message_key) {
|
||||||
|
uni.showToast({
|
||||||
|
title: response.data.system_result_message_key + ",请重新登录",
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 等待 logout 操作完成
|
||||||
|
await userStore.actions.logout();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/login/login'
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查HTTP状态码
|
||||||
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||||
|
// 检查业务状态码
|
||||||
|
if (response.data && typeof response.data === 'object') {
|
||||||
|
if (response.data.code === '0' || response.data.code === 0 || response.data.code === 200) {
|
||||||
|
// 业务成功
|
||||||
|
return response.data;
|
||||||
|
} else {
|
||||||
|
// 业务失败
|
||||||
|
const error = new Error(response.data.message || response.data.msg || '请求失败');
|
||||||
|
error.code = response.data.code;
|
||||||
|
error.data = response.data;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非JSON响应,直接返回
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// HTTP状态码错误
|
||||||
|
const error = new Error(
|
||||||
|
`HTTP ${response.statusCode}: ${this.getStatusText(response.statusCode)}`
|
||||||
|
);
|
||||||
|
error.statusCode = response.statusCode;
|
||||||
|
error.response = response;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('响应拦截器错误:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取状态码描述
|
||||||
|
* @param {number} statusCode HTTP状态码
|
||||||
|
* @returns {string} 状态描述
|
||||||
|
*/
|
||||||
|
getStatusText(statusCode) {
|
||||||
|
const statusMap = {
|
||||||
|
400: 'Bad Request',
|
||||||
|
401: 'Unauthorized',
|
||||||
|
403: 'Forbidden',
|
||||||
|
404: 'Not Found',
|
||||||
|
500: 'Internal Server Error',
|
||||||
|
502: 'Bad Gateway',
|
||||||
|
503: 'Service Unavailable'
|
||||||
|
};
|
||||||
|
return statusMap[statusCode] || 'Unknown Error';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用请求方法
|
||||||
|
* @param {Object} options 请求选项
|
||||||
|
* @returns {Promise} 请求Promise
|
||||||
|
*/
|
||||||
|
request(options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 合并配置,特别处理 header
|
||||||
|
const config = {
|
||||||
|
...this.config,
|
||||||
|
...options,
|
||||||
|
header: {
|
||||||
|
...this.config.header,
|
||||||
|
...(options.header || options.headers || {})
|
||||||
|
},
|
||||||
|
withCredentials: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发起请求
|
||||||
|
uni.request({
|
||||||
|
...config,
|
||||||
|
success: (response) => {
|
||||||
|
this.interceptResponse(response)
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
const err = new Error(error.errMsg || '网络请求失败');
|
||||||
|
err.error = error;
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET请求
|
||||||
|
* @param {string} url 请求地址
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
* @param {Object} options 其他选项
|
||||||
|
* @returns {Promise} 请求Promise
|
||||||
|
*/
|
||||||
|
get(url, params = {}, options = {}) {
|
||||||
|
// 构建查询字符串
|
||||||
|
const queryString = Object.keys(params)
|
||||||
|
.filter(key => params[key] !== undefined && params[key] !== null)
|
||||||
|
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||||
|
.join('&');
|
||||||
|
|
||||||
|
const finalUrl = queryString ? `${url}${url.includes('?') ? '&' : '?'}${queryString}` : url;
|
||||||
|
|
||||||
|
return this.request({
|
||||||
|
url: finalUrl,
|
||||||
|
method: 'GET',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST请求
|
||||||
|
* @param {string} url 请求地址
|
||||||
|
* @param {Object} data 请求体数据
|
||||||
|
* @param {Object} options 其他选项
|
||||||
|
* @returns {Promise} 请求Promise
|
||||||
|
*/
|
||||||
|
post(url, data = {}, options = {}) {
|
||||||
|
return this.request({
|
||||||
|
url,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT请求
|
||||||
|
* @param {string} url 请求地址
|
||||||
|
* @param {Object} data 请求体数据
|
||||||
|
* @param {Object} options 其他选项
|
||||||
|
* @returns {Promise} 请求Promise
|
||||||
|
*/
|
||||||
|
put(url, data = {}, options = {}) {
|
||||||
|
return this.request({
|
||||||
|
url,
|
||||||
|
method: 'PUT',
|
||||||
|
data,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE请求
|
||||||
|
* @param {string} url 请求地址
|
||||||
|
* @param {Object} params 查询参数
|
||||||
|
* @param {Object} options 其他选项
|
||||||
|
* @returns {Promise} 请求Promise
|
||||||
|
*/
|
||||||
|
delete(url, params = {}, options = {}) {
|
||||||
|
return this.get(url, params, {
|
||||||
|
...options,
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建请求实例
|
||||||
|
const httpRequest = new HttpRequest();
|
||||||
|
|
||||||
|
export default httpRequest;
|
||||||
|
|
||||||
|
// 导出常用方法
|
||||||
|
export const {
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: del
|
||||||
|
} = httpRequest;
|
||||||
757
api/user.js
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
/**
|
||||||
|
* 用户相关API接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
import httpRequest from './request.js';
|
||||||
|
import {
|
||||||
|
generateRfm
|
||||||
|
} from '@/utils/common.js';
|
||||||
|
|
||||||
|
class UserApi {
|
||||||
|
constructor() {
|
||||||
|
this.baseUrl = '/kyhl-weixin-1.0';
|
||||||
|
this.cardBaseUrl = '/cm-api/v1'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有公众号OpenId(获取Cookie的关键接口)
|
||||||
|
async getCookie() {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.cardBaseUrl}/auth/get-auth`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkHasGzhOpenId请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备信息(管理平台)
|
||||||
|
async getDeviceInfoAdmin(device_id) {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.cardBaseUrl}/device/query`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, {
|
||||||
|
page: 1,
|
||||||
|
page_size: 1,
|
||||||
|
device_id
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设备信息(管理平台):', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取WXUrl
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"code": "0",
|
||||||
|
"current_session_user_resource_ids_index": "",
|
||||||
|
"app_result_key": "0",
|
||||||
|
"wxAuthUrl": "",
|
||||||
|
"system_result_key": "0"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async getWxUrl() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'checkHasGzhOpenId',
|
||||||
|
needGzhOpenId: true,
|
||||||
|
currentPageUrl: 'https://m1.whjhft.com/pages/index/index',
|
||||||
|
checkFrom: 'cardlogin',
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/wxauth/checkHasGzhOpenId.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 用户登录接口
|
||||||
|
async login(iccidMark) {
|
||||||
|
try {
|
||||||
|
console.log(iccidMark);
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'findByiccidMarkCallback',
|
||||||
|
iccidMark,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/card/login.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否实名接口
|
||||||
|
async getRealNameInfo() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
iccidOrPhone: "",
|
||||||
|
responseFunction: "getRealNameInfoCallback",
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/user/getRealNameInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取实名信息失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取用户信息接口
|
||||||
|
async getUserInfo() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'findByOpenIdCallback',
|
||||||
|
DoNotGetRealNameInfo: true,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/user/findByOpenId.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换运营商 1: 电信 2: 联通 3: 移动 esim参数值
|
||||||
|
async changeOperator(esim, iccidMark) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'updateWifi',
|
||||||
|
optwifi: "esim",
|
||||||
|
esim,
|
||||||
|
iccidMark,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/card/updateWifi.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('切换运营商失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入实名是否只显示主卡
|
||||||
|
async getIsOnlyMainCard(iccidMark) {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.cardBaseUrl}/call/device/${iccidMark}/exists`;
|
||||||
|
|
||||||
|
const response = await httpRequest.get(fullUrl);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('进入实名是否只显示主卡请求失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取openId接口
|
||||||
|
async getOpenId() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'getUserCardInfo',
|
||||||
|
DoNotGetRealNameInfo: true,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/usercard/getUserCardInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取openId失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进行授权
|
||||||
|
async getAuthorize(code) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
origin_html_url: 'https://m1.whjhft.com/pages/index/index',
|
||||||
|
code: code,
|
||||||
|
state: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/wxauth/recvCode.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.get(fullUrl);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取授权失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取微信支付签名
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"code": "0",
|
||||||
|
"signature": "3771f2fce5802b3630377e7d2618a7195d136007",
|
||||||
|
"current_session_user_resource_ids_index": "",
|
||||||
|
"appId": "wxea8c599fe100ce8a",
|
||||||
|
"nonceStr": "31565688940e48cb91827adbf6aeb499",
|
||||||
|
"system_result_key": "0",
|
||||||
|
"timestamp": 1766209145,
|
||||||
|
"isSuccess": "0"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async getWxPaySign() {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.baseUrl}/weixinPay/getWxSign.do`;
|
||||||
|
|
||||||
|
const response = await httpRequest.get(fullUrl);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取微信支付签名失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentPageUrl() {
|
||||||
|
try {
|
||||||
|
// #ifdef H5
|
||||||
|
if (typeof window !== 'undefined' && window.location) {
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
console.log('当前页面URL:', currentUrl);
|
||||||
|
return encodeURIComponent(currentUrl);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
const defaultUrl = "https://m1.whjhft.com/pages/index/index";
|
||||||
|
console.log('使用默认URL:', defaultUrl);
|
||||||
|
return encodeURIComponent(defaultUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取当前页面URL失败:', error);
|
||||||
|
return encodeURIComponent('https://m1.whjhft.com/pages/index/index');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取套餐列表 smList
|
||||||
|
async getPackageList() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'findByCardNoCallback',
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}//setmeal/findByCardNo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取套餐列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 1. checkHasGzhOpenId 判断当前用户是否已经绑定了微信公众号的 OpenId
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"code": "0",
|
||||||
|
"current_session_user_resource_ids_index": "",
|
||||||
|
"app_result_key": "0",
|
||||||
|
"wxAuthUrl": "",
|
||||||
|
"system_result_key": "0"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// 如果返回的wxAuthUrl: 是空的就表示已经授权了, 没有就需要再来一次授权流程
|
||||||
|
async checkHasGzhOpenId() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'checkHasGzhOpenId',
|
||||||
|
needGzhOpenId: true,
|
||||||
|
currentPageUrl: "https://m1.whjhft.com/pages/package-order/package-order",
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}//wxauth/checkHasGzhOpenId.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkHasGzhOpenId:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. checkYgosWxInfo 检查当前用户是否具有与微信支付相关的特定信息
|
||||||
|
async checkYgosWxInfo() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'checkYgosWxInfo',
|
||||||
|
hasYgGzhPm: false,
|
||||||
|
currentPageUrl: "https://m1.whjhft.com/pages/package-order/package-order",
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}//weixinPay/checkYgosWxInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkYgosWxInfo:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. checkXwzfWxInfo 检查微信信息
|
||||||
|
async checkXwzfWxInfo() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'checkXwzfWxInfo',
|
||||||
|
hasXwzfPm: false,
|
||||||
|
zwxMchId: "",
|
||||||
|
currentPageUrl: "https://m1.whjhft.com/pages/package-order/package-order",
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}//weixinPay/checkXwzfWxInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkXwzfWxInfo:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 4. 获取支付方式
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"onlyWalletRechargeCard": false,
|
||||||
|
"code": "0",
|
||||||
|
"walletBalance": 0.0,
|
||||||
|
"current_session_user_resource_ids_index": "",
|
||||||
|
"payMethodList": [
|
||||||
|
{
|
||||||
|
"mybatisRecordCount": 0,
|
||||||
|
"orderNo": "",
|
||||||
|
"jsonUpdateFlag": "0",
|
||||||
|
"id": "a333",
|
||||||
|
"createDate": "",
|
||||||
|
"createUserId": "",
|
||||||
|
"createUserName": "",
|
||||||
|
"payMethod": 2,
|
||||||
|
"showStatus": "",
|
||||||
|
"selectStatus": 2,
|
||||||
|
"sortNum": 44,
|
||||||
|
"showName": "余额支付",
|
||||||
|
"showIconUrl": "http://jh.whjhft.com/kyhl-weixin-1.0/img/yezf.png",
|
||||||
|
"appId": "",
|
||||||
|
"appSecret": "",
|
||||||
|
"gzhId": "",
|
||||||
|
"gzhName": "",
|
||||||
|
"gzhUsername": "",
|
||||||
|
"gzhPassword": "",
|
||||||
|
"mchId": "",
|
||||||
|
"mchKey": "",
|
||||||
|
"systemDomain": "",
|
||||||
|
"mchCertUrl": "",
|
||||||
|
"notifyUrl": "",
|
||||||
|
"returnUrl": "",
|
||||||
|
"profitSharingConfigNo": "",
|
||||||
|
"createDateStr": "",
|
||||||
|
"showApp": true,
|
||||||
|
"needOpenId": false,
|
||||||
|
"payMethodStr": "余额支付",
|
||||||
|
"showStatusStr": "",
|
||||||
|
"selectStatusStr": "否"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mybatisRecordCount": 0,
|
||||||
|
"orderNo": "",
|
||||||
|
"jsonUpdateFlag": "0",
|
||||||
|
"id": "4F31156EE9654A35B16CF0E3A6569F53",
|
||||||
|
"createDate": "",
|
||||||
|
"createUserId": "",
|
||||||
|
"createUserName": "",
|
||||||
|
"payMethod": 1,
|
||||||
|
"showStatus": "",
|
||||||
|
"selectStatus": 1,
|
||||||
|
"sortNum": "",
|
||||||
|
"showName": "微信支付",
|
||||||
|
"showIconUrl": "http://jhft.whjhft.com/kyhl-weixin-1.0/img/wx.png",
|
||||||
|
"appId": "",
|
||||||
|
"appSecret": "",
|
||||||
|
"gzhId": "",
|
||||||
|
"gzhName": "",
|
||||||
|
"gzhUsername": "",
|
||||||
|
"gzhPassword": "",
|
||||||
|
"mchId": "",
|
||||||
|
"mchKey": "",
|
||||||
|
"systemDomain": "",
|
||||||
|
"mchCertUrl": "",
|
||||||
|
"notifyUrl": "",
|
||||||
|
"returnUrl": "",
|
||||||
|
"profitSharingConfigNo": "",
|
||||||
|
"createDateStr": "",
|
||||||
|
"showApp": false,
|
||||||
|
"needOpenId": true,
|
||||||
|
"payMethodStr": "微信支付",
|
||||||
|
"showStatusStr": "",
|
||||||
|
"selectStatusStr": "是"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"app_result_key": "0",
|
||||||
|
"buyMealMealPrice": 29.0,
|
||||||
|
"pwdEmpty": false,
|
||||||
|
"system_result_key": "0",
|
||||||
|
"isSuccess": "1"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async getPayList(mealId) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'getNeedPayMoneyAndWalletBalance',
|
||||||
|
mealId,
|
||||||
|
getMealPrice: true,
|
||||||
|
allowWalletPay: true,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}//cardwallet/getNeedPayMoneyAndWalletBalance.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取支付方式失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"code": "0",
|
||||||
|
"current_session_user_resource_ids_index": "",
|
||||||
|
"wp": {
|
||||||
|
"appId": "wxea8c599fe100ce8a",
|
||||||
|
"timeStamp": "1766388518476",
|
||||||
|
"nonceStr": "1d64e63b37f349cfb3699643757268a3",
|
||||||
|
"prepayId": "prepay_id=wx2215283841316122e94a592ddd8e7f0000",
|
||||||
|
"paySign": "FDCD3D358E55C568725A10B4E7BC4F5B",
|
||||||
|
"signType": ""
|
||||||
|
},
|
||||||
|
"system_result_key": "0",
|
||||||
|
"isSuccess": "0"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// 5. 调用 orderPayPageUse.do 创建订单 并拉起支付 我只要微信支付即可
|
||||||
|
async createOrder(data) {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.baseUrl}/weixinPay/orderPayPageUse.do`;
|
||||||
|
|
||||||
|
// 将数据转换为 URL 编码格式
|
||||||
|
const p = new URLSearchParams(data).toString();
|
||||||
|
|
||||||
|
// 确保 header 中的 Content-Type 为 x-www-form-urlencoded
|
||||||
|
const response = await httpRequest.post(fullUrl, p, {
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建订单失败', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取切卡列表-设备流量-WIFI信息-连接数
|
||||||
|
async getSwitchCardList() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'findNetWorkInfo',
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/card/findCardMchInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取切卡列表-设备流量-WIFI信息-连接数异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取卡信息(过期时间expireDate-状态statusStr)
|
||||||
|
async getCardInfo() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
iccidOrPhone: '',
|
||||||
|
responseFunction: 'findCardInfoCallback',
|
||||||
|
skipGift: true,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/card/findCardInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取切卡列表-设备流量-WIFI信息-连接数异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 修改WiFi信息
|
||||||
|
async modifyWifi({
|
||||||
|
cardNo,
|
||||||
|
ssid,
|
||||||
|
password
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.cardBaseUrl}/device/wifi-config`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, {
|
||||||
|
cardNo,
|
||||||
|
ssid,
|
||||||
|
password
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('修改WiFi信息失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重启设备
|
||||||
|
async restartDevice(deviceId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.cardBaseUrl}/device/restart`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, {
|
||||||
|
cardNo: deviceId
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重启设备失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复出厂设置
|
||||||
|
async restDevice(deviceId) {
|
||||||
|
try {
|
||||||
|
const fullUrl = `${this.cardBaseUrl}/device/factory-reset`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, {
|
||||||
|
cardNo: deviceId
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('恢复出厂设置失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取实名地址
|
||||||
|
async getRealNameAddress(iccidMark) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
iccidOrPhone: "",
|
||||||
|
responseFunction: "getRealNameInfoCallback",
|
||||||
|
force: true,
|
||||||
|
iccidMark,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/user/getRealNameInfo.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取实名地址异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能诊断
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"internalRetMsg": "",
|
||||||
|
"code": "0",
|
||||||
|
"current_session_user_resource_ids_index": "",
|
||||||
|
"app_result_key": "0",
|
||||||
|
"retMsg": "已提交复机申请,预计1小时内复机。",
|
||||||
|
"system_result_key": "0",
|
||||||
|
"isSuccess": "0"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async intelligentDiagnosis(iccidMark) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: "intelliDiagnose"
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/card/intelliDiagnose.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('智能诊断异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备绑定的手机号
|
||||||
|
async getDeviceBindPhone() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'findMyBindRecord',
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/phonebindrecord/findMyBindRecord.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设备绑定的手机号异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取短信验证码
|
||||||
|
async getSmsNumber(mobile) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'sendSms',
|
||||||
|
mobile,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/phonebindrecord/sendSms.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取短信验证码异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定手机号
|
||||||
|
async bindCardPhone(mobile, code) {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'saveBind',
|
||||||
|
mobile,
|
||||||
|
code,
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/phonebindrecord/saveBind1.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('绑定手机号异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
async logout() {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
responseFunction: 'logoutCallback',
|
||||||
|
rfm: generateRfm()
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryString = this.buildQueryString(params);
|
||||||
|
const fullUrl = `${this.baseUrl}/card/logout.do?${queryString}`;
|
||||||
|
|
||||||
|
const response = await httpRequest.post(fullUrl, null);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('退出登录异常:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildQueryString(params) {
|
||||||
|
return Object.keys(params)
|
||||||
|
.filter(key => params[key] !== undefined && params[key] !== null)
|
||||||
|
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||||
|
.join('&');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userApi = new UserApi();
|
||||||
|
|
||||||
|
export default userApi;
|
||||||
23
index.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script>
|
||||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||||
|
CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/misans@4.1.0/lib/Normal/MiSans-Regular.min.css"/>
|
||||||
|
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||||||
|
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
main.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import App from './App'
|
||||||
|
import uviewPlus from 'uview-plus'
|
||||||
|
|
||||||
|
// #ifndef VUE3
|
||||||
|
import Vue from 'vue'
|
||||||
|
import './uni.promisify.adaptor'
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
App.mpType = 'app'
|
||||||
|
const app = new Vue({
|
||||||
|
...App
|
||||||
|
})
|
||||||
|
app.$mount()
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef VUE3
|
||||||
|
import { createSSRApp } from 'vue'
|
||||||
|
export function createApp() {
|
||||||
|
const app = createSSRApp(App)
|
||||||
|
app.use(uviewPlus)
|
||||||
|
return {
|
||||||
|
app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
101
manifest.json
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"name": "singer-card-h5",
|
||||||
|
"appid": "__UNI__45F0251",
|
||||||
|
"description": "",
|
||||||
|
"versionName": "1.0.0",
|
||||||
|
"versionCode": "100",
|
||||||
|
"transformPx": false,
|
||||||
|
/* 5+App特有相关 */
|
||||||
|
"app-plus": {
|
||||||
|
"usingComponents": true,
|
||||||
|
"nvueStyleCompiler": "uni-app",
|
||||||
|
"compilerVersion": 3,
|
||||||
|
"splashscreen": {
|
||||||
|
"alwaysShowBeforeRender": true,
|
||||||
|
"waiting": true,
|
||||||
|
"autoclose": true,
|
||||||
|
"delay": 0
|
||||||
|
},
|
||||||
|
/* 模块配置 */
|
||||||
|
"modules": {},
|
||||||
|
/* 应用发布信息 */
|
||||||
|
"distribute": {
|
||||||
|
/* android打包配置 */
|
||||||
|
"android": {
|
||||||
|
"permissions": [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/* ios打包配置 */
|
||||||
|
"ios": {},
|
||||||
|
/* SDK配置 */
|
||||||
|
"sdkConfigs": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 快应用特有相关 */
|
||||||
|
"quickapp": {},
|
||||||
|
/* 小程序特有相关 */
|
||||||
|
"mp-weixin": {
|
||||||
|
"appid": "",
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false
|
||||||
|
},
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-alipay": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-baidu": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-toutiao": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"uniStatistics": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"vueVersion": "3",
|
||||||
|
"h5": {
|
||||||
|
"title": "物联网卡查询",
|
||||||
|
"template": "index.html",
|
||||||
|
"router": {
|
||||||
|
"mode": "history",
|
||||||
|
"base": "/singer-card/"
|
||||||
|
},
|
||||||
|
"optimization": {
|
||||||
|
"treeShaking": {
|
||||||
|
"enable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publicPath": "./singer-card/",
|
||||||
|
"devServer": {
|
||||||
|
"proxy": {
|
||||||
|
"/kyhl-weixin-1.0": {
|
||||||
|
"target": "http://jhwl.whjhft.com",
|
||||||
|
"changeOrigin": true,
|
||||||
|
"secure": false
|
||||||
|
},
|
||||||
|
"/cm-api": {
|
||||||
|
"target": "http://report.whjhft.com",
|
||||||
|
"changeOrigin": true,
|
||||||
|
"secure": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sdkConfigs": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
1621
package-lock.json
generated
Normal file
11
package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"sass": "^1.63.2",
|
||||||
|
"sass-loader": "^10.4.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"clipboard": "^2.0.11",
|
||||||
|
"dayjs": "^1.11.19",
|
||||||
|
"uview-plus": "^3.6.29"
|
||||||
|
}
|
||||||
|
}
|
||||||
61
pages.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"easycom": {
|
||||||
|
"autoscan": true,
|
||||||
|
// 注意一定要放在custom里,否则无效,https://ask.dcloud.net.cn/question/131175
|
||||||
|
"custom": {
|
||||||
|
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||||
|
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
|
||||||
|
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pages": [{
|
||||||
|
"path": "pages/login/login",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/index/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/package-order/package-order",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "套餐列表"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/switch/switch",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "切换运营商"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/bind/bind",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "绑定手机号"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/auth/auth",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "实名认证"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/error/error",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalStyle": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationBarTitleText": "物联网卡查询",
|
||||||
|
"navigationBarBackgroundColor": "#F8F8F8",
|
||||||
|
"backgroundColor": "#F8F8F8"
|
||||||
|
},
|
||||||
|
"uniIdRouter": {}
|
||||||
|
}
|
||||||
221
pages/auth/auth.vue
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view class="card" v-for="item in list">
|
||||||
|
<view class="flex-row-g20">
|
||||||
|
<view class="logo">
|
||||||
|
<image :src="getLogo(item.category).logo" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<view class="flex-col-g20">
|
||||||
|
<view class="iccid">
|
||||||
|
ICCID: {{item.iccidMark}}
|
||||||
|
</view>
|
||||||
|
<view class="operator">
|
||||||
|
运营商: {{getLogo(item.category).name}}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="btn mt-30 flex-col-g20">
|
||||||
|
<up-button class="btn-apple btn-primary" v-if="item.isRealName" type="primary">
|
||||||
|
已实名
|
||||||
|
</up-button>
|
||||||
|
<up-button class="btn-apple btn-success" v-else type="success" @tap="toReal(item.iccidMark)">
|
||||||
|
去实名
|
||||||
|
</up-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
reactive,
|
||||||
|
ref,
|
||||||
|
computed
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
|
||||||
|
let mchList = reactive([])
|
||||||
|
|
||||||
|
let list = reactive([])
|
||||||
|
|
||||||
|
let currentIccid = ref("")
|
||||||
|
let phone = ref("")
|
||||||
|
|
||||||
|
const device_id = uni.getStorageSync("device_id")
|
||||||
|
|
||||||
|
let opratorList = reactive([{
|
||||||
|
category: "124", // 中国电信
|
||||||
|
logo: "https://img2.baidu.com/it/u=139558247,3893370039&fm=253&fmt=auto?w=529&h=500",
|
||||||
|
esim: 1,
|
||||||
|
name: "中国电信"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "125", // 中国联通
|
||||||
|
logo: "https://img1.baidu.com/it/u=2816777816,1756344384&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
|
||||||
|
esim: 2,
|
||||||
|
name: "中国联通"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "126", // 中国移动
|
||||||
|
logo: "https://img2.baidu.com/it/u=915783975,1594870591&fm=253&fmt=auto&app=120&f=PNG?w=182&h=182",
|
||||||
|
esim: 3,
|
||||||
|
name: "中国移动"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const getLogo = computed(() => {
|
||||||
|
return (category) => {
|
||||||
|
const operator = opratorList.find(item => item.category == category);
|
||||||
|
return operator ? operator : ''; // 返回logo路径
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 是否显示主号 (导入的设备号)
|
||||||
|
const showMainNumber = async () => {
|
||||||
|
if (!device_id) {
|
||||||
|
uni.showToast({
|
||||||
|
title: 'ICCID不能为空',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const result = await userStore.actions.getIsOnlyMainCard(device_id)
|
||||||
|
if (result.data.exists && mchList.length > 0) {
|
||||||
|
// 使用 filter() 筛选符合条件的项
|
||||||
|
let matchedItems = mchList.filter(item => item.iccidMark === currentIccid.value);
|
||||||
|
// 将筛选后的数组重新赋值给 mchList
|
||||||
|
Object.assign(list, matchedItems)
|
||||||
|
} else {
|
||||||
|
Object.assign(list, mchList)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toReal = async (iccid) => {
|
||||||
|
const result = await userStore.actions.getRealNameAddress(iccid)
|
||||||
|
let url = result.accountEntity.realNameUrl
|
||||||
|
if (url) {
|
||||||
|
// 替换url里面的 ${iccid} 替换为iccid, ${phone}替换为 phone.value
|
||||||
|
url = url.replace("${iccid}", iccid).replace("${phone}", phone.value)
|
||||||
|
uni.showToast({
|
||||||
|
title: "正在跳转实名",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
window.location.href = url
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '未获取到实名地址',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取列表
|
||||||
|
const getList = async () => {
|
||||||
|
const mainInfo = await userStore.actions.getSwitchCardList()
|
||||||
|
if (mainInfo?.cardEntity) {
|
||||||
|
currentIccid.value = mainInfo.cardEntity.iccidMark
|
||||||
|
phone.value = mainInfo.cardEntity.phone
|
||||||
|
}
|
||||||
|
if (mainInfo?.mchList.length > 0) {
|
||||||
|
Object.assign(mchList, mainInfo.mchList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getList()
|
||||||
|
await showMainNumber()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
.card {
|
||||||
|
.logo {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 120rpx;
|
||||||
|
border: 1rpx solid var(--apple-blue);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-apple {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: var(--system-gray-6);
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
/* 按钮变体 */
|
||||||
|
&.btn-primary {
|
||||||
|
background: var(--apple-blue);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-success {
|
||||||
|
background: var(--apple-green);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-warning {
|
||||||
|
background: var(--apple-orange);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background: var(--apple-red);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-secondary {
|
||||||
|
background: var(--system-gray-4);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 小号按钮 */
|
||||||
|
&.btn-mini {
|
||||||
|
min-height: 40rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大号按钮 */
|
||||||
|
&.btn-large {
|
||||||
|
min-height: 64rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.btn-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
148
pages/bind/bind.vue
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view v-if="isBind" class="card flex-col-g20">
|
||||||
|
<view class="flex-row-g20">
|
||||||
|
<label for="">手机号:</label>
|
||||||
|
<up-input placeholder="请输入绑定的手机号" border="surround" v-model="bind.mobile" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex-row-g20">
|
||||||
|
<label for="">验证码:</label>
|
||||||
|
<up-input placeholder="验证码" v-model="bind.code">
|
||||||
|
<template #suffix>
|
||||||
|
<up-button @tap="getCode" text="获取验证码" type="success"></up-button>
|
||||||
|
</template>
|
||||||
|
</up-input>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="btn">
|
||||||
|
<up-button type="primary" @tap="bindPhone">绑定手机号</up-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="card" v-else>
|
||||||
|
<up-cell-group>
|
||||||
|
<up-cell title="绑定手机号" :value="bindInfo.phone"></up-cell>
|
||||||
|
<up-cell title="绑定时间" :value="bindInfo.bindTime"></up-cell>
|
||||||
|
<up-cell title="ICCID" :value="bindInfo.iccid"></up-cell>
|
||||||
|
<up-cell title="状态" :value="bindInfo.status"></up-cell>
|
||||||
|
</up-cell-group>
|
||||||
|
|
||||||
|
<view class="btn mt-30 flex-col-g20">
|
||||||
|
<up-button type="primary" @tap="changeBind">更换绑定</up-button>
|
||||||
|
<up-button type="success" @tap="logout">退出</up-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
onMounted,
|
||||||
|
reactive
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
|
||||||
|
let isBind = ref(false)
|
||||||
|
|
||||||
|
let bind = reactive({
|
||||||
|
mobile: "",
|
||||||
|
code: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
let bindInfo = reactive({
|
||||||
|
phone: "",
|
||||||
|
iccid: "",
|
||||||
|
bindTime: "",
|
||||||
|
status: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeBind = () => {
|
||||||
|
isBind.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取绑定信息
|
||||||
|
const getBindInfo = async () => {
|
||||||
|
const result = await userStore.actions.getDeviceBindPhone()
|
||||||
|
if (result.myBindRecord.bindPhone) {
|
||||||
|
isBind.value = false
|
||||||
|
bindInfo.phone = result.myBindRecord.bindPhone
|
||||||
|
bindInfo.iccid = result.myBindRecord.iccidMark
|
||||||
|
bindInfo.bindTime = result.myBindRecord.createDateStr
|
||||||
|
bindInfo.status = result.myBindRecord.status === 1 ? "已绑定" : "未绑定"
|
||||||
|
} else {
|
||||||
|
isBind.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
const getCode = async () => {
|
||||||
|
if (!bind.mobile) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请输入手机号",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = await userStore.actions.getSmsNumber(bind.mobile)
|
||||||
|
if (data.isSuccess && data?.errorMsg) {
|
||||||
|
uni.showToast({
|
||||||
|
title: data.errorMsg,
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "验证码已发送",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 绑定手机号
|
||||||
|
const bindPhone = async () => {
|
||||||
|
if (!bind.mobile && !bind.code) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "手机号和验证码都不能为空",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = await userStore.actions.bindCardPhone(bind.mobile, bind.code)
|
||||||
|
if (data.isSuccess && data?.errorMsg) {
|
||||||
|
uni.showToast({
|
||||||
|
title: data.errorMsg,
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "绑定成功",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
isBind.value = false
|
||||||
|
getBindInfo()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 退出
|
||||||
|
const logout = async() => {
|
||||||
|
await userStore.actions.logout()
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/login/login"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getBindInfo()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
21
pages/error/error.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<view class="error">
|
||||||
|
请在微信中打开...
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.error{
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1102
pages/index/index.vue
Normal file
109
pages/login/login.vue
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view class="card">
|
||||||
|
<view class="title-login">
|
||||||
|
物联网卡登录
|
||||||
|
</view>
|
||||||
|
<view class="input">
|
||||||
|
<up-input class="mt-30" v-model="device_id" placeholder="请输入ICCID"></up-input>
|
||||||
|
</view>
|
||||||
|
<view class="button">
|
||||||
|
<up-button class="mt-30 btn-apple btn-primary" type=" primary" @click="login">立即登录</up-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
reactive,
|
||||||
|
onMounted,
|
||||||
|
computed
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
|
||||||
|
const device_id = ref('');
|
||||||
|
|
||||||
|
const login = async () => {
|
||||||
|
if (!device_id.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入ICCID',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await userStore.actions.login(device_id.value);
|
||||||
|
|
||||||
|
if (result.system_result_message_key) {
|
||||||
|
uni.showToast({
|
||||||
|
title: result.system_result_message_key,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '登录成功',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 登录成功后跳转到首页(登录状态已在store中设置)
|
||||||
|
uni.redirectTo({
|
||||||
|
url: "/pages/index/index"
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录过程中发生错误:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '登录失败,请稍后重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取扫码后路由中带的device_id
|
||||||
|
const getPathDeviceId = () => {
|
||||||
|
const path = window.location.href.split("=")
|
||||||
|
if (path.length > 1) {
|
||||||
|
device_id.value = path[1]
|
||||||
|
login()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入页面后自动获取Cookie
|
||||||
|
const getCookieAndWxUrl = async () => {
|
||||||
|
await userStore.actions.getWxUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCookieAndWxUrl()
|
||||||
|
getPathDeviceId()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
padding-top: 30vh;
|
||||||
|
|
||||||
|
.title-login {
|
||||||
|
color: #333;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin: 70rpx 0 50rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
301
pages/package-order/package-order.vue
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view v-if="packageList.length > 0" class="card flex-col-g20" v-for="item in packageList">
|
||||||
|
<view class="title">
|
||||||
|
{{item.name}}
|
||||||
|
</view>
|
||||||
|
<view class="price">
|
||||||
|
¥{{item.accountMoney}}
|
||||||
|
</view>
|
||||||
|
<view class="desc">
|
||||||
|
请在套餐有效期内使用,有效期内流量用完可充值加餐包即可继续使用
|
||||||
|
</view>
|
||||||
|
<view class="btn">
|
||||||
|
<up-button type="primary" @tap="showBuy=true">立即订购</up-button>
|
||||||
|
</view>
|
||||||
|
<up-modal :title="`您确定要订购${item.name}套餐吗?`" :show="showBuy" showCancelButton @confirm="SubscribeNow(item)"
|
||||||
|
@cancel="showBuy=false" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="card">
|
||||||
|
<view class="title" style="text-align: center;">
|
||||||
|
该设备暂无套餐, 请联系客服购买
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
reactive,
|
||||||
|
ref
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
|
||||||
|
let showBuy = ref(false)
|
||||||
|
|
||||||
|
// 套餐列表
|
||||||
|
let packageList = reactive([]);
|
||||||
|
|
||||||
|
// 支付列表
|
||||||
|
let payList = reactive([]);
|
||||||
|
|
||||||
|
// 获取套餐列表
|
||||||
|
const getPackageList = async () => {
|
||||||
|
const result = await userStore.actions.getPackageList()
|
||||||
|
if (result.smList.length > 0) {
|
||||||
|
packageList.push(...result.smList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即订购
|
||||||
|
const SubscribeNow = async (item) => {
|
||||||
|
// 1. 判断当前用户是否已经绑定了微信公众号的 OpenId
|
||||||
|
await checkHasGzhOpenId()
|
||||||
|
|
||||||
|
// 2. checkYgosWxInfo 检查当前用户是否具有与微信支付相关的特定信息
|
||||||
|
await userStore.actions.checkYgosWxInfo()
|
||||||
|
|
||||||
|
// 3. checkXwzfWxInfo 检查当前用户是否具有与微信支付相关的特定信息
|
||||||
|
await userStore.actions.checkXwzfWxInfo()
|
||||||
|
|
||||||
|
// 4. 获取支付列表
|
||||||
|
await getPayList(item.id)
|
||||||
|
|
||||||
|
let id = "";
|
||||||
|
|
||||||
|
if (payList.length === 2) {
|
||||||
|
id = payList[1].id
|
||||||
|
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "未找到微信支付",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
mealId: item.id,
|
||||||
|
cardID: "",
|
||||||
|
money: item.money,
|
||||||
|
mealType: item.type,
|
||||||
|
cardCount: item.cardCount,
|
||||||
|
mealName: item.name,
|
||||||
|
strEffectType: "-1",
|
||||||
|
__pay_method: "1",
|
||||||
|
__merchant_cfg_id: id,
|
||||||
|
payPwd: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 创建订单, 获取支付参数
|
||||||
|
await createOrder(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 判断当前用户是否已经绑定了微信公众号的 OpenId
|
||||||
|
const checkHasGzhOpenId = async () => {
|
||||||
|
const result = await userStore.actions.checkHasGzhOpenId()
|
||||||
|
if (result.wxAuthUrl) {
|
||||||
|
// 再次进行授权
|
||||||
|
uni.showToast({
|
||||||
|
title: "该用户需要再次授权",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
toAuth()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 获取支付列表
|
||||||
|
const getPayList = async (mealId) => {
|
||||||
|
const result = await userStore.actions.getPayList(mealId)
|
||||||
|
if (result.payMethodList.length > 0) {
|
||||||
|
Object.assign(payList, result.payMethodList)
|
||||||
|
} else {
|
||||||
|
toAuth()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 创建订单, 获取支付参数, 并拉起微信支付
|
||||||
|
const createOrder = async (params) => {
|
||||||
|
showBuy.value = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调用后端接口,获取支付参数
|
||||||
|
const data = await userStore.actions.createOrder(params);
|
||||||
|
|
||||||
|
// 如果有错误信息,显示错误提示
|
||||||
|
if (data?.errorMsg) {
|
||||||
|
uni.showToast({
|
||||||
|
title: data?.errorMsg,
|
||||||
|
icon: "none"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调起微信支付
|
||||||
|
if (data?.wp) {
|
||||||
|
await invokeWechatPay(data?.wp);
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取支付参数失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "拉起微信支付失败",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调起微信支付
|
||||||
|
const invokeWechatPay = (payParam) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log('[PackageOrder] 调起微信支付...', payParam)
|
||||||
|
|
||||||
|
const onBridgeReady = () => {
|
||||||
|
window.WeixinJSBridge.invoke(
|
||||||
|
'getBrandWCPayRequest', {
|
||||||
|
appId: payParam.appId,
|
||||||
|
timeStamp: payParam.timeStamp,
|
||||||
|
nonceStr: payParam.nonceStr,
|
||||||
|
package: payParam.prepayId,
|
||||||
|
signType: payParam.signType || 'MD5',
|
||||||
|
paySign: payParam.paySign
|
||||||
|
},
|
||||||
|
(res) => {
|
||||||
|
console.log('[PackageOrder] 微信支付结果:', res)
|
||||||
|
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||||||
|
resolve(res)
|
||||||
|
} else if (res.err_msg === 'get_brand_wcpay_request:cancel') {
|
||||||
|
reject(new Error('用户取消支付'))
|
||||||
|
} else {
|
||||||
|
reject(new Error('支付失败: ' + res.err_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window.WeixinJSBridge === 'undefined') {
|
||||||
|
if (document.addEventListener) {
|
||||||
|
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false)
|
||||||
|
} else if (document.attachEvent) {
|
||||||
|
document.attachEvent('WeixinJSBridgeReady', onBridgeReady)
|
||||||
|
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onBridgeReady()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到微信授权页面
|
||||||
|
const toAuth = async () => {
|
||||||
|
try {
|
||||||
|
const result = await userStore.actions.getWxUrl();
|
||||||
|
|
||||||
|
if (result.wxAuthUrl) {
|
||||||
|
// 获取当前页面的URL
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
|
||||||
|
// 检查当前URL是否已经包含 code 参数
|
||||||
|
if (!currentUrl.includes("code=")) {
|
||||||
|
// 如果没有code参数,跳转到微信授权页面
|
||||||
|
let originalUrl = result.wxAuthUrl;
|
||||||
|
|
||||||
|
// 目标替换的 redirect_uri
|
||||||
|
const newRedirectUri = "https://m1.whjhft.com/pages/index/index";
|
||||||
|
|
||||||
|
// 替换 "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxea8c599fe100ce8a&redirect_uri=https://m1.whjhft.com/my/my&response_type=code&scope=snsapi_userinfo&state=cardlogin#wechat_redirect"
|
||||||
|
// 使用正则表达式替换 redirect_uri 的值
|
||||||
|
const updatedUrl = originalUrl.replace(/(redirect_uri=)[^&]*/,
|
||||||
|
`$1${encodeURIComponent(newRedirectUri)}`);
|
||||||
|
|
||||||
|
if (updatedUrl) {
|
||||||
|
window.location.href = updatedUrl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果已经包含code参数, 就进行授权
|
||||||
|
authoration()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '该用户已授权',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
uni.showToast({
|
||||||
|
title: e,
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 授权
|
||||||
|
const authoration = async () => {
|
||||||
|
try {
|
||||||
|
// 获取当前页面的完整 URL https://m1.whjhft.com/pages/index/index?code=081KSnml23G2Ug4XViml2VFHyJ0KSnmF&state=cardlogin
|
||||||
|
// const url = window.location.href;
|
||||||
|
|
||||||
|
const code = (location.search.match(/[?&]code=([^&]*)/) || [])[1];
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
// 如果 code 存在,则调用获取授权的动作
|
||||||
|
await userStore.actions.getAuthorize(code);
|
||||||
|
removeCodeFromUrl()
|
||||||
|
} else {
|
||||||
|
// 如果 code 不存在
|
||||||
|
uni.showToast({
|
||||||
|
title: "微信授权失败!",
|
||||||
|
icon: "none"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// 错误处理
|
||||||
|
console.error('授权失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "授权失败,请重试",
|
||||||
|
icon: "none"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPackageList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
.card {
|
||||||
|
.title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #aa5500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
.pay-title {
|
||||||
|
font-size: 35rpx;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
234
pages/switch/switch.vue
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<view class="card" v-for="item in mchList">
|
||||||
|
<view class="flex-row-sb mt-30">
|
||||||
|
<view class="flex-row-g20">
|
||||||
|
<view class="logo">
|
||||||
|
<image :src="getLogo(item.category).logo" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<view class="flex-col-g20">
|
||||||
|
<view class="flex-row-g20">
|
||||||
|
<view class="iccid">
|
||||||
|
ICCID: {{item.iccidMark}}
|
||||||
|
</view>
|
||||||
|
<view class="operator">
|
||||||
|
<up-tag type="success"
|
||||||
|
size="mini">{{getLogo(item.category).name}}</up-tag>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex-row-g20">
|
||||||
|
<view class="operator" v-if="item.iccidMark===currentIccid">
|
||||||
|
<up-tag type="success"
|
||||||
|
size="mini">{{item.iccidMark===currentIccid ? "当前使用" : ""}}</up-tag>
|
||||||
|
</view>
|
||||||
|
<view class="operator" v-if="item.iccidMark===currentIccidMain">
|
||||||
|
<up-tag type="success"
|
||||||
|
size="mini">{{item.iccidMark===currentIccidMain ? "当前主卡" : ""}}</up-tag>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="operator">
|
||||||
|
<up-tag :type="item.isRealName ? 'primary': 'success'"
|
||||||
|
size="mini">{{item.isRealName ? "已实名" : "未实名"}}</up-tag>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<view class="btn flex-col-g20 mt-30">
|
||||||
|
<up-button class="btn-apple btn-success" v-if="item.iccidMark!==currentIccid" type="success"
|
||||||
|
@tap="switchShow=true">
|
||||||
|
切换此运营商
|
||||||
|
</up-button>
|
||||||
|
<up-button class="btn-apple btn-success" v-if="!item.isRealName" type="success" @tap="goToReal">
|
||||||
|
跳转实名页面
|
||||||
|
</up-button>
|
||||||
|
</view>
|
||||||
|
<!-- 切换运营商 -->
|
||||||
|
<up-modal title="您确定要切换运营商吗?" :show="switchShow" showCancelButton @confirm="changeOpera(item)"
|
||||||
|
@cancel="switchShow=false" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
reactive,
|
||||||
|
ref,
|
||||||
|
computed
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
onLoad
|
||||||
|
} from '@dcloudio/uni-app'
|
||||||
|
|
||||||
|
import {
|
||||||
|
userStore
|
||||||
|
} from '@/store/index.js';
|
||||||
|
|
||||||
|
let mchList = reactive([])
|
||||||
|
|
||||||
|
// 当前主卡
|
||||||
|
let currentIccidMain = ref("")
|
||||||
|
|
||||||
|
// 当前卡
|
||||||
|
let currentIccid = ref("")
|
||||||
|
|
||||||
|
// 是否显示
|
||||||
|
let switchShow = ref(false)
|
||||||
|
|
||||||
|
let opratorList = reactive([{
|
||||||
|
category: "124", // 中国电信
|
||||||
|
logo: "https://img2.baidu.com/it/u=139558247,3893370039&fm=253&fmt=auto?w=529&h=500",
|
||||||
|
esim: 1,
|
||||||
|
name: "中国电信"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "125", // 中国联通
|
||||||
|
logo: "https://img1.baidu.com/it/u=2816777816,1756344384&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
|
||||||
|
esim: 2,
|
||||||
|
name: "中国联通"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "126", // 中国移动
|
||||||
|
logo: "https://img2.baidu.com/it/u=915783975,1594870591&fm=253&fmt=auto&app=120&f=PNG?w=182&h=182",
|
||||||
|
esim: 3,
|
||||||
|
name: "中国移动"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const getLogo = computed(() => {
|
||||||
|
return (category) => {
|
||||||
|
const operator = opratorList.find(item => item.category == category);
|
||||||
|
return operator ? operator : ''; // 返回logo路径
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeOpera = async (data) => {
|
||||||
|
let matchedItem = opratorList.find(item => item.category === data.category);
|
||||||
|
await userStore.actions.changeOperator(matchedItem.esim, data.iccidMark)
|
||||||
|
uni.showToast({
|
||||||
|
title: "切换成功, 3-5分钟后生效!",
|
||||||
|
icon: "none"
|
||||||
|
})
|
||||||
|
switchShow.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToReal = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/auth/auth"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取列表
|
||||||
|
const getList = async () => {
|
||||||
|
const mainInfo = await userStore.actions.getSwitchCardList()
|
||||||
|
if (mainInfo?.cardEntity) {
|
||||||
|
// 当前主卡
|
||||||
|
currentIccidMain.value = mainInfo.cardEntity.iccidMark
|
||||||
|
// 当前使用卡
|
||||||
|
currentIccid.value = mainInfo.gswlinfo.iccid
|
||||||
|
}
|
||||||
|
if (mainInfo?.mchList.length > 0) {
|
||||||
|
Object.assign(mchList, mainInfo.mchList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const YesSwitch = async () => {
|
||||||
|
switchShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
.card {
|
||||||
|
.logo {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 120rpx;
|
||||||
|
border: 1rpx solid var(--apple-blue);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-apple {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: var(--system-gray-6);
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
/* 按钮变体 */
|
||||||
|
&.btn-primary {
|
||||||
|
background: var(--apple-blue);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-success {
|
||||||
|
background: var(--apple-green);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-warning {
|
||||||
|
background: var(--apple-orange);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-danger {
|
||||||
|
background: var(--apple-red);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-secondary {
|
||||||
|
background: var(--system-gray-4);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 小号按钮 */
|
||||||
|
&.btn-mini {
|
||||||
|
min-height: 40rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大号按钮 */
|
||||||
|
&.btn-large {
|
||||||
|
min-height: 64rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用状态 */
|
||||||
|
&.btn-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
static/authentication.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
static/back.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
static/bind-phone.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
static/change.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
static/diagnosis.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
static/link.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
static/login.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
static/out.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
static/recover.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
static/restart.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
static/shop.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
15
store/index.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 简单的响应式状态管理
|
||||||
|
* 基于 Vue 3 reactive API
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import userStore from './modules/user.js';
|
||||||
|
|
||||||
|
// 创建全局状态
|
||||||
|
const globalStore = reactive({
|
||||||
|
user: userStore
|
||||||
|
});
|
||||||
|
|
||||||
|
export default globalStore;
|
||||||
|
export { userStore };
|
||||||
435
store/modules/user.js
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
/**
|
||||||
|
* 用户状态管理模块
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
reactive,
|
||||||
|
computed
|
||||||
|
} from 'vue';
|
||||||
|
import userApi from '@/api/user.js';
|
||||||
|
|
||||||
|
// 用户状态数据
|
||||||
|
const state = reactive({
|
||||||
|
userInfo: {
|
||||||
|
nickname: '单卡用户', // 昵称
|
||||||
|
avatar: 'https://img1.baidu.com/it/u=2462918877,1866131262&fm=253&fmt=auto&app=138&f=JPEG?w=506&h=500', // 头像
|
||||||
|
deviceId: '' // 设备ID
|
||||||
|
},
|
||||||
|
// 用户信息
|
||||||
|
isLoggedIn: false, // 登录状态
|
||||||
|
error: null // 错误信息
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const getters = {
|
||||||
|
// 是否已登录
|
||||||
|
isLoggedIn: computed(() => state.isLoggedIn && !!state.userInfo.deviceId),
|
||||||
|
// 用户信息
|
||||||
|
userInfo: computed(() => state.userInfo)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 操作方法
|
||||||
|
const actions = {
|
||||||
|
// 获取Cookies
|
||||||
|
async getCookie() {
|
||||||
|
try {
|
||||||
|
return userApi.getCookie();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取微信授权失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
async login(iccidMark) {
|
||||||
|
try {
|
||||||
|
// 调用login接口
|
||||||
|
const result = await userApi.login(iccidMark);
|
||||||
|
|
||||||
|
// 如果登录成功,设置登录状态和用户信息
|
||||||
|
if (!result.system_result_message_key) {
|
||||||
|
state.isLoggedIn = true;
|
||||||
|
state.userInfo.deviceId = iccidMark;
|
||||||
|
// 同步到本地存储
|
||||||
|
uni.setStorageSync('device_id', iccidMark);
|
||||||
|
uni.setStorageSync('isLoggedIn', true);
|
||||||
|
console.log('登录成功,已设置登录状态');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登录失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
state.isLoggedIn = false;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取用户实名信息
|
||||||
|
async getRealNameInfo() {
|
||||||
|
try {
|
||||||
|
return userApi.getRealNameInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户实名信息失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
async getUserInfo() {
|
||||||
|
try {
|
||||||
|
const result = await userApi.getUserInfo();
|
||||||
|
if (result?.entity) {
|
||||||
|
state.userInfo.avatar = result.entity.headimgurl
|
||||||
|
state.userInfo.nickname = result.entity.nickname
|
||||||
|
} else {
|
||||||
|
const result_once = await userApi.getUserInfo();
|
||||||
|
if (result_once?.entity) {
|
||||||
|
state.userInfo.avatar = result_once.entity.headimgurl
|
||||||
|
state.userInfo.nickname = result_once.entity.nickname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取wxUrl
|
||||||
|
async getWxUrl() {
|
||||||
|
try {
|
||||||
|
return userApi.getWxUrl();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取获取wxUrl失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取getOpenId
|
||||||
|
async getOpenId() {
|
||||||
|
try {
|
||||||
|
return userApi.getOpenId();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取获取getOpenId失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 微信授权
|
||||||
|
async getAuthorize(code) {
|
||||||
|
try {
|
||||||
|
return userApi.getAuthorize(code);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取微信授权失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取微信支付签名
|
||||||
|
async getWxPaySign() {
|
||||||
|
try {
|
||||||
|
return userApi.getWxPaySign();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取微信支付签名失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 恢复出厂设置
|
||||||
|
async restDevice(deviceId) {
|
||||||
|
try {
|
||||||
|
return userApi.restDevice(deviceId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('恢复出厂设置失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重启设备
|
||||||
|
async restartDevice(deviceId) {
|
||||||
|
try {
|
||||||
|
return userApi.restartDevice(deviceId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重启设备失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取设备信息
|
||||||
|
async getCardInfo() {
|
||||||
|
try {
|
||||||
|
return userApi.getCardInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设备信息失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取设备信息(管理平台)
|
||||||
|
async getDeviceInfoAdmin(device_id) {
|
||||||
|
try {
|
||||||
|
return userApi.getDeviceInfoAdmin(device_id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设备信息(管理平台)', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取首页信息-设备列表
|
||||||
|
async getSwitchCardList() {
|
||||||
|
try {
|
||||||
|
return userApi.getSwitchCardList();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取首页信息-设备列表失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改WiFi
|
||||||
|
async modifyWifi(data) {
|
||||||
|
try {
|
||||||
|
return userApi.modifyWifi(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('修改WiFi失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取套餐列表
|
||||||
|
async getPackageList() {
|
||||||
|
try {
|
||||||
|
return userApi.getPackageList();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取套餐列表失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 智能诊断
|
||||||
|
async intelligentDiagnosis() {
|
||||||
|
try {
|
||||||
|
return userApi.intelligentDiagnosis();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('智能诊断失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取绑定手机号
|
||||||
|
async getDeviceBindPhone() {
|
||||||
|
try {
|
||||||
|
return userApi.getDeviceBindPhone();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取绑定手机号失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
async getSmsNumber(mobile) {
|
||||||
|
try {
|
||||||
|
return userApi.getSmsNumber(mobile);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取验证码失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 绑定手机号
|
||||||
|
async bindCardPhone(mobile, code) {
|
||||||
|
try {
|
||||||
|
return userApi.bindCardPhone(mobile, code);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('绑定手机号失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 是否显示主号
|
||||||
|
async getIsOnlyMainCard(iccid) {
|
||||||
|
try {
|
||||||
|
return userApi.getIsOnlyMainCard(iccid);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('是否显示主号失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取实名地址
|
||||||
|
async getRealNameAddress(iccid) {
|
||||||
|
try {
|
||||||
|
return userApi.getRealNameAddress(iccid);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取实名地址失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 切换运营商
|
||||||
|
async changeOperator(esim, iccid) {
|
||||||
|
try {
|
||||||
|
return userApi.changeOperator(esim, iccid);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('切换运营商失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 1. checkHasGzhOpenId 判断当前用户是否已经绑定了微信公众号的
|
||||||
|
async checkHasGzhOpenId() {
|
||||||
|
try {
|
||||||
|
return userApi.checkHasGzhOpenId();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkHasGzhOpenId 失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2. checkYgosWxInfo 检查当前用户是否具有与微信支付相关的特定信息
|
||||||
|
async checkYgosWxInfo() {
|
||||||
|
try {
|
||||||
|
return userApi.checkYgosWxInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkYgosWxInfo 失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 3. checkXwzfWxInfo 检查当前用户是否具有与微信支付相关的特定信息
|
||||||
|
async checkXwzfWxInfo() {
|
||||||
|
try {
|
||||||
|
return userApi.checkXwzfWxInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('checkXwzfWxInfo 失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 4. 获取支付列表
|
||||||
|
async getPayList(mealId) {
|
||||||
|
try {
|
||||||
|
return userApi.getPayList(mealId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取支付列表失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5. createOrder 创建订单 并拉起支付 这个会返回微信支付参数
|
||||||
|
async createOrder(data) {
|
||||||
|
try {
|
||||||
|
return userApi.createOrder(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('createOrder 失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 登出(清除Cookie和状态)
|
||||||
|
async logout() {
|
||||||
|
try {
|
||||||
|
await userApi.logout();
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
state.isLoggedIn = false;
|
||||||
|
state.userInfo = {
|
||||||
|
nickname: '',
|
||||||
|
avatar: '',
|
||||||
|
deviceId: ''
|
||||||
|
};
|
||||||
|
state.error = null;
|
||||||
|
|
||||||
|
// 清除本地存储
|
||||||
|
uni.removeStorageSync('device_id');
|
||||||
|
uni.removeStorageSync('isLoggedIn');
|
||||||
|
|
||||||
|
console.log('退出登录成功,已清除登录状态');
|
||||||
|
|
||||||
|
// 跳转到登录页
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/login/login'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('登出失败:', error);
|
||||||
|
state.error = error.message;
|
||||||
|
|
||||||
|
// 即使服务端退出失败,也要清除本地状态
|
||||||
|
state.isLoggedIn = false;
|
||||||
|
state.userInfo = {
|
||||||
|
nickname: '',
|
||||||
|
avatar: '',
|
||||||
|
deviceId: ''
|
||||||
|
};
|
||||||
|
uni.removeStorageSync('device_id');
|
||||||
|
uni.removeStorageSync('isLoggedIn');
|
||||||
|
|
||||||
|
// 跳转到登录页
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/login/login'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置错误状态
|
||||||
|
clearError() {
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 初始化登录状态(从本地存储恢复)
|
||||||
|
initLoginState() {
|
||||||
|
try {
|
||||||
|
const deviceId = uni.getStorageSync('device_id');
|
||||||
|
const isLoggedIn = uni.getStorageSync('isLoggedIn');
|
||||||
|
|
||||||
|
if (deviceId && isLoggedIn) {
|
||||||
|
state.isLoggedIn = true;
|
||||||
|
state.userInfo.deviceId = deviceId;
|
||||||
|
console.log('从本地存储恢复登录状态:', deviceId);
|
||||||
|
} else {
|
||||||
|
state.isLoggedIn = false;
|
||||||
|
state.userInfo.deviceId = '';
|
||||||
|
console.log('未找到有效的登录状态');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化登录状态失败:', error);
|
||||||
|
state.isLoggedIn = false;
|
||||||
|
state.userInfo.deviceId = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建用户store
|
||||||
|
const userStore = {
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
actions
|
||||||
|
};
|
||||||
|
|
||||||
|
export default userStore;
|
||||||
15
tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"sourceMap": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"types": [
|
||||||
|
"@dcloudio/types",
|
||||||
|
"uview-plus/types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
||||||
13
uni.promisify.adaptor.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
uni.addInterceptor({
|
||||||
|
returnValue (res) {
|
||||||
|
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
res.then((res) => {
|
||||||
|
if (!res) return resolve(res)
|
||||||
|
return res[0] ? reject(res[0]) : resolve(res[1])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
76
uni.scss
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* 这里是uni-app内置的常用样式变量
|
||||||
|
*
|
||||||
|
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
|
||||||
|
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
|
||||||
|
*
|
||||||
|
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 颜色变量 */
|
||||||
|
@import 'uview-plus/theme.scss';
|
||||||
|
/* 行为相关颜色 */
|
||||||
|
$uni-color-primary: #007aff;
|
||||||
|
$uni-color-success: #4cd964;
|
||||||
|
$uni-color-warning: #f0ad4e;
|
||||||
|
$uni-color-error: #dd524d;
|
||||||
|
|
||||||
|
/* 文字基本颜色 */
|
||||||
|
$uni-text-color:#333;//基本色
|
||||||
|
$uni-text-color-inverse:#fff;//反色
|
||||||
|
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
|
||||||
|
$uni-text-color-placeholder: #808080;
|
||||||
|
$uni-text-color-disable:#c0c0c0;
|
||||||
|
|
||||||
|
/* 背景颜色 */
|
||||||
|
$uni-bg-color:#ffffff;
|
||||||
|
$uni-bg-color-grey:#f8f8f8;
|
||||||
|
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
|
||||||
|
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
|
||||||
|
|
||||||
|
/* 边框颜色 */
|
||||||
|
$uni-border-color:#c8c7cc;
|
||||||
|
|
||||||
|
/* 尺寸变量 */
|
||||||
|
|
||||||
|
/* 文字尺寸 */
|
||||||
|
$uni-font-size-sm:12px;
|
||||||
|
$uni-font-size-base:14px;
|
||||||
|
$uni-font-size-lg:16px;
|
||||||
|
|
||||||
|
/* 图片尺寸 */
|
||||||
|
$uni-img-size-sm:20px;
|
||||||
|
$uni-img-size-base:26px;
|
||||||
|
$uni-img-size-lg:40px;
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
$uni-border-radius-sm: 2px;
|
||||||
|
$uni-border-radius-base: 3px;
|
||||||
|
$uni-border-radius-lg: 6px;
|
||||||
|
$uni-border-radius-circle: 50%;
|
||||||
|
|
||||||
|
/* 水平间距 */
|
||||||
|
$uni-spacing-row-sm: 5px;
|
||||||
|
$uni-spacing-row-base: 10px;
|
||||||
|
$uni-spacing-row-lg: 15px;
|
||||||
|
|
||||||
|
/* 垂直间距 */
|
||||||
|
$uni-spacing-col-sm: 4px;
|
||||||
|
$uni-spacing-col-base: 8px;
|
||||||
|
$uni-spacing-col-lg: 12px;
|
||||||
|
|
||||||
|
/* 透明度 */
|
||||||
|
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
|
||||||
|
|
||||||
|
/* 文章场景相关 */
|
||||||
|
$uni-color-title: #2C405A; // 文章标题颜色
|
||||||
|
$uni-font-size-title:20px;
|
||||||
|
$uni-color-subtitle: #555555; // 二级标题颜色
|
||||||
|
$uni-font-size-subtitle:26px;
|
||||||
|
$uni-color-paragraph: #3F536E; // 文章段落颜色
|
||||||
|
$uni-font-size-paragraph:15px;
|
||||||
92
utils/common.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* 公共工具函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机浮点数参数(rfm)
|
||||||
|
* 用于接口请求的防重复参数
|
||||||
|
* @returns {number} 0到1之间的随机浮点数
|
||||||
|
*/
|
||||||
|
export function generateRfm() {
|
||||||
|
return Math.random();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间戳
|
||||||
|
* @returns {number} 当前时间戳
|
||||||
|
*/
|
||||||
|
export function getTimestamp() {
|
||||||
|
return Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成带时间戳的随机参数
|
||||||
|
* @returns {number} 带时间戳的随机数
|
||||||
|
*/
|
||||||
|
export function generateTimestampRfm() {
|
||||||
|
return Math.random() + Date.now() / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建通用请求参数
|
||||||
|
* @param {Object} customParams - 自定义参数
|
||||||
|
* @returns {Object} 包含通用参数的对象
|
||||||
|
*/
|
||||||
|
export function buildCommonParams(customParams = {}) {
|
||||||
|
return {
|
||||||
|
rfm: generateRfm(),
|
||||||
|
...customParams
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 转换成GB
|
||||||
|
* */
|
||||||
|
export function convertToGB(value, type) {
|
||||||
|
if (type === 'flowSize') {
|
||||||
|
// flowSize 单位是 MB,转换为 GB
|
||||||
|
return (value / 1024).toFixed(2);
|
||||||
|
} else if (type === 'totalBytesCnt') {
|
||||||
|
// totalBytesCnt 单位是 MB,转换为 GB
|
||||||
|
return (value / 1024).toFixed(2);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秒转时间字符串
|
||||||
|
* @param {number} seconds 秒
|
||||||
|
* @returns {string} HH:mm:ss
|
||||||
|
*/
|
||||||
|
export function formatSecondsToTime(seconds) {
|
||||||
|
if (!seconds && seconds !== 0) return '00:00:00';
|
||||||
|
|
||||||
|
const h = Math.floor(seconds / 3600);
|
||||||
|
const m = Math.floor((seconds % 3600) / 60);
|
||||||
|
const s = seconds % 60;
|
||||||
|
|
||||||
|
return (
|
||||||
|
String(h).padStart(2, '0') + ':' +
|
||||||
|
String(m).padStart(2, '0') + ':' +
|
||||||
|
String(s).padStart(2, '0')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据信号强度(dBm)返回信号等级与体验描述
|
||||||
|
* @param {number} dbm 信号强度(负数,如 -65)
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function getSignalText(dbm) {
|
||||||
|
if (!dbm) {
|
||||||
|
return '未知'
|
||||||
|
}
|
||||||
|
if (dbm >= -50) {
|
||||||
|
return '极强'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbm >= -70) {
|
||||||
|
return '良好'
|
||||||
|
}
|
||||||
|
|
||||||
|
return '一般'
|
||||||
|
}
|
||||||