feat: 初始化
This commit is contained in:
40
components/CopyIcon/CopyIcon.vue
Normal file
40
components/CopyIcon/CopyIcon.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<image :style="{ width: size + 'px', height: size + 'px', flexShrink: 0 }" src="/static/copy.svg" mode="aspectFit" @click="handleCopy"></image>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CopyIcon',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 14
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCopy() {
|
||||
const that = this;
|
||||
uni.setClipboardData({
|
||||
data: this.value.toString(),
|
||||
success: function () {
|
||||
uni.showToast({
|
||||
title: that.$t('common.success.copy'),
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
0
components/CopyIcon/index.scss
Normal file
0
components/CopyIcon/index.scss
Normal file
102
components/CustomModal/CustomModal.vue
Normal file
102
components/CustomModal/CustomModal.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<view v-if="show">
|
||||
<uni-popup ref="customModalRef" :isMaskClick="options.isMaskClick" :mask-background-color="options.type === 'message' ? 'transparent' : ''">
|
||||
<view :class="['customModal', options.type === 'confirm' ? 'confirmModal' : 'messageModal']">
|
||||
<image src="/static/closeIcon.svg" mode="aspectFit" class="customModalCloseIcon" @click="onClose"></image>
|
||||
<view class="customModalDecoration">
|
||||
<view class="customModalDecorationLeft"></view>
|
||||
<view class="customModalDecorationRight"></view>
|
||||
</view>
|
||||
<view class="customModalContent">
|
||||
<view class="customModalContentStatusIcon" v-if="options.type === 'message'">
|
||||
<image v-if="options.status === 'success'" src="/static/success.png" mode="aspectFit" style="width: 35px; height: 35px"></image>
|
||||
<image v-if="options.status === 'warning'" src="/static/warning.png" mode="aspectFit" style="width: 35px; height: 35px"></image>
|
||||
</view>
|
||||
<view class="customModalContentText">
|
||||
<text>{{ options.contentText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="customModalConfirmBtns" v-if="options.type === 'confirm'">
|
||||
<button class="secondaryButton cancelBtn" @click="onCancel">{{ options.cancelText }}</button>
|
||||
<view :disabled="confirmBtnLoading" class="primaryButton confirmBtn" @click="onConfirm">
|
||||
<image v-show="confirmBtnLoading" src="/static/loadingCircle.svg" mode="aspectFit" style="width: 16px; height: 16px"></image>
|
||||
{{ options.confirmText }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CustomModal',
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
type: 'confirm',
|
||||
isMaskClick: false,
|
||||
closeAfterConfirm: true,
|
||||
confirmText: this.$t('common.okText'),
|
||||
cancelText: this.$t('common.cancelText')
|
||||
},
|
||||
confirmBtnLoading: false,
|
||||
show: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showDialog(options) {
|
||||
this.show = true
|
||||
this.options = { ...this.options, ...options };
|
||||
this.$nextTick(() => {
|
||||
this.$refs.customModalRef.open();
|
||||
})
|
||||
},
|
||||
async onConfirm() {
|
||||
try {
|
||||
if (this.options.onConfirm) {
|
||||
this.confirmBtnLoading = true;
|
||||
await this.options.onConfirm();
|
||||
this.confirmBtnLoading = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('e==>', e);
|
||||
}
|
||||
if (this.options.closeAfterConfirm) {
|
||||
this.closeDialog();
|
||||
}
|
||||
},
|
||||
async onCancel() {
|
||||
try {
|
||||
if (this.options.onCancel) {
|
||||
await this.options.onCancel();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('e==>', e);
|
||||
}
|
||||
await this.onClose();
|
||||
this.closeDialog();
|
||||
},
|
||||
async onClose() {
|
||||
try {
|
||||
if (this.options.onClose) {
|
||||
await this.options.onClose();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('e==>', e);
|
||||
}
|
||||
this.closeDialog();
|
||||
},
|
||||
closeDialog() {
|
||||
this.$refs.customModalRef.close();
|
||||
this.$nextTick(() => {
|
||||
this.show = false
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
82
components/CustomModal/index.scss
Normal file
82
components/CustomModal/index.scss
Normal file
@ -0,0 +1,82 @@
|
||||
.customModal {
|
||||
position: relative;
|
||||
width: 90vw;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 2px 9px rgba(0, 0, 0, 0.25);
|
||||
&.confirmModal {
|
||||
max-width: 360px;
|
||||
border-radius: 18px;
|
||||
.customModalContent {
|
||||
padding: 54px 54px 45px;
|
||||
}
|
||||
}
|
||||
&.messageModal {
|
||||
max-width: 264px;
|
||||
border-radius: 12px;
|
||||
.customModalContent {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
.customModalCloseIcon {
|
||||
top: 14px;
|
||||
right: 10px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.customModalDecoration {
|
||||
height: 5px;
|
||||
.customModalDecorationLeft {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
.customModalContent {
|
||||
.customModalContentText {
|
||||
line-height: 29px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.customModalCloseIcon {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
right: 22px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.customModalDecoration {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
.customModalDecorationLeft {
|
||||
width: 90px;
|
||||
height: 100%;
|
||||
background-color: #0f3675;
|
||||
}
|
||||
.customModalDecorationRight {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
background-color: #29bbe4;
|
||||
}
|
||||
}
|
||||
.customModalContent {
|
||||
box-sizing: border-box;
|
||||
.customModalContentStatusIcon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.customModalContentText {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 35px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.customModalConfirmBtns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 37px;
|
||||
padding: 0px 41px 31px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
64
components/HomeSwiper/HomeSwiper.vue
Normal file
64
components/HomeSwiper/HomeSwiper.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<view class="swipperWrapper" v-show="showSwiper && swiperItems.length">
|
||||
<swiper class="swiper" circular :indicator-dots="true" :autoplay="false" :interval="interval" :duration="duration">
|
||||
<swiper-item v-for="item in swiperItems" :key="item.img_url">
|
||||
<view class="swiperItem">
|
||||
<image :src="item.img_url" mode="aspectFill" class="swiperItemImage" @click="toTarget(item)"></image>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<image src="/static/close.png" mode="aspectFit" class="closeIcon" @click="closeSwiper"></image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSwiperList } from '@/services/home/home';
|
||||
export default {
|
||||
name: 'HomeSwiper',
|
||||
props: {
|
||||
position: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1 // 1 首页
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSwiper: true,
|
||||
autoplay: true,
|
||||
interval: 2000,
|
||||
duration: 500,
|
||||
swiperItems: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async getSwiperData() {
|
||||
const res = await getSwiperList(this.position);
|
||||
if (res && res.code === 0) {
|
||||
this.swiperItems = res.data;
|
||||
}
|
||||
},
|
||||
toTarget(item) {
|
||||
if (item.act_type == 1 || item.act_type == 2) {
|
||||
if (item.contentId) {
|
||||
uni.navigateTo({ url: `/pages/activity/detail?contentId=${item.contentId}&contentType=${item.act_type}` });
|
||||
}
|
||||
} else if (item.act_type == 3) {
|
||||
uni.navigateTo({ url: '/pages/activity/shop/index' });
|
||||
}
|
||||
},
|
||||
closeSwiper() {
|
||||
this.showSwiper = false;
|
||||
uni.removeStorageSync('swiper');
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSwiperData();
|
||||
this.showSwiper = uni.getStorageSync('swiper');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
38
components/HomeSwiper/index.scss
Normal file
38
components/HomeSwiper/index.scss
Normal file
@ -0,0 +1,38 @@
|
||||
.swipperWrapper {
|
||||
position: relative;
|
||||
padding-bottom: 16px;
|
||||
.closeIcon {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
right: 9px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.swiper {
|
||||
height: 120px;
|
||||
}
|
||||
::v-deep .uni-swiper-wrapper {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
::v-deep .swiperItem {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
::v-deep .uni-swiper-dots-horizontal {
|
||||
bottom: 0px;
|
||||
}
|
||||
::v-deep .uni-swiper-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(229, 229, 229, 1);
|
||||
}
|
||||
::v-deep .uni-swiper-dot-active {
|
||||
background-color: rgba(229, 229, 229, 1);
|
||||
}
|
||||
.swiperItemImage {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
}
|
||||
}
|
340
components/NavBar/NavBar.vue
Normal file
340
components/NavBar/NavBar.vue
Normal file
@ -0,0 +1,340 @@
|
||||
<template>
|
||||
<page-meta :page-style="`overflow:${show ? 'visible' : 'hidden'};height:100%;font-family:${fontFamily};`"></page-meta>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="status_bar">
|
||||
<view class="top_view"></view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<CustomModal ref="cusModal" />
|
||||
<view class="navBarWrapper">
|
||||
<view class="navBar" v-if="token">
|
||||
<image src="/static/menu.png" mode="aspectFit" style="width: 16px; height: 13px" @click="toggleLeftDrawerVisible"></image>
|
||||
<image src="/static/logo.png" alt="" style="width: 86px; height: 31px" @click="redirectToTarget('/pages/home/index')" />
|
||||
<image src="/static/notification.png" mode="aspectFit" alt="" style="width: 17px; height: 19px; margin-right: 6px" @click="toggleRightDrawerVisible" />
|
||||
<uni-drawer ref="showLeft" mode="left" :width="300" class="leftDrawer" @change="handleLeftDrawerChange">
|
||||
<scroll-view class="scrollView" scroll-y="true">
|
||||
<view class="menuItem userName" @click="toPersonalInfoPage">
|
||||
<image style="width: 18px; height: 18px" src="/static/avatar.png" mode="aspectFit"></image>
|
||||
<text>{{ userInfo?.name }}</text>
|
||||
</view>
|
||||
<view class="menuItem balance" v-if="isIb">
|
||||
<image style="width: 18px; height: 18px" src="/static/wallet.png" mode="aspectFit"></image>
|
||||
<view>
|
||||
<text class="value">{{ ibFund?.amount }}</text>
|
||||
USD
|
||||
</view>
|
||||
</view>
|
||||
<uni-collapse accordion @change="change">
|
||||
<view :class="['menuItem', isActived('/pages/home/index') ? 'active' : '']" @tap="goToTarget('/pages/home/index')">
|
||||
<image
|
||||
style="width: 18px; height: 18px"
|
||||
:src="`/static/${isActived('/pages/home/index') ? 'myAccountActived' : 'myAccount'}.png`"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<text class="value">{{ $t('menu.myAccount') }}</text>
|
||||
</view>
|
||||
<view v-for="menu in menuListData" :key="menu.route_key">
|
||||
<view :class="['menuItem', isActived(menuLinks[menu.route_key]) ? 'active' : '']" @tap="goToTarget(menuLinks[menu.route_key])">
|
||||
<image class="menuIcon" :src="`/static/${menu.route_key}${isActived(menuLinks[menu.route_key]) ? 'Active' : ''}.svg`" mode="aspectFit"></image>
|
||||
<text>{{ $t(`menu.${menu.route_key}`) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view style="border-top: 1px solid #ececec; margin-top: 8px">
|
||||
<MenuLanguageSelector />
|
||||
</view>
|
||||
<view class="menuItem" style="min-height: 24px" @click="handleLogout">
|
||||
<image style="width: 18px; height: 18px" src="/static/logout.svg" mode="aspectFit"></image>
|
||||
<text class="value">{{ $t('menu.logOut') }}</text>
|
||||
</view>
|
||||
</uni-collapse>
|
||||
</scroll-view>
|
||||
</uni-drawer>
|
||||
<uni-drawer ref="showRight" mode="right" :width="300" class="rightDrawer" @change="handleRightDrawerChange">
|
||||
<view class="rightContent">
|
||||
<scroll-view v-if="noticeList.length" :style="{ height: '100%' }" scroll-y="true">
|
||||
<view
|
||||
v-for="notice in noticeList"
|
||||
:key="notice.id"
|
||||
class="noticeItem"
|
||||
@click="
|
||||
(e) => {
|
||||
showDialog(notice);
|
||||
}
|
||||
"
|
||||
>
|
||||
<view class="noticeSubject">{{ notice.subject }}</view>
|
||||
<view class="noticeSummary">{{ notice.summary }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-else style="margin-top: 20px">{{ $t('notice.empty') }}</view>
|
||||
</view>
|
||||
</uni-drawer>
|
||||
</view>
|
||||
<view class="navBar" v-else>
|
||||
<image src="/static/logo.png" alt="" style="width: 70px; height: 26px; margin-left: 4px" />
|
||||
<LanguageSelector />
|
||||
</view>
|
||||
<!-- 普通弹窗 -->
|
||||
<uni-popup ref="alertDialog" :isMaskClick="false">
|
||||
<view class="detailModal">
|
||||
<uni-icons type="closeempty" size="17" class="closeIcon" @click="closeDialog"></uni-icons>
|
||||
<view class="detailContent">
|
||||
<view class="notice-content" v-html="currentNotice.content"></view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
<view></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import LanguageSelector from '@/pages/components/languageSelector/index.vue';
|
||||
import MenuLanguageSelector from '@/pages/components/menuLanguageSelector/index.vue';
|
||||
import { getToken } from '@/utils/const.ts';
|
||||
import { queryMenuList, getNotice, setNoticeReadStatus } from '@/services/home/home';
|
||||
import { logout } from '@/services/user/loginAndRegister';
|
||||
import { UserLanguage } from '@/utils/const';
|
||||
|
||||
//获取屏幕边界到安全区域距离
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
fontFamily: 'SourceHanSans',
|
||||
type: 'center',
|
||||
msgType: 'success',
|
||||
messageText: '这是一条成功提示',
|
||||
value: '',
|
||||
show: true,
|
||||
showDl: true,
|
||||
userInfo: {},
|
||||
isIb: false,
|
||||
ibFund: {},
|
||||
currentPage: '',
|
||||
menuListData: [],
|
||||
menuLinks: {
|
||||
partner: '/pages/partner/index',
|
||||
capital: '/pages/capital/index',
|
||||
activity: '/pages/activity/index',
|
||||
download: '/pages/download/index',
|
||||
contact: '/pages/contact/index',
|
||||
news: '/pages/news/index',
|
||||
fts: '/pages/fts/index',
|
||||
pamm: '/pages/pamm/index',
|
||||
tools: '/pages/toolsAndReport/index'
|
||||
},
|
||||
noticeList: [],
|
||||
currentNotice: {},
|
||||
windowScrollY: 0,
|
||||
contentSize: 0
|
||||
};
|
||||
},
|
||||
emits: ["navLeft", "@navRight"],
|
||||
computed: {
|
||||
paddingTop: () => {
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync();
|
||||
return safeAreaInsets?.top + 'px';
|
||||
},
|
||||
token: () => {
|
||||
return getToken();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
const userStore = useUserStore();
|
||||
this.userInfo = Object.assign(this.userInfo, userStore.userInfo);
|
||||
this.ibFund = Object.assign(this.ibFund, userStore.ibFund);
|
||||
this.isIb = Number(userStore.userInfo.user_type) === 1;
|
||||
},
|
||||
isActived(path) {
|
||||
return path.includes(this.currentPage);
|
||||
},
|
||||
showDialog(notice = {}) {
|
||||
if (notice.content.length) {
|
||||
notice.content = `<div style="zoom:${this.contentSize};">`+notice.content+'</div>'
|
||||
}
|
||||
this.currentNotice = notice;
|
||||
this.show = false;
|
||||
this.toggleRightDrawerVisible();
|
||||
if (notice.status === '0') {
|
||||
setNoticeReadStatus({ id: notice.id });
|
||||
}
|
||||
this.showDl = true;
|
||||
this.$refs.alertDialog.open();
|
||||
},
|
||||
closeDialog() {
|
||||
this.show = true;
|
||||
this.toggleRightDrawerVisible();
|
||||
this.showDl = false;
|
||||
this.$refs.alertDialog.close();
|
||||
},
|
||||
goToTarget(target) {
|
||||
const pages = getCurrentPages();
|
||||
if (!target.endsWith(pages[pages.length - 1].route)) {
|
||||
uni.navigateTo({
|
||||
url: target
|
||||
});
|
||||
this.show = true;
|
||||
this.$refs.showLeft.close();
|
||||
}
|
||||
},
|
||||
toPersonalInfoPage() {
|
||||
this.$refs.showLeft.close();
|
||||
uni.navigateTo({ url: '/pages/user/index' });
|
||||
},
|
||||
redirectToTarget(target) {
|
||||
uni.redirectTo({
|
||||
url: target
|
||||
});
|
||||
},
|
||||
async handleLogout() {
|
||||
this.$cusModal.showModal({
|
||||
type: 'confirm',
|
||||
contentText: this.$t('modal.logoutContent'),
|
||||
closeAfterConfirm: true,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const res = await logout();
|
||||
if (res && res.code === 0) {
|
||||
const userStore = useUserStore();
|
||||
userStore.clear();
|
||||
uni.removeStorageSync('access_token');
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('e===>', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
async getMenuListData() {
|
||||
const res = await queryMenuList();
|
||||
if (res && res.code === 0) {
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
const { route_key } = res.data[i]
|
||||
if (route_key == 'capital') {
|
||||
res.data[i].rank = 2
|
||||
} else if (route_key == 'partner') {
|
||||
res.data[i].rank = 3
|
||||
} else if (route_key == 'download') {
|
||||
res.data[i].rank = 8
|
||||
} else if (route_key == 'pamm') {
|
||||
res.data[i].rank = 7
|
||||
} else if (route_key == 'tools') {
|
||||
res.data[i].rank = 5
|
||||
} else if (route_key == 'fts') {
|
||||
res.data[i].rank = 6
|
||||
} else if (route_key == 'news') {
|
||||
res.data[i].rank = 9
|
||||
} else if (route_key == 'activity') {
|
||||
res.data[i].rank = 4
|
||||
} else if (route_key == 'contact') {
|
||||
res.data[i].rank = 10
|
||||
}
|
||||
}
|
||||
res.data.sort((a, b) => a.rank - b.rank)
|
||||
this.menuListData = res.data;
|
||||
}
|
||||
},
|
||||
async getNoticeList() {
|
||||
const res = await getNotice();
|
||||
if (res && res.code === 0) {
|
||||
this.noticeList = res.data;
|
||||
}
|
||||
},
|
||||
recordWindowScrollY() {
|
||||
// #ifdef H5
|
||||
this.windowScrollY = window.scrollY;
|
||||
// #endif
|
||||
},
|
||||
windowScrollByRecord() {
|
||||
// #ifdef H5
|
||||
setTimeout(() => {
|
||||
window.scroll(0, this.windowScrollY);
|
||||
}, 0);
|
||||
// #endif
|
||||
},
|
||||
handleLeftDrawerChange(e) {
|
||||
if (!e) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
}
|
||||
this.$emit('navLeft', e || this.showDl)
|
||||
},
|
||||
toggleLeftDrawerVisible() {
|
||||
if (this.$refs.showLeft.visibleSync) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
this.$refs.showLeft.close();
|
||||
} else {
|
||||
this.initData();
|
||||
this.show = false;
|
||||
this.recordWindowScrollY();
|
||||
this.$refs.showLeft.open();
|
||||
//如果接口出错没请求到数据,则打开侧边栏的时候再请求一次
|
||||
if (!this.menuListData.length) {
|
||||
this.getMenuListData();
|
||||
}
|
||||
}
|
||||
},
|
||||
handleRightDrawerChange(e) {
|
||||
if (!e) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
}
|
||||
this.$emit('navRight', e || this.showDl)
|
||||
},
|
||||
toggleRightDrawerVisible() {
|
||||
if (this.$refs.showRight.visibleSync) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
this.$refs.showRight.close();
|
||||
} else {
|
||||
this.getNoticeList();
|
||||
this.show = false;
|
||||
this.recordWindowScrollY();
|
||||
this.$refs.showRight.open();
|
||||
}
|
||||
},
|
||||
open() {
|
||||
// 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
|
||||
this.$refs.popup.open('top');
|
||||
},
|
||||
toggle(type) {
|
||||
this.type = type;
|
||||
// open 方法传入参数 等同在 uni-popup 组件上绑定 type属性
|
||||
this.$refs.popup.open(type);
|
||||
},
|
||||
change(e) {
|
||||
console.log('popup_change', e);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const pages = getCurrentPages();
|
||||
this.currentPage = pages[pages.length - 1].route;
|
||||
const locale = uni.getLocale();
|
||||
if (['zh-CN', 'zh-TW'].includes(locale)) {
|
||||
this.fontFamily = 'SourceHanSans';
|
||||
} else {
|
||||
this.fontFamily = 'Arial';
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (getToken()) {
|
||||
this.getMenuListData();
|
||||
}
|
||||
this.$cusModal.register(this.$refs.cusModal);
|
||||
this.contentSize = uni.upx2px(666) / 800
|
||||
},
|
||||
components: {
|
||||
LanguageSelector,
|
||||
MenuLanguageSelector
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
173
components/NavBar/index.scss
Normal file
173
components/NavBar/index.scss
Normal file
@ -0,0 +1,173 @@
|
||||
.status_bar {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
z-index: 98;
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
background-color: #f8f8f8;
|
||||
.top_view {
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
background-color: #f8f8f8;
|
||||
top: 0;
|
||||
z-index: 98;
|
||||
}
|
||||
}
|
||||
.navBarWrapper {
|
||||
position: sticky;
|
||||
top: var(--status-bar-height);
|
||||
/* #ifdef H5 */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
z-index: 98;
|
||||
background-color: #fff;
|
||||
.navBar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
padding-right: 16px;
|
||||
height: 44px;
|
||||
border: 1px solid rgba(236, 236, 236, 1);
|
||||
border-left-width: 0px;
|
||||
border-right-width: 0px;
|
||||
}
|
||||
}
|
||||
.leftDrawer {
|
||||
top: calc(46px + var(--status-bar-height));
|
||||
.submenu {
|
||||
height: 48px;
|
||||
padding: 0 48px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
.scrollView {
|
||||
height: 100%;
|
||||
::v-deep .uni-scroll-view-content {
|
||||
height: unset;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
}
|
||||
.menuItem {
|
||||
min-height: 50px;
|
||||
margin: 0px 28px 0px 16px;
|
||||
padding: 0px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
&.active {
|
||||
color: #fff;
|
||||
background-color: #29bbe4;
|
||||
}
|
||||
.menuIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
&.userName {
|
||||
height: 65px;
|
||||
}
|
||||
&.balance {
|
||||
height: 65px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
.value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.rightDrawer {
|
||||
top: calc(46px + var(--status-bar-height));
|
||||
.rightContent {
|
||||
padding: 0 20px;
|
||||
.noticeItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
padding: 20px 0 14px;
|
||||
border-bottom: 1px solid rgba(236, 236, 236, 1);
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.noticeSubject {
|
||||
color: rgba(51, 51, 51, 1);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.noticeSummary {
|
||||
color: rgba(102, 102, 102, 1);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.popup-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
height: 50px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.popup-height {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
width: 200px;
|
||||
}
|
||||
.detailModal {
|
||||
position: relative;
|
||||
width: calc(100vw - 50px);
|
||||
width: 726upx;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
.detailContent {
|
||||
padding: 32px 30upx 24px;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.closeIcon {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 18px;
|
||||
right: 21px;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
.titleWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
row-gap: 12px;
|
||||
color: #333333;
|
||||
padding: 12px;
|
||||
margin: 24px 0;
|
||||
box-shadow: -1px 0 5px 1px rgba(0, 0, 0, 0.1);
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 34px;
|
||||
}
|
||||
.value {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: #29bbe4;
|
||||
line-height: 52px;
|
||||
}
|
||||
}
|
||||
.btnsWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
.delBtn {
|
||||
background-color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
57
components/PageTitle/PageTitle.vue
Normal file
57
components/PageTitle/PageTitle.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<view class="titleWrapper" @click="back">
|
||||
<image src="/static/backIcon.png" mode="aspectFit" class="titleIcon"></image>
|
||||
<text class="titleText" :style="{ letterSpacing: letterSpacing ?? '' }">{{ title }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PageTitle',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
backTarget: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
onBack: {
|
||||
type: Function,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
letterSpacing: undefined
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
if (this.onBack) {
|
||||
this.onBack();
|
||||
}
|
||||
if (this.backTarget) {
|
||||
uni.redirectTo({ url: this.backTarget });
|
||||
} else {
|
||||
uni.navigateBack();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const locale = uni.getLocale();
|
||||
if (['zh-CN', 'zh-TW'].includes(locale)) {
|
||||
this.letterSpacing = '2px';
|
||||
} else {
|
||||
this.letterSpacing = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
20
components/PageTitle/index.scss
Normal file
20
components/PageTitle/index.scss
Normal file
@ -0,0 +1,20 @@
|
||||
.titleWrapper {
|
||||
display: flex;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
cursor: pointer;
|
||||
.titleIcon {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
margin-top: 9px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 11px;
|
||||
}
|
||||
.titleText {
|
||||
word-break: break-all;
|
||||
line-height: 41px;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
39
components/Progress/Progress.vue
Normal file
39
components/Progress/Progress.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<view v-if="Math.min(progress, 100) !== 100 || error" class="progressWrapper" :style="{ height: height, backgroundColor: backgroundColor }">
|
||||
<view v-if="!error" class="progressBar" :style="{ width: `${Math.min(progress, 100)}%`, backgroundColor: activeColor }"></view>
|
||||
<view v-else class="progressBar error"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Progress',
|
||||
props: {
|
||||
progress: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 100
|
||||
},
|
||||
error: { type: Boolean, required: false, default: false },
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '2px'
|
||||
},
|
||||
activeColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '#29BBE4'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '#EBEBEB'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
14
components/Progress/index.scss
Normal file
14
components/Progress/index.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.progressWrapper {
|
||||
width: 100%;
|
||||
border-radius: 100px;
|
||||
overflow: hidden;
|
||||
.progressBar {
|
||||
height: 100%;
|
||||
border-radius: 100px;
|
||||
transition: all 0.3s ease-out;
|
||||
&.error {
|
||||
width: 100%;
|
||||
background-color: #ff5733;
|
||||
}
|
||||
}
|
||||
}
|
31
components/Spin/Spin.vue
Normal file
31
components/Spin/Spin.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<view class="spinWrapper">
|
||||
<view class="spin" :style="{ color: color }">
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
<view class="spinItem"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Spin',
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '#0E3673'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
109
components/Spin/index.scss
Normal file
109
components/Spin/index.scss
Normal file
@ -0,0 +1,109 @@
|
||||
.spinWrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
transform: scale(0.8);
|
||||
margin-top: 64px; //临时
|
||||
margin-bottom: 64px; //临时
|
||||
.spin {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
.spinItem:nth-child(1) {
|
||||
top: 25px;
|
||||
left: 0;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.12s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.12s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(2) {
|
||||
top: 17.04545px;
|
||||
left: 17.04545px;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.24s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.24s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(3) {
|
||||
top: 0;
|
||||
left: 25px;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.36s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.36s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(4) {
|
||||
top: -17.04545px;
|
||||
left: 17.04545px;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.48s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.48s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(5) {
|
||||
top: -25px;
|
||||
left: 0;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.6s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.6s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(6) {
|
||||
top: -17.04545px;
|
||||
left: -17.04545px;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.72s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.72s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(7) {
|
||||
top: 0;
|
||||
left: -25px;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.84s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.84s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem:nth-child(8) {
|
||||
top: 17.04545px;
|
||||
left: -17.04545px;
|
||||
-webkit-animation: ball-spin-fade-loader 1s -0.96s infinite linear;
|
||||
animation: ball-spin-fade-loader 1s -0.96s infinite linear;
|
||||
}
|
||||
|
||||
.spinItem {
|
||||
background-color: currentColor;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 100%;
|
||||
margin: 2px;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes ball-spin-fade-loader {
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ball-spin-fade-loader {
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
30
components/nvue/NavBar/drawer.css
Normal file
30
components/nvue/NavBar/drawer.css
Normal file
@ -0,0 +1,30 @@
|
||||
.drawer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: -300px; /* 隐藏状态 */
|
||||
width: 300px;
|
||||
background-color: #fff;
|
||||
transition: left 0.3s ease;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.drawer.open {
|
||||
left: 0; /* 显示状态 */
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
width: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.overlay.open {
|
||||
width: 750rpx;
|
||||
}
|
159
components/nvue/NavBar/index.css
Normal file
159
components/nvue/NavBar/index.css
Normal file
@ -0,0 +1,159 @@
|
||||
.navBarWrapper {
|
||||
position: sticky;
|
||||
z-index: 98;
|
||||
background-color: #fff;
|
||||
width: 750rpx;
|
||||
}
|
||||
.navBarWrapper .navBar {
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
padding-right: 16px;
|
||||
height: 44px;
|
||||
border: 1px solid rgb(236, 236, 236);
|
||||
border-left-width: 0px;
|
||||
border-right-width: 0px;
|
||||
}
|
||||
|
||||
.leftDrawer {
|
||||
z-index: 100;
|
||||
}
|
||||
.leftDrawer .submenu {
|
||||
height: 48px;
|
||||
padding: 0 48px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.leftDrawer .scrollView ::v-deep .uni-scroll-view-content {
|
||||
height: unset;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
.leftDrawer .menuItem {
|
||||
height: 50px;
|
||||
margin: 0px 28px 0px 16px;
|
||||
padding: 0px 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.leftDrawer .menuItem.active {
|
||||
color: #fff;
|
||||
background-color: #29bbe4;
|
||||
}
|
||||
.leftDrawer .menuItem.active .menuText {
|
||||
color: #fff;
|
||||
}
|
||||
.leftDrawer .menuItem .menuText, .leftDrawer .menuItem .value {
|
||||
font-size: 18px;
|
||||
}
|
||||
.leftDrawer .menuItem .menuIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
.leftDrawer .menuItem.userName {
|
||||
height: 65px;
|
||||
}
|
||||
.leftDrawer .menuItem.balance {
|
||||
height: 65px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.leftDrawer .menuItem.balance .value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rightDrawer .rightContent {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.rightDrawer .rightContent .noticeItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
padding: 20px 0 14px;
|
||||
border-bottom: 1px solid rgb(236, 236, 236);
|
||||
}
|
||||
.rightDrawer .rightContent .noticeItem:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.rightDrawer .rightContent .noticeItem .noticeSubject {
|
||||
color: rgb(51, 51, 51);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.rightDrawer .rightContent .noticeItem .noticeSummary {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
width: 726rpx;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
height: 50px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.popup-height {
|
||||
height: 100px;
|
||||
flex: 1;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.detailModal {
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
width: 726rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
.detailModal .detailContent {
|
||||
flex-direction: row;
|
||||
width: 726rpx;
|
||||
padding: 32px 30upx 24px;
|
||||
}
|
||||
.detailModal .closeIcon {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 18px;
|
||||
right: 21px;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
.detailModal .titleWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
row-gap: 12px;
|
||||
color: #333333;
|
||||
padding: 12px;
|
||||
margin: 24px 0;
|
||||
box-shadow: -1px 0 5px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.detailModal .titleWrapper .title {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 34px;
|
||||
}
|
||||
.detailModal .titleWrapper .value {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: #29bbe4;
|
||||
line-height: 52px;
|
||||
}
|
||||
.detailModal .btnsWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.detailModal .btnsWrapper .delBtn {
|
||||
background-color: #f56c6c;
|
||||
}
|
323
components/nvue/NavBar/index.nvue
Normal file
323
components/nvue/NavBar/index.nvue
Normal file
@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<page-meta :page-style="`overflow:${show ? 'visible' : 'hidden'};height:100%;font-family:${fontFamily};`"></page-meta>
|
||||
<view :style="{'width': '750rpx','margin-top': safeTop}"></view>
|
||||
<view class="navBarWrapper">
|
||||
<view class="navBar">
|
||||
<image src="/static/menu.png" mode="aspectFit" style="width: 16px; height: 13px" @click="toggleLeftDrawerVisible"></image>
|
||||
<image src="/static/logo.png" alt="" style="width: 86px; height: 31px" @click="redirectToTarget('/pages/home/index')" />
|
||||
<!-- <image src="/static/notification.png" mode="aspectFit" alt="" style="width: 17px; height: 19px; margin-right: 6px" @click="toggleRightDrawerVisible" /> -->
|
||||
<view></view>
|
||||
</view>
|
||||
<uni-popup ref="alertDialog" :isMaskClick="false">
|
||||
<view class="detailModal">
|
||||
<uni-icons type="closeempty" size="17" class="closeIcon" @click="closeDialog"></uni-icons>
|
||||
<view class="detailContent">
|
||||
<view class="notice-content" v-html="currentNotice.content"></view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
<view :class="['overlay', { 'open': isOpen }]" @click="toggleLeftDrawerVisible" :style="{'height': contentHeight}" ></view>
|
||||
<view :class="['leftDrawer', 'drawer', { 'open': isOpen }]" :style="{'height': contentHeight}" >
|
||||
<view class="drawer-content" :style="{'height': contentHeight}">
|
||||
<scroll-view class="scrollView" :scroll-y="true" :style="{'height': contentHeight}" >
|
||||
<view>
|
||||
<view class="menuItem userName" @click="toPersonalInfoPage">
|
||||
<image style="width: 18px; height: 18px;margin-right: 10px;" src="/static/avatar.png" mode="aspectFit"></image>
|
||||
<text class="menuText" >{{ userInfo?.name }}</text>
|
||||
</view>
|
||||
<view class="menuItem balance" v-if="isIb">
|
||||
<image style="width: 18px; height: 18px;margin-right: 10px;" src="/static/wallet.png" mode="aspectFit"></image>
|
||||
<view style="flex-direction: row;align-items: center;">
|
||||
<text class="value">{{ ibFund?.amount }}</text>
|
||||
USD
|
||||
</view>
|
||||
</view>
|
||||
<uni-collapse accordion @change="change">
|
||||
<view :class="['menuItem', isActived('/pages/home/index') ? 'active' : '']" @tap="goToTarget('/pages/home/index')">
|
||||
<image
|
||||
style="width: 18px; height: 18px;margin-right: 10px;"
|
||||
:src="`/static/${isActived('/pages/home/index') ? 'myAccountActived' : 'myAccount'}.png`"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<text class="value">{{ t('menu.myAccount') }}</text>
|
||||
</view>
|
||||
<view v-for="menu in menuListData" :key="menu.route_key">
|
||||
<view :class="['menuItem', isActived(menuLinks[menu.route_key]) ? 'active' : '']" @tap="goToTarget(menuLinks[menu.route_key])">
|
||||
<image style="margin-right: 10px;" class="menuIcon" :src="`/static/nvue/${menu.route_key}${isActived(menuLinks[menu.route_key]) ? 'Active' : ''}.png`" mode="aspectFit"></image>
|
||||
<text class="menuText">{{ t(`menu.${menu.route_key}`) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view style="border-top: 1px solid #ececec; margin-top: 8px">
|
||||
<MenuLanguageSelector />
|
||||
</view>
|
||||
<view class="menuItem" style="min-height: 24px" @click="handleLogout">
|
||||
<image style="width: 18px; height: 18px;margin-right: 10px;" src="/static/nvue/logout.png" mode="aspectFit"></image>
|
||||
<text class="value">{{ t('menu.logOut') }}</text>
|
||||
</view>
|
||||
</uni-collapse>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import MenuLanguageSelector from '@/components/nvue/menuLanguageSelector/index.vue';
|
||||
import { getToken } from '@/utils/const.ts';
|
||||
import { queryMenuList, getNotice, setNoticeReadStatus } from '@/services/home/home';
|
||||
import { logout } from '@/services/user/loginAndRegister';
|
||||
import { UserLanguage } from '@/utils/const';
|
||||
import { i18n } from '@/locale/index.ts';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
safeTop: uni.getSystemInfoSync().statusBarHeight + 'px',
|
||||
bodyHeight: (uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight) + 'px',
|
||||
contentHeight: (uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight - 44) + 'px',
|
||||
fontFamily: 'SourceHanSans',
|
||||
type: 'center',
|
||||
msgType: 'success',
|
||||
messageText: '这是一条成功提示',
|
||||
value: '',
|
||||
show: true,
|
||||
showDl: true,
|
||||
userInfo: {},
|
||||
isIb: false,
|
||||
ibFund: {},
|
||||
currentPage: '',
|
||||
menuListData: [],
|
||||
menuLinks: {
|
||||
partner: '/pages/partner/index',
|
||||
capital: '/pages/capital/index',
|
||||
activity: '/pages/activity/index',
|
||||
download: '/pages/download/index',
|
||||
contact: '/pages/contact/index',
|
||||
news: '/pages/news/index',
|
||||
fts: '/pages/fts/index',
|
||||
pamm: '/pages/pamm/index',
|
||||
tools: '/pages/toolsAndReport/index'
|
||||
},
|
||||
noticeList: [],
|
||||
currentNotice: {},
|
||||
windowScrollY: 0,
|
||||
contentSize: 0,
|
||||
t: i18n.global.t,
|
||||
isOpen: false
|
||||
}
|
||||
},
|
||||
emits: ["navLeft", "navRight"],
|
||||
computed: {
|
||||
paddingTop: () => {
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync();
|
||||
return safeAreaInsets?.top + 'px';
|
||||
},
|
||||
token: () => {
|
||||
return getToken();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
const userStore = useUserStore();
|
||||
this.userInfo = Object.assign(this.userInfo, userStore.userInfo);
|
||||
this.ibFund = Object.assign(this.ibFund, userStore.ibFund);
|
||||
this.isIb = Number(userStore.userInfo.user_type) === 1;
|
||||
},
|
||||
isActived(path) {
|
||||
return path.includes(this.currentPage)
|
||||
},
|
||||
showDialog(notice = {}) {
|
||||
if (notice.content.length) {
|
||||
notice.content = `<div style="zoom:${this.contentSize};">`+notice.content+'</div>'
|
||||
}
|
||||
this.currentNotice = notice;
|
||||
this.show = false;
|
||||
this.toggleRightDrawerVisible();
|
||||
if (notice.status === '0') {
|
||||
setNoticeReadStatus({ id: notice.id });
|
||||
}
|
||||
this.showDl = true;
|
||||
this.$refs.alertDialog.open();
|
||||
},
|
||||
closeDialog() {
|
||||
this.show = true;
|
||||
this.toggleRightDrawerVisible();
|
||||
this.showDl = false;
|
||||
this.$refs.alertDialog.close();
|
||||
},
|
||||
goToTarget(target) {
|
||||
const pages = getCurrentPages();
|
||||
if (!target.endsWith(pages[pages.length - 1].route)) {
|
||||
uni.navigateTo({
|
||||
url: target
|
||||
});
|
||||
this.show = true;
|
||||
this.isOpen = false;
|
||||
}
|
||||
},
|
||||
toPersonalInfoPage() {
|
||||
this.$refs.showLeft.close();
|
||||
uni.navigateTo({ url: '/pages/user/index' });
|
||||
},
|
||||
redirectToTarget(target) {
|
||||
uni.redirectTo({
|
||||
url: target
|
||||
});
|
||||
},
|
||||
async handleLogout() {
|
||||
this.$cusModal.showModal({
|
||||
type: 'confirm',
|
||||
contentText: t('modal.logoutContent'),
|
||||
closeAfterConfirm: true,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const res = await logout();
|
||||
if (res && res.code === 0) {
|
||||
const userStore = useUserStore();
|
||||
userStore.clear();
|
||||
uni.removeStorageSync('access_token');
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('e===>', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
async getMenuListData() {
|
||||
const res = await queryMenuList();
|
||||
if (res && res.code === 0) {
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
const { route_key } = res.data[i]
|
||||
if (route_key == 'capital') {
|
||||
res.data[i].rank = 2
|
||||
} else if (route_key == 'partner') {
|
||||
res.data[i].rank = 3
|
||||
} else if (route_key == 'download') {
|
||||
res.data[i].rank = 8
|
||||
} else if (route_key == 'pamm') {
|
||||
res.data[i].rank = 7
|
||||
} else if (route_key == 'tools') {
|
||||
res.data[i].rank = 5
|
||||
} else if (route_key == 'fts') {
|
||||
res.data[i].rank = 6
|
||||
} else if (route_key == 'news') {
|
||||
res.data[i].rank = 9
|
||||
} else if (route_key == 'activity') {
|
||||
res.data[i].rank = 4
|
||||
} else if (route_key == 'contact') {
|
||||
res.data[i].rank = 10
|
||||
}
|
||||
}
|
||||
res.data.sort((a, b) => a.rank - b.rank)
|
||||
this.menuListData = res.data;
|
||||
}
|
||||
},
|
||||
async getNoticeList() {
|
||||
const res = await getNotice();
|
||||
if (res && res.code === 0) {
|
||||
this.noticeList = res.data;
|
||||
}
|
||||
},
|
||||
recordWindowScrollY() {
|
||||
// #ifdef H5
|
||||
this.windowScrollY = window.scrollY;
|
||||
// #endif
|
||||
},
|
||||
windowScrollByRecord() {
|
||||
// #ifdef H5
|
||||
setTimeout(() => {
|
||||
window.scroll(0, this.windowScrollY);
|
||||
}, 0);
|
||||
// #endif
|
||||
},
|
||||
handleLeftDrawerChange(e) {
|
||||
if (!e) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
}
|
||||
this.$emit('navLeft', e || this.showDl)
|
||||
},
|
||||
toggleLeftDrawerVisible() {
|
||||
if (this.isOpen) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
this.isOpen = false;
|
||||
} else {
|
||||
this.initData();
|
||||
this.show = false;
|
||||
this.recordWindowScrollY();
|
||||
this.isOpen = true;
|
||||
//如果接口出错没请求到数据,则打开侧边栏的时候再请求一次
|
||||
if (!this.menuListData.length) {
|
||||
this.getMenuListData();
|
||||
}
|
||||
}
|
||||
},
|
||||
handleRightDrawerChange(e) {
|
||||
if (!e) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
}
|
||||
this.$emit('navRight', e || this.showDl)
|
||||
},
|
||||
toggleRightDrawerVisible() {
|
||||
if (this.$refs.showRight.visibleSync) {
|
||||
this.show = true;
|
||||
this.windowScrollByRecord();
|
||||
this.$refs.showRight.close();
|
||||
} else {
|
||||
this.getNoticeList();
|
||||
this.show = false;
|
||||
this.recordWindowScrollY();
|
||||
this.$refs.showRight.open();
|
||||
}
|
||||
},
|
||||
open() {
|
||||
// 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
|
||||
this.$refs.popup.open('top');
|
||||
},
|
||||
toggle(type) {
|
||||
this.type = type;
|
||||
// open 方法传入参数 等同在 uni-popup 组件上绑定 type属性
|
||||
this.$refs.popup.open(type);
|
||||
},
|
||||
change(e) {
|
||||
console.log('popup_change', e);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const domModule = weex.requireModule('dom')
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': "SourceHanSans",
|
||||
'src': "url('../../../static/fonts/SourceHanSansCN-Regular.otf')"
|
||||
});
|
||||
const pages = getCurrentPages();
|
||||
this.currentPage = pages[pages.length - 1].route;
|
||||
const locale = uni.getLocale();
|
||||
if (['zh-CN', 'zh-TW'].includes(locale)) {
|
||||
this.fontFamily = 'SourceHanSans';
|
||||
} else {
|
||||
this.fontFamily = 'Arial';
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (getToken()) {
|
||||
this.getMenuListData();
|
||||
}
|
||||
this.$cusModal.register(this.$refs.cusModal);
|
||||
this.contentSize = uni.upx2px(666) / 800
|
||||
},
|
||||
components: {
|
||||
MenuLanguageSelector
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import './index.css';
|
||||
@import './drawer.css';
|
||||
</style>
|
61
components/nvue/menuLanguageSelector/index.css
Normal file
61
components/nvue/menuLanguageSelector/index.css
Normal file
@ -0,0 +1,61 @@
|
||||
.menuItem {
|
||||
height: 50px;
|
||||
margin: 0px 28px 0px 16px;
|
||||
padding: 0px 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.menuItem.active {
|
||||
color: #fff;
|
||||
background-color: #29bbe4;
|
||||
}
|
||||
|
||||
.selectorWrapper {
|
||||
position: relative;
|
||||
}
|
||||
.selectorWrapper .selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
margin: 0px 10px 0px 16px;
|
||||
padding: 0px 10px;
|
||||
width: 274px;
|
||||
}
|
||||
.selectorWrapper .selector .selectorLabel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
width: 226px;
|
||||
}
|
||||
.selectorWrapper .optionsWrapper {
|
||||
flex-shrink: 0;
|
||||
height: 0px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
.selectorWrapper .optionsWrapper.visible {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 320px;
|
||||
opacity: 1;
|
||||
}
|
||||
.selectorWrapper .optionsWrapper .option {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 20px 0 50px;
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
justify-content: flex-start;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.selectorWrapper .optionsWrapper .option.active {
|
||||
color: #fff;
|
||||
background-color: #29bbe4;
|
||||
}
|
82
components/nvue/menuLanguageSelector/index.vue
Normal file
82
components/nvue/menuLanguageSelector/index.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<view class="selectorWrapper">
|
||||
<view class="selector" @click="toggleOptionsVisible">
|
||||
<image src="/static/nvue/global2.png" mode="aspectFit" style="width: 18px; height: 18px;margin-right: 10px;"></image>
|
||||
<view class="selectorLabel">
|
||||
<text style="margin-right: auto;">{{ t('locale.current') }}</text>
|
||||
<image src="/static/downArrow.png" mode="aspectFit" style="width: 14px; height: 8px"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view ref="languageSelector" :class="['optionsWrapper', { visible: optionsVisible }]">
|
||||
<text
|
||||
:class="['option', locale === language.value ? 'active' : '']"
|
||||
:style="{'text-align': language.value=='ar-SA'?'right':'left'}"
|
||||
v-for="(language, index) in languageOptions"
|
||||
:key="language.value"
|
||||
@click="changeLanguage(language)"
|
||||
>
|
||||
{{ language.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setLocale } from '@/utils/const.ts';
|
||||
import { setLoveLanguage } from '@/services/user.ts';
|
||||
import { languages } from '@/utils/const.ts';
|
||||
import { i18n } from '@/locale/index.ts';
|
||||
export default {
|
||||
name: 'MenuLanguageSelector',
|
||||
data() {
|
||||
return {
|
||||
local: undefined,
|
||||
optionsVisible: false,
|
||||
languageOptions: [
|
||||
{ label: 'العربية', value: 'ar-SA' },
|
||||
{ label: 'English', value: 'en-US' },
|
||||
{ label: 'Spanish', value: 'es-ES' },
|
||||
{ label: 'हिंदी', value: 'hi-IN' },
|
||||
{ label: '한국어', value: 'ko-KR' },
|
||||
{ label: 'ภาษาไทย', value: 'th-TH' },
|
||||
{ label: 'Tiếng Việt', value: 'vi-VN' },
|
||||
{ label: '简体中文', value: 'zh-CN' },
|
||||
{ label: '繁體中文', value: 'zh-TW' },
|
||||
{ label: 'Indonesia', value: 'in-ID' }
|
||||
],
|
||||
t: i18n.global.t
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleOptionsVisible() {
|
||||
this.optionsVisible = !this.optionsVisible;
|
||||
},
|
||||
async changeLanguage(language) {
|
||||
// #ifndef H5
|
||||
uni.showModal({
|
||||
content: this.t('modal.changeLanguage') + this.t(`locale.${language.value}`) + ',' + this.t('locale.languageChangeConfirm'),
|
||||
confirmColor: '#4DC0E5',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
await setLoveLanguage({ qcc_language: languages[language.value] ?? 'en_US' });
|
||||
setLocale(language.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
await setLoveLanguage({ qcc_language: languages[language.value] ?? 'en_US' });
|
||||
setLocale(language.value);
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.locale = uni.getLocale();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import './index.css';
|
||||
</style>
|
26
components/nvue/segmented/index.css
Normal file
26
components/nvue/segmented/index.css
Normal file
@ -0,0 +1,26 @@
|
||||
.segmentedWrapper {
|
||||
height: 37px;
|
||||
}
|
||||
.segmentedWrapper .segmented {
|
||||
position: relative;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ececec;
|
||||
font-size: 18px;
|
||||
flex: 1;
|
||||
}
|
||||
.segmentedWrapper .segmented .segmentedItem {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
padding: 0px 6px;
|
||||
}
|
||||
.segmentedWrapper .segmented .segmentedThumb {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
height: 2px;
|
||||
background-color: #333333;
|
||||
transition: left 0.3s ease-out;
|
||||
}
|
105
components/nvue/segmented/index.nvue
Normal file
105
components/nvue/segmented/index.nvue
Normal file
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<view class="segmentedWrapper" :style="{'width': contentWidth+'px'}">
|
||||
<view class="segmented"
|
||||
:style="{ fontSize: fontSize, borderBottomWidth: underLineHeight }">
|
||||
<view class="segmentedThumb" :style="{
|
||||
width: (1 / values.length) * contentWidth + 'px',
|
||||
height: thumbHeight,
|
||||
left: (1 / values.length) * contentWidth * current + 'px'
|
||||
}"></view>
|
||||
<text class="segmentedItem" :style="{
|
||||
width: (values.length ? contentWidth/values.length : contentWidth)+'px',
|
||||
color: current === index ? activeColor : inActiveColor,
|
||||
fontWeight: current === index ? activeFontWeight : inActiveFontWeight
|
||||
}" v-for="(item, index) in values" :key="index" @click="_onClick(index)">
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Segmented',
|
||||
emits: ['clickItem'],
|
||||
props: {
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: '18px'
|
||||
},
|
||||
thumbHeight: {
|
||||
type: String,
|
||||
default: '2px'
|
||||
},
|
||||
underLineHeight: {
|
||||
type: String,
|
||||
default: '1px'
|
||||
},
|
||||
gap: {
|
||||
type: String,
|
||||
default: '10px'
|
||||
},
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
inActiveColor: {
|
||||
type: String,
|
||||
default: '#999999'
|
||||
},
|
||||
activeFontWeight: {
|
||||
type: Number,
|
||||
default: 400
|
||||
},
|
||||
inActiveFontWeight: {
|
||||
type: Number,
|
||||
default: 400
|
||||
},
|
||||
styleType: {
|
||||
type: String,
|
||||
default: 'button'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
contentWidth: uni.getSystemInfoSync().windowWidth - 32
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
current(val) {
|
||||
if (val !== this.currentIndex) {
|
||||
this.currentIndex = val;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
created() {
|
||||
this.currentIndex = this.current;
|
||||
},
|
||||
methods: {
|
||||
_onClick(index) {
|
||||
if (this.currentIndex !== index) {
|
||||
this.currentIndex = index;
|
||||
this.$emit('clickItem', {
|
||||
currentIndex: index
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import './index.css';
|
||||
</style>
|
Reference in New Issue
Block a user