Browse Source

'Save'

hfll
hflllll 1 month ago
parent
commit
21fb82d055
13 changed files with 423 additions and 116 deletions
  1. +11
    -2
      mixins/config.js
  2. +205
    -0
      pages/components/SplashScreen.vue
  3. +6
    -3
      pages/index/desk.vue
  4. +14
    -0
      pages/index/home.vue
  5. +6
    -2
      pages/index/user.vue
  6. +13
    -2
      stores/index.js
  7. +3
    -1
      subPages/home/book.vue
  8. +3
    -4
      subPages/home/directory.vue
  9. +4
    -67
      subPages/home/search.vue
  10. +11
    -1
      subPages/login/login.vue
  11. +49
    -18
      subPages/login/userInfo.vue
  12. +67
    -16
      subPages/user/profile.vue
  13. +31
    -0
      uni.scss

+ 11
- 2
mixins/config.js View File

@ -31,11 +31,17 @@ export default {
// 默认的全局分享参数 // 默认的全局分享参数
GShare() { GShare() {
const obj = {}
if (this.$store.state.userInfo.id) {
obj.path = this.configParamContent('xcxSharePage') + '?inviter=' + this.$store.state.userInfo.id
}else {
obj.path = this.configParamContent('xcxSharePage')
}
return { return {
title: this.configParamContent('app_name'), title: this.configParamContent('app_name'),
desc: this.configParamContent('share_desc'), desc: this.configParamContent('share_desc'),
imageUrl: this.configParamContent('login_logo'), imageUrl: this.configParamContent('login_logo'),
path: this.configParamContent('xcxSharePage'),
...obj
} }
} }
}, },
@ -53,7 +59,10 @@ export default {
...this.mixinCustomShare() ...this.mixinCustomShare()
} }
}, },
onLoad() {
onLoad(args) {
// this.calculateContainerHeight(); // this.calculateContainerHeight();
if (args.inviter){
uni.setStorageSync('inviter', args.inviter)
}
} }
} }

+ 205
- 0
pages/components/SplashScreen.vue View File

@ -0,0 +1,205 @@
<template>
<view v-if="showSplash" class="splash-screen">
<!-- 富文本內容 -->
<view class="splash-content">
<image
:src="splashContent"
mode="scaleToFill"
class="splash-image"
/>
</view>
<!-- 跳過按鈕 -->
<view class="skip-button" >
<text class="skip-text">跳過 ({{ countdown }}s)</text>
</view>
<!-- 定位英語文字和中文文字到左下角 -->
<view class="text-container">
<text class="english-text">
{{ configParamContent('creen_en') }}
</text>
<text class="chinese-text">
{{ configParamContent('creen_zh') }}
</text>
</view>
</view>
</template>
<script>
// import config from '../../mixins/config.js'
export default {
name: 'SplashScreen',
// mixins: [config],
props: {
//
duration: {
type: Number,
default: 5
}
},
data() {
return {
showSplash: false,
countdown: 5,
timer: null,
splashContent: ''
}
},
computed: {
image() {
return this.configParamContent('creen_image') || uni.getStorageSync('screen_image')
}
},
mounted() {
this.initSplash()
uni.hideTabBar()
},
beforeDestroy() {
this.clearTimer()
},
methods: {
//
async initSplash() {
try {
//
this.splashContent = this.image
//
if (this.splashContent) {
this.countdown = this.duration
this.showSplash = true
this.startCountdown()
}
} catch (error) {
console.error('獲取開動頁面內容失敗:', error)
}
},
//
startCountdown() {
this.timer = setInterval(() => {
this.countdown--
if (this.countdown <= 0) {
this.closeSplash()
}
}, 1000)
},
//
closeSplash() {
this.showSplash = false
uni.showTabBar({
animation: true
})
this.clearTimer()
this.$emit('close')
},
//
clearTimer() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
}
}
}
</script>
<style lang="scss" scoped>
.splash-screen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #fff;
z-index: 9999;
display: flex;
flex-direction: column;
.splash-image{
width: 100%;
height: 100%;
}
}
.splash-content {
flex: 1;
width: 100%;
height: 100%;
overflow: hidden;
//
:deep(rich-text) {
width: 100%;
height: 100%;
display: block;
img{
width: 100%;
height: 100%;
}
}
}
.skip-button {
position: absolute;
top: 100rpx;
left: 40rpx;
background-color: #0000004D;
border-radius: 100rpx;
width: 148rpx ;
height: 60rpx;
text-align: center;
z-index: 10000;
.skip-text {
font-size: 24rpx;
line-height: 60rpx;
color: #fff;
}
}
//
.text-container {
position: absolute;
bottom: 80rpx;
left: 40rpx;
z-index: 10001;
display: flex;
flex-direction: column;
gap: 8rpx;
}
//
.english-text {
font-family: PingFang SC;
font-weight: 600;
font-size: 32rpx; // 16pxrpx
line-height: 48rpx; // 24pxrpx
letter-spacing: 0;
color: #FFFFFF;
background: transparent;
}
//
.chinese-text {
font-family: PingFang SC;
// font-weight: 600;
font-size: 32rpx; // 16pxrpx
line-height: 48rpx; // 24pxrpx
letter-spacing: 0;
color: #FFFFFF;
background: transparent;
}
// tabbar
.splash-screen {
// tabbar
position: fixed !important;
z-index: 9999 !important;
}
</style>

