@ -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,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"
|
||||
></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,131 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<HeaderCustom title="我的"></HeaderCustom>
|
||||
<div class="titleBox">
|
||||
<div class="avatar">
|
||||
<img class="img" src="/static/imgs/avatar.png" alt="">
|
||||
</div>
|
||||
<div class="name" v-if="authStore?.isLoggedIn">{{userStore.userInfo.nickname}}</div>
|
||||
<div class="name" v-else @click="handleLogin">登录/注册</div>
|
||||
</div>
|
||||
<div class="cellBox">
|
||||
<uv-cell-group :customStyle="{backgroundColor:'#fff'}">
|
||||
<uv-cell icon="server-fill" title="联系客服" isLink></uv-cell>
|
||||
<uv-cell icon="more-circle-fill" title="加盟卡叔" url="/pagesA/JoiningUs/JoiningUs" isLink></uv-cell>
|
||||
<uv-cell v-if="userStore.userInfo.identitys && userStore.userInfo.identitys.some(item => item.code==='superadmin' || item.code==='enterprise')" icon="order" title="企业门店列表" url="/pagesA/StoreManage/StoreList" isLink></uv-cell>
|
||||
<uv-cell v-if="userStore.userInfo.identitys && userStore.userInfo.identitys.some(item => item.code==='superadmin' || item.code==='enterprise') && userStore.userInfo.jurisdictions && userStore.userInfo.jurisdictions.some(item => item.code==='superadmin')" icon="calendar" title="企业门店审核" url="/pagesA/StoreManage/StoreReview" isLink></uv-cell>
|
||||
<!-- <uv-cell icon="edit-pen-fill" title="公司简介" isLink></uv-cell> -->
|
||||
<!-- <uv-cell icon="file-text-fill" title="文件管理" isLink></uv-cell> -->
|
||||
<uv-cell icon="empty-news" title="隐私政策" url="/pagesA/privacy-policy/privacy-policy" isLink></uv-cell>
|
||||
<uv-cell icon="empty-order" title="用户协议" url="/pagesA/user-agreement/user-agreement" isLink></uv-cell>
|
||||
<uv-cell icon="chat-fill" title="关于我们" isLink></uv-cell>
|
||||
</uv-cell-group>
|
||||
</div>
|
||||
<uv-button v-if="authStore?.isLoggedIn" type="error" text="退出登录" customStyle="margin-top: 20px" @click="() => modal.open()"></uv-button>
|
||||
<uv-modal ref="modal" showCancelButton content='确认退出登录?' @confirm="logout"></uv-modal>
|
||||
</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 logout = () => {
|
||||
authStore.signOut()
|
||||
userStore.setUserInfo({username: "",})
|
||||
modal.value.close()
|
||||
}
|
||||
|
||||
const handleLogin = () => {
|
||||
uni.showLoading({
|
||||
title: "登录中...",
|
||||
});
|
||||
uni.login({
|
||||
success: function(loginRes) {
|
||||
console.log('loginRes', loginRes);
|
||||
RPC.post('/account/miniprogram/phonelogin', {
|
||||
code: loginRes.code
|
||||
}).then(res => {
|
||||
console.log('phonelogin', res)
|
||||
uni.showToast({
|
||||
title: "登录成功",
|
||||
icon: "success",
|
||||
});
|
||||
authStore.setAccessToken(res.token)
|
||||
userStore.setUserInfo(res)
|
||||
}).catch(() => {
|
||||
uni.showToast({
|
||||
title: "登录失败",
|
||||
icon: "error",
|
||||
});
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading();
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
font-size: 14px;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.titleBox {
|
||||
height: 90px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 5vw;
|
||||
background-color: #fff;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
margin-right: 3vw;
|
||||
background-color: #f8f8f8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
.img {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.cellBox {
|
||||
:deep(.uv-cell__body) {
|
||||
padding: 20px 5vw;
|
||||
}
|
||||
|
||||
:deep(.uv-cell__title-text) {
|
||||
color: #000;
|
||||
font-weight: 400;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view class="container" ref="backTop">
|
||||
<HeaderCustom title="产品详情" :showBack="true"></HeaderCustom>
|
||||
<!-- <uv-sticky bgColor="#fff" offsetTop="90">
|
||||
<uv-tabs :list="list" @click="clickTab"></uv-tabs>
|
||||
</uv-sticky> -->
|
||||
<div class="contentBox">
|
||||
<video v-if="videoUrl" class="video" :src="videoUrl" controls></video>
|
||||
<image class="img" v-for="item in product_images_list" :key="item" :src="item" mode="widthFix" />
|
||||
<image class="img" v-for="item in qualification_images_list" :key="item" :src="item" mode="widthFix" />
|
||||
</div>
|
||||
<div class="pdf_box">
|
||||
<div class="pdf_item" v-for="(item,index) in product_pdfs_list" :key="item.url" @click="openPdf(item)">
|
||||
<div class="pdf_icon"><uv-icon name="file-text-fill" color="#2979ff" size="48"></uv-icon></div>
|
||||
<div class="nameBox">
|
||||
<div class="name">{{item.name}}</div>
|
||||
<div class="pdfInfoBox">
|
||||
<div class="size">{{item.size}}MB</div>
|
||||
<div class="date">{{item.date}}</div>
|
||||
<!-- <div class="time">{{item.time}}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<uv-back-top :scroll-top="scrollTop"></uv-back-top>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref
|
||||
} from "vue";
|
||||
|
||||
import {
|
||||
onPageScroll,
|
||||
onShow
|
||||
} from "@dcloudio/uni-app";
|
||||
import RPC from "@/utils/request";
|
||||
import HeaderCustom from '/components/HeaderCustom/HeaderCustom.vue'
|
||||
const props = defineProps({
|
||||
id: [String]
|
||||
});
|
||||
console.log('props', props.id)
|
||||
|
||||
const list = ref([])
|
||||
|
||||
const videoUrl = ref('')
|
||||
const images = ref([])
|
||||
|
||||
const clickTab = item => {
|
||||
// console.log(item)
|
||||
videoUrl.value = item.video || ''
|
||||
images.value = item.images || []
|
||||
}
|
||||
|
||||
const product_images_list = ref([])
|
||||
const qualification_images_list = ref([])
|
||||
const product_pdfs_list = ref([])
|
||||
|
||||
const openPdf = (item) => {
|
||||
uni.downloadFile({
|
||||
url: item.url,
|
||||
success: function (res) {
|
||||
var filePath = res.tempFilePath;
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
showMenu: true,
|
||||
success: function (res) {
|
||||
console.log('打开文档成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getDetail = () => {
|
||||
RPC.post('/store/detail', {
|
||||
id: Number(props.id)
|
||||
}).then(res => {
|
||||
console.log('/store/detail', res)
|
||||
// list.value = res.categories?.map(item => {
|
||||
// return {
|
||||
// ...item,
|
||||
// images:item.images ? JSON.parse(item.images) : []
|
||||
// }
|
||||
// })
|
||||
videoUrl.value = res?.store?.video_intro || ''
|
||||
product_images_list.value = res?.store?.product_images ? res?.store?.product_images.split(',') : []
|
||||
qualification_images_list.value = res?.store?.qualification_images ? res?.store?.qualification_images.split(',') : []
|
||||
product_pdfs_list.value = res?.store?.product_pdfs ? JSON.parse(res?.store?.product_pdfs) : []
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => getDetail())
|
||||
|
||||
const scrollTop = ref(0)
|
||||
onPageScroll((e) => {
|
||||
scrollTop.value = e.scrollTop;
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
font-size: 14px;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.contentBox {
|
||||
.video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.pdf_box {
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 14px;
|
||||
.pdf_item {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #ccc;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
position: relative;
|
||||
.pdf_icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nameBox {
|
||||
flex:1;
|
||||
.name {
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.pdfInfoBox {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.date {
|
||||
margin: 0 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<view class="container" ref="backTop">
|
||||
<HeaderCustom title="门店企业列表" :showBack="true"></HeaderCustom>
|
||||
<uv-sticky bgColor="#fff" offsetTop="90">
|
||||
<div class="tabBox">
|
||||
<div class="tabItem" :class="{selected:active==='门店'}" @click="() => active = '门店'">门店</div>
|
||||
<div class="tabItem" :class="{selected:active==='企业'}" @click="() => active = '企业'">企业</div>
|
||||
</div>
|
||||
</uv-sticky>
|
||||
<div class="contentBox" v-show="active==='门店'">
|
||||
<div class="contentItem" v-for="(item,index) in storeList" :key="item.id" @click="goDetail(item.id)">
|
||||
<div class="statusBox" v-if="item.status == 2">
|
||||
<uv-icon name="checkmark-circle-fill" color="#6AD307" size="12"></uv-icon>
|
||||
已通过
|
||||
</div>
|
||||
<div class="statusBox reject" v-if="item.status == 3">
|
||||
<uv-icon name="close-circle-fill" color="#F40404" size="12"></uv-icon>
|
||||
已驳回
|
||||
</div>
|
||||
<div class="statusBox pedding" v-if="item.status == 1">
|
||||
<uv-icon name="clock-fill" color="#F49843" size="12"></uv-icon>
|
||||
待审核
|
||||
</div>
|
||||
<div class="title">{{item.store_name}}</div>
|
||||
<div class="infoBox">
|
||||
<div class="line">
|
||||
<div class="infoItem">地区:{{item.location}}</div>
|
||||
<div class="infoItem">地址详情:{{item.address}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">电话:{{item.contact_phone}}</div>
|
||||
<div class="infoItem">工作时间:{{item.business_hours}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">业务类型:{{item.tags}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">
|
||||
注册日期:{{item.created_at ? dayjs(item.created_at * 1000).format('YYYY-MM-DD HH:mm:ss') : ''}}
|
||||
</div>
|
||||
<div class="infoItem" v-if="item.status == 3">驳回原因:{{item.review_note}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttonBox">
|
||||
<uv-button v-if="item.status != 1" size="small" shape="circle" text="删除" @click="delStore(item)"></uv-button>
|
||||
<uv-button @click="goAdd(item.id)" customStyle="margin-left:10px" size="small" type="primary" shape="circle" text="修改"
|
||||
v-if="item.status != 1"></uv-button>
|
||||
</div>
|
||||
</div>
|
||||
<uv-load-more @loadmore="storeGetMore" :status="storeloadingStatus" />
|
||||
</div>
|
||||
|
||||
<div class="contentBox" v-show="active==='企业'">
|
||||
<div class="contentItem" v-for="(item,index) in businessList" :key="item.id">
|
||||
<div class="statusBox" v-if="item.status == 2">
|
||||
<uv-icon name="checkmark-circle-fill" color="#6AD307" size="12"></uv-icon>
|
||||
已通过
|
||||
</div>
|
||||
<div class="statusBox reject" v-if="item.status == 3">
|
||||
<uv-icon name="close-circle-fill" color="#F40404" size="12"></uv-icon>
|
||||
已驳回
|
||||
</div>
|
||||
<div class="statusBox pedding" v-if="item.status == 1">
|
||||
<uv-icon name="clock-fill" color="#F49843" size="12"></uv-icon>
|
||||
待审核
|
||||
</div>
|
||||
<div class="title">{{item.company_name}}</div>
|
||||
<div class="infoBox">
|
||||
<div class="line">
|
||||
<div class="infoItem">姓名:{{item.contact_person}}</div>
|
||||
<div class="infoItem">邮箱:{{item.contact_email}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">手机号码:{{item.contact_phone}}</div>
|
||||
<div class="infoItem">地址:{{item.address}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">更新日期:{{item.updated_at ? dayjs(item.updated_at * 1000).format('YYYY-MM-DD HH:mm:ss') : ''}}</div>
|
||||
<div class="infoItem" v-if="item.status == 3">驳回原因:{{item.review_note}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttonBox">
|
||||
<uv-button size="small" shape="circle" v-if="item.status != 1" text="删除" @click="delStore(item)"></uv-button>
|
||||
<uv-button v-if="item.status != 1" customStyle="margin-left:10px" size="small" type="primary" shape="circle"
|
||||
text="修改" @click="goEdit(item.id)"></uv-button>
|
||||
</div>
|
||||
</div>
|
||||
<uv-load-more @loadmore="businessGetMore" :status="businessloadingStatus" />
|
||||
</div>
|
||||
<div class="addBox" @click="goAdd(null)" v-if="active==='门店'"><uv-button iconColor="#fff" iconSize="14" icon="plus" type="primary"
|
||||
shape="circle" text="新增"></uv-button></div>
|
||||
<uv-modal showCancelButton ref="modal" content='确认删除?' :asyncClose="true" @confirm="confirmDel"></uv-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref
|
||||
} from "vue";
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
onShow,
|
||||
onReachBottom
|
||||
} from "@dcloudio/uni-app";
|
||||
import RPC from "@/utils/request";
|
||||
import HeaderCustom from '/components/HeaderCustom/HeaderCustom.vue'
|
||||
|
||||
import {
|
||||
useAuthStore
|
||||
} from "@/store";
|
||||
const authStore = useAuthStore()
|
||||
// const props = defineProps({
|
||||
// id: [String]
|
||||
// });
|
||||
// console.log('props', props.id)
|
||||
const goAdd = (id) => {
|
||||
uni.$uv.route({
|
||||
url: '/pagesA/StoreManage/AddStore',
|
||||
params: {
|
||||
id: id,
|
||||
}
|
||||
});
|
||||
}
|
||||
const goEdit = (id) => {
|
||||
uni.$uv.route({
|
||||
url: '/pagesA/StoreManage/businessEdit',
|
||||
params: {
|
||||
id: id,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const goDetail = (id) => {
|
||||
uni.$uv.route({
|
||||
url: '/pagesA/ProductDetails/ProductDetails',
|
||||
params: {
|
||||
id: id,
|
||||
}
|
||||
});
|
||||
};
|
||||
const active = ref('门店')
|
||||
const modal = ref(null)
|
||||
|
||||
const storeList = ref([])
|
||||
const storeloadingStatus = ref('loadmore')
|
||||
const storePage = ref(1)
|
||||
const storeCount = ref(0)
|
||||
|
||||
const businessList = ref([])
|
||||
const businessloadingStatus = ref('loadmore')
|
||||
const businessPage = ref(1)
|
||||
const businessCount = ref(0)
|
||||
|
||||
const confirmDel = () => {
|
||||
if (active.value === '门店') {
|
||||
RPC.post('/store/delete', {
|
||||
id: currentItem.value.id
|
||||
}).then((res) => {
|
||||
storeList.value = storeList.value.filter(item => item.id !== currentItem.value.id)
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '删除成功'
|
||||
})
|
||||
modal.value.close()
|
||||
}).catch(() => {
|
||||
modal.value.closeLoading()
|
||||
})
|
||||
}
|
||||
if (active.value === '企业') {
|
||||
RPC.post('/enterprise/delete', {
|
||||
id: currentItem.value.id
|
||||
}).then((res) => {
|
||||
businessList.value = businessList.value.filter(item => item.id !== currentItem.value.id)
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '删除成功'
|
||||
})
|
||||
modal.value.close()
|
||||
}).catch(() => {
|
||||
modal.value.closeLoading()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const currentItem = ref({})
|
||||
const delStore = item => {
|
||||
currentItem.value = item
|
||||
modal.value.open();
|
||||
}
|
||||
|
||||
const getStoreList = () => {
|
||||
storeloadingStatus.value === 'loading'
|
||||
RPC.post('/store/list', {
|
||||
page: storePage.value,
|
||||
limit: 20
|
||||
}).then((res) => {
|
||||
storeList.value = [...storeList.value, ...res.rows || []]
|
||||
storeCount.value = res.count || 0
|
||||
if (storeList.value.length >= storeCount.value) {
|
||||
storeloadingStatus.value = 'nomore'
|
||||
} else {
|
||||
storeloadingStatus.value = 'loadmore'
|
||||
}
|
||||
}).catch(() => {
|
||||
storeloadingStatus.value = 'loadmore'
|
||||
})
|
||||
}
|
||||
const storeGetMore = () => {
|
||||
if (storeList.value.length >= storeCount.value || storeloadingStatus.value === 'loading') return
|
||||
storeloadingStatus.value = 'loading';
|
||||
storePage.value = ++storePage.value;
|
||||
getStoreList()
|
||||
}
|
||||
// getStoreList()
|
||||
|
||||
const getBusinessList = () => {
|
||||
businessloadingStatus.value === 'loading'
|
||||
RPC.post('/enterprise/list', {
|
||||
page: businessPage.value,
|
||||
limit: 20
|
||||
}).then((res) => {
|
||||
businessList.value = [...businessList.value, ...res.rows || []]
|
||||
businessCount.value = res.count || 0
|
||||
if (businessList.value.length >= businessCount.value) {
|
||||
businessloadingStatus.value = 'nomore'
|
||||
} else {
|
||||
businessloadingStatus.value = 'loadmore'
|
||||
}
|
||||
}).catch(() => {
|
||||
businessloadingStatus.value = 'loadmore'
|
||||
})
|
||||
}
|
||||
const businessGetMore = () => {
|
||||
if (businessList.value.length >= businessCount.value || businessloadingStatus.value === 'loading') return
|
||||
businessloadingStatus.value = 'loading';
|
||||
businessPage.value = ++businessPage.value;
|
||||
getBusinessList()
|
||||
}
|
||||
getBusinessList()
|
||||
|
||||
onReachBottom(() => {
|
||||
console.log('shangladaodi')
|
||||
if (active.value === '门店') {
|
||||
storeGetMore()
|
||||
}
|
||||
if (active.value === '企业') {
|
||||
businessGetMore()
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (!authStore?.isLoggedIn) {
|
||||
uni.switchTab({
|
||||
url: `/pages/index/index`,
|
||||
});
|
||||
uni.showToast({
|
||||
title: "请先登录",
|
||||
icon: "none",
|
||||
})
|
||||
return
|
||||
}
|
||||
storeList.value = []
|
||||
storePage.value = 1
|
||||
getStoreList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
font-size: 14px;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.tabBox {
|
||||
height: 50px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
|
||||
.tabItem {
|
||||
height: 100%;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
|
||||
&.selected {
|
||||
color: #5479ED;
|
||||
border-bottom: 1px solid #5479ED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contentBox {
|
||||
padding: 10px 14px 54px;
|
||||
|
||||
.contentItem {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0px 2px 23px 0px rgba(217, 217, 217, 0.5);
|
||||
|
||||
.statusBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
justify-content: space-between;
|
||||
padding: 4px 8px 4px 12px;
|
||||
width: 56px;
|
||||
color: #6AD307;
|
||||
background-color: #E4FBE9;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-radius: 0 0 0 20px;
|
||||
|
||||
&.reject {
|
||||
color: #F40404;
|
||||
background-color: #FAE6E3;
|
||||
}
|
||||
|
||||
&.pedding {
|
||||
color: #F49843;
|
||||
background-color: #FBF3E5;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.infoBox {
|
||||
.line {
|
||||
display: flex;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.infoItem {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
color: #A7A7A7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttonBox {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addBox {
|
||||
position: fixed;
|
||||
width: calc(100% - 28px);
|
||||
left: 14px;
|
||||
bottom: 14px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<view class="container" ref="backTop">
|
||||
<HeaderCustom title="门店新增" :showBack="true"></HeaderCustom>
|
||||
<uv-sticky bgColor="#fff" offsetTop="90">
|
||||
<div class="tabBox">
|
||||
<div class="tabItem" :class="{selected:active==='门店'}" @click="() => active = '门店'">门店</div>
|
||||
<div class="tabItem" :class="{selected:active==='企业'}" @click="() => active = '企业'">企业</div>
|
||||
</div>
|
||||
</uv-sticky>
|
||||
<div class="contentBox" v-show="active==='门店'">
|
||||
<div class="contentItem" v-for="(item,index) in storeList" :key="item.id">
|
||||
<div class="title">{{item.store_name}}</div>
|
||||
<div class="infoBox">
|
||||
<div class="line">
|
||||
<div class="infoItem">地区:{{item.location}}</div>
|
||||
<div class="infoItem">地址详情:{{item.address}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">电话:{{item.contact_phone}}</div>
|
||||
<div class="infoItem">工作时间:{{item.business_hours}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">业务类型:{{item.tags}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">
|
||||
注册日期:{{item.created_at ? dayjs(item.created_at * 1000).format('YYYY-MM-DD HH:mm:ss') : ''}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttonBox">
|
||||
<uv-button size="small" type="error" shape="circle" text="驳回" @click="handleReject(item)"></uv-button>
|
||||
<uv-button customStyle="margin-left:10px" size="small" type="primary" shape="circle" text="通过" @click="handlePass(item)"></uv-button>
|
||||
</div>
|
||||
</div>
|
||||
<uv-load-more @loadmore="storeGetMore" :status="storeloadingStatus" />
|
||||
</div>
|
||||
|
||||
<div class="contentBox" v-show="active==='企业'">
|
||||
<div class="contentItem" v-for="(item,index) in businessList" :key="item.id">
|
||||
<div class="title">{{item.company_name}}</div>
|
||||
<div class="infoBox">
|
||||
<div class="line">
|
||||
<div class="infoItem">姓名:{{item.contact_person}}</div>
|
||||
<div class="infoItem">邮箱:{{item.contact_email}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">手机号码:{{item.contact_phone}}</div>
|
||||
<div class="infoItem">地址:{{item.address}}</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="infoItem">更新日期:{{item.updated_at ? dayjs(item.updated_at * 1000).format('YYYY-MM-DD HH:mm:ss') : ''}}</div>
|
||||
<div class="infoItem" v-if="item.status == 3">驳回原因:{{item.review_note}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttonBox">
|
||||
<uv-button size="small" type="error" shape="circle" text="驳回" @click="handleReject(item)"></uv-button>
|
||||
<uv-button customStyle="margin-left:10px" size="small" type="primary" shape="circle"
|
||||
text="通过" @click="handlePass(item)"></uv-button>
|
||||
</div>
|
||||
</div>
|
||||
<uv-load-more @loadmore="businessGetMore" :status="businessloadingStatus" />
|
||||
</div>
|
||||
<uv-modal showCancelButton ref="modal" content='确认通过?' :asyncClose="true" @confirm="confirmPass"></uv-modal>
|
||||
<uv-modal showCancelButton ref="reject_modal" :asyncClose="true" @confirm="confirmReject">
|
||||
<view class="slot-content">
|
||||
<uv-input placeholder="请输入驳回原因" border="surround" v-model="reject_note"></uv-input>
|
||||
</view>
|
||||
</uv-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref
|
||||
} from "vue";
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
onShow,
|
||||
onReachBottom
|
||||
} from "@dcloudio/uni-app";
|
||||
import RPC from "@/utils/request";
|
||||
import HeaderCustom from '/components/HeaderCustom/HeaderCustom.vue'
|
||||
import {
|
||||
useAuthStore
|
||||
} from "@/store";
|
||||
const authStore = useAuthStore()
|
||||
// const props = defineProps({
|
||||
// id: [String]
|
||||
// });
|
||||
// console.log('props', props.id)
|
||||
|
||||
const active = ref('门店')
|
||||
const modal = ref(null)
|
||||
|
||||
const reject_note = ref('')
|
||||
const reject_modal = ref(null)
|
||||
|
||||
const storeList = ref([])
|
||||
const storeloadingStatus = ref('loadmore')
|
||||
const storePage = ref(1)
|
||||
const storeCount = ref(0)
|
||||
|
||||
const businessList = ref([])
|
||||
const businessloadingStatus = ref('loadmore')
|
||||
const businessPage = ref(1)
|
||||
const businessCount = ref(0)
|
||||
|
||||
const confirmPass = () => {
|
||||
if (active.value === '门店') {
|
||||
RPC.post('/store/review', {
|
||||
id: currentItem.value.id,
|
||||
status:2
|
||||
}).then((res) => {
|
||||
storeList.value = storeList.value.filter(item => item.id !== currentItem.value.id)
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '通过成功'
|
||||
})
|
||||
modal.value.close()
|
||||
}).catch(() => {
|
||||
modal.value.closeLoading()
|
||||
})
|
||||
}
|
||||
if (active.value === '企业') {
|
||||
RPC.post('/enterprise/review', {
|
||||
id: currentItem.value.id,
|
||||
status:2
|
||||
}).then((res) => {
|
||||
businessList.value = businessList.value.filter(item => item.id !== currentItem.value.id)
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '通过成功'
|
||||
})
|
||||
modal.value.close()
|
||||
}).catch(() => {
|
||||
modal.value.closeLoading()
|
||||
})
|
||||
}
|
||||
}
|
||||
const confirmReject = () => {
|
||||
if(!reject_note.value) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请填写驳回原因'
|
||||
})
|
||||
reject_modal.value.closeLoading()
|
||||
return
|
||||
}
|
||||
if (active.value === '门店') {
|
||||
RPC.post('/store/review', {
|
||||
id: currentItem.value.id,
|
||||
status:3,
|
||||
note:reject_note.value
|
||||
}).then((res) => {
|
||||
storeList.value = storeList.value.filter(item => item.id !== currentItem.value.id)
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '驳回成功',
|
||||
})
|
||||
reject_modal.value.close()
|
||||
}).catch(() => {
|
||||
reject_modal.value.closeLoading()
|
||||
})
|
||||
}
|
||||
if (active.value === '企业') {
|
||||
RPC.post('/enterprise/review', {
|
||||
id: currentItem.value.id,
|
||||
status:3,
|
||||
note:reject_note.value
|
||||
}).then((res) => {
|
||||
businessList.value = businessList.value.filter(item => item.id !== currentItem.value.id)
|
||||
uni.showToast({
|
||||
icon: 'success',
|
||||
title: '驳回成功'
|
||||
})
|
||||
reject_modal.value.close()
|
||||
}).catch(() => {
|
||||
reject_modal.value.closeLoading()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const currentItem = ref({})
|
||||
const handlePass = item => {
|
||||
currentItem.value = item
|
||||
modal.value.open();
|
||||
}
|
||||
const handleReject = item => {
|
||||
reject_note.value = ''
|
||||
currentItem.value = item
|
||||
reject_modal.value.open();
|
||||
}
|
||||
|
||||
const getStoreList = () => {
|
||||
storeloadingStatus.value === 'loading'
|
||||
RPC.post('/store/pending_list', {
|
||||
page: storePage.value,
|
||||
limit: 20
|
||||
}).then((res) => {
|
||||
storeList.value = [...storeList.value, ...res.rows || []]
|
||||
storeCount.value = res.count || 0
|
||||
if (storeList.value.length >= storeCount.value) {
|
||||
storeloadingStatus.value = 'nomore'
|
||||
} else {
|
||||
storeloadingStatus.value = 'loadmore'
|
||||
}
|
||||
}).catch(() => {
|
||||
storeloadingStatus.value = 'loadmore'
|
||||
})
|
||||
}
|
||||
const storeGetMore = () => {
|
||||
if (storeList.value.length >= storeCount.value || storeloadingStatus.value === 'loading') return
|
||||
storeloadingStatus.value = 'loading';
|
||||
storePage.value = ++storePage.value;
|
||||
getStoreList()
|
||||
}
|
||||
getStoreList()
|
||||
|
||||
const getBusinessList = () => {
|
||||
businessloadingStatus.value === 'loading'
|
||||
RPC.post('/enterprise/pending', {
|
||||
page: businessPage.value,
|
||||
limit: 20
|
||||
}).then((res) => {
|
||||
businessList.value = [...businessList.value, ...res.rows || []]
|
||||
businessCount.value = res.count || 0
|
||||
if (businessList.value.length >= businessCount.value) {
|
||||
businessloadingStatus.value = 'nomore'
|
||||
} else {
|
||||
businessloadingStatus.value = 'loadmore'
|
||||
}
|
||||
}).catch(() => {
|
||||
businessloadingStatus.value = 'loadmore'
|
||||
})
|
||||
}
|
||||
const businessGetMore = () => {
|
||||
if (businessList.value.length >= businessCount.value || businessloadingStatus.value === 'loading') return
|
||||
businessloadingStatus.value = 'loading';
|
||||
businessPage.value = ++businessPage.value;
|
||||
getBusinessList()
|
||||
}
|
||||
getBusinessList()
|
||||
|
||||
onReachBottom(() => {
|
||||
console.log('shangladaodi')
|
||||
if (active.value === '门店') {
|
||||
storeGetMore()
|
||||
}
|
||||
if (active.value === '企业') {
|
||||
businessGetMore()
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (!authStore?.isLoggedIn) {
|
||||
uni.switchTab({
|
||||
url: `/pages/index/index`,
|
||||
});
|
||||
uni.showToast({
|
||||
title: "请先登录",
|
||||
icon: "none",
|
||||
})
|
||||
return
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
font-size: 14px;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.tabBox {
|
||||
height: 50px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
|
||||
.tabItem {
|
||||
height: 100%;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
|
||||
&.selected {
|
||||
color: #5479ED;
|
||||
border-bottom: 1px solid #5479ED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contentBox {
|
||||
padding: 10px 14px 54px;
|
||||
|
||||
.contentItem {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0px 2px 23px 0px rgba(217, 217, 217, 0.5);
|
||||
|
||||
.statusBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
justify-content: space-between;
|
||||
padding: 4px 8px 4px 12px;
|
||||
width: 56px;
|
||||
color: #6AD307;
|
||||
background-color: #E4FBE9;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-radius: 0 0 0 20px;
|
||||
|
||||
&.reject {
|
||||
color: #F40404;
|
||||
background-color: #FAE6E3;
|
||||
}
|
||||
|
||||
&.pedding {
|
||||
color: #F49843;
|
||||
background-color: #FBF3E5;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.infoBox {
|
||||
.line {
|
||||
display: flex;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.infoItem {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
color: #A7A7A7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttonBox {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addBox {
|
||||
position: fixed;
|
||||
width: calc(100% - 28px);
|
||||
left: 14px;
|
||||
bottom: 14px;
|
||||
}
|
||||
</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: 5.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||