Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d2d8a6d258 | 4 months ago |
|
|
4532f2c4cb | 4 months ago |
@ -0,0 +1,32 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
/unpackage/*
|
||||
@ -0,0 +1,198 @@
|
||||
<script>
|
||||
import apiAuth from "@/api/auth.js";
|
||||
import apiStations from "@/api/stations";
|
||||
|
||||
export default {
|
||||
// 生命周期
|
||||
onLaunch: function () {
|
||||
console.log("App Launch");
|
||||
|
||||
// 根据 token 获取用户信息
|
||||
const authToken = uni.getStorageSync("auth-token");
|
||||
if (authToken) {
|
||||
apiAuth
|
||||
.detail({ authToken })
|
||||
.then((res) => {
|
||||
this.$store.dispatch("updateAuth", { ...res.data, token: authToken });
|
||||
})
|
||||
.catch(() => {
|
||||
uni.removeStorageSync("auth-token");
|
||||
});
|
||||
}
|
||||
|
||||
// 获取所有站点
|
||||
apiStations
|
||||
.get()
|
||||
.then((res) => this.$store.dispatch("updateStations", res.data))
|
||||
.catch(() => {});
|
||||
},
|
||||
onShow: function () {
|
||||
console.log("App Show");
|
||||
},
|
||||
onHide: function () {
|
||||
console.log("App Hide");
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "uview-ui/index.scss";
|
||||
@import "uview-ui/libs/css/flex.scss";
|
||||
|
||||
.u-flex-shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 间距 $spacers 在 uni.scss 中 */
|
||||
@each $key, $val in $spacers {
|
||||
.mt-#{$key},
|
||||
.my-#{$key},
|
||||
.m-#{$key} {
|
||||
margin-top: $val;
|
||||
}
|
||||
.mb-#{$key},
|
||||
.my-#{$key},
|
||||
.m-#{$key} {
|
||||
margin-bottom: $val;
|
||||
}
|
||||
.ms-#{$key},
|
||||
.mx-#{$key},
|
||||
.m-#{$key} {
|
||||
margin-left: $val;
|
||||
}
|
||||
.me-#{$key},
|
||||
.mx-#{$key},
|
||||
.m-#{$key} {
|
||||
margin-right: $val;
|
||||
}
|
||||
.pt-#{$key},
|
||||
.py-#{$key},
|
||||
.p-#{$key} {
|
||||
padding-top: $val;
|
||||
}
|
||||
.pb-#{$key},
|
||||
.py-#{$key},
|
||||
.p-#{$key} {
|
||||
padding-bottom: $val;
|
||||
}
|
||||
.ps-#{$key},
|
||||
.px-#{$key},
|
||||
.p-#{$key} {
|
||||
padding-left: $val;
|
||||
}
|
||||
.pe-#{$key},
|
||||
.px-#{$key},
|
||||
.p-#{$key} {
|
||||
padding-right: $val;
|
||||
}
|
||||
|
||||
.rounded-#{$key} {
|
||||
border-radius: $val;
|
||||
}
|
||||
}
|
||||
:root {
|
||||
@each $key, $val in $spacers {
|
||||
--space-#{$key}: #{$val};
|
||||
--rounded-#{$key}: #{$val};
|
||||
}
|
||||
}
|
||||
|
||||
/* 颜色 $colors 在 uni.scss 中 */
|
||||
@each $key, $val in $colors {
|
||||
.text-#{$key} {
|
||||
color: $val;
|
||||
}
|
||||
}
|
||||
:root {
|
||||
@each $key, $val in $colors {
|
||||
--color-#{$key}: #{$val};
|
||||
}
|
||||
}
|
||||
|
||||
/* 每个页面公共css */
|
||||
page {
|
||||
background-color: $u-bg-color; // uview-ui/theme.scss
|
||||
}
|
||||
|
||||
.p-page {
|
||||
@extend .p-base;
|
||||
}
|
||||
|
||||
.page-box-x-overflow {
|
||||
margin-left: calc(var(--space-base) * -1);
|
||||
margin-right: calc(var(--space-base) * -1);
|
||||
}
|
||||
.page-box-t-overflow {
|
||||
margin-top: calc(var(--space-base) * -1);
|
||||
}
|
||||
|
||||
.page-box {
|
||||
@extend .bg-white, .rounded-sm;
|
||||
}
|
||||
.page-box + .page-box {
|
||||
@extend .mt-base;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.fw-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-end {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.TrainItem {
|
||||
.OrderInfo {
|
||||
font-size: 14px;
|
||||
color: var(--color-info);
|
||||
}
|
||||
|
||||
.Time {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.Station {
|
||||
font-size: 14px;
|
||||
color: var(--color-main);
|
||||
}
|
||||
|
||||
.OptionItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: var(--space-base);
|
||||
padding-bottom: var(--space-base);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
&.lg {
|
||||
.Time {
|
||||
font-size: 22px;
|
||||
}
|
||||
.Station {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@ -0,0 +1,12 @@
|
||||
const apiOther = {
|
||||
/**
|
||||
* 发送手机验证码
|
||||
*/
|
||||
sendMobileCode: (params) => {
|
||||
return uni.$u.http.post("/mobile/get_verify_code", {
|
||||
mobileNo: params.mobileNo, // 手机号码
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default apiOther;
|
||||
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 站点接口
|
||||
*/
|
||||
|
||||
const apiStations = {
|
||||
/**
|
||||
* 站点列表
|
||||
*/
|
||||
get: (params) => {
|
||||
return uni.$u.http.get("/stations");
|
||||
},
|
||||
};
|
||||
|
||||
export default apiStations;
|
||||
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 车票接口
|
||||
*/
|
||||
|
||||
const apiTickets = {
|
||||
/**
|
||||
* 车票列表
|
||||
*/
|
||||
get: (params) => {
|
||||
return uni.$u.http.get("/tickets", {
|
||||
params: {
|
||||
state: 1, // 已出票(订单已支付)
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default apiTickets;
|
||||
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 车次接口
|
||||
*/
|
||||
|
||||
const apiTrains = {
|
||||
/**
|
||||
* 车次列表
|
||||
*/
|
||||
get: (params) => {
|
||||
return uni.$u.http.get("/trains/query_train", {
|
||||
params: {
|
||||
from: params.from,
|
||||
to: params.to,
|
||||
date: params.date,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default apiTrains;
|
||||
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,118 @@
|
||||
{
|
||||
"name": "Mini-12306",
|
||||
"appid": "",
|
||||
"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_MOBILE_NO_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": {
|
||||
"dSYMs": false
|
||||
},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs": {
|
||||
"ad": {}
|
||||
},
|
||||
"icons": {
|
||||
"android": {
|
||||
"hdpi": "static/app-icon/72x72.png",
|
||||
"xhdpi": "static/app-icon/96x96.png",
|
||||
"xxhdpi": "static/app-icon/144x144.png",
|
||||
"xxxhdpi": "static/app-icon/192x192.png"
|
||||
},
|
||||
"ios": {
|
||||
"appstore": "",
|
||||
"ipad": {
|
||||
"app": "",
|
||||
"app@2x": "",
|
||||
"notification": "",
|
||||
"notification@2x": "",
|
||||
"proapp@2x": "",
|
||||
"settings": "",
|
||||
"settings@2x": "",
|
||||
"spotlight": "",
|
||||
"spotlight@2x": ""
|
||||
},
|
||||
"iphone": {
|
||||
"app@2x": "",
|
||||
"app@3x": "",
|
||||
"notification@2x": "",
|
||||
"notification@3x": "",
|
||||
"settings@2x": "",
|
||||
"settings@3x": "",
|
||||
"spotlight@2x": "",
|
||||
"spotlight@3x": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"splashscreen": {
|
||||
"androidStyle": "default",
|
||||
"android": {
|
||||
"hdpi": "static/splashscreen/hdpi/_20240628103445.9.png",
|
||||
"mdpi": "static/splashscreen/mdpi/_20240628103445.9.png",
|
||||
"xhdpi": "static/splashscreen/xhdpi/_20240628103445.9.png",
|
||||
"xxhdpi": "static/splashscreen/xxhdpi/_20240628103445.9.png",
|
||||
"xxxhdpi": "static/splashscreen/xxxhdpi/_20240628103445.9.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"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": "2"
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "Mini-12306",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"dayjs": "^1.11.11",
|
||||
"uview-ui": "^2.0.36"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.11",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz",
|
||||
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
|
||||
},
|
||||
"node_modules/uview-ui": {
|
||||
"version": "2.0.36",
|
||||
"resolved": "https://registry.npmmirror.com/uview-ui/-/uview-ui-2.0.36.tgz",
|
||||
"integrity": "sha512-ASSZT6M8w3GTO1eFPbsgEFV0U5UujK+8pTNr+MSUbRNcRMC1u63DDTLJVeArV91kWM0bfAexK3SK9pnTqF9TtA==",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"dayjs": "1.11.11",
|
||||
"uview-ui": "2.0.36"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<view class="p-page">
|
||||
<u-navbar
|
||||
:placeholder="true"
|
||||
bg-color="transparent"
|
||||
:auto-back="true"
|
||||
></u-navbar>
|
||||
|
||||
<view class="mt-llg p-base u-flex u-flex-column u-flex-items-center">
|
||||
<u-image
|
||||
src="@/static/logo.png"
|
||||
:width="60"
|
||||
:height="60"
|
||||
:radius="10"
|
||||
></u-image>
|
||||
<view class="mt-sm">
|
||||
<u-text text="欢迎登录" :size="20"></u-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="p-base">
|
||||
<u-form
|
||||
label-position="top"
|
||||
labelWidth="auto"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
ref="formRef"
|
||||
>
|
||||
<u-form-item prop="account">
|
||||
<u-input
|
||||
v-model="formData.account"
|
||||
:custom-style="{ backgroundColor: 'white' }"
|
||||
@focus="$refs.formRef.clearValidate('account')"
|
||||
>
|
||||
<view slot="prefix" class="u-flex-y-center">
|
||||
<view class="ms-base me-sm">用户</view>
|
||||
<u-line
|
||||
direction="col"
|
||||
length="26px"
|
||||
:style="{ margin: '0 var(--space-base)' }"
|
||||
></u-line>
|
||||
</view>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item prop="password">
|
||||
<u-input
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
:custom-style="{ backgroundColor: 'white' }"
|
||||
@focus="$refs.formRef.clearValidate('password')"
|
||||
>
|
||||
<view slot="prefix" class="u-flex-y-center">
|
||||
<view class="ms-base me-sm">密码</view>
|
||||
<u-line
|
||||
direction="col"
|
||||
length="26px"
|
||||
:style="{ margin: '0 var(--space-base)' }"
|
||||
></u-line> </view
|
||||
></u-input>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
|
||||
<view class="mt-llg">
|
||||
<u-button type="primary" @tap="handleLogin">登录</u-button>
|
||||
</view>
|
||||
<view class="mt-base">
|
||||
<u-button @tap="handleToRegister">注册</u-button>
|
||||
</view>
|
||||
|
||||
<view class="mt-llg text-center text-primary">
|
||||
<text :style="{ fontSize: '14px' }">《服务条款》《隐私权政策》</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from "vuex";
|
||||
import { validateRequired } from "@/utils/form-validators";
|
||||
import apiAuth from "@/api/auth";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
account: "",
|
||||
password: "",
|
||||
},
|
||||
formRules: {
|
||||
// 登录时不用 trigger 触发验证时为了交互流畅
|
||||
account: {
|
||||
validator: (rule, value, callback) =>
|
||||
validateRequired(rule, value, callback, { label: "用户名" }),
|
||||
},
|
||||
password: {
|
||||
validator: (rule, value, callback) =>
|
||||
validateRequired(rule, value, callback, { label: "密码" }),
|
||||
},
|
||||
},
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
...mapActions({ updateAuth: "updateAuth" }),
|
||||
handleLogin() {
|
||||
this.$refs.formRef
|
||||
.validate()
|
||||
.then(() => {
|
||||
uni.showLoading({ title: "正在登录", mask: true });
|
||||
|
||||
apiAuth
|
||||
.login({
|
||||
account: this.formData.account,
|
||||
password: this.formData.password,
|
||||
})
|
||||
.then((res) => {
|
||||
uni.hideLoading();
|
||||
this.updateAuth(res.data);
|
||||
uni.switchTab({
|
||||
url: "/pages/HomeView/HomeView",
|
||||
});
|
||||
})
|
||||
.catch(() => uni.hideLoading());
|
||||
})
|
||||
.catch();
|
||||
},
|
||||
handleToRegister() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/RegistrationView/RegistrationView",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<u-index-list :index-list="stationList.indexList">
|
||||
<template v-for="(item, index) in stationList.itemArr">
|
||||
<u-index-item>
|
||||
<u-index-anchor :text="stationList.indexList[index]"></u-index-anchor>
|
||||
<view
|
||||
v-for="(cell, i) in item"
|
||||
@tap="handleSelectedStation(cell)"
|
||||
class="py-base mx-base"
|
||||
:class="[{ 'u-border-bottom': i !== item.length - 1 }]"
|
||||
>{{ cell }}</view
|
||||
>
|
||||
</u-index-item>
|
||||
</template>
|
||||
</u-index-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
key: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ stations: "stations" }),
|
||||
stationList() {
|
||||
// updateStations 中已对 stations 进行排序
|
||||
const indexList = [];
|
||||
const itemArr = [];
|
||||
for (const station of this.stations) {
|
||||
const currChar = indexList.slice(-1)[0];
|
||||
const char = station.pinyin[0];
|
||||
if (currChar === char) {
|
||||
itemArr.slice(-1)[0].push(station.name);
|
||||
} else {
|
||||
indexList.push(char);
|
||||
itemArr.push([station.name]);
|
||||
}
|
||||
}
|
||||
return { indexList, itemArr };
|
||||
},
|
||||
},
|
||||
onLoad(query) {
|
||||
const { key } = query;
|
||||
this.key = key;
|
||||
},
|
||||
methods: {
|
||||
handleSelectedStation(stationName) {
|
||||
const pages = getCurrentPages();
|
||||
const prevPage = pages[pages.length - 2];
|
||||
prevPage.$vm[this.key] = stationName;
|
||||
uni.navigateBack({ delta: 1 });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<view v-if="nextTicket" class="page-box TrainItem lg">
|
||||
<view class="px-base py-llg">
|
||||
<view>
|
||||
<u-row :gutter="12">
|
||||
<u-col :span="4">
|
||||
<view class="Time">{{
|
||||
nextTicket.fromTime | timeFormat("HH:mm")
|
||||
}}</view>
|
||||
<view class="Station">{{ nextTicket.from }}</view>
|
||||
</u-col>
|
||||
<u-col :span="4">
|
||||
<view class="text-center">
|
||||
<view class="my-ssm">{{ nextTicket.trainNo }}</view>
|
||||
<u-line></u-line>
|
||||
<view
|
||||
class="my-ssm text-center text-info"
|
||||
:style="{ fontSize: '15px' }"
|
||||
>{{ $u.dayjs(nextTicket.date).format("MM月DD日") }} {{
|
||||
$u.dayjs(nextTicket.date).isToday()
|
||||
? "今天"
|
||||
: $u.timeFormat(nextTicket.date, "[周]dd")
|
||||
}}</view
|
||||
>
|
||||
</view>
|
||||
</u-col>
|
||||
<u-col :span="4">
|
||||
<view class="Time text-end">{{
|
||||
nextTicket.toTime | timeFormat("HH:mm")
|
||||
}}</view>
|
||||
<view class="Station text-end">{{ nextTicket.to }}</view>
|
||||
</u-col>
|
||||
</u-row>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="loading" class="u-flex-xy-center p-base">
|
||||
<text class="text-info">加载中...</text>
|
||||
</view>
|
||||
<view v-else class="u-flex-xy-center p-base">
|
||||
<text class="text-info">当前没有出行计划</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import apiTickets from "@/api/tickets";
|
||||
import { timeAscCompareFn } from "@/utils/functions.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
nextTicket: undefined,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ auth: "auth" }),
|
||||
},
|
||||
mounted() {
|
||||
this.loadMyTickets();
|
||||
},
|
||||
methods: {
|
||||
loadMyTickets() {
|
||||
this.loading = true;
|
||||
apiTickets
|
||||
.get()
|
||||
.then((res) => {
|
||||
this.nextTicket = res.data
|
||||
.sort(
|
||||
(a, b) =>
|
||||
timeAscCompareFn(a.fromTime, b.fromTime) ||
|
||||
timeAscCompareFn(a.toTime, b.toTime) ||
|
||||
a.id - b.id
|
||||
)
|
||||
.find((tkt) => uni.$u.dayjs().isBefore(uni.$u.dayjs(tkt.toTime)));
|
||||
})
|
||||
.catch(() => {
|
||||
this.nextTicket = undefined;
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 762 B |
|
After Width: | Height: | Size: 502 B |
|
After Width: | Height: | Size: 978 B |
|
After Width: | Height: | Size: 734 B |
|
After Width: | Height: | Size: 883 B |
|
After Width: | Height: | Size: 841 B |
|
After Width: | Height: | Size: 454 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 510 B |
|
After Width: | Height: | Size: 525 B |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 652 B |
|
After Width: | Height: | Size: 579 B |
|
After Width: | Height: | Size: 944 B |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 578 B |
|
After Width: | Height: | Size: 594 B |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 366 B |
|
After Width: | Height: | Size: 558 B |
|
After Width: | Height: | Size: 186 B |
|
After Width: | Height: | Size: 294 B |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 727 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 335 KiB |
|
After Width: | Height: | Size: 652 KiB |
|
After Width: | Height: | Size: 1023 KiB |
@ -0,0 +1,35 @@
|
||||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
|
||||
import storeAuth from "./modules/auth";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
stations: [],
|
||||
},
|
||||
mutations: {
|
||||
SET_STATIONS(state, stations) {
|
||||
state.stations = stations || [];
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* [{ id: 1, name: "长沙南站" }]
|
||||
*/
|
||||
updateStations({ commit }, stations) {
|
||||
commit(
|
||||
"SET_STATIONS",
|
||||
stations.sort((a, b) =>
|
||||
a.pinyin < b.pinyin ? -1 : a.pinyin > b.pinyin ? 1 : 0
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
modules: {
|
||||
auth: storeAuth,
|
||||
},
|
||||
});
|
||||
|
||||
export default store;
|
||||
@ -0,0 +1,16 @@
|
||||
import getDefaultAuth from "./state";
|
||||
|
||||
export function updateAuth({ commit }, auth) {
|
||||
auth = auth ?? getDefaultAuth();
|
||||
if ("token" in auth) uni.setStorageSync("auth-token", auth.token);
|
||||
if ("id" in auth) commit("SET_AUTH_ID", auth.id);
|
||||
if ("account" in auth) commit("SET_AUTH_ACCOUNT", auth.account);
|
||||
if ("name" in auth) commit("SET_AUTH_NAME", auth.name);
|
||||
if ("idCardNo" in auth) commit("SET_AUTH_ID_CARD_NO", auth.idCardNo);
|
||||
if ("mobileNo" in auth) commit("SET_AUTH_MOBILE_NO", auth.mobileNo);
|
||||
if ("bankCardNo" in auth) commit("SET_AUTH_BANK_CARD_NO", auth.bankCardNo);
|
||||
}
|
||||
|
||||
export function revertAuth({ dispatch }) {
|
||||
dispatch("updateAuth", getDefaultAuth());
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Auth 代表登录用户信息
|
||||
*/
|
||||
|
||||
import getDefaultAuth from "./state";
|
||||
import * as mutations from "./mutations";
|
||||
import * as actions from "./actions";
|
||||
|
||||
const storeAuth = {
|
||||
state: getDefaultAuth(),
|
||||
mutations,
|
||||
actions,
|
||||
};
|
||||
|
||||
export default storeAuth;
|
||||
@ -0,0 +1,25 @@
|
||||
import getDefaultAuth from "./state";
|
||||
|
||||
export function SET_AUTH_ID(state, id) {
|
||||
state.id = id || getDefaultAuth().id;
|
||||
}
|
||||
|
||||
export function SET_AUTH_ACCOUNT(state, account) {
|
||||
state.account = account || getDefaultAuth().account;
|
||||
}
|
||||
|
||||
export function SET_AUTH_NAME(state, name) {
|
||||
state.name = name || getDefaultAuth().name;
|
||||
}
|
||||
|
||||
export function SET_AUTH_ID_CARD_NO(state, idCardNo) {
|
||||
state.idCardNo = idCardNo || getDefaultAuth().idCardNo;
|
||||
}
|
||||
|
||||
export function SET_AUTH_MOBILE_NO(state, mobileNo) {
|
||||
state.mobileNo = mobileNo || getDefaultAuth().mobileNo;
|
||||
}
|
||||
|
||||
export function SET_AUTH_BANK_CARD_NO(state, bankCardNo) {
|
||||
state.bankCardNo = bankCardNo || getDefaultAuth().bankCardNo;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
function getDefaultAuth() {
|
||||
return {
|
||||
id: undefined,
|
||||
account: "", // 用户名
|
||||
name: "", // 姓名
|
||||
idCardNo: "", // 身份证号
|
||||
mobileNo: undefined, // 手机号
|
||||
bankCardNo: undefined, // 银行卡号
|
||||
};
|
||||
}
|
||||
|
||||
export default getDefaultAuth;
|
||||
@ -0,0 +1,10 @@
|
||||
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) => res[0] ? reject(res[0]) : resolve(res[1]));
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
export function timeAscCompareFn(aTime, bTime) {
|
||||
const at = uni.$u.dayjs(aTime);
|
||||
const bt = uni.$u.dayjs(bTime);
|
||||
|
||||
if (at.isBefore(bt)) return -1;
|
||||
else if (at.isAfter(bt)) return 1;
|
||||
else return 0;
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
uNavbarBgColor: "transparent",
|
||||
uNavbarTitleColor: "transparent",
|
||||
uNavbarIconColor: "var(--color-white)",
|
||||
};
|
||||
},
|
||||
onPageScroll(e) {
|
||||
let per = e.scrollTop / 200;
|
||||
if (per < 0) per = 0;
|
||||
else if (per > 1) per = 1;
|
||||
|
||||
this.uNavbarBgColor = `rgba(245,245,245,${per})`;
|
||||
this.uNavbarTitleColor = `rgba(48,49,51,${per})`;
|
||||
this.uNavbarIconColor = `rgba(${255 - (255 - 96) * per},${
|
||||
255 - (255 - 98) * per
|
||||
},${255 - (255 - 102) * per})`;
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
transpileDependencies: ["uview-ui"],
|
||||
};
|
||||