@ -0,0 +1,8 @@
|
|||||||
|
# H5应用端口
|
||||||
|
VITE_APP_PORT = 3000
|
||||||
|
|
||||||
|
# H5代理前缀
|
||||||
|
VITE_APP_PROXY_PREFIX = "/dev-api"
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
VITE_APP_BASE_URL = "http://localhost:8080"
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
# 接口地址
|
||||||
|
VITE_APP_BASE_URL = "http://prod-api"
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
.hbuilderx
|
||||||
|
.project
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
unpackage
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
@ -0,0 +1 @@
|
|||||||
|
npx --no-install commitlint --edit $1
|
||||||
@ -0,0 +1 @@
|
|||||||
|
npm run lint:lint-staged
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
"*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}": ["prettier --write"],
|
||||||
|
};
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
unpackage
|
||||||
|
node_modules
|
||||||
|
uni_modules
|
||||||
|
static
|
||||||
|
.husky
|
||||||
|
.hbuilderx
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.sh
|
||||||
|
*.md
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
onLaunch,
|
||||||
|
onShow,
|
||||||
|
onHide,
|
||||||
|
onError,
|
||||||
|
onPageNotFound,
|
||||||
|
} from "@dcloudio/uni-app";
|
||||||
|
import { checkForUpdate } from "@/utils";
|
||||||
|
|
||||||
|
onLaunch(() => {
|
||||||
|
console.log("App Launch");
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
checkForUpdate();
|
||||||
|
// #endif
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
console.log("App Show");
|
||||||
|
});
|
||||||
|
|
||||||
|
onHide(() => {
|
||||||
|
console.log("App Hide");
|
||||||
|
});
|
||||||
|
|
||||||
|
onError((error) => {
|
||||||
|
console.error("App Error", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
onPageNotFound((res) => {
|
||||||
|
console.error("PageNotFound", res);
|
||||||
|
uni.redirectTo({
|
||||||
|
url: "/pages/404/404",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* 每个页面公共css */
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uv-ui基础样式 */
|
||||||
|
@import "@/uni_modules/uv-ui-tools/index.scss";
|
||||||
|
</style>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 白開水
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
import AuthAPI from "./modules/auth";
|
||||||
|
import UserAPI from "./modules/user";
|
||||||
|
|
||||||
|
export { AuthAPI, UserAPI };
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import http from "@/utils/request";
|
||||||
|
|
||||||
|
class AuthAPI {
|
||||||
|
// 注册
|
||||||
|
static signUp(data) {
|
||||||
|
return http.post("/auth/signup", data, {
|
||||||
|
custom: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
static signIn(data) {
|
||||||
|
return http.post("/auth/signin", data, {
|
||||||
|
custom: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新token
|
||||||
|
static refreshToken(header = {}) {
|
||||||
|
return http.post("/auth/refresh", undefined, {
|
||||||
|
header,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthAPI;
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import http from "@/utils/request";
|
||||||
|
|
||||||
|
class UserAPI {
|
||||||
|
// test get api
|
||||||
|
static getUserList(params = {}) {
|
||||||
|
return http.get("/user/list", {
|
||||||
|
params,
|
||||||
|
custom: {
|
||||||
|
auth: true,
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserAPI;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<text
|
||||||
|
:class="`iconfont icon-${name}`"
|
||||||
|
:style="{
|
||||||
|
color: color,
|
||||||
|
'font-size': size + 'px',
|
||||||
|
}"
|
||||||
|
@click="handleClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const emit = defineEmits(["click"]);
|
||||||
|
const props = defineProps({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: "#333333",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 16,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
emit("click");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import "./iconfont.css";
|
||||||
|
</style>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "iconfont"; /* Project id 4657887 */
|
||||||
|
src:
|
||||||
|
url("iconfont.woff2?t=1724050101808") format("woff2"),
|
||||||
|
url("iconfont.woff?t=1724050101808") format("woff"),
|
||||||
|
url("iconfont.ttf?t=1724050101808") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconfont {
|
||||||
|
font-family: "iconfont" !important;
|
||||||
|
font-size: 16px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-uniapp:before {
|
||||||
|
content: "\e624";
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"id": "4657887",
|
||||||
|
"name": "vue3-uniapp-template",
|
||||||
|
"font_family": "iconfont",
|
||||||
|
"css_prefix_text": "icon-",
|
||||||
|
"description": "",
|
||||||
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "19317486",
|
||||||
|
"name": "uniapp",
|
||||||
|
"font_class": "uniapp",
|
||||||
|
"unicode": "e624",
|
||||||
|
"unicode_decimal": 58916
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<picker
|
||||||
|
range-key="label"
|
||||||
|
:range="langOptions"
|
||||||
|
:value="langIndex"
|
||||||
|
@change="handleLangChange"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
<image
|
||||||
|
:style="langStyle"
|
||||||
|
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAMAAAAL34HQAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAJMUExURUdwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTIN/sAAADDdFJOUwAU0qz4D0aAQPoBOWx5n+AHLfs4Asfa8BP8GfQs7HPC3RY9ohUIBmKmycFllp4i/gTG/WvmH1OR+el4M97czb7rBVdC7RxorWCT1jUS5RqrcQyb9XdQEAkX8Vxjw3DPkiTX9+HvPHuN0Bvi515qINW1CkeluFWXmIQeQQM2HU5U86FIf9u2mcRWxdQycva8T1qPnUw0h9nouSE7t3Z9fqiVzMtDtI58b4hu04xno00RPkSndDeUspzOKaBtX/J6qe5LPwdbu1UAAAUcSURBVHja7Zr3XxQ5GMaDwAKLKyzrimIDQSwoIoKCnDQVG9i7nr1jv6J3Z+/lzt57773cWc46/5gsouSdSTLJTDaOfvL8mOSdPJv5buZ9M4OQlpaWlpaWlkMlYEr0ji0DU4K2pW1pW9qWtqVt/ZC2EozoSNvStrQtbUvb0ra+N1uJCW60CndSjvd8y8RizRvc1kGv5Dv9cFeVrT3iyp+J27rllcVaCyAv9oqt1birnl5xld8Ht7XcK7YG4K7ap3rE1ZoJuK3fvbJYNwDwxzziqrY97qqFJ8nyDPArk3BXmVHf4adU4vPFbaQMewIW61D01+FnMOFO8qAOhtrFQii3CEzZnzioAoypVp4XGPVTCEO2gSFzldCcOx9Mutg6ousgjgWVrjwwabugZcDib7BYCBXA5bpi7h8FntHGJFWb0mZYLsxg3sIl6jbLA2DiBZNB53Vo+pnCVArs4ca/jFu4TuWzZT1ckYnUW3hynEpbqcvA5IXJX3t+gYaPqn0Ub4Kzl2Y0tdfA9jGMSyzCqtnpUakfDKNvQWNrCEKXPpV+AR945MdLspUxDfq6G2nMKoWNeYwLxIKRLWUt12+mw44HCP0Vhk2sIiwGrqu89PUV9LBlDpoNW0pWMqIDpl+VLMvWuJ/ghVvMMM00kBWdaRrcW9pyhQqZR1g9WcmfzzxaGvQIbWe5qqtF3MBLhR61DtNdJe1H/MBLrtlGl1Jt5TEDA4SIZHm+Qu0ortgE+1MIIa0kPoRqyK7Kq5hRPlKMROgR6k2aYchodlAs8be0lGirioB94SN2DAF46QcVWXWW6z+1CQlQgJQIPerS0XL5YaniwEuG/m/Sf3HJUGHgpUKfepE8QccuwsBLhH7oC+oM/ahBU8E4cKKxKlpYNSs7lxI1EOSvH0FQSIKrm2nMDOJyDgfwI1AbudBnnLJ7OVc53R74IHotE3p/zTSOt4YV+TbA90UoPk4e9HNmkv5+hLZF5sgc0P1/Q0srvOGOG1cT5xEcVLftTGgdGWLs8OmRffeMpJ0+WE6Yvr6soecw6U6uAMGgAJjd2DRSBvSnt5L29fDnv111ibXrMQv4iG67h/6PI8RdIbugqb+syNyV4mcBH5Fr6JfuIqYkSdh9ygmzyjKY0gxvas12ld5kXCohPwAB1QXZ0HIMG/iIFrqAPmvAPPLmtM+cLtzDlySWtcN/kdOdvmrSsDiyqbTj1uQqOKu532cHvOXNCDf0oV5DaPv4rCCxgOw0wR74cHO7A+iLL+ygPlz+OUcr6Iubtta39sBHdF8Y+l/pj7zO+Yy44e8jxQYH8I0pkjD0Y2mm0vNs/iPvGrJ6HuAdQf+S4upalm3oh6K1PMBbfjwX9MtIpnYP5gEAXn4MYYf/ooecGXezxhOOrv508NyiFK1OC9n+FtIHO3maUotWh4Xs5D4yTNGLVqeF7F5s/MyrTrM0n8g3ejzQn/96EPO8zHlGGyv07SDHTr/h81vWs3tyXeTZAsBzQu9fYBgnAkvdlUoBQ0wc0I//b6HrsjJT0Ja8c3ppwEs/spQEvOwjS0nAq/q2KmCIKzn6tkDRWkH92DglWuf0PMB3oI7rpBZ6AHwP+ri2aSqhh8B3Y4zsrhL6APcXqIkKoYcpTS/m2DbqoOcFXjH0vMCrhZ4feKXQ8wOvEnoR4BVCLwK8QuhFgFcHvRjwyqAXA14V9KLAK4JeFHhF0IsCrwZ6ceCVQB+TiKsr7wsAEIW0tLS0tLS0tLS0vKZPHNE+dYS3Jg4AAAAASUVORK5CYII="
|
||||||
|
></image>
|
||||||
|
</slot>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const emit = defineEmits(["change"]);
|
||||||
|
const props = defineProps({
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 40,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { locale, t } = useI18n();
|
||||||
|
const langStyle = {
|
||||||
|
width: props.size + "rpx",
|
||||||
|
height: props.size + "rpx",
|
||||||
|
};
|
||||||
|
const langOptions = computed(() => {
|
||||||
|
return [
|
||||||
|
{ label: t("locale.en"), value: "en" },
|
||||||
|
{ label: t("locale.zh-hans"), value: "zh-Hans" },
|
||||||
|
{ label: t("locale.zh-hant"), value: "zh-Hant" },
|
||||||
|
{ label: t("locale.ja"), value: "ja" },
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const langIndex = computed(() => {
|
||||||
|
return langOptions.value.findIndex((item) => {
|
||||||
|
return item.value === locale.value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
function handleLangChange(event) {
|
||||||
|
const lang = langOptions.value[event.detail.value].value;
|
||||||
|
locale.value = lang;
|
||||||
|
uni.setLocale(lang);
|
||||||
|
emit("change", lang);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { hasPerm } from "@/plugins/permission";
|
||||||
|
import { currentRoute } from "@/router";
|
||||||
|
|
||||||
|
// 对某些特殊场景需要在页面onShow生命周期中校验权限:
|
||||||
|
// 1.微信小程序端点击tabbar的底层逻辑不触发uni.switchTab
|
||||||
|
// 2.h5在浏览器地址栏输入url后跳转不触发uni的路由api
|
||||||
|
// 3.首次启动加载的页面不触发uni的路由api
|
||||||
|
export async function usePermission() {
|
||||||
|
return hasPerm(currentRoute());
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<!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,8 @@
|
|||||||
|
{
|
||||||
|
"locale.auto": "System",
|
||||||
|
"locale.en": "English",
|
||||||
|
"locale.zh-hans": "Simplified Chinese",
|
||||||
|
"locale.zh-hant": "Traditional Chinese",
|
||||||
|
"locale.ja": "Japanese",
|
||||||
|
"index.demo": "uni-app internationalization demo"
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { createI18n } from "vue-i18n"; // v9.x
|
||||||
|
import en from "./en.json";
|
||||||
|
import ja from "./ja.json";
|
||||||
|
import zhHans from "./zh-Hans.json";
|
||||||
|
import zhHant from "./zh-Hant.json";
|
||||||
|
|
||||||
|
const i18n = createI18n({
|
||||||
|
legacy: false, // 必须设置false才能使用Composition API
|
||||||
|
globalInjection: true, // 为每个组件注入$为前缀的全局属性和函数
|
||||||
|
locale: uni.getLocale(),
|
||||||
|
messages: {
|
||||||
|
en,
|
||||||
|
"zh-Hans": zhHans,
|
||||||
|
"zh-Hant": zhHant,
|
||||||
|
ja,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function setupI18n(app) {
|
||||||
|
app.use(i18n);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { i18n };
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"locale.auto": "システム",
|
||||||
|
"locale.en": "英語",
|
||||||
|
"locale.zh-hans": "簡体字中国語",
|
||||||
|
"locale.zh-hant": "繁体字中国語",
|
||||||
|
"locale.ja": "日本語",
|
||||||
|
"index.demo": "uni-app 国際化デモ"
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"uni.app.quit": "もう一度押すと、アプリケーションが終了します",
|
||||||
|
"uni.async.error": "サーバーへの接続がタイムアウトしました。画面をクリックして再試行してください",
|
||||||
|
"uni.showActionSheet.cancel": "キャンセル",
|
||||||
|
"uni.showToast.unpaired": "使用するには、showToastとhideToastをペアにする必要があることに注意してください",
|
||||||
|
"uni.showLoading.unpaired": "使用するには、showLoadingとhideLoadingをペアにする必要があることに注意してください",
|
||||||
|
"uni.showModal.cancel": "キャンセル",
|
||||||
|
"uni.showModal.confirm": "OK",
|
||||||
|
"uni.chooseImage.cancel": "キャンセル",
|
||||||
|
"uni.chooseImage.sourceType.album": "アルバムから選択",
|
||||||
|
"uni.chooseImage.sourceType.camera": "カメラ",
|
||||||
|
"uni.chooseVideo.cancel": "キャンセル",
|
||||||
|
"uni.chooseVideo.sourceType.album": "アルバムから選択",
|
||||||
|
"uni.chooseVideo.sourceType.camera": "カメラ",
|
||||||
|
"uni.previewImage.cancel": "キャンセル",
|
||||||
|
"uni.previewImage.button.save": "画像を保存",
|
||||||
|
"uni.previewImage.save.success": "画像をアルバムに正常に保存します",
|
||||||
|
"uni.previewImage.save.fail": "画像をアルバムに保存できませんでした",
|
||||||
|
"uni.setClipboardData.success": "コンテンツがコピーされました",
|
||||||
|
"uni.scanCode.title": "スキャンコード",
|
||||||
|
"uni.scanCode.album": "アルバム",
|
||||||
|
"uni.scanCode.fail": "認識に失敗しました",
|
||||||
|
"uni.scanCode.flash.on": "タッチして点灯",
|
||||||
|
"uni.scanCode.flash.off": "タップして閉じる",
|
||||||
|
"uni.startSoterAuthentication.authContent": "指紋認識...",
|
||||||
|
"uni.picker.done": "完了",
|
||||||
|
"uni.picker.cancel": "キャンセル",
|
||||||
|
"uni.video.danmu": "「弾幕」",
|
||||||
|
"uni.video.volume": "ボリューム",
|
||||||
|
"uni.button.feedback.title": "質問のフィードバック",
|
||||||
|
"uni.button.feedback.send": "送信"
|
||||||
|
},
|
||||||
|
"ios": {},
|
||||||
|
"android": {}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"locale.auto": "系统",
|
||||||
|
"locale.en": "英语",
|
||||||
|
"locale.zh-hans": "简体中文",
|
||||||
|
"locale.zh-hant": "繁体中文",
|
||||||
|
"locale.ja": "日语",
|
||||||
|
"index.demo": "uni-app 国际化演示"
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"locale.auto": "系統",
|
||||||
|
"locale.en": "英語",
|
||||||
|
"locale.zh-hans": "簡體中文",
|
||||||
|
"locale.zh-hant": "繁體中文",
|
||||||
|
"locale.ja": "日語",
|
||||||
|
"index.demo": "uni-app 國際化演示"
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import App from "./App";
|
||||||
|
import { createSSRApp } from "vue";
|
||||||
|
import setupPlugins from "@/plugins";
|
||||||
|
|
||||||
|
export function createApp() {
|
||||||
|
const app = createSSRApp(App);
|
||||||
|
app.use(setupPlugins);
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"name" : "卡叔电梯",
|
||||||
|
"appid" : "__UNI__3A16C1C",
|
||||||
|
"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\"/>"
|
||||||
|
],
|
||||||
|
"minSdkVersion" : 21
|
||||||
|
},
|
||||||
|
/* ios打包配置 */
|
||||||
|
"ios" : {},
|
||||||
|
/* SDK配置 */
|
||||||
|
"sdkConfigs" : {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 快应用特有相关 */
|
||||||
|
"quickapp" : {},
|
||||||
|
/* 小程序特有相关 */
|
||||||
|
"mp-weixin" : {
|
||||||
|
"appid" : "wx2c04ebae2099b3d2",
|
||||||
|
"setting" : {
|
||||||
|
"urlCheck" : false,
|
||||||
|
"es6" : true,
|
||||||
|
"postcss" : true,
|
||||||
|
"minified" : true
|
||||||
|
},
|
||||||
|
"usingComponents" : true,
|
||||||
|
"permission" : {
|
||||||
|
"scope.userLocation" : {
|
||||||
|
"desc" : "获取当前位置"
|
||||||
|
},
|
||||||
|
"scope.userFuzzyLocation" : {
|
||||||
|
"desc" : "您的位置信息将用于小程序位置接口的效果展示"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 地理位置相关接口配置项 @see https://developers.weixin.qq.com/community/develop/doc/000a02f2c5026891650e7f40351c01 */
|
||||||
|
"requiredPrivateInfos" : [ "getLocation", "chooseLocation" ]
|
||||||
|
},
|
||||||
|
"mp-alipay" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-baidu" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-toutiao" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"uniStatistics" : {
|
||||||
|
"enable" : false
|
||||||
|
},
|
||||||
|
"vueVersion" : "3",
|
||||||
|
"h5" : {
|
||||||
|
"optimization" : {
|
||||||
|
"treeShaking" : {
|
||||||
|
"enable" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devServer" : {
|
||||||
|
"https" : false
|
||||||
|
},
|
||||||
|
"router" : {
|
||||||
|
"mode" : "history"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"locale" : "auto"
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "lift-wechat",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "电梯图纸生成",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "husky",
|
||||||
|
"lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"",
|
||||||
|
"lint:lint-staged": "lint-staged",
|
||||||
|
"commit": "git-cz"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"卡叔电梯"
|
||||||
|
],
|
||||||
|
"author": "Lufaneng",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"dayjs": "^1.11.18",
|
||||||
|
"three": "^0.178.0",
|
||||||
|
"vue-draggable-plus": "^0.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^19.1.0",
|
||||||
|
"@commitlint/config-conventional": "^19.1.0",
|
||||||
|
"@dcloudio/types": "^3.4.11",
|
||||||
|
"commitizen": "^4.3.0",
|
||||||
|
"cz-git": "^1.9.0",
|
||||||
|
"husky": "^9.0.11",
|
||||||
|
"lint-staged": "^15.2.2",
|
||||||
|
"prettier": "^3.2.5"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "node_modules/cz-git"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,380 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 顶部筛选标签 -->
|
||||||
|
<view class="filter-tabs">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'all' }"
|
||||||
|
@click="switchTab('all')"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'generate' }"
|
||||||
|
@click="switchTab('generate')"
|
||||||
|
>
|
||||||
|
生成
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'member' }"
|
||||||
|
@click="switchTab('member')"
|
||||||
|
>
|
||||||
|
会员
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'other' }"
|
||||||
|
@click="switchTab('other')"
|
||||||
|
>
|
||||||
|
其他
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 消费记录列表 -->
|
||||||
|
<view class="consumption-list">
|
||||||
|
<view
|
||||||
|
class="consumption-item"
|
||||||
|
v-for="item in filteredConsumptions"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<!-- 消费项目和金额 -->
|
||||||
|
<view class="item-header">
|
||||||
|
<view class="item-title">{{ item.title }}</view>
|
||||||
|
<view
|
||||||
|
class="item-amount"
|
||||||
|
:class="{
|
||||||
|
positive: item.amount > 0,
|
||||||
|
negative: item.amount < 0,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ item.amount > 0 ? "+" : "" }}¥{{
|
||||||
|
Math.abs(item.amount).toFixed(2)
|
||||||
|
}}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 消费详情 -->
|
||||||
|
<view class="item-details">
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="detail-label">消费编号:</text>
|
||||||
|
<text class="detail-value">{{ item.consumptionNumber }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="detail-label">消费时间:</text>
|
||||||
|
<text class="detail-value">{{ item.consumptionTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="balance-row">
|
||||||
|
<view class="balance-item">
|
||||||
|
<text class="balance-label">账前金额:</text>
|
||||||
|
<text class="balance-value">
|
||||||
|
¥{{ item.beforeBalance.toFixed(2) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="balance-item">
|
||||||
|
<text class="balance-label">账后金额:</text>
|
||||||
|
<text class="balance-value">
|
||||||
|
¥{{ item.afterBalance.toFixed(2) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view v-if="filteredConsumptions.length === 0" class="empty-state">
|
||||||
|
<u-icon name="file-text" size="60" color="#ccc"></u-icon>
|
||||||
|
<text class="empty-text">暂无消费记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多 -->
|
||||||
|
<view v-if="hasMore && filteredConsumptions.length > 0" class="load-more">
|
||||||
|
<u-button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
:loading="loading"
|
||||||
|
@click="loadMore"
|
||||||
|
>
|
||||||
|
{{ loading ? "加载中..." : "加载更多" }}
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from "vue";
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
|
import { useAuthStore, useUserStore } from "@/store";
|
||||||
|
import { regions } from "/static/json/regions.js";
|
||||||
|
import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue";
|
||||||
|
import RPC from "@/utils/request";
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const activeTab = ref("all");
|
||||||
|
const loading = ref(false);
|
||||||
|
const hasMore = ref(true);
|
||||||
|
const consumptions = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "乘客电梯图纸生成",
|
||||||
|
amount: -30.0,
|
||||||
|
consumptionNumber: "CONS20230516024",
|
||||||
|
consumptionTime: "2025-05-16 14:30:22",
|
||||||
|
beforeBalance: 1250.0,
|
||||||
|
afterBalance: 1220.0,
|
||||||
|
type: "generate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "货运电梯图纸生成",
|
||||||
|
amount: -45.0,
|
||||||
|
consumptionNumber: "CONS20230516024",
|
||||||
|
consumptionTime: "2025-05-16 14:30:22",
|
||||||
|
beforeBalance: 1295.0,
|
||||||
|
afterBalance: 1250.0,
|
||||||
|
type: "generate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "包年会员订阅",
|
||||||
|
amount: -45.0,
|
||||||
|
consumptionNumber: "CONS20230516024",
|
||||||
|
consumptionTime: "2025-05-16 14:30:22",
|
||||||
|
beforeBalance: 1295.0,
|
||||||
|
afterBalance: 1250.0,
|
||||||
|
type: "member",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "账户充值",
|
||||||
|
amount: 1000.0,
|
||||||
|
consumptionNumber: "CONS20230516024",
|
||||||
|
consumptionTime: "2025-05-16 14:30:22",
|
||||||
|
beforeBalance: 1795.0,
|
||||||
|
afterBalance: 2795.0,
|
||||||
|
type: "other",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 计算过滤后的消费记录
|
||||||
|
const filteredConsumptions = computed(() => {
|
||||||
|
if (activeTab.value === "all") {
|
||||||
|
return consumptions.value;
|
||||||
|
}
|
||||||
|
return consumptions.value.filter((item) => item.type === activeTab.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 切换标签
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
activeTab.value = tab;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
const loadMore = async () => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
// 模拟API调用
|
||||||
|
// const result = await RPC.get('/api/consumptions', {
|
||||||
|
// page: currentPage.value + 1,
|
||||||
|
// type: activeTab.value
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 模拟加载延迟
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// 模拟没有更多数据
|
||||||
|
hasMore.value = false;
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "没有更多数据了",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载更多失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "加载失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载消费记录
|
||||||
|
const loadConsumptions = async () => {
|
||||||
|
try {
|
||||||
|
// 调用获取消费记录API
|
||||||
|
// const result = await RPC.get('/api/consumptions', {
|
||||||
|
// type: activeTab.value
|
||||||
|
// })
|
||||||
|
// consumptions.value = result.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载消费记录失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "加载失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
const onRefresh = async () => {
|
||||||
|
hasMore.value = true;
|
||||||
|
await loadConsumptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadConsumptions();
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 页面显示时刷新数据
|
||||||
|
loadConsumptions();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
background: white;
|
||||||
|
padding: 0 16px;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #4285f4;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 20px;
|
||||||
|
height: 2px;
|
||||||
|
background: #4285f4;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.consumption-list {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.consumption-item {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-amount {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.positive {
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.negative {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-details {
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
color: #999;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-value {
|
||||||
|
color: #666;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.balance-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.balance-label {
|
||||||
|
color: #999;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-value {
|
||||||
|
color: #333;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 80px 20px;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-more {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,457 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 顶部标题 -->
|
||||||
|
<view class="header">
|
||||||
|
<view class="header-title">管理您的账户信息和服务</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 用户信息卡片 -->
|
||||||
|
<view class="user-card">
|
||||||
|
<view class="user-info">
|
||||||
|
<image
|
||||||
|
class="avatar"
|
||||||
|
src="/static/imgs/avatar.png"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<view class="user-details">
|
||||||
|
<view class="username">
|
||||||
|
张三
|
||||||
|
<u-icon
|
||||||
|
name="checkmark-circle-fill"
|
||||||
|
color="#ffa500"
|
||||||
|
size="16"
|
||||||
|
></u-icon>
|
||||||
|
</view>
|
||||||
|
<view class="user-id">用户ID: U12345678</view>
|
||||||
|
<view class="membership">
|
||||||
|
会员等级: 年会员 (有效期至: 2026-05-15)
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="edit-btn" @click="editProfile">
|
||||||
|
<text>点击编辑资料</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 账户余额 -->
|
||||||
|
<view class="balance-card">
|
||||||
|
<view class="balance-header">
|
||||||
|
<view class="balance-label">账户余额</view>
|
||||||
|
<u-button type="default" size="small" shape="round" @click="recharge">
|
||||||
|
充值
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
<view class="balance-amount">¥50.00</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 会员套餐 -->
|
||||||
|
<view class="membership-section">
|
||||||
|
<view class="membership-grid">
|
||||||
|
<view
|
||||||
|
class="membership-item"
|
||||||
|
:class="{ active: selectedPlan === 'monthly' }"
|
||||||
|
@click="selectPlan('monthly')"
|
||||||
|
>
|
||||||
|
<view class="tag new">新人特惠</view>
|
||||||
|
<view class="plan-name">包月会员</view>
|
||||||
|
<view class="plan-price">¥120</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="membership-item"
|
||||||
|
:class="{ active: selectedPlan === 'season' }"
|
||||||
|
@click="selectPlan('season')"
|
||||||
|
>
|
||||||
|
<view class="tag popular">最受欢迎</view>
|
||||||
|
<view class="plan-name">包季会员</view>
|
||||||
|
<view class="plan-price">¥300</view>
|
||||||
|
<view class="plan-discount">立减¥60</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="membership-item"
|
||||||
|
:class="{ active: selectedPlan === 'yearly' }"
|
||||||
|
@click="selectPlan('yearly')"
|
||||||
|
>
|
||||||
|
<view class="tag yearly">年度首享</view>
|
||||||
|
<view class="plan-name">包年会员</view>
|
||||||
|
<view class="plan-price">¥1000</view>
|
||||||
|
<view class="plan-discount">立减¥440</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 支付方式 -->
|
||||||
|
<view class="payment-section">
|
||||||
|
<view
|
||||||
|
class="payment-item"
|
||||||
|
:class="{ active: selectedPayment === 'alipay' }"
|
||||||
|
@click="selectPayment('alipay')"
|
||||||
|
>
|
||||||
|
<view class="payment-info">
|
||||||
|
<u-icon name="alipay-circle-fill" color="#1677ff" size="24"></u-icon>
|
||||||
|
<text class="payment-name">支付宝</text>
|
||||||
|
</view>
|
||||||
|
<u-icon
|
||||||
|
:name="
|
||||||
|
selectedPayment === 'alipay'
|
||||||
|
? 'checkmark-circle-fill'
|
||||||
|
: 'radio-button-off'
|
||||||
|
"
|
||||||
|
:color="selectedPayment === 'alipay' ? '#ff3333' : '#ccc'"
|
||||||
|
size="20"
|
||||||
|
></u-icon>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="payment-item"
|
||||||
|
:class="{ active: selectedPayment === 'wechat' }"
|
||||||
|
@click="selectPayment('wechat')"
|
||||||
|
>
|
||||||
|
<view class="payment-info">
|
||||||
|
<u-icon name="weixin-fill" color="#07c160" size="24"></u-icon>
|
||||||
|
<text class="payment-name">支付宝</text>
|
||||||
|
</view>
|
||||||
|
<u-icon
|
||||||
|
:name="
|
||||||
|
selectedPayment === 'wechat'
|
||||||
|
? 'checkmark-circle-fill'
|
||||||
|
: 'radio-button-off'
|
||||||
|
"
|
||||||
|
:color="selectedPayment === 'wechat' ? '#ff3333' : '#ccc'"
|
||||||
|
size="20"
|
||||||
|
></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="bottom-section">
|
||||||
|
<u-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
@click="confirmPayment"
|
||||||
|
:loading="paying"
|
||||||
|
>
|
||||||
|
确认支付
|
||||||
|
</u-button>
|
||||||
|
<view class="agreement">
|
||||||
|
购买前请仔细阅读
|
||||||
|
<text class="link" @click="showAgreement">《会员服务协议》</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue";
|
||||||
|
import { useAuthStore, useUserStore } from "@/store";
|
||||||
|
import RPC from "@/utils/request";
|
||||||
|
|
||||||
|
const modal = ref(null);
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const selectedPlan = ref("season"); // 默认选中包季会员
|
||||||
|
const selectedPayment = ref("alipay"); // 默认选中支付宝
|
||||||
|
const paying = ref(false);
|
||||||
|
|
||||||
|
// 选择会员套餐
|
||||||
|
const selectPlan = (plan) => {
|
||||||
|
selectedPlan.value = plan;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择支付方式
|
||||||
|
const selectPayment = (payment) => {
|
||||||
|
selectedPayment.value = payment;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑资料
|
||||||
|
const editProfile = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pagesA/edit-profile/edit-profile",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 充值
|
||||||
|
const recharge = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/recharge/recharge",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认支付
|
||||||
|
const confirmPayment = async () => {
|
||||||
|
if (!selectedPlan.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请选择会员套餐",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedPayment.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请选择支付方式",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paying.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
plan: selectedPlan.value,
|
||||||
|
paymentMethod: selectedPayment.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 这里调用支付API
|
||||||
|
// const result = await RPC.post('/api/payment', params)
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "正在跳转支付...",
|
||||||
|
icon: "loading",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 模拟支付过程
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.showToast({
|
||||||
|
title: "支付成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
paying.value = false;
|
||||||
|
}, 2000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("支付失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "支付失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
paying.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示协议
|
||||||
|
const showAgreement = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pages/agreement/agreement",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(180deg, #7b9cdb 0%, #f8f8f8 40%);
|
||||||
|
padding-bottom: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 20px 16px 16px;
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card {
|
||||||
|
margin: 0 16px 16px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-details {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.username {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-id {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.membership {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-btn {
|
||||||
|
text-align: right;
|
||||||
|
color: #4285f4;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-card {
|
||||||
|
margin: 0 16px 16px;
|
||||||
|
background: linear-gradient(135deg, #4285f4 0%, #6fa8f5 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
.balance-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.balance-label {
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.balance-amount {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.membership-section {
|
||||||
|
margin: 0 16px 24px;
|
||||||
|
|
||||||
|
.membership-grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.membership-item {
|
||||||
|
flex: 1;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px 12px;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #4285f4;
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&.new {
|
||||||
|
background: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.popular {
|
||||||
|
background: #ff3333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.yearly {
|
||||||
|
background: #ff3333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
margin: 12px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-price {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ff3333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-discount {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-section {
|
||||||
|
margin: 0 16px 24px;
|
||||||
|
|
||||||
|
.payment-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #4285f4;
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.payment-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-section {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: white;
|
||||||
|
padding: 16px;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
|
||||||
|
.agreement {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: #4285f4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,528 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 顶部筛选标签 -->
|
||||||
|
<view class="filter-tabs">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'all' }"
|
||||||
|
@click="switchTab('all')"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'recharge' }"
|
||||||
|
@click="switchTab('recharge')"
|
||||||
|
>
|
||||||
|
充值
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'monthly' }"
|
||||||
|
@click="switchTab('monthly')"
|
||||||
|
>
|
||||||
|
包月
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'season' }"
|
||||||
|
@click="switchTab('season')"
|
||||||
|
>
|
||||||
|
包季
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'yearly' }"
|
||||||
|
@click="switchTab('yearly')"
|
||||||
|
>
|
||||||
|
包年
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 订单列表 -->
|
||||||
|
<view class="order-list">
|
||||||
|
<view
|
||||||
|
class="order-item"
|
||||||
|
v-for="order in filteredOrders"
|
||||||
|
:key="order.id"
|
||||||
|
>
|
||||||
|
<!-- 订单头部 -->
|
||||||
|
<view class="order-header">
|
||||||
|
<view class="order-info">
|
||||||
|
<text class="order-number">订单编号: {{ order.orderNumber }}</text>
|
||||||
|
<view class="order-type-tag">{{ order.typeLabel }}</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="order-status"
|
||||||
|
:class="order.status"
|
||||||
|
>
|
||||||
|
{{ order.statusText }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 订单详情 -->
|
||||||
|
<view class="order-details">
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="label">用户名称:</text>
|
||||||
|
<text class="value">{{ order.userName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="label">用户ID:</text>
|
||||||
|
<text class="value">{{ order.userId }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="label">订单类型:</text>
|
||||||
|
<text class="value">{{ order.orderType }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="label">订单金额:</text>
|
||||||
|
<text class="value price">¥{{ order.amount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row" v-if="order.discount > 0">
|
||||||
|
<text class="label">折扣金额:</text>
|
||||||
|
<text class="value discount">-¥{{ order.discount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="label">实付金额:</text>
|
||||||
|
<text class="value final-price">¥{{ order.finalAmount }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="detail-row">
|
||||||
|
<text class="label">{{ order.status === 'pending' ? '创建时间:' : '支付时间:' }}</text>
|
||||||
|
<text class="value">{{ order.payTime }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 订单操作 -->
|
||||||
|
<view class="order-actions">
|
||||||
|
<u-button
|
||||||
|
v-if="order.status === 'pending'"
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
@click="cancelOrder(order)"
|
||||||
|
>
|
||||||
|
取消订单
|
||||||
|
</u-button>
|
||||||
|
<u-button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
@click="deleteOrder(order)"
|
||||||
|
>
|
||||||
|
删除订单
|
||||||
|
</u-button>
|
||||||
|
<u-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="viewOrderDetail(order)"
|
||||||
|
>
|
||||||
|
查看详情
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 待支付订单底部 -->
|
||||||
|
<view v-if="order.status === 'pending'" class="pending-footer">
|
||||||
|
<view class="countdown">
|
||||||
|
<text class="countdown-text">支付剩余时间: {{ order.countdown }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="pending-actions">
|
||||||
|
<u-button
|
||||||
|
type="default"
|
||||||
|
size="large"
|
||||||
|
@click="cancelOrder(order)"
|
||||||
|
>
|
||||||
|
取消订单
|
||||||
|
</u-button>
|
||||||
|
<u-button
|
||||||
|
type="error"
|
||||||
|
size="large"
|
||||||
|
@click="payOrder(order)"
|
||||||
|
>
|
||||||
|
去支付
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view v-if="filteredOrders.length === 0" class="empty-state">
|
||||||
|
<u-icon name="file-text" size="60" color="#ccc"></u-icon>
|
||||||
|
<text class="empty-text">暂无订单记录</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
ref
|
||||||
|
} from "vue";
|
||||||
|
import {
|
||||||
|
onShow
|
||||||
|
} from "@dcloudio/uni-app";
|
||||||
|
import {
|
||||||
|
useAuthStore,
|
||||||
|
useUserStore
|
||||||
|
} from "@/store";
|
||||||
|
import {
|
||||||
|
regions
|
||||||
|
} from '/static/json/regions.js'
|
||||||
|
import HeaderCustom from '/components/HeaderCustom/HeaderCustom.vue'
|
||||||
|
import RPC from "@/utils/request";
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const activeTab = ref('all')
|
||||||
|
const orders = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
orderNumber: 'ORD20230515001',
|
||||||
|
typeLabel: '续费',
|
||||||
|
status: 'paid',
|
||||||
|
statusText: '已支付',
|
||||||
|
userName: '张三',
|
||||||
|
userId: 'U12345678',
|
||||||
|
orderType: '包年会员',
|
||||||
|
amount: '1200.00',
|
||||||
|
discount: 200.00,
|
||||||
|
finalAmount: '1000.00',
|
||||||
|
payTime: '2025-05-15 10:30',
|
||||||
|
type: 'yearly'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
orderNumber: 'ORD20230515002',
|
||||||
|
typeLabel: '续费',
|
||||||
|
status: 'paid',
|
||||||
|
statusText: '已支付',
|
||||||
|
userName: '李四',
|
||||||
|
userId: 'U12345666',
|
||||||
|
orderType: '充值',
|
||||||
|
amount: '500.00',
|
||||||
|
discount: 0,
|
||||||
|
finalAmount: '500.00',
|
||||||
|
payTime: '2025-06-15 11:30',
|
||||||
|
type: 'recharge'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
orderNumber: 'ORD20230515332',
|
||||||
|
typeLabel: '续费',
|
||||||
|
status: 'pending',
|
||||||
|
statusText: '待支付',
|
||||||
|
userName: '小客',
|
||||||
|
userId: 'U12345555',
|
||||||
|
orderType: '包月会员',
|
||||||
|
amount: '120.00',
|
||||||
|
discount: 12.00,
|
||||||
|
finalAmount: '108.00',
|
||||||
|
payTime: '2025-08-12 15:20',
|
||||||
|
countdown: '30分钟',
|
||||||
|
type: 'monthly'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// 计算过滤后的订单
|
||||||
|
const filteredOrders = computed(() => {
|
||||||
|
if (activeTab.value === 'all') {
|
||||||
|
return orders.value
|
||||||
|
}
|
||||||
|
return orders.value.filter(order => order.type === activeTab.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 切换标签
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
activeTab.value = tab
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看订单详情
|
||||||
|
const viewOrderDetail = (order) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/order-detail/order-detail?id=${order.id}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消订单
|
||||||
|
const cancelOrder = (order) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认取消',
|
||||||
|
content: '确定要取消这个订单吗?',
|
||||||
|
success: async (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
// 调用取消订单API
|
||||||
|
// await RPC.post('/api/order/cancel', { orderId: order.id })
|
||||||
|
|
||||||
|
// 更新订单状态
|
||||||
|
const index = orders.value.findIndex(item => item.id === order.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
orders.value[index].status = 'cancelled'
|
||||||
|
orders.value[index].statusText = '已取消'
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '订单已取消',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('取消订单失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '取消失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除订单
|
||||||
|
const deleteOrder = (order) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '确定要删除这个订单吗?删除后无法恢复。',
|
||||||
|
success: async (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
// 调用删除订单API
|
||||||
|
// await RPC.post('/api/order/delete', { orderId: order.id })
|
||||||
|
|
||||||
|
// 从列表中移除订单
|
||||||
|
const index = orders.value.findIndex(item => item.id === order.id)
|
||||||
|
if (index !== -1) {
|
||||||
|
orders.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '订单已删除',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除订单失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付订单
|
||||||
|
const payOrder = (order) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/payment/payment?orderId=${order.id}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载订单数据
|
||||||
|
const loadOrders = async () => {
|
||||||
|
try {
|
||||||
|
// 调用获取订单列表API
|
||||||
|
// const result = await RPC.get('/api/orders')
|
||||||
|
// orders.value = result.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载订单失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadOrders()
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 页面显示时刷新订单列表
|
||||||
|
loadOrders()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
background: white;
|
||||||
|
padding: 0 16px;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: #4285f4;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 20px;
|
||||||
|
height: 2px;
|
||||||
|
background: #4285f4;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-list {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.order-item {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.order-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 16px 0;
|
||||||
|
|
||||||
|
.order-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.order-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-type-tag {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.paid {
|
||||||
|
background: #e8f5e8;
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pending {
|
||||||
|
background: #fff7e6;
|
||||||
|
color: #fa8c16;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cancelled {
|
||||||
|
background: #fff1f0;
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details {
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&.price {
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.discount {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.final-price {
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
padding-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pending-footer {
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.countdown {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.countdown-text {
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pending-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.u-button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 80px 20px;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,460 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 个人信息设置 -->
|
||||||
|
<view class="profile-section">
|
||||||
|
<!-- 头像 -->
|
||||||
|
<view class="profile-item" @click="changeAvatar">
|
||||||
|
<view class="item-label">头像</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 用户名 -->
|
||||||
|
<view class="profile-item" @click="editUsername">
|
||||||
|
<view class="item-label">用户名</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<text class="item-value">{{ userInfo.username }}</text>
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 电话 -->
|
||||||
|
<view class="profile-item" @click="editPhone">
|
||||||
|
<view class="item-label">电话</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<text class="item-value">{{ userInfo.phone }}</text>
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 邮箱 -->
|
||||||
|
<view class="profile-item" @click="editEmail">
|
||||||
|
<view class="item-label">邮箱</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<text class="item-value">{{ userInfo.email }}</text>
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 应用设置 -->
|
||||||
|
<view class="app-section">
|
||||||
|
<!-- 缓存 -->
|
||||||
|
<!-- <view class="profile-item" @click="clearCache">
|
||||||
|
<view class="item-label">缓存</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<text class="item-value">{{ cacheSize }}</text>
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view> -->
|
||||||
|
|
||||||
|
<!-- 用户协议 -->
|
||||||
|
<view class="profile-item" @click="viewUserAgreement">
|
||||||
|
<view class="item-label">用户协议</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 关于我们 -->
|
||||||
|
<view class="profile-item" @click="viewAbout">
|
||||||
|
<view class="item-label">关于我们</view>
|
||||||
|
<view class="item-content">
|
||||||
|
<u-icon name="arrow-right" size="16" color="#ccc"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 退出登录按钮 -->
|
||||||
|
<view class="logout-section">
|
||||||
|
<u-button type="primary" size="large" @click="logout" :loading="logoutLoading">
|
||||||
|
退出登陆
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 编辑弹窗 -->
|
||||||
|
<u-popup :show="showEditModal" mode="center" @close="closeEditModal" :round="10">
|
||||||
|
<view class="edit-modal">
|
||||||
|
<view class="modal-title">
|
||||||
|
编辑{{
|
||||||
|
editType === "username"
|
||||||
|
? "用户名"
|
||||||
|
: editType === "phone"
|
||||||
|
? "电话"
|
||||||
|
: "邮箱"
|
||||||
|
}}
|
||||||
|
</view>
|
||||||
|
<u-input v-model="editValue" :placeholder="getPlaceholder()" :type="
|
||||||
|
editType === 'phone'
|
||||||
|
? 'number'
|
||||||
|
: editType === 'email'
|
||||||
|
? 'email'
|
||||||
|
: 'text'
|
||||||
|
" border="bottom" clearable></u-input>
|
||||||
|
<view class="modal-actions">
|
||||||
|
<u-button type="default" @click="closeEditModal">取消</u-button>
|
||||||
|
<u-button type="primary" @click="saveEdit">保存</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
|
||||||
|
<!-- 头像选择弹窗 -->
|
||||||
|
<u-action-sheet :show="showAvatarSheet" :actions="avatarActions" @close="showAvatarSheet = false"
|
||||||
|
@select="onAvatarSelect"></u-action-sheet>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
onMounted
|
||||||
|
} from "vue";
|
||||||
|
import {
|
||||||
|
onShow
|
||||||
|
} from "@dcloudio/uni-app";
|
||||||
|
import {
|
||||||
|
useAuthStore,
|
||||||
|
useUserStore
|
||||||
|
} from "@/store";
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const userInfo = ref({
|
||||||
|
avatar: "/static/imgs/avatar.png",
|
||||||
|
username: "张三",
|
||||||
|
phone: "15825556676",
|
||||||
|
email: "235382903@qq.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
const cacheSize = ref("36MB");
|
||||||
|
const logoutLoading = ref(false);
|
||||||
|
const showEditModal = ref(false);
|
||||||
|
const showAvatarSheet = ref(false);
|
||||||
|
const editType = ref("");
|
||||||
|
const editValue = ref("");
|
||||||
|
|
||||||
|
// 头像选择选项
|
||||||
|
const avatarActions = ref([{
|
||||||
|
name: "拍照",
|
||||||
|
value: "camera",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "从相册选择",
|
||||||
|
value: "album",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 更换头像
|
||||||
|
const changeAvatar = () => {
|
||||||
|
showAvatarSheet.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 头像选择回调
|
||||||
|
const onAvatarSelect = (item) => {
|
||||||
|
showAvatarSheet.value = false;
|
||||||
|
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ["compressed"],
|
||||||
|
sourceType: item.value === "camera" ? ["camera"] : ["album"],
|
||||||
|
success: (res) => {
|
||||||
|
const tempFilePath = res.tempFilePaths[0];
|
||||||
|
|
||||||
|
// 这里应该上传图片到服务器
|
||||||
|
// uploadAvatar(tempFilePath)
|
||||||
|
|
||||||
|
// 临时更新头像显示
|
||||||
|
userInfo.value.avatar = tempFilePath;
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "头像更新成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: "选择图片失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑用户名
|
||||||
|
const editUsername = () => {
|
||||||
|
editType.value = "username";
|
||||||
|
editValue.value = userInfo.value.username;
|
||||||
|
showEditModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑电话
|
||||||
|
const editPhone = () => {
|
||||||
|
editType.value = "phone";
|
||||||
|
editValue.value = userInfo.value.phone;
|
||||||
|
showEditModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑邮箱
|
||||||
|
const editEmail = () => {
|
||||||
|
editType.value = "email";
|
||||||
|
editValue.value = userInfo.value.email;
|
||||||
|
showEditModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取输入框占位符
|
||||||
|
const getPlaceholder = () => {
|
||||||
|
switch (editType.value) {
|
||||||
|
case "username":
|
||||||
|
return "请输入用户名";
|
||||||
|
case "phone":
|
||||||
|
return "请输入手机号";
|
||||||
|
case "email":
|
||||||
|
return "请输入邮箱地址";
|
||||||
|
default:
|
||||||
|
return "请输入";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭编辑弹窗
|
||||||
|
const closeEditModal = () => {
|
||||||
|
showEditModal.value = false;
|
||||||
|
editType.value = "";
|
||||||
|
editValue.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存编辑
|
||||||
|
const saveEdit = async () => {
|
||||||
|
if (!editValue.value.trim()) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请输入内容",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证格式
|
||||||
|
if (editType.value === "phone" && !/^1[3-9]\d{9}$/.test(editValue.value)) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请输入正确的手机号",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
editType.value === "email" &&
|
||||||
|
!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(editValue.value)
|
||||||
|
) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "请输入正确的邮箱地址",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调用更新用户信息API
|
||||||
|
// await RPC.post('/api/user/update', {
|
||||||
|
// [editType.value]: editValue.value
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 更新本地数据
|
||||||
|
userInfo.value[editType.value] = editValue.value;
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "保存成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
|
||||||
|
closeEditModal();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("保存失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "保存失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清除缓存
|
||||||
|
const clearCache = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: "清除缓存",
|
||||||
|
content: "确定要清除应用缓存吗?",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: "清除中...",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 模拟清除缓存
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading();
|
||||||
|
cacheSize.value = "0MB";
|
||||||
|
uni.showToast({
|
||||||
|
title: "缓存清除成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看用户协议
|
||||||
|
const viewUserAgreement = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pagesA/user-agreement/user-agreement",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看关于我们
|
||||||
|
const viewAbout = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: "/pagesA/about/about",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
const logout = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: "退出登录",
|
||||||
|
content: "确定要退出登录吗?",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
logoutLoading.value = true;
|
||||||
|
|
||||||
|
// 模拟退出登录
|
||||||
|
setTimeout(() => {
|
||||||
|
authStore.signOut()
|
||||||
|
logoutLoading.value = false;
|
||||||
|
|
||||||
|
// 清除用户信息和token
|
||||||
|
// authStore.logout()
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "已退出登录",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 跳转到登录页
|
||||||
|
uni.reLaunch({
|
||||||
|
url: "/pages/login/login",
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载用户信息
|
||||||
|
const loadUserInfo = async () => {
|
||||||
|
try {
|
||||||
|
// 调用获取用户信息API
|
||||||
|
// const result = await RPC.get('/api/user/info')
|
||||||
|
// userInfo.value = result.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载用户信息失败:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算缓存大小
|
||||||
|
const calculateCacheSize = () => {
|
||||||
|
// 实际项目中应该计算真实的缓存大小
|
||||||
|
// const size = await getCacheSize()
|
||||||
|
// cacheSize.value = formatSize(size)
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadUserInfo();
|
||||||
|
calculateCacheSize();
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 页面显示时刷新用户信息
|
||||||
|
loadUserInfo();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-section,
|
||||||
|
.app-section {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.profile-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-label {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-section {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-modal {
|
||||||
|
width: 300px;
|
||||||
|
padding: 24px;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.u-button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { setupI18n } from "@/locale";
|
||||||
|
import { setupStore } from "@/store";
|
||||||
|
import { setupPermission } from "./permission";
|
||||||
|
import { setupUI } from "./ui";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install(app) {
|
||||||
|
// UI扩展配置
|
||||||
|
app.use(setupUI);
|
||||||
|
// 状态管理
|
||||||
|
app.use(setupStore);
|
||||||
|
// 国际化
|
||||||
|
app.use(setupI18n);
|
||||||
|
// 路由拦截
|
||||||
|
app.use(setupPermission);
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import pagesJson from "@/pages.json";
|
||||||
|
|
||||||
|
// 路径常量
|
||||||
|
export const HOME_PATH = "/pages/index/index";
|
||||||
|
export const LOGIN_PATH = "/pages/login/login";
|
||||||
|
export const ERROR404_PATH = "/pages/404/404";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析路由地址
|
||||||
|
* @param {object} pagesJson
|
||||||
|
* @returns [{"path": "/pages/index/index","needLogin": false},...]
|
||||||
|
*/
|
||||||
|
function parseRoutes(pagesJson = {}) {
|
||||||
|
if (!pagesJson.pages) {
|
||||||
|
pagesJson.pages = [];
|
||||||
|
}
|
||||||
|
if (!pagesJson.subPackages) {
|
||||||
|
pagesJson.subPackages = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePages(pages = [], rootPath = "") {
|
||||||
|
const routes = [];
|
||||||
|
for (let i = 0; i < pages.length; i++) {
|
||||||
|
routes.push({
|
||||||
|
path: rootPath ? `/${rootPath}/${pages[i].path}` : `/${pages[i].path}`,
|
||||||
|
needLogin: pages[i].needLogin === true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSubPackages(subPackages = []) {
|
||||||
|
const routes = [];
|
||||||
|
for (let i = 0; i < subPackages.length; i++) {
|
||||||
|
routes.push(...parsePages(subPackages[i].pages, subPackages[i].root));
|
||||||
|
}
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...parsePages(pagesJson.pages),
|
||||||
|
...parseSubPackages(pagesJson.subPackages),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
export const routes = parseRoutes(pagesJson);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前路由
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
export function currentRoute() {
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
if (pages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentPage = pages[pages.length - 1];
|
||||||
|
return currentPage?.$page?.fullPath || currentPage.route;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去除查询字符串
|
||||||
|
* @param {String} path
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function removeQueryString(path = "") {
|
||||||
|
return path.split("?")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路径是否存在
|
||||||
|
* @param {String} path
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isPathExists(path = "") {
|
||||||
|
const cleanPath = removeQueryString(path);
|
||||||
|
return routes.some((item) => item.path === cleanPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是tabbar页面路径
|
||||||
|
* @param {String} path
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isTabBarPath(path = "") {
|
||||||
|
const cleanPath = removeQueryString(path);
|
||||||
|
return (
|
||||||
|
pagesJson.tabBar?.list?.some(
|
||||||
|
(item) => `/${item.pagePath}` === cleanPath
|
||||||
|
) === true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 799 B |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
@ -0,0 +1,12 @@
|
|||||||
|
import { createPinia } from "pinia";
|
||||||
|
|
||||||
|
const store = createPinia();
|
||||||
|
|
||||||
|
export function setupStore(app) {
|
||||||
|
app.use(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { store };
|
||||||
|
|
||||||
|
export * from "./modules/user";
|
||||||
|
export * from "./modules/auth";
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import AuthAPI from "@/api/modules/auth";
|
||||||
|
import { store } from "@/store";
|
||||||
|
import { cache } from "@/utils/cache";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore("auth", () => {
|
||||||
|
const ACCESS_TOKEN_KEY = "accessToken";
|
||||||
|
const REFRESH_TOKEN_KEY = "refreshToken";
|
||||||
|
const accessToken = ref(cache(ACCESS_TOKEN_KEY) ?? "");
|
||||||
|
const refreshToken = ref(cache(REFRESH_TOKEN_KEY) ?? "");
|
||||||
|
const isLoggedIn = computed(() => !!accessToken.value);
|
||||||
|
|
||||||
|
watch(accessToken, (newValue) => cache(ACCESS_TOKEN_KEY, newValue || null));
|
||||||
|
watch(refreshToken, (newValue) => cache(REFRESH_TOKEN_KEY, newValue || null));
|
||||||
|
|
||||||
|
// 注册
|
||||||
|
async function signUp(payload) {
|
||||||
|
const response = await AuthAPI.signUp(payload);
|
||||||
|
const prefix = response.tokenType ? `${response.tokenType} ` : "";
|
||||||
|
accessToken.value = prefix + response.accessToken;
|
||||||
|
refreshToken.value = prefix + response.refreshToken;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
async function signIn(payload) {
|
||||||
|
const response = await AuthAPI.signIn(payload);
|
||||||
|
const prefix = response.tokenType ? `${response.tokenType} ` : "";
|
||||||
|
accessToken.value = prefix + response.accessToken;
|
||||||
|
refreshToken.value = prefix + response.refreshToken;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登出
|
||||||
|
function signOut() {
|
||||||
|
accessToken.value = "";
|
||||||
|
refreshToken.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新token
|
||||||
|
async function refresh(payload) {
|
||||||
|
const response = await AuthAPI.refreshToken(payload);
|
||||||
|
const prefix = response.tokenType ? `${response.tokenType} ` : "";
|
||||||
|
accessToken.value = prefix + response.accessToken;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAccessToken(payload) {
|
||||||
|
accessToken.value = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken,
|
||||||
|
refreshToken,
|
||||||
|
isLoggedIn,
|
||||||
|
signUp,
|
||||||
|
signIn,
|
||||||
|
signOut,
|
||||||
|
refresh,
|
||||||
|
setAccessToken
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 手动提供给 useStore() 函数 pinia 实例
|
||||||
|
// https://pinia.vuejs.org/zh/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component
|
||||||
|
export function useAuthStoreHook() {
|
||||||
|
return useAuthStore(store);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref, watch } from "vue";
|
||||||
|
import { cache } from "@/utils/cache";
|
||||||
|
|
||||||
|
export const useUserStore = defineStore("user", () => {
|
||||||
|
const userInfo = ref(cache('USERINFO_KEY') ? JSON.parse(cache('USERINFO_KEY')) : {
|
||||||
|
username: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const adressInfo = ref(cache('ADRESS_KEY') ? JSON.parse(cache('ADRESS_KEY')) : {
|
||||||
|
latitude: "",
|
||||||
|
longitude: "",
|
||||||
|
adress: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(userInfo, (newValue) => cache('USERINFO_KEY', newValue ? JSON.stringify(newValue) : null));
|
||||||
|
watch(adressInfo, (newValue) => cache('ADRESS_KEY', newValue ? JSON.stringify(newValue) : null));
|
||||||
|
|
||||||
|
function setUserInfo(payload) {
|
||||||
|
userInfo.value = payload;
|
||||||
|
}
|
||||||
|
function setAdressInfo(payload) {
|
||||||
|
adressInfo.value = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userInfo,
|
||||||
|
setUserInfo,
|
||||||
|
adressInfo,
|
||||||
|
setAdressInfo
|
||||||
|
};
|
||||||
|
});
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxFactory": "h",
|
||||||
|
"jsxFragmentFactory": "Fragment",
|
||||||
|
"jsxImportSource": "vue",
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"noEmit": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noLib": false,
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
},
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "esnext",
|
||||||
|
"types": ["@dcloudio/types"],
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"verbatimModuleSyntax": true
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "uni_modules", "unpackage", "**/*.js"],
|
||||||
|
"include": ["**/*.{ts,vue}"]
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"id": "uv-album",
|
||||||
|
"displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端",
|
||||||
|
"version": "1.0.4",
|
||||||
|
"description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码",
|
||||||
|
"keywords": [
|
||||||
|
"album",
|
||||||
|
"uv-ui",
|
||||||
|
"uvui",
|
||||||
|
"相册",
|
||||||
|
"图片"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [
|
||||||
|
"uv-ui-tools",
|
||||||
|
"uv-text"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"id": "uv-alert",
|
||||||
|
"displayName": "uv-alert 警告提示 全面兼容小程序、nvue、vue2、vue3等多端",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "uv-alert 警告提示,展现需要关注的信息。灵活配置,功能齐全,兼容全端",
|
||||||
|
"keywords": [
|
||||||
|
"alert",
|
||||||
|
"uvui",
|
||||||
|
"uv-ui",
|
||||||
|
"警告提示"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [
|
||||||
|
"uv-ui-tools",
|
||||||
|
"uv-transition",
|
||||||
|
"uv-icon"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
// 头像图片组
|
||||||
|
urls: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 最多展示的头像数量
|
||||||
|
maxCount: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 5
|
||||||
|
},
|
||||||
|
// 头像形状
|
||||||
|
shape: {
|
||||||
|
type: String,
|
||||||
|
default: 'circle'
|
||||||
|
},
|
||||||
|
// 图片裁剪模式
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'scaleToFill'
|
||||||
|
},
|
||||||
|
// 超出maxCount时是否显示查看更多的提示
|
||||||
|
showMore: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 头像大小
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 40
|
||||||
|
},
|
||||||
|
// 指定从数组的对象元素中读取哪个属性作为图片地址
|
||||||
|
keyName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 头像之间的遮挡比例
|
||||||
|
gap: {
|
||||||
|
type: [String, Number],
|
||||||
|
validator(value) {
|
||||||
|
return value >= 0 && value <= 1
|
||||||
|
},
|
||||||
|
default: 0.5
|
||||||
|
},
|
||||||
|
// 需额外显示的值
|
||||||
|
extraValue: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
...uni.$uv?.props?.avatarGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"id": "uv-avatar",
|
||||||
|
"displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端",
|
||||||
|
"version": "1.0.5",
|
||||||
|
"description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。",
|
||||||
|
"keywords": [
|
||||||
|
"uv-avatar",
|
||||||
|
"uvui",
|
||||||
|
"uv-ui",
|
||||||
|
"avatar",
|
||||||
|
"头像"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [
|
||||||
|
"uv-ui-tools",
|
||||||
|
"uv-icon",
|
||||||
|
"uv-text"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"id": "uv-back-top",
|
||||||
|
"displayName": "uv-back-top 返回顶部 全面兼容小程序、nvue、vue2、vue3等多端",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "返回顶部 组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。",
|
||||||
|
"keywords": [
|
||||||
|
"uv-back-top",
|
||||||
|
"uvui",
|
||||||
|
"uv-ui",
|
||||||
|
"avatar",
|
||||||
|
"返回顶部"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [
|
||||||
|
"uv-ui-tools",
|
||||||
|
"uv-icon",
|
||||||
|
"uv-transition"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"id": "uv-badge",
|
||||||
|
"displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端",
|
||||||
|
"version": "1.0.2",
|
||||||
|
"description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。",
|
||||||
|
"keywords": [
|
||||||
|
"uv-badge",
|
||||||
|
"uvui",
|
||||||
|
"uv-ui",
|
||||||
|
"徽标数",
|
||||||
|
"数字角标"
|
||||||
|
],
|
||||||
|
"repository": "",
|
||||||
|
"engines": {
|
||||||
|
"HBuilderX": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [
|
||||||
|
"uv-ui-tools"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
$uv-button-active-opacity:0.75 !default;
|
||||||
|
$uv-button-loading-text-margin-left:4px !default;
|
||||||
|
$uv-button-text-color: #FFFFFF !default;
|
||||||
|
$uv-button-text-plain-error-color:$uv-error !default;
|
||||||
|
$uv-button-text-plain-warning-color:$uv-warning !default;
|
||||||
|
$uv-button-text-plain-success-color:$uv-success !default;
|
||||||
|
$uv-button-text-plain-info-color:$uv-info !default;
|
||||||
|
$uv-button-text-plain-primary-color:$uv-primary !default;
|
||||||
|
.uv-button {
|
||||||
|
&--active {
|
||||||
|
opacity: $uv-button-active-opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active--plain {
|
||||||
|
background-color: rgb(217, 217, 217);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__loading-text {
|
||||||
|
margin-left:$uv-button-loading-text-margin-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text,
|
||||||
|
&__loading-text {
|
||||||
|
color:$uv-button-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text--plain--error {
|
||||||
|
color:$uv-button-text-plain-error-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text--plain--warning {
|
||||||
|
color:$uv-button-text-plain-warning-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text--plain--success{
|
||||||
|
color:$uv-button-text-plain-success-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text--plain--info {
|
||||||
|
color:$uv-button-text-plain-info-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text--plain--primary {
|
||||||
|
color:$uv-button-text-plain-primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||