@ -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;
|
||||
}
|
||||
}
|
||||