+ 6
- 3
pages/index/desk.vue View File

@ -51,8 +51,10 @@
</view> </view>
</view> </view>
</view> </view>
<uv-loading-icon text="加载中" textSize="30rpx" v-if="isLoading"></uv-loading-icon>
<uv-empty v-else-if="!list.length" text="暂无数据"></uv-empty>
<view style="margin: 300rpx auto">
<uv-loading-icon text="加载中" textSize="30rpx" v-if="isLoading"></uv-loading-icon>
<uv-empty v-else-if="!list.length" text="暂无数据"></uv-empty>
</view>
</view> </view>
</view> </view>
</template> </template>
@ -95,7 +97,7 @@ export default {
title: '确认删除', title: '确认删除',
content: `确定要从书桌中移除《${book.book.booksName}》吗?`, content: `确定要从书桌中移除《${book.book.booksName}》吗?`,
confirmText: '删除', confirmText: '删除',
confirmColor: '#ff4757',
confirmColor: '#06DADC',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
this.deleteBook(book, index) this.deleteBook(book, index)
@ -144,6 +146,7 @@ export default {
onShow() { onShow() {
// //
if (uni.getStorageSync('token')) { if (uni.getStorageSync('token')) {
this.list = []
this.initPage() this.initPage()
this.getList(true) this.getList(true)
} }


+ 14
- 0
pages/index/home.vue View File

@ -1,5 +1,8 @@
<template> <template>
<view class="home-container"> <view class="home-container">
<!-- 開動頁面組件 -->
<SplashScreen @close="onSplashClose" />
<!-- 状态栏安全区域 --> <!-- 状态栏安全区域 -->
<uv-status-bar></uv-status-bar> <uv-status-bar></uv-status-bar>
@ -164,7 +167,12 @@
</template> </template>
<script> <script>
import SplashScreen from '../components/SplashScreen.vue'
export default { export default {
components: {
SplashScreen
},
data() { data() {
return { return {
// Tab // Tab
@ -194,6 +202,12 @@ export default {
}, },
methods: { methods: {
//
onSplashClose() {
console.log('開動頁面已關閉')
//
},
// Tab // Tab
async switchTab(index) { async switchTab(index) {
this.activeTab = index this.activeTab = index


+ 6
- 2
pages/index/user.vue View File

@ -199,6 +199,8 @@ export default {
const res = await this.$api.login.getUserInfo(); const res = await this.$api.login.getUserInfo();
if (res.code === 200) { if (res.code === 200) {
this.userInfo = res.result; this.userInfo = res.result;
// store
this.$store.dispatch('updateUserInfo', this.userInfo)
} }
}, },
@ -264,12 +266,14 @@ export default {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定要退出登录吗?', content: '确定要退出登录吗?',
confirmColor: '#C70019',
confirmColor: '#06DADC',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
// //
uni.removeStorageSync('token') uni.removeStorageSync('token')
// store
this.$store.commit('updateUserInfo', { })
// //
this.userInfo = { this.userInfo = {
avatar: '/static/默认头像.png', avatar: '/static/默认头像.png',


+ 13
- 2
stores/index.js View File

@ -10,7 +10,7 @@ const store = new Vuex.Store({
configList: [], configList: [],
departmentList: [], departmentList: [],
categoryList: [], categoryList: [],
userInfo:{}
}, },
mutations: { mutations: {
// 构造用于uv-picker的树状结构数组 // 构造用于uv-picker的树状结构数组
@ -56,7 +56,9 @@ const store = new Vuex.Store({
setDepartmentList(state, data) { setDepartmentList(state, data) {
state.departmentList = data state.departmentList = data
}, },
setUserInfo(state, data) {
state.userInfo = data
},
// //
}, },
actions: { actions: {
@ -74,6 +76,11 @@ const store = new Vuex.Store({
}, {}) }, {})
console.log('configList列表为:', config); console.log('configList列表为:', config);
// 事先永久存儲首屏圖片的數據
if (config.creen_image) {
uni.setStorageSync('screen_image', config.creen_image.content)
}
commit('setConfigList', config) commit('setConfigList', config)
}, },
// 查询部门列表 // 查询部门列表
@ -106,6 +113,10 @@ const store = new Vuex.Store({
console.error('配置数据初始化失败:', error) console.error('配置数据初始化失败:', error)
} }
}, },
// 更新/存儲用戶最新數據
updateUserInfo({ commit }, userInfo) {
commit('setUserInfo', userInfo)
},
} }
}) })

+ 3
- 1
subPages/home/book.vue View File

@ -34,8 +34,9 @@
<text class="member-button-text">升級會員解鎖</text> <text class="member-button-text">升級會員解鎖</text>
</view> </view>
</view> </view>
<!-- 图片卡片页面 --> <!-- 图片卡片页面 -->
<view class="card-content" v-else-if="currentPageType === '1'">
<view class="card-content" v-else-if="pageTypes[index] === '1'">
<view class="card-line"> <view class="card-line">
<image :src="configParamContent('highlight_icon')" class="card-line-image" mode="aspectFill" /> <image :src="configParamContent('highlight_icon')" class="card-line-image" mode="aspectFill" />
<text class="card-line-text">划线重点</text> <text class="card-line-text">划线重点</text>
@ -715,6 +716,7 @@ export default {
console.log('未找到单词释义:', word); console.log('未找到单词释义:', word);
} }
}, },
// //
async calculateTotalDuration() { async calculateTotalDuration() {
let totalDuration = 0; let totalDuration = 0;


+ 3
- 4
subPages/home/directory.vue View File

@ -63,9 +63,8 @@
<!-- 简介部分 --> <!-- 简介部分 -->
<view class="intro-section"> <view class="intro-section">
<view class="intro-title">简介</view> <view class="intro-title">简介</view>
<view class="intro-content">
{{ bookInfo.booksIntro }}
</view>
<rich-text :nodes="bookInfo.booksIntro" class="intro-content">
</rich-text>
</view> </view>
<!-- 作者部分 --> <!-- 作者部分 -->
@ -95,7 +94,7 @@
<image src="/static/课程图标.png" class="button-icon" mode="aspectFill"></image> <image src="/static/课程图标.png" class="button-icon" mode="aspectFill"></image>
<text>加入课程</text> <text>加入课程</text>
</view> </view>
<view class="action-button primary">
<view class="action-button primary" @click="startLearning(courseList.records[0].id)">
<image src="/static/内容图标.png" class="button-icon" ></image> <image src="/static/内容图标.png" class="button-icon" ></image>
<text>内容朗读</text> <text>内容朗读</text>
</view> </view>


+ 4
- 67
subPages/home/search.vue View File

@ -95,75 +95,12 @@ export default {
categoryTabs: [ ], categoryTabs: [ ],
// //
classMap: { classMap: {
'盛放会员': 'book-membership-premium',
vip: 'book-membership-vip',
basic: 'book-membership-basic',
'蕾朵会员': 'book-membership-premium',
'盛放会员': 'book-membership-vip',
'萌芽会员': 'book-membership-basic',
}, },
bookList: [ bookList: [
{
cover: '/static/主页图标.png',
title: '短文全脑孩子:12项革命性策略...',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '蕾朵会员',
membershipType: 'premium'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '盛放会员',
membershipType: 'vip'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '蕾朵会员',
membershipType: 'premium'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '盛放会员',
membershipType: 'vip'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '萌芽会员',
membershipType: 'basic'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '萌芽会员',
membershipType: 'basic'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '萌芽会员',
membershipType: 'basic'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
author: 'Daniel J. Siegel / Tina Payne Bryson / Del...',
duration: '03:24',
membership: '萌芽会员',
membershipType: 'basic'
},
] ]
} }
}, },


+ 11
- 1
subPages/login/login.vue View File

@ -81,12 +81,22 @@ export default {
provider: 'weixin', provider: 'weixin',
success: async (res) => { success: async (res) => {
console.log('登录成功', res); console.log('登录成功', res);
const obj = { }
if (uni.getStorageSync('inviter')){
obj.inviter = uni.getStorageSync('inviter')
}
const { result: loginRes } = await this.$api.login.login({ const { result: loginRes } = await this.$api.login.login({
code: res.code
code: res.code,
...obj
}) })
uni.setStorageSync('token', loginRes.token) uni.setStorageSync('token', loginRes.token)
const userInfo = loginRes.userInfo const userInfo = loginRes.userInfo
// store
this.$store.dispatch('updateUserInfo', userInfo)
if ( !userInfo.avatar || !userInfo.name || !userInfo.phone ){ if ( !userInfo.avatar || !userInfo.name || !userInfo.phone ){
uni.navigateTo({ uni.navigateTo({
url: '/subPages/login/userInfo' url: '/subPages/login/userInfo'


+ 49
- 18
subPages/login/userInfo.vue View File

@ -5,8 +5,8 @@
<!-- 头像区域 --> <!-- 头像区域 -->
<view class="avatar-section"> <view class="avatar-section">
<view class="app-info"> <view class="app-info">
<image class="app-logo" :src="configParamImage('app_logo')" mode="aspectFit"></image>
<text class="app-name">{{ configParamText('app_name') }}</text>
<image class="app-logo" :src="configParamContent('login_logo')" mode="aspectFit"></image>
<text class="app-name">{{ configParamContent('app_name') }}</text>
<text class="app-subname">申请获取您的头像昵称</text> <text class="app-subname">申请获取您的头像昵称</text>
</view> </view>
</view> </view>
@ -16,13 +16,15 @@
<!-- 头像 --> <!-- 头像 -->
<view class="form-item"> <view class="form-item">
<text class="form-label">头像</text> <text class="form-label">头像</text>
<button class="avatar-button" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image
class="avatar-image"
:src="userInfo.avatar || '/static/默认头像.png'"
mode="aspectFill"
></image>
</button>
<view class="avatar-button">
<button class="avatar-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image
class="avatar-image"
:src="userInfo.avatar || '/static/默认头像.png'"
mode="aspectFill"
></image>
</button>
</view>
</view> </view>
<!-- 昵称 --> <!-- 昵称 -->
@ -30,10 +32,11 @@
<text class="form-label">昵称</text> <text class="form-label">昵称</text>
<input <input
class="form-input" class="form-input"
type="name"
type="nickname"
v-model="userInfo.name" v-model="userInfo.name"
placeholder="请输入昵称" placeholder="请输入昵称"
@blur="nameBlur" @blur="nameBlur"
/> />
</view> </view>
@ -208,6 +211,23 @@ export default {
} }
}, },
// //
// onNicknameReview(e) {
// console.log('', e);
// if (e.detail.pass) {
// // 使
// this.userInfo.name = e.detail.value;
// console.log(':', e.detail.value);
// } else {
// //
// console.log('');
// uni.showToast({
// title: '',
// icon: 'none'
// });
// }
// },
// //
showDepartmentPicker() { showDepartmentPicker() {
this.$refs.departmentPicker.open(); this.$refs.departmentPicker.open();
@ -398,15 +418,26 @@ export default {
// //
.avatar-button { .avatar-button {
margin: 0;
padding: 0;
background: transparent;
border: none;
width: 100rpx;
height: 100rpx;
border: 2rpx solid #cccccc;
&::after {
display: flex;
justify-content: flex-start;
align-items: center;
.avatar-btn {
margin: 0;
padding: 0;
background: transparent;
border: none; border: none;
width: 100rpx;
height: 100rpx;
border: 2rpx solid #cccccc;
text-align: left;
display: flex;
align-items: center;
justify-content: flex-start;
&::after {
border: none;
}
} }
.avatar-image { .avatar-image {


+ 67
- 16
subPages/user/profile.vue View File

@ -13,6 +13,7 @@
<uv-input <uv-input
v-model="userInfo.name" v-model="userInfo.name"
placeholder="请输入昵称" placeholder="请输入昵称"
type="nickname"
:customStyle="inputStyle" :customStyle="inputStyle"
border="bottom" border="bottom"
></uv-input> ></uv-input>
@ -39,7 +40,7 @@
<text class="required">*</text> <text class="required">*</text>
<text>头像</text> <text>头像</text>
</view> </view>
<view class="avatar-container" @click="uploadAvatar">
<button class="avatar-container" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<view <view
v-if="userInfo.avatar === 'undefined'" v-if="userInfo.avatar === 'undefined'"
class="avatar-image" class="avatar-image"
@ -56,7 +57,7 @@
<view class="avatar-mask" :class="{ 'fade-out': showMask }" v-if="showMask"> <view class="avatar-mask" :class="{ 'fade-out': showMask }" v-if="showMask">
<text>点击上传头像</text> <text>点击上传头像</text>
</view> </view>
</view>
</button>
</view> </view>
</view> </view>
@ -102,25 +103,74 @@ export default {
} }
}, },
methods: { methods: {
// //
// onNicknameReview(e) {
// console.log('', e);
// if (e.detail.pass) {
// // 使
// this.userInfo.name = e.detail.value;
// console.log(':', e.detail.value);
// uni.showToast({
// title: '',
// icon: 'success'
// });
// } else {
// //
// console.log('');
// uni.showToast({
// title: '',
// icon: 'none'
// });
// }
// },
//
async uploadAvatar() {
try {
const result = await this.$utils.chooseAndUpload()
if (result && result.success) {
console.log(result);
// OSS
async onChooseAvatar(e) {
console.log('选择头像回调', e);
if (e.detail.avatarUrl) {
try {
//
uni.showLoading({ title: '上传头像中...' });
//
const file = {
path: e.detail.avatarUrl,
tempFilePath: e.detail.avatarUrl
};
// OSS
const uploadResult = await this.$utils.uploadImage(file);
this.userInfo.avatar = result.url
uni.hideLoading();
if (uploadResult.success) {
// URL
this.userInfo.avatar = uploadResult.url;
console.log('头像上传成功', uploadResult.url);
uni.showToast({
title: '头像上传成功',
icon: 'success'
});
} else {
}
} catch (error) {
uni.hideLoading();
console.error('头像上传异常:', error);
// 使
this.userInfo.avatar = e.detail.avatarUrl;
uni.showToast({
title: '头像处理异常,使用本地头像',
icon: 'none'
});
} }
} catch (error) {
console.error('头像上传失败:', error)
} else {
uni.showToast({ uni.showToast({
title: '头像上传失败',
icon: 'error'
})
title: '头像选择失败',
icon: 'none'
});
} }
}, },
// //
@ -175,6 +225,7 @@ export default {
const res = await this.$api.login.getUserInfo() const res = await this.$api.login.getUserInfo()
if (res.code === 200) { if (res.code === 200) {
this.userInfo = res.result this.userInfo = res.result
this.$store.dispatch('updateUserInfo', this.userInfo)
} }
} }
}, },


+ 31
- 0
uni.scss View File

@ -21,6 +21,36 @@ $secondary-color: #8B8B8B;
$primary-text-color: #000; $primary-text-color: #000;
$secondary-text-color: #999; $secondary-text-color: #999;
/* 全局隱藏滾動條樣式 */
/* 隱藏 scroll-view 滾動條 */
scroll-view {
/* 隱藏 WebKit 瀏覽器的滾動條 */
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
/* 隱藏 Firefox 的滾動條 */
scrollbar-width: none;
/* 確保滾動功能正常 */
-ms-overflow-style: none;
}
/* 通用滾動條隱藏類 */
.hide-scrollbar {
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
// //
.click-animation{ .click-animation{
&:active { &:active {
@ -90,3 +120,4 @@ $uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px; $uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色 $uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px; $uni-font-size-paragraph:15px;

Loading…
Cancel
Save