Browse Source

我的设置,消息通知

Karsa 2 years ago
parent
commit
da7e30e63f

+ 31 - 0
api/message.js

@@ -0,0 +1,31 @@
+import {httpGet,httpPost} from "@/utils/request.js"
+
+/**
+ * 消息列表
+ * @param type 0 1 2
+ */
+export const apiMessageList = params =>{
+	return httpGet('/message/list',params)
+}
+
+/**
+ * 一键已读
+ */
+export const apiReadAll = params => {
+	return httpPost('/message/batch/read',params)
+}
+
+/**
+ * 单条已读 msg_id
+ */
+export const apiReadOneMessage = params => {
+	return httpPost('/message/batch/read',params)
+}
+
+/**
+ * 删除 msg_id
+ */
+export const apiDelMessage = params => {
+	return httpPost('/message/delete',params)
+}
+

+ 8 - 0
api/user.js

@@ -54,4 +54,12 @@ export const apiApplyPermission=params=>{
  */
 export const apiLastApplyRecord=(params)=>{
 	return httpGet('/user/get_last_apply_record',params)
+}
+
+
+/**
+ * 个人设置 头像/昵称  nick_name head_img_url
+ */
+export const apiSetMyinfo = params => {
+	return httpPost('/user/set',params)
 }

+ 274 - 0
pages-user/messageList.vue

@@ -0,0 +1,274 @@
+<template>
+	<view class="message-page">
+		<van-sticky>
+			<view class="flex headar">
+				<view class="flex tab">
+					<view 
+						:class="['tab-item',{act: default_tab === tab.type}]"
+						v-for="tab in tabs"
+						:key="tab.type"
+						@click="switchTabHandle(tab.type)"
+					>
+						{{ tab.label }}
+					</view>
+				</view>
+				<text class="right-txt" v-if="messageList.length" @click="allReadHandle">一键已读</text>
+			</view>
+		</van-sticky>
+		<view class="messlist-cont" v-if="messageList.length">
+			<view 
+				class="message-item"
+				@touchstart="drawStart(item,$event)" 
+				@touchmove="drawMove" 
+				@touchend="drawEnd" 
+				v-for="(item,index) in messageList"
+				:key="item.msg_id"
+				:data-index="index" 
+				:style="'right:'+item.right+'rpx'"
+			>
+				<view class="info">
+					<view class="left flex">
+						<image :src="item.img" mode="" class="avatar"/>
+						<view class="content">
+							<view class="text_oneLine">{{item.content_first}}</view>
+							<view class="msg text_oneLine">{{item.content_second}}</view>
+							<view class="time">{{item.content_second}}</view>
+						</view>
+					</view>
+					<text class="read-ico" v-if="!item.is_read"/>
+				</view>
+				<view class="action" @click.stop="delMessageHandle(item)">删除</view>
+			</view>
+		</view>
+		<view class="nodata" v-else>
+			<image src="@/static/nodata.png" mode="" class="nodata-img"/>
+			<view>暂时没有新的消息哦</view>
+			<view>快去留言试试吧~</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import * as $message from '@/api/message'
+	export default {
+		data() {
+			return {
+				default_tab: 0,
+				tabs: [
+					{
+						label: '全部消息',
+						type: 0
+					},{
+						label: '回复',
+						type: 1
+					},{
+						label: '精选',
+						type: 2
+					},
+				],
+				page_no: 1,
+				messageList: [],
+				isHaveMore: true,
+				delBtnWidth: 176,
+				startX: 0
+			};
+		},
+		methods: {
+			/* 切换tab */
+			switchTabHandle(type) {
+				this.default_tab = type;
+				this.page_no = 1;
+				this.getMessageList();
+			},
+			
+			/* 获取列表 */
+			async getMessageList() {
+				const { code,data } = $message.apiMessageList({ 
+					type: this.default_tab,
+					current_index: this.page_no,
+					page_size: 20
+				})
+				if( code!==200 ) return
+				
+				const { list,paging } = data;
+				
+				this.isHaveMore = this.page_no >= paging.pages ? false : true;
+				this.messageList = this.page_no === 1 ? (list || []) : [...this.messageList,...list]
+			},
+			
+			/* 一键已读 */
+			async allReadHandle() {
+				const { code } = $message.apiReadAll({ type: this.default_tab })
+				if( code!==200 ) return
+				
+				this.page_no = 1;
+				this.getMessageList();
+				
+			},
+			
+			/* 删除消息 */
+			delMessageHandle() {
+				uni.showModal({
+					title: "",
+					content: "确定要删除该留言吗?",
+					confirmColor: "#6784A7",
+					success: function() {
+						
+					}
+				})
+			},
+			
+			onPullDownRefresh() {
+			    this.page_no = 1;
+			    setTimeout(() => {
+			        uni.stopPullDownRefresh()
+			    }, 1500)
+			},
+			
+			onReachBottom() {
+			    if (!this.isHaveMore) return
+			    this.page_no++
+			    this.getMessageList()
+			},
+			
+			async drawStart(item,e) {
+				let touch = e.touches[0];
+				this.startX = touch.clientX;
+				
+				const { msg_id } = item;
+				const { code } = await apiReadOneMessage({ msg_id })
+				if( code!==200 ) return
+				item.is_read = 1;
+			},
+			//触摸滑动
+			drawMove(e) {
+				// console.log("滑动");
+				for (let index in this.messageList) {
+					this.$set(this.messageList[index],'right',0);
+				}
+				let touch = e.touches[0];
+				let item = this.messageList[e.currentTarget.dataset.index];
+				let disX = this.startX - touch.clientX;
+				if (disX >= 20) {
+						if (disX > this.delBtnWidth) {
+							disX = this.delBtnWidth;
+						}
+						this.$set(this.messageList[e.currentTarget.dataset.index],'right',disX);
+				} else {
+					this.$set(this.messageList[e.currentTarget.dataset.index],'right',0);
+				}
+			},
+			//触摸滑动结束
+			drawEnd(e) {
+				let item = this.messageList[e.currentTarget.dataset.index];
+				if (item.right >= this.delBtnWidth / 2) {
+					this.$set(this.messageList[e.currentTarget.dataset.index],'right',this.delBtnWidth);
+				} else {
+					this.$set(this.messageList[e.currentTarget.dataset.index],'right',0);
+				}
+			},
+		},
+		onShow() {
+			this.getMessageList()
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.message-page {
+	padding: 40rpx 0;
+	.headar {
+		padding:  0 34rpx 20rpx;
+		justify-content: space-between;
+		font-size: 32rpx;
+		color: #E3B377;
+		background-color: #fff;
+		
+		.right-txt {
+			font-size: 26rpx;
+		}
+		.tab-item {
+			color: #333;
+			margin-right: 50rpx;
+			&.act {
+				color: #E3B377;
+				position: relative;
+				&::after {
+					content: '';
+					position: absolute;
+					bottom: -20rpx;
+					left: 20%;
+					width: 50%;
+					height: 4rpx;
+					background-color: #E3B377;
+				}
+			}
+		}
+	}
+
+	.messlist-cont {
+		.message-item {
+			position: relative;
+			margin: 30rpx 0;
+			padding: 0 30rpx;
+			.info {
+				position: relative;
+			}
+			.left {
+				.avatar {
+					width: 65rpx;
+					height: 65rpx;
+					margin-right: 20rpx;
+				}
+				.content {
+					color: #666;
+					font-size: 28rpx;
+					width: 80%;
+					.msg {
+						color: #333;
+						font-size: 30rpx;
+						margin: 10rpx 0;
+					}
+					.time {
+						color: #999;
+						font-size: 24rpx;
+					}
+				}
+			}
+			.read-ico {
+				width: 30rpx;
+				height: 30rpx;
+				border-radius: 50%;
+				position: absolute;
+				right: 0;
+				top: 50%;
+				transform: translateY(-50%);
+				background-color: #D62020;
+			}
+		
+			.action {
+				width: 176rpx;
+				height: 100%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				background-color: #E86F6F;
+				color: #fff;
+				position: absolute;
+				right: -176rpx;
+				top: 0;
+				
+			}
+		}
+	}
+
+	.nodata {
+		color: #999;
+		font-size: 34rpx;
+		text-align: center;
+		.nodata-img {
+			margin: 100rpx auto 60rpx;
+		}
+	}
+}
+</style>

+ 143 - 0
pages-user/mysetting.vue

@@ -0,0 +1,143 @@
+<template>
+	<view class="mysetting-page">
+		<view class="form-cont">
+			<vew class="item">
+				<text>头像</text>
+				<button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="chooseAvatar">
+					<image :src="avatar" mode="aspectFill" class="avatar"></image>
+					<image src="@/static/upload_avatar.png" class="avatar_ico"></image>
+				</button>
+			</vew>
+			<view class="item">
+				<text>昵称</text>
+			  <input v-model="nickname" type="nickname" class="nick-field" placeholder="请输入昵称"/>
+			</view>
+		</view>
+		<view class="btn-cont">
+			<van-button round size="large" block color="#DBA665" @click="saveMySetting">保存</van-button>
+			<van-button round size="large" block color="#F5F5F5" class="cancel" @click="cancel">取消</van-button>
+		</view>
+		<view class="bottom-text">专注为全球投资者提供中立客观的研究服务</view>
+	</view>
+</template>
+
+<script>
+	import { apiSetMyinfo } from '@/api/user'
+	import { uploadToServer } from '@/utils/upload.js'
+	export default {
+		data() {
+			return {
+				avatar: '',
+				nickname:'',
+			}
+		},
+		methods: {
+			chooseAvatar(e) {
+				console.log(e)
+				const { avatarUrl } = e.detail;
+				
+				uploadToServer(avatarUrl).then(res => {
+					this.avatar = res;
+				})
+			},
+			
+			/* 保存我的设置 */
+			async saveMySetting() {
+				if(!this.avatar || !this.nickname ) return  uni.showToast({
+          title: '请先设置头像和昵称哦~',
+          icon: 'none'
+        })
+				
+				const { code } =  await apiSetMyinfo({ nick_name: this.nickname, head_img_url: this.avatar  })	
+				if(code !== 200) return
+				
+				uni.showToast({
+				  title: '保存成功',
+				  icon: 'none',
+					success: ()=> {
+						setTimeout(() =>{
+							this.cancel()
+						},1000)
+					}
+				})
+			},
+			
+			cancel() {
+				uni.navigateBack({
+					delta: 1
+				})
+			},
+			
+			initData() {
+				const { head_img_url,nick_name } = this.userInfo;
+				this.avatar = head_img_url;
+				this.nickname = nick_name;
+			}
+		},
+		onShow() {
+			this.initData()
+		}
+	}
+</script>
+
+<style lang="scss">
+.mysetting-page {
+	padding: 120rpx 60rpx;
+	.form-cont {
+		.item {
+			font-size: 34rpx;
+			display: flex;
+			margin-bottom: 30rpx;
+			&:first-child {
+				align-items: center;
+			}
+			.avatar-wrapper {
+				position: relative;
+				margin-left: 50rpx;
+				background: #fff;
+				padding: 0;
+				&::after {
+					border: none;
+				}
+				.avatar {
+					width: 140rpx;
+					height: 140rpx;
+					border-radius: 50%;
+				}
+				.avatar_ico {
+					width: 40rpx;
+					height: 40rpx;
+					position: absolute;
+					bottom: 24rpx;
+					right: 0;
+					z-index: 2;
+				}
+			}
+			.nick-field {
+				font-size: 32rpx;
+				width: 478rpx;
+				margin-left: 50rpx;
+				padding-bottom: 20rpx;
+				border-bottom: 1px solid #DFAD6F;
+			}
+		}
+	}
+	.btn-cont {
+		padding: 0 40rpx;
+		margin-top: 150rpx;
+		.cancel .van-button{
+			color: #999 !important;
+			margin-top: 30rpx;
+		}
+	}
+	.bottom-text {
+		width: 100%;
+		position: fixed;
+		left: 50%;
+		text-align: center;
+		color: #999999;
+		transform: translateX(-50%);
+		bottom: 10%;
+	}
+}
+</style>

+ 17 - 3
pages.json

@@ -106,8 +106,21 @@
 					"style":{
 						"navigationBarTitleText": "品种权限"
 					}
-				}
-			]
+				},
+				{
+					"path": "messageList",
+					"style":{
+						"navigationBarTitleText": "消息通知",
+						"enablePullDownRefresh": true
+					}
+				},
+			   {
+			   	"path": "mysetting",
+			   	"style":{
+			   		"navigationBarTitleText": "我的设置"
+			   	}
+			   }
+      ]
 		},
 		// 图库模块
 		{
@@ -262,7 +275,8 @@
 			"van-tag": "/wxcomponents/vant/tag/index",
 			"van-row": "/wxcomponents/vant/row/index",
   		"van-col": "/wxcomponents/vant/col/index",
-			"van-progress": "/wxcomponents/vant/progress/index"
+			"van-progress": "/wxcomponents/vant/progress/index",
+			"van-dialog": "/wxcomponents/vant/dialog/index"
 		}
 	}
 }

+ 53 - 4
pages/user/user.vue

@@ -1,13 +1,18 @@
 <template>
 	<view class="user-page">
 		<view class="top-box">
-			<view class="flex">
+			<view class="flex" @click="toMySetting">
 			<view class="avatar">
 				<!-- <open-data type="userAvatarUrl"></open-data> -->
-				<image style="width:100%;height:100%" :src="globalImgUrls.defaultAvatar" mode="aspectFill"/>
+				<image style="width:100%;height:100%;border-radius: 50%;" :src="userInfo.head_img_url" mode="aspectFill"/>
+				<image src="@/static/upload_avatar.png" class="avatar_ico"></image>
 			</view>
 			<view style="padding-top:10rpx">
 				<view class="user-name">{{userInfo.real_name||'--'}}</view>
+				<view class="nick-name">
+					昵称:{{userInfo.nick_name || ''}}
+					<text v-if="!userInfo.nick_name" style="color: #E3B377;">设置专属昵称</text>
+				</view>
 				<view class="tel" v-if="userInfo.mobile">{{userInfo.mobile}}</view>
 				<view class="tel" v-else>{{userInfo.email}}</view>
 			</view>
@@ -46,6 +51,16 @@
 					<van-icon name="arrow"></van-icon>
 				</view>
 			</view>
+			<view class="flex item-card" >
+				<image src="@/static/message_ico.png" mode="widthFix" />
+				<view class="label flex" style="align-items: center;">消息通知 
+					<text class="unread-ico" v-if="userInfo.un_read">{{userInfo.un_read}}</text> 
+				</view>
+				<view class="right-text look" @click="toMessageHadle">
+					<text>查看</text>
+					<van-icon name="arrow"></van-icon>
+				</view>
+			</view>
 			<view class="flex item-card">
 				<image src="../../static/calendar.png" mode="widthFix" />
 				<text class="label">服务截止日期</text>
@@ -141,6 +156,16 @@
 				if(this.userInfo.permission_list.length==0) return
 				uni.navigateTo({ url: '/pages-user/permissionList' })
 			},
+			
+			/* 消息通知页 */
+			toMessageHadle() {
+				uni.navigateTo({ url: '/pages-user/messageList' })
+			},
+			
+			/* 我的设置 */
+			toMySetting() {
+				uni.navigateTo({ url: '/pages-user/mysetting' })
+			},
 
 			handleContact(){
 				apiApplyPermission({
@@ -175,8 +200,17 @@
 			width: 154rpx;
 			height: 154rpx;
 			border-radius: 50%;
-			overflow: hidden;
+			// overflow: hidden;
 			margin-right: 40rpx;
+			position: relative;
+			.avatar_ico {
+				width: 40rpx;
+				height: 40rpx;
+				position: absolute;
+				bottom: 0;
+				right: 0;
+				z-index: 2;
+			}
 		}
 		.user-name{
 			font-size: 24px;
@@ -184,7 +218,6 @@
 			color: #060606;
 		}
 		.tel{
-			margin-top: 26rpx;
 			color: #999;
 		}
 		.company{
@@ -196,6 +229,10 @@
 			}
 			color:#DBA665;
 		}
+		.nick-name {
+			font-size: 28rpx;
+			margin: 20rpx 0;
+		}
 	}
 	.content{
 		background-color: #fff;
@@ -234,6 +271,18 @@
 				text-align: right;
 				line-height: 100rpx;
 			}
+			.unread-ico {
+				width: 30rpx;
+				height: 30rpx;
+				display: inline-block;
+				border-radius: 50%;
+				color: #ffff;
+				background: #D62020;
+				line-height: 30rpx;
+				text-align: center;
+				font-size: 24rpx;
+				margin-left: 20rpx;
+			}
 		}
 		.item-card:last-child{
 			border: none;

BIN
static/hz_avatar.png


BIN
static/message_ico.png


BIN
static/nodata.png


BIN
static/upload_avatar.png


+ 6 - 0
style/common.scss

@@ -91,4 +91,10 @@ view{
         width: 346rpx;
         margin-bottom: 57rpx;
     }
+}
+
+.text_oneLine {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }

+ 1 - 1
utils/upload.js

@@ -9,7 +9,7 @@ import store from '@/store/index.js'
 /**
  * 上传文件到服务器
  */
-const uploadToServer = async (tempFilePath) => {
+export const uploadToServer = async (tempFilePath) => {
   const temres = await uniAsync.uploadFile({
     url: baseApiUrl + "/public/upload",
     filePath: tempFilePath,