Compare commits
No commits in common. "main" and "main-ai对话" have entirely different histories.
2
App.vue
|
|
@ -34,8 +34,6 @@ export default {
|
|||
}
|
||||
},
|
||||
});
|
||||
// 先返回
|
||||
return
|
||||
|
||||
if (!that.vuex_token) {
|
||||
const type = that.vuex_userType || 0; // 0:学生 1:教师
|
||||
|
|
|
|||
|
|
@ -48,12 +48,11 @@ const install = (Vue, vm) => {
|
|||
Vue.prototype.$u.http.interceptor.response = (res) => {
|
||||
// uni.hideLoading();
|
||||
|
||||
// 先注释
|
||||
// 检查是否为401未授权错误
|
||||
// if (res.statusCode === 401) {
|
||||
// handleAuthError(vm);
|
||||
// return false;
|
||||
// }
|
||||
if (res.statusCode === 401) {
|
||||
handleAuthError(vm);
|
||||
return false;
|
||||
}
|
||||
|
||||
return res.data;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<view class="empty-container">
|
||||
<image :src="getImage" class="empty-image" mode="widthFix"></image>
|
||||
<text class="empty-text">{{ text }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 暂无数据
|
||||
import noDataDefault from "@/static/common/img/noData/no-data.png";
|
||||
// 暂无搜索
|
||||
import noDataSearch from "@/static/common/img/noData/no-search.png";
|
||||
// 暂无消息
|
||||
import noDataMessage from "@/static/common/img/noData/no-message.png";
|
||||
// 网络异常
|
||||
import errorNet from "@/static/common/img/noData/error-net.png";
|
||||
// 数据加载失败
|
||||
import errorData from "@/static/common/img/noData/error-data.png";
|
||||
// 维护中
|
||||
import maintenance from "@/static/common/img/noData/maintenance.png";
|
||||
// 暂无附件简历
|
||||
import noAttachmentResume from "@/static/common/img/noData/no-attachment-resume.png";
|
||||
// 暂无收藏
|
||||
import noCollection from "@/static/common/img/noData/no-collection.png";
|
||||
// 暂无评论
|
||||
import noComments from "@/static/common/img/noData/no-comments.png";
|
||||
// 暂无内容
|
||||
import noContent from "@/static/common/img/noData/no-content.png";
|
||||
// 暂无文件
|
||||
import noFile from "@/static/common/img/noData/no-file.png";
|
||||
// 暂无好友
|
||||
import noFriends from "@/static/common/img/noData/no-friends.png";
|
||||
// 暂无网络
|
||||
import noInternet from "@/static/common/img/noData/no-internet.png";
|
||||
// 暂无权限
|
||||
import noPermissions from "@/static/common/img/noData/no-permissions.png";
|
||||
// 暂无图片
|
||||
import noPicture from "@/static/common/img/noData/no-picture.png";
|
||||
|
||||
export default {
|
||||
name: "NoData",
|
||||
props: {
|
||||
// 图片类型
|
||||
type: {
|
||||
type: String,
|
||||
default: "default",
|
||||
},
|
||||
// 描述文字,默认值为"暂无数据"
|
||||
text: {
|
||||
type: String,
|
||||
default: "暂无数据",
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
getImage() {
|
||||
const imageMap = {
|
||||
default: noDataDefault,
|
||||
search: noDataSearch,
|
||||
message: noDataMessage,
|
||||
errorNet: errorNet,
|
||||
errorData: errorData,
|
||||
maintenance: maintenance,
|
||||
resume: noAttachmentResume,
|
||||
collection: noCollection,
|
||||
comments: noComments,
|
||||
content: noContent,
|
||||
file: noFile,
|
||||
friends: noFriends,
|
||||
internet: noInternet,
|
||||
permissions: noPermissions,
|
||||
picture: noPicture,
|
||||
};
|
||||
return imageMap[this.type] || imageMap.default;
|
||||
},
|
||||
},
|
||||
data: {
|
||||
/*
|
||||
<no-data type="default" text="暂无数据" />
|
||||
<no-data type="errorNet" text="网络错误" />
|
||||
<no-data type="search" text="暂无搜索内容" />
|
||||
<no-data type="errorData" text="数据加载失败" />
|
||||
<no-data type="message" text="暂无消息" />
|
||||
<no-data type="maintenance" text="系统维护中" />
|
||||
<no-data type="resume" text="暂无简历附件" />
|
||||
<no-data type="collection" text="暂无收藏" />
|
||||
<no-data type="comments" text="暂无评论" />
|
||||
<no-data type="content" text="暂无内容" />
|
||||
<no-data type="file" text="暂无文件" />
|
||||
<no-data type="friends" text="暂无好友" />
|
||||
<no-data type="internet" text="无网络连接" />
|
||||
<no-data type="permissions" text="暂无权限" />
|
||||
<no-data type="picture" text="暂无图片" /> */
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
<template>
|
||||
<u-tabbar
|
||||
v-if="show"
|
||||
:value="currentIndex"
|
||||
:show="true"
|
||||
:bg-color="style.backgroundColor"
|
||||
:border-top="style.borderTop"
|
||||
:fixed="true"
|
||||
:height="style.height"
|
||||
:list="formattedTabList"
|
||||
@change="handleTabChange"
|
||||
>
|
||||
</u-tabbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tabbarConfig from "@/config/tabbar.config.js";
|
||||
|
||||
export default {
|
||||
name: "TabBar",
|
||||
props: {
|
||||
// 当前页面路径(可选)
|
||||
currentPath: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
// 是否显示 TabBar
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
tabList: tabbarConfig.tabs,
|
||||
style: tabbarConfig.style,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 格式化为 uview tabbar 所需格式
|
||||
formattedTabList() {
|
||||
return this.tabList.map((item) => ({
|
||||
text: item.text,
|
||||
iconPath: item.icon,
|
||||
selectedIconPath: item.activeIcon,
|
||||
pagePath: item.pagePath,
|
||||
count: item.badge || 0,
|
||||
isDot: item.dot || false,
|
||||
}));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 监听 currentPath 变化
|
||||
currentPath: {
|
||||
handler(newPath) {
|
||||
if (newPath) {
|
||||
this.updateActiveTab(newPath);
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initActiveTab();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化激活的 tab
|
||||
*/
|
||||
initActiveTab() {
|
||||
const path = this.currentPath || this.getCurrentPagePath();
|
||||
this.updateActiveTab(path);
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新激活的 tab
|
||||
*/
|
||||
updateActiveTab(path) {
|
||||
const index = this.tabList.findIndex((item) =>
|
||||
this.isPathMatch(path, item.pagePath)
|
||||
);
|
||||
if (index !== -1) {
|
||||
this.currentIndex = index;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 路径匹配判断
|
||||
*/
|
||||
isPathMatch(currentPath, targetPath) {
|
||||
const normalize = (path) => path.replace(/^\//, "");
|
||||
return normalize(currentPath) === normalize(targetPath);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前页面路径
|
||||
*/
|
||||
getCurrentPagePath() {
|
||||
const pages = getCurrentPages();
|
||||
console.log('pages',pages);
|
||||
|
||||
if (pages.length > 0) {
|
||||
return "/" + pages[pages.length - 1].route;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理 tab 切换
|
||||
*/
|
||||
handleTabChange(index) {
|
||||
if (this.currentIndex === index) {
|
||||
this.$emit("reclick", this.tabList[index].pagePath, index);
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTab = this.tabList[index];
|
||||
if (!targetTab) return;
|
||||
|
||||
this.currentIndex = index;
|
||||
this.$emit("change", targetTab.pagePath, index);
|
||||
|
||||
// 跳转
|
||||
this.navigateToTab(targetTab.pagePath);
|
||||
},
|
||||
|
||||
/**
|
||||
* 跳转到 tab 页面
|
||||
*/
|
||||
navigateToTab(url) {
|
||||
uni.switchTab({
|
||||
url,
|
||||
fail: () => {
|
||||
// 降级方案
|
||||
uni.reLaunch({ url });
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置角标
|
||||
* @param {number} index - tab 索引
|
||||
* @param {string|number} badge - 角标内容
|
||||
*/
|
||||
setBadge(index, badge) {
|
||||
if (this.tabList[index]) {
|
||||
this.$set(this.tabList[index], "badge", badge);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 显示/隐藏小红点
|
||||
* @param {number} index - tab 索引
|
||||
* @param {boolean} show - 是否显示
|
||||
*/
|
||||
setDot(index, show) {
|
||||
if (this.tabList[index]) {
|
||||
this.$set(this.tabList[index], "dot", show);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* TabBar 配置文件
|
||||
* 集中管理底部导航栏配置
|
||||
*/
|
||||
|
||||
export const TAB_BAR_CONFIG = [
|
||||
{
|
||||
text: "在线咨询",
|
||||
icon: "/static/tabbar/tabbar-icon1.png",
|
||||
activeIcon: "/static/tabbar/tabbar-icon1-active.png",
|
||||
pagePath: "/pages/consultation/index",
|
||||
// 可选配置
|
||||
badge: "", // 角标文字
|
||||
dot: false, // 是否显示小红点
|
||||
},
|
||||
{
|
||||
text: "人工转接",
|
||||
icon: "/static/tabbar/tabbar-icon4.png",
|
||||
activeIcon: "/static/tabbar/tabbar-icon4-active.png",
|
||||
pagePath: "/pages/transfer/index",
|
||||
badge: "",
|
||||
dot: false,
|
||||
},
|
||||
{
|
||||
text: "我的",
|
||||
icon: "/static/tabbar/tabbar-icon3.png",
|
||||
activeIcon: "/static/tabbar/tabbar-icon3-active.png",
|
||||
pagePath: "/pages/my/index",
|
||||
badge: "",
|
||||
dot: false,
|
||||
},
|
||||
];
|
||||
|
||||
// TabBar 样式配置
|
||||
export const TAB_BAR_STYLE = {
|
||||
color: "#999999", // 未激活颜色
|
||||
selectedColor: "#4a6cf7", // 激活颜色
|
||||
backgroundColor: "#ffffff", // 背景色
|
||||
borderTop: false, // 是否显示顶部边框
|
||||
height: 148, // 高度(rpx)
|
||||
};
|
||||
|
||||
export default {
|
||||
tabs: TAB_BAR_CONFIG,
|
||||
style: TAB_BAR_STYLE,
|
||||
};
|
||||
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
/**
|
||||
* WebSocket 配置文件
|
||||
*
|
||||
* 集中管理 WebSocket 相关配置
|
||||
*/
|
||||
|
||||
// 根据环境选择 WebSocket 地址
|
||||
const getWebSocketUrl = () => {
|
||||
// #ifdef H5
|
||||
// H5 开发环境
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return 'ws://localhost:8082/ws/chat'
|
||||
}
|
||||
// H5 生产环境
|
||||
return 'wss://120.55.234.65:8082/ws/chat'
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序必须使用 wss(加密连接)
|
||||
return 'wss://120.55.234.65:8082/ws/chat'
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// App 可以使用 ws 或 wss
|
||||
return 'wss://120.55.234.65:8082/ws/chat'
|
||||
// #endif
|
||||
|
||||
// 默认地址
|
||||
return 'ws://120.55.234.65:8082/ws/chat'
|
||||
}
|
||||
|
||||
export default {
|
||||
// WebSocket 服务器地址(动态获取)
|
||||
url: getWebSocketUrl(),
|
||||
|
||||
// 重连配置
|
||||
reconnect: {
|
||||
enabled: true, // 是否启用自动重连
|
||||
maxAttempts: 10, // 最大重连次数
|
||||
interval: 3000, // 重连间隔(毫秒)
|
||||
incrementInterval: true, // 是否递增重连间隔
|
||||
maxInterval: 30000 // 最大重连间隔
|
||||
},
|
||||
|
||||
// 心跳配置
|
||||
heartbeat: {
|
||||
enabled: true, // 是否启用心跳
|
||||
interval: 30000, // 心跳间隔(毫秒)
|
||||
timeout: 10000, // 心跳超时时间
|
||||
pingMessage: { // 心跳消息格式
|
||||
type: 'ping',
|
||||
timestamp: () => Date.now()
|
||||
}
|
||||
},
|
||||
|
||||
// 消息队列配置
|
||||
messageQueue: {
|
||||
enabled: true, // 是否启用消息队列
|
||||
maxSize: 100, // 队列最大长度
|
||||
clearOnConnect: false // 连接成功后是否清空队列
|
||||
},
|
||||
|
||||
// 日志配置
|
||||
log: {
|
||||
enabled: true, // 是否启用日志
|
||||
level: 'info' // 日志级别:debug, info, warn, error
|
||||
},
|
||||
|
||||
// 消息类型定义
|
||||
messageTypes: {
|
||||
// 文字消息
|
||||
TEXT: 'text',
|
||||
// 图片消息
|
||||
IMAGE: 'image',
|
||||
// 语音消息
|
||||
VOICE: 'voice',
|
||||
// 系统消息
|
||||
SYSTEM: 'system',
|
||||
// 心跳
|
||||
PING: 'ping',
|
||||
PONG: 'pong',
|
||||
// 已读回执
|
||||
READ: 'read',
|
||||
// 输入中
|
||||
TYPING: 'typing',
|
||||
// 人工转接
|
||||
TRANSFER: 'transfer'
|
||||
},
|
||||
|
||||
// 事件名称定义
|
||||
events: {
|
||||
OPEN: 'open',
|
||||
CLOSE: 'close',
|
||||
ERROR: 'error',
|
||||
MESSAGE: 'message',
|
||||
RECONNECT: 'reconnect',
|
||||
RECONNECT_FAILED: 'reconnect_failed'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,607 +0,0 @@
|
|||
# WebSocket 使用示例
|
||||
|
||||
## 📦 已创建的文件
|
||||
|
||||
✅ `/utils/websocket-manager.js` - WebSocket 管理器(已完成)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 在 App.vue 中初始化连接
|
||||
|
||||
```vue
|
||||
<script>
|
||||
import wsManager from '@/utils/websocket-manager.js'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState(['vuex_token', 'vuex_user'])
|
||||
},
|
||||
|
||||
onLaunch() {
|
||||
console.log('App Launch')
|
||||
|
||||
// 如果已登录,连接 WebSocket
|
||||
if (this.vuex_token) {
|
||||
this.connectWebSocket()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
connectWebSocket() {
|
||||
// 替换为你的 WebSocket 服务器地址
|
||||
const wsUrl = `wss://your-server.com/chat?token=${this.vuex_token}`
|
||||
|
||||
wsManager.connect(wsUrl, {
|
||||
reconnectInterval: 3000, // 重连间隔 3 秒
|
||||
maxReconnectAttempts: 10, // 最多重连 10 次
|
||||
heartbeatInterval: 30000 // 心跳间隔 30 秒
|
||||
})
|
||||
|
||||
// 监听连接成功
|
||||
wsManager.on('open', () => {
|
||||
console.log('[App] WebSocket 连接成功')
|
||||
uni.showToast({
|
||||
title: '已连接',
|
||||
icon: 'success'
|
||||
})
|
||||
})
|
||||
|
||||
// 监听连接关闭
|
||||
wsManager.on('close', (res) => {
|
||||
console.log('[App] WebSocket 已关闭', res)
|
||||
})
|
||||
|
||||
// 监听连接错误
|
||||
wsManager.on('error', (err) => {
|
||||
console.error('[App] WebSocket 错误', err)
|
||||
uni.showToast({
|
||||
title: '连接失败',
|
||||
icon: 'none'
|
||||
})
|
||||
})
|
||||
|
||||
// 监听重连
|
||||
wsManager.on('reconnect', (attempts) => {
|
||||
console.log(`[App] 正在重连 (${attempts})`)
|
||||
})
|
||||
|
||||
// 监听消息(全局消息处理)
|
||||
wsManager.on('message', (data) => {
|
||||
console.log('[App] 收到消息:', data)
|
||||
|
||||
// 根据消息类型处理
|
||||
switch (data.type) {
|
||||
case 'message':
|
||||
// 新消息
|
||||
this.handleNewMessage(data)
|
||||
break
|
||||
case 'notification':
|
||||
// 系统通知
|
||||
this.handleNotification(data)
|
||||
break
|
||||
default:
|
||||
console.log('[App] 未知消息类型:', data.type)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleNewMessage(data) {
|
||||
// 更新 Vuex 中的消息列表
|
||||
this.$store.commit('addMessage', data)
|
||||
|
||||
// 显示新消息提示
|
||||
uni.showTabBarRedDot({
|
||||
index: 1 // 消息页的 tabBar 索引
|
||||
})
|
||||
},
|
||||
|
||||
handleNotification(data) {
|
||||
uni.showToast({
|
||||
title: data.content,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
onHide() {
|
||||
// App 进入后台,保持连接
|
||||
console.log('App Hide')
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// App 从后台回到前台
|
||||
console.log('App Show')
|
||||
|
||||
// 如果连接已断开,重新连接
|
||||
const state = wsManager.getState()
|
||||
if (!state.isConnected && !state.isConnecting) {
|
||||
this.connectWebSocket()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 在聊天页面中使用
|
||||
|
||||
#### 方式 A:修改现有的 `dialogBox.vue`
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view>
|
||||
<!-- 消息列表 -->
|
||||
<scroll-view
|
||||
:scroll-top="scrollTop"
|
||||
:scroll-with-animation="true"
|
||||
class="scroll"
|
||||
scroll-y="true"
|
||||
>
|
||||
<view v-for="(msg, index) in messageList" :key="index" class="message-item">
|
||||
<!-- 我方消息 -->
|
||||
<view v-if="msg.fromUserId == vuex_user.id" class="my-message">
|
||||
<text>{{ msg.content }}</text>
|
||||
</view>
|
||||
<!-- 对方消息 -->
|
||||
<view v-else class="other-message">
|
||||
<text>{{ msg.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<view class="input-box">
|
||||
<input
|
||||
v-model="inputValue"
|
||||
placeholder="请输入消息"
|
||||
@confirm="sendMessage"
|
||||
/>
|
||||
<button @click="sendMessage">发送</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wsManager from '@/utils/websocket-manager.js'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
messageList: [],
|
||||
inputValue: '',
|
||||
scrollTop: 0,
|
||||
targetUserId: '', // 对方用户 ID
|
||||
messageHandler: null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['vuex_user'])
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
// 获取对方用户 ID
|
||||
this.targetUserId = options.userId || ''
|
||||
|
||||
// 加载历史消息
|
||||
this.loadHistoryMessages()
|
||||
|
||||
// 监听新消息
|
||||
this.messageHandler = (data) => {
|
||||
// 只处理与当前聊天对象相关的消息
|
||||
if (data.fromUserId === this.targetUserId || data.toUserId === this.targetUserId) {
|
||||
this.messageList.push({
|
||||
fromUserId: data.fromUserId,
|
||||
toUserId: data.toUserId,
|
||||
content: data.content,
|
||||
timestamp: data.timestamp
|
||||
})
|
||||
|
||||
// 滚动到底部
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
|
||||
// 发送已读回执
|
||||
this.sendReadReceipt(data.messageId)
|
||||
}
|
||||
}
|
||||
|
||||
wsManager.on('message', this.messageHandler)
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
// 移除消息监听
|
||||
if (this.messageHandler) {
|
||||
wsManager.off('message', this.messageHandler)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 加载历史消息
|
||||
async loadHistoryMessages() {
|
||||
try {
|
||||
const res = await this.$u.api.getMessages({
|
||||
userId: this.targetUserId,
|
||||
page: 1,
|
||||
pageSize: 50
|
||||
})
|
||||
|
||||
this.messageList = res.data || []
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('加载历史消息失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage() {
|
||||
if (!this.inputValue.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入消息',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 通过 WebSocket 发送
|
||||
wsManager.send({
|
||||
type: 'message',
|
||||
fromUserId: this.vuex_user.id,
|
||||
toUserId: this.targetUserId,
|
||||
content: this.inputValue,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
|
||||
// 立即显示到界面(乐观更新)
|
||||
this.messageList.push({
|
||||
fromUserId: this.vuex_user.id,
|
||||
toUserId: this.targetUserId,
|
||||
content: this.inputValue,
|
||||
timestamp: Date.now(),
|
||||
sending: true // 标记为发送中
|
||||
})
|
||||
|
||||
// 清空输入框
|
||||
this.inputValue = ''
|
||||
|
||||
// 滚动到底部
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
},
|
||||
|
||||
// 发送已读回执
|
||||
sendReadReceipt(messageId) {
|
||||
wsManager.send({
|
||||
type: 'read',
|
||||
messageId: messageId,
|
||||
userId: this.vuex_user.id
|
||||
})
|
||||
},
|
||||
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
this.scrollTop = 99999
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 在 Vuex 中管理 WebSocket 状态(可选)
|
||||
|
||||
```javascript
|
||||
// store/index.js
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
// ... 其他状态
|
||||
wsConnected: false,
|
||||
unreadCount: 0,
|
||||
messages: []
|
||||
},
|
||||
|
||||
mutations: {
|
||||
// WebSocket 连接状态
|
||||
SET_WS_CONNECTED(state, connected) {
|
||||
state.wsConnected = connected
|
||||
},
|
||||
|
||||
// 新消息
|
||||
ADD_MESSAGE(state, message) {
|
||||
state.messages.push(message)
|
||||
state.unreadCount++
|
||||
},
|
||||
|
||||
// 清空未读
|
||||
CLEAR_UNREAD(state) {
|
||||
state.unreadCount = 0
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 在 App.vue 中调用
|
||||
wsConnected({ commit }) {
|
||||
commit('SET_WS_CONNECTED', true)
|
||||
},
|
||||
|
||||
wsDisconnected({ commit }) {
|
||||
commit('SET_WS_CONNECTED', false)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 消息协议设计
|
||||
|
||||
### 客户端 → 服务器
|
||||
|
||||
#### 1. 发送文字消息
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"fromUserId": "user123",
|
||||
"toUserId": "user456",
|
||||
"content": "你好",
|
||||
"timestamp": 1635678901234
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 发送心跳
|
||||
```json
|
||||
{
|
||||
"type": "ping",
|
||||
"timestamp": 1635678901234
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 发送已读回执
|
||||
```json
|
||||
{
|
||||
"type": "read",
|
||||
"messageId": "msg123",
|
||||
"userId": "user123"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 服务器 → 客户端
|
||||
|
||||
#### 1. 推送消息
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"messageId": "msg123",
|
||||
"fromUserId": "user456",
|
||||
"toUserId": "user123",
|
||||
"content": "你好",
|
||||
"timestamp": 1635678901234
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 心跳响应
|
||||
```json
|
||||
{
|
||||
"type": "pong",
|
||||
"timestamp": 1635678901234
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 系统通知
|
||||
```json
|
||||
{
|
||||
"type": "notification",
|
||||
"content": "系统维护通知",
|
||||
"timestamp": 1635678901234
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 错误消息
|
||||
```json
|
||||
{
|
||||
"type": "error",
|
||||
"code": 401,
|
||||
"message": "未授权",
|
||||
"timestamp": 1635678901234
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 后端实现参考
|
||||
|
||||
### Node.js + ws
|
||||
|
||||
```javascript
|
||||
const WebSocket = require('ws')
|
||||
const wss = new WebSocket.Server({ port: 8080 })
|
||||
|
||||
// 存储所有连接(用户 ID -> WebSocket)
|
||||
const clients = new Map()
|
||||
|
||||
wss.on('connection', (ws, req) => {
|
||||
// 从 URL 获取 token
|
||||
const token = new URL(req.url, 'http://localhost').searchParams.get('token')
|
||||
|
||||
// 验证 token,获取用户 ID
|
||||
const userId = verifyToken(token)
|
||||
|
||||
if (!userId) {
|
||||
ws.close(4001, '未授权')
|
||||
return
|
||||
}
|
||||
|
||||
// 保存连接
|
||||
clients.set(userId, ws)
|
||||
console.log(`用户 ${userId} 已连接`)
|
||||
|
||||
// 处理消息
|
||||
ws.on('message', (message) => {
|
||||
const data = JSON.parse(message)
|
||||
|
||||
switch (data.type) {
|
||||
case 'message':
|
||||
// 转发消息
|
||||
handleMessage(data)
|
||||
break
|
||||
case 'ping':
|
||||
// 心跳响应
|
||||
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }))
|
||||
break
|
||||
case 'read':
|
||||
// 已读回执
|
||||
handleReadReceipt(data)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
// 处理断开
|
||||
ws.on('close', () => {
|
||||
clients.delete(userId)
|
||||
console.log(`用户 ${userId} 已断开`)
|
||||
})
|
||||
})
|
||||
|
||||
// 转发消息
|
||||
function handleMessage(data) {
|
||||
const { toUserId, fromUserId, content } = data
|
||||
|
||||
// 保存到数据库
|
||||
saveMessage({ fromUserId, toUserId, content })
|
||||
|
||||
// 推送给接收者
|
||||
const targetWs = clients.get(toUserId)
|
||||
if (targetWs && targetWs.readyState === WebSocket.OPEN) {
|
||||
targetWs.send(JSON.stringify({
|
||||
type: 'message',
|
||||
messageId: generateId(),
|
||||
fromUserId,
|
||||
toUserId,
|
||||
content,
|
||||
timestamp: Date.now()
|
||||
}))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 完整流程图
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ App 启动 │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ 检查登录状态 │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼ (已登录)
|
||||
┌─────────────┐
|
||||
│ 连接 WS │ ← wsManager.connect()
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ 监听消息 │ ← wsManager.on('message')
|
||||
└──────┬──────┘
|
||||
│
|
||||
├─→ (收到消息) → 更新 UI
|
||||
│
|
||||
└─→ (发送消息) → wsManager.send()
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ 服务器处理 │
|
||||
└──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ 推送给对方 │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试清单
|
||||
|
||||
### 功能测试
|
||||
- [ ] 连接成功
|
||||
- [ ] 发送消息
|
||||
- [ ] 接收消息
|
||||
- [ ] 心跳正常
|
||||
- [ ] 手动断开
|
||||
- [ ] 自动重连
|
||||
- [ ] 消息队列
|
||||
|
||||
### 场景测试
|
||||
- [ ] App 切换到后台
|
||||
- [ ] App 从后台恢复
|
||||
- [ ] 网络断开
|
||||
- [ ] 网络恢复
|
||||
- [ ] 服务器重启
|
||||
- [ ] 并发多人聊天
|
||||
|
||||
### 性能测试
|
||||
- [ ] 消息延迟 < 200ms
|
||||
- [ ] 重连时间 < 5s
|
||||
- [ ] 内存占用正常
|
||||
- [ ] CPU 占用正常
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
现在你已经有:
|
||||
|
||||
1. ✅ **WebSocket 管理器** (`utils/websocket-manager.js`)
|
||||
- 自动重连
|
||||
- 心跳检测
|
||||
- 消息队列
|
||||
- 事件监听
|
||||
|
||||
2. ✅ **使用示例**
|
||||
- App.vue 初始化
|
||||
- 聊天页面使用
|
||||
- Vuex 状态管理
|
||||
|
||||
3. ✅ **消息协议**
|
||||
- 客户端 → 服务器
|
||||
- 服务器 → 客户端
|
||||
|
||||
4. ✅ **后端参考**
|
||||
- Node.js + ws 实现
|
||||
|
||||
---
|
||||
|
||||
## 🚀 下一步
|
||||
|
||||
1. **确认后端技术栈**
|
||||
- 告诉我你用什么后端,我帮你写完整的服务端代码
|
||||
|
||||
2. **集成到现有聊天页面**
|
||||
- 我帮你修改 `dialogBox.vue`
|
||||
|
||||
3. **添加更多功能**
|
||||
- 图片消息
|
||||
- 语音消息
|
||||
- 消息已读
|
||||
- 输入中状态
|
||||
|
||||
随时告诉我需要什么!🎯
|
||||
|
||||
|
|
@ -1,616 +0,0 @@
|
|||
# WebSocket 实施计划
|
||||
|
||||
## 🎯 项目信息
|
||||
|
||||
- **项目名称**:英星AI - 浙江科技大学招生咨询系统
|
||||
- **技术栈**:uni-app + Vue 2
|
||||
- **后端**:ASP.NET Core(推测)
|
||||
- **方案选择**:WebSocket 原生方案 ✅
|
||||
|
||||
---
|
||||
|
||||
## 📋 实施步骤
|
||||
|
||||
### 阶段一:前端准备(已完成 ✅)
|
||||
|
||||
#### 1. WebSocket 管理器
|
||||
- ✅ 文件:`utils/websocket-manager.js`
|
||||
- ✅ 功能:自动重连、心跳检测、消息队列、事件监听
|
||||
|
||||
#### 2. WebSocket 配置
|
||||
- ✅ 文件:`config/websocket.config.js`
|
||||
- ✅ 功能:集中管理 WebSocket 配置
|
||||
|
||||
#### 3. 使用文档
|
||||
- ✅ 文件:`docs/WebSocket使用示例.md`
|
||||
- ✅ 文件:`docs/实时通讯方案对比.md`
|
||||
- ✅ 文件:`docs/实时通讯快速选型.md`
|
||||
|
||||
---
|
||||
|
||||
### 阶段二:后端开发(待实施 ⏳)
|
||||
|
||||
#### 选项 A:ASP.NET Core WebSocket 服务(推荐)
|
||||
|
||||
**时间**:3-5 天
|
||||
|
||||
**工作量:**
|
||||
1. 创建 WebSocket 中间件(1 天)
|
||||
2. 实现消息路由(1 天)
|
||||
3. 集成用户认证(1 天)
|
||||
4. 消息持久化(1 天)
|
||||
5. 测试调试(1 天)
|
||||
|
||||
**文件清单:**
|
||||
- `WebSocketMiddleware.cs` - WebSocket 中间件
|
||||
- `WebSocketConnectionManager.cs` - 连接管理器
|
||||
- `WebSocketMessageHandler.cs` - 消息处理器
|
||||
- `ChatController.cs` - HTTP API(获取历史消息)
|
||||
- `MessageRepository.cs` - 消息存储
|
||||
|
||||
**后端代码**:见下文 `.NET Core WebSocket 服务端代码`
|
||||
|
||||
#### 选项 B:使用现有 SignalR(备选)
|
||||
|
||||
你的项目已经有 SignalR:
|
||||
```javascript
|
||||
// main.js
|
||||
var connection = new HubConnectionBuilder()
|
||||
.withUrl("http://sl.vrgon.com:8003/ChatHub")
|
||||
.build();
|
||||
```
|
||||
|
||||
**建议:**
|
||||
- 如果 SignalR 服务正常,可以继续使用
|
||||
- 如果需要切换到原生 WebSocket,按选项 A 实施
|
||||
|
||||
---
|
||||
|
||||
### 阶段三:前端集成(2-3 天)
|
||||
|
||||
#### 1. 在 App.vue 初始化 WebSocket
|
||||
|
||||
**文件**:`App.vue`
|
||||
|
||||
```javascript
|
||||
import wsManager from '@/utils/websocket-manager.js'
|
||||
import wsConfig from '@/config/websocket.config.js'
|
||||
|
||||
export default {
|
||||
onLaunch() {
|
||||
// 登录后连接 WebSocket
|
||||
if (this.vuex_token) {
|
||||
this.connectWebSocket()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
connectWebSocket() {
|
||||
// 构建 WebSocket URL(带 token)
|
||||
const wsUrl = `${wsConfig.url}?token=${this.vuex_token}`
|
||||
|
||||
// 连接
|
||||
wsManager.connect(wsUrl, {
|
||||
reconnectInterval: wsConfig.reconnect.interval,
|
||||
maxReconnectAttempts: wsConfig.reconnect.maxAttempts,
|
||||
heartbeatInterval: wsConfig.heartbeat.interval
|
||||
})
|
||||
|
||||
// 监听事件
|
||||
this.setupWebSocketListeners()
|
||||
},
|
||||
|
||||
setupWebSocketListeners() {
|
||||
// 连接成功
|
||||
wsManager.on('open', () => {
|
||||
console.log('[App] WebSocket 已连接')
|
||||
})
|
||||
|
||||
// 收到消息
|
||||
wsManager.on('message', (data) => {
|
||||
this.handleWebSocketMessage(data)
|
||||
})
|
||||
|
||||
// 连接关闭
|
||||
wsManager.on('close', () => {
|
||||
console.log('[App] WebSocket 已断开')
|
||||
})
|
||||
},
|
||||
|
||||
handleWebSocketMessage(data) {
|
||||
// 根据消息类型处理
|
||||
switch (data.type) {
|
||||
case 'message':
|
||||
// 新消息提醒
|
||||
this.$store.commit('addMessage', data)
|
||||
break
|
||||
case 'system':
|
||||
// 系统通知
|
||||
uni.showToast({ title: data.content, icon: 'none' })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 修改聊天页面
|
||||
|
||||
**文件**:`pages/message/dialogBox/dialogBox.vue`
|
||||
|
||||
需要:
|
||||
1. 导入 WebSocket 管理器
|
||||
2. 监听实时消息
|
||||
3. 发送消息通过 WebSocket
|
||||
4. 加载历史消息通过 HTTP API
|
||||
|
||||
**代码示例**:见 `docs/WebSocket使用示例.md`
|
||||
|
||||
#### 3. 添加聊天相关 API
|
||||
|
||||
**文件**:`common/http.api.js`
|
||||
|
||||
```javascript
|
||||
// 获取聊天历史记录
|
||||
let getChatHistory = (params = {}) => vm.$u.get('api/Chat/GetHistory', params);
|
||||
|
||||
// 发送离线消息(WebSocket 断开时使用)
|
||||
let sendOfflineMessage = (params = {}) => vm.$u.post('api/Chat/SendOffline', params);
|
||||
|
||||
// 标记消息已读
|
||||
let markMessageRead = (params = {}) => vm.$u.post('api/Chat/MarkRead', params);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段四:测试验证(2-3 天)
|
||||
|
||||
#### 1. 功能测试
|
||||
|
||||
- [ ] 连接建立
|
||||
- [ ] 发送文字消息
|
||||
- [ ] 接收文字消息
|
||||
- [ ] 心跳保持
|
||||
- [ ] 自动重连
|
||||
- [ ] 离线消息推送
|
||||
- [ ] 消息已读回执
|
||||
|
||||
#### 2. 场景测试
|
||||
|
||||
- [ ] 单人聊天
|
||||
- [ ] 多人并发
|
||||
- [ ] 网络断开
|
||||
- [ ] 网络恢复
|
||||
- [ ] App 切换后台
|
||||
- [ ] App 恢复前台
|
||||
- [ ] 登录/登出
|
||||
|
||||
#### 3. 性能测试
|
||||
|
||||
- [ ] 消息延迟 < 200ms
|
||||
- [ ] 重连时间 < 5s
|
||||
- [ ] 内存占用正常
|
||||
- [ ] CPU 占用正常
|
||||
- [ ] 100+ 并发用户
|
||||
|
||||
#### 4. 兼容性测试
|
||||
|
||||
- [ ] H5(Chrome、Safari)
|
||||
- [ ] 微信小程序
|
||||
- [ ] Android App
|
||||
- [ ] iOS App
|
||||
|
||||
---
|
||||
|
||||
## 🔧 后端实现
|
||||
|
||||
### .NET Core WebSocket 服务端代码
|
||||
|
||||
#### 1. WebSocket 中间件
|
||||
|
||||
**文件**:`WebSocketMiddleware.cs`
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class WebSocketMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly WebSocketConnectionManager _connectionManager;
|
||||
|
||||
public WebSocketMiddleware(RequestDelegate next, WebSocketConnectionManager connectionManager)
|
||||
{
|
||||
_next = next;
|
||||
_connectionManager = connectionManager;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
// 检查是否是 WebSocket 请求
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证 token
|
||||
var token = context.Request.Query["token"].ToString();
|
||||
var userId = ValidateToken(token);
|
||||
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("Unauthorized");
|
||||
return;
|
||||
}
|
||||
|
||||
// 接受 WebSocket 连接
|
||||
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
// 保存连接
|
||||
_connectionManager.AddConnection(userId, webSocket);
|
||||
|
||||
Console.WriteLine($"[WebSocket] 用户 {userId} 已连接");
|
||||
|
||||
try
|
||||
{
|
||||
await HandleWebSocketAsync(userId, webSocket);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 移除连接
|
||||
_connectionManager.RemoveConnection(userId);
|
||||
Console.WriteLine($"[WebSocket] 用户 {userId} 已断开");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleWebSocketAsync(string userId, WebSocket webSocket)
|
||||
{
|
||||
var buffer = new byte[1024 * 4];
|
||||
|
||||
while (webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
var result = await webSocket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await webSocket.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
"关闭连接",
|
||||
CancellationToken.None
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// 解析消息
|
||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
await HandleMessageAsync(userId, message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleMessageAsync(string userId, string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<WebSocketMessage>(message);
|
||||
|
||||
switch (data.Type)
|
||||
{
|
||||
case "message":
|
||||
// 处理聊天消息
|
||||
await HandleChatMessageAsync(userId, data);
|
||||
break;
|
||||
|
||||
case "ping":
|
||||
// 处理心跳
|
||||
await SendToUserAsync(userId, new
|
||||
{
|
||||
type = "pong",
|
||||
timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
|
||||
});
|
||||
break;
|
||||
|
||||
case "read":
|
||||
// 处理已读回执
|
||||
await HandleReadReceiptAsync(userId, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.WriteLine($"未知消息类型: {data.Type}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"消息处理失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleChatMessageAsync(string fromUserId, WebSocketMessage data)
|
||||
{
|
||||
var toUserId = data.ToUserId;
|
||||
var content = data.Content;
|
||||
|
||||
// 保存消息到数据库
|
||||
var messageId = await SaveMessageAsync(fromUserId, toUserId, content);
|
||||
|
||||
// 构建消息对象
|
||||
var messageObj = new
|
||||
{
|
||||
type = "message",
|
||||
messageId = messageId,
|
||||
fromUserId = fromUserId,
|
||||
toUserId = toUserId,
|
||||
content = content,
|
||||
timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
|
||||
};
|
||||
|
||||
// 发送给接收者
|
||||
await SendToUserAsync(toUserId, messageObj);
|
||||
|
||||
// 发送给发送者(确认消息已发送)
|
||||
await SendToUserAsync(fromUserId, messageObj);
|
||||
}
|
||||
|
||||
private async Task<string> SaveMessageAsync(string fromUserId, string toUserId, string content)
|
||||
{
|
||||
// TODO: 保存消息到数据库
|
||||
// 返回消息 ID
|
||||
return Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
private async Task HandleReadReceiptAsync(string userId, WebSocketMessage data)
|
||||
{
|
||||
// TODO: 更新消息已读状态
|
||||
Console.WriteLine($"用户 {userId} 已读消息 {data.MessageId}");
|
||||
}
|
||||
|
||||
private async Task SendToUserAsync(string userId, object message)
|
||||
{
|
||||
var webSocket = _connectionManager.GetConnection(userId);
|
||||
if (webSocket != null && webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(message);
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
await webSocket.SendAsync(
|
||||
new ArraySegment<byte>(bytes),
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private string ValidateToken(string token)
|
||||
{
|
||||
// TODO: 验证 JWT token,返回用户 ID
|
||||
// 这里简化处理,直接返回一个用户 ID
|
||||
return "user123";
|
||||
}
|
||||
}
|
||||
|
||||
// 消息实体
|
||||
public class WebSocketMessage
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string FromUserId { get; set; }
|
||||
public string ToUserId { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string MessageId { get; set; }
|
||||
public long Timestamp { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 连接管理器
|
||||
|
||||
**文件**:`WebSocketConnectionManager.cs`
|
||||
|
||||
```csharp
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.WebSockets;
|
||||
|
||||
public class WebSocketConnectionManager
|
||||
{
|
||||
// 存储所有连接(用户 ID -> WebSocket)
|
||||
private readonly ConcurrentDictionary<string, WebSocket> _connections =
|
||||
new ConcurrentDictionary<string, WebSocket>();
|
||||
|
||||
// 添加连接
|
||||
public void AddConnection(string userId, WebSocket webSocket)
|
||||
{
|
||||
_connections.TryAdd(userId, webSocket);
|
||||
}
|
||||
|
||||
// 移除连接
|
||||
public void RemoveConnection(string userId)
|
||||
{
|
||||
_connections.TryRemove(userId, out _);
|
||||
}
|
||||
|
||||
// 获取连接
|
||||
public WebSocket GetConnection(string userId)
|
||||
{
|
||||
_connections.TryGetValue(userId, out var webSocket);
|
||||
return webSocket;
|
||||
}
|
||||
|
||||
// 获取所有在线用户
|
||||
public IEnumerable<string> GetOnlineUsers()
|
||||
{
|
||||
return _connections.Keys;
|
||||
}
|
||||
|
||||
// 获取在线用户数
|
||||
public int GetOnlineCount()
|
||||
{
|
||||
return _connections.Count;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 注册中间件
|
||||
|
||||
**文件**:`Startup.cs`
|
||||
|
||||
```csharp
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// 注册连接管理器为单例
|
||||
services.AddSingleton<WebSocketConnectionManager>();
|
||||
|
||||
// 其他服务...
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
// 启用 WebSocket
|
||||
app.UseWebSockets(new WebSocketOptions
|
||||
{
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(30)
|
||||
});
|
||||
|
||||
// 注册 WebSocket 中间件
|
||||
app.Map("/ws/chat", builder =>
|
||||
{
|
||||
builder.UseMiddleware<WebSocketMiddleware>();
|
||||
});
|
||||
|
||||
// 其他中间件...
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. HTTP API(获取历史消息)
|
||||
|
||||
**文件**:`ChatController.cs`
|
||||
|
||||
```csharp
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ChatController : ControllerBase
|
||||
{
|
||||
// 获取聊天历史
|
||||
[HttpGet("GetHistory")]
|
||||
public async Task<IActionResult> GetHistory(string userId, int page = 1, int pageSize = 50)
|
||||
{
|
||||
// TODO: 从数据库查询历史消息
|
||||
var messages = new List<object>
|
||||
{
|
||||
new
|
||||
{
|
||||
messageId = "msg001",
|
||||
fromUserId = "user123",
|
||||
toUserId = userId,
|
||||
content = "你好",
|
||||
timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
success = true,
|
||||
data = messages,
|
||||
total = messages.Count
|
||||
});
|
||||
}
|
||||
|
||||
// 发送离线消息
|
||||
[HttpPost("SendOffline")]
|
||||
public async Task<IActionResult> SendOffline([FromBody] SendMessageRequest request)
|
||||
{
|
||||
// TODO: 保存离线消息到数据库
|
||||
return Ok(new { success = true });
|
||||
}
|
||||
|
||||
// 标记已读
|
||||
[HttpPost("MarkRead")]
|
||||
public async Task<IActionResult> MarkRead([FromBody] MarkReadRequest request)
|
||||
{
|
||||
// TODO: 更新消息已读状态
|
||||
return Ok(new { success = true });
|
||||
}
|
||||
}
|
||||
|
||||
public class SendMessageRequest
|
||||
{
|
||||
public string ToUserId { get; set; }
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
public class MarkReadRequest
|
||||
{
|
||||
public string MessageId { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 时间安排
|
||||
|
||||
| 阶段 | 工作内容 | 预计时间 | 负责人 |
|
||||
|------|----------|----------|--------|
|
||||
| **阶段一** | 前端准备 | ✅ 已完成 | 前端 |
|
||||
| **阶段二** | 后端开发 | 3-5 天 | 后端 |
|
||||
| **阶段三** | 前端集成 | 2-3 天 | 前端 |
|
||||
| **阶段四** | 测试验证 | 2-3 天 | 全员 |
|
||||
| **总计** | | **7-11 天** | |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步行动
|
||||
|
||||
### 立即可做(前端)
|
||||
|
||||
1. **在 App.vue 中初始化 WebSocket**
|
||||
- 导入 `websocket-manager.js`
|
||||
- 在 `onLaunch` 中调用 `wsManager.connect()`
|
||||
- 设置事件监听
|
||||
|
||||
2. **测试 WebSocket 管理器**
|
||||
- 使用在线 WebSocket 测试服务(如 `wss://echo.websocket.org`)
|
||||
- 验证连接、发送、接收功能
|
||||
|
||||
### 需要后端配合
|
||||
|
||||
1. **确认后端技术栈**
|
||||
- 是否是 ASP.NET Core?
|
||||
- 后端开发人员是谁?
|
||||
|
||||
2. **部署 WebSocket 服务**
|
||||
- 使用上面提供的 .NET Core 代码
|
||||
- 或者告诉我你的后端技术,我提供对应代码
|
||||
|
||||
3. **配置服务器**
|
||||
- 确保服务器支持 WebSocket
|
||||
- 配置 Nginx 反向代理(如果需要)
|
||||
|
||||
---
|
||||
|
||||
## 📞 联系与支持
|
||||
|
||||
如果你需要:
|
||||
1. ✅ 其他后端语言的 WebSocket 服务端代码(Node.js/Java/Go/Python)
|
||||
2. ✅ 帮助修改 `dialogBox.vue` 集成 WebSocket
|
||||
3. ✅ Nginx 配置 WebSocket 反向代理
|
||||
4. ✅ 调试和测试支持
|
||||
|
||||
随时告诉我!🚀
|
||||
|
||||
168
pages.json
|
|
@ -27,7 +27,20 @@
|
|||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "pages/notes/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "留言板",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/my/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/my/personalInfo",
|
||||
"style": {
|
||||
|
|
@ -38,7 +51,7 @@
|
|||
{
|
||||
"path": "pages/home/admissions/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "在线咨询",
|
||||
"navigationBarTitleText": "招办在线",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
|
|
@ -52,28 +65,13 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/consultation/index",
|
||||
"path": "pages/home/conversations/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "在线咨询",
|
||||
"navigationBarTitleText": "会话列表",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/transfer/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "人工转接",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/my/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login/index",
|
||||
"style": {
|
||||
|
|
@ -91,18 +89,138 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/home/history/history",
|
||||
"path": "pages/login/login/login",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/register/register",
|
||||
"style": {
|
||||
"navigationBarTitleText": "注册",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/perfect/perfect",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/graduateCertification/graduateCertification",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/msgList/msgList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/dialogBox/dialogBox",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/interactionList/interactionList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "互动消息",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/sysList/sysList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "系统消息",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/attentionList/attentionList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新增关注",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/message/adminList/adminList",
|
||||
"style": {
|
||||
"navigationBarTitleText": "管理列表",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/ForgetPassword/ForgetPassword",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/confirmPwd/confirmPwd",
|
||||
"style": {
|
||||
"navigationBarTitleText": "确认密码",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/roleSelection",
|
||||
"style": {
|
||||
"navigationBarTitleText": "角色选择",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/teacherCertification",
|
||||
"style": {
|
||||
"navigationBarTitleText": "教职工认证",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/recognitionResult/recognitionResult",
|
||||
"style": {
|
||||
"navigationBarTitleText": "提示",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/recognitionResult/recognitionFailed",
|
||||
"style": {
|
||||
"navigationBarTitleText": "认证失败",
|
||||
"enablePullDownRefresh": false,
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
"subPackages": [],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "源小新",
|
||||
"navigationBarTitleText": "英星AI",
|
||||
"navigationBarBackgroundColor": "#F8F8F8",
|
||||
"backgroundColor": "#F8F8F8"
|
||||
},
|
||||
|
|
@ -113,16 +231,16 @@
|
|||
"backgroundColor": "#ffffff",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/consultation/index",
|
||||
"pagePath": "pages/home/conversations/index",
|
||||
"iconPath": "static/tabbar/icon_home.png",
|
||||
"selectedIconPath": "static/tabbar/icon_home_active.png",
|
||||
"text": "在线咨询"
|
||||
"text": "会话列表"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/transfer/index",
|
||||
"pagePath": "pages/notes/index",
|
||||
"iconPath": "static/tabbar/icon_message.png",
|
||||
"selectedIconPath": "static/tabbar/icon_message_active.png",
|
||||
"text": "人工转接"
|
||||
"text": "留言板"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/my/index",
|
||||
|
|
|
|||
|
|
@ -1,224 +0,0 @@
|
|||
<template>
|
||||
<view class="page-container">
|
||||
<view class="page-header">
|
||||
<PageHeader title="在线咨询" :is-back="false" :border-bottom="false" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="page-main"
|
||||
scroll-y
|
||||
enable-back-to-top
|
||||
>
|
||||
<!-- 内容内层 - 添加内边距和安全区域 -->
|
||||
<view class="main-content">
|
||||
<!-- 咨询入口列表 -->
|
||||
<view
|
||||
class="consultation-item"
|
||||
v-for="(item, index) in consultationList"
|
||||
:key="index"
|
||||
@click="handleConsultation(item)"
|
||||
>
|
||||
<view class="item-left">
|
||||
<image class="item-icon" :src="item.icon"></image>
|
||||
<view class="item-info">
|
||||
<text class="item-title">{{ item.title }}</text>
|
||||
<text class="item-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" color="#999" size="20"></u-icon>
|
||||
</view>
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<view class="empty-tip" v-if="consultationList.length === 0">
|
||||
暂无咨询服务
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="page-tabbar">
|
||||
<TabBar :currentPath="'/pages/consultation/index'" @change="handleTabChange" />
|
||||
</view>
|
||||
|
||||
<u-modal
|
||||
v-model="showModal"
|
||||
:show-cancel-button="false"
|
||||
title="提示"
|
||||
:content="modalContent"
|
||||
@confirm="showModal = false"
|
||||
></u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabBar from "@/components/TabBar-optimized.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
|
||||
export default {
|
||||
name: "ConsultationPage",
|
||||
components: {
|
||||
TabBar,
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
modalContent: '',
|
||||
consultationList: [
|
||||
{
|
||||
id: 1,
|
||||
title: "智能问答",
|
||||
desc: "AI智能机器人为您解答",
|
||||
icon: "/static/common/icon/robot.png",
|
||||
type: "ai"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "招生咨询",
|
||||
desc: "招生相关问题咨询",
|
||||
icon: "/static/common/icon/admissions.png",
|
||||
type: "admissions"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "教务咨询",
|
||||
desc: "教务相关问题咨询",
|
||||
icon: "/static/common/icon/academic.png",
|
||||
type: "academic"
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleTabChange(path, index) {
|
||||
console.log("切换到标签页:", path, index);
|
||||
},
|
||||
handleConsultation(item) {
|
||||
// 这里可以跳转到具体的咨询页面
|
||||
this.modalContent = `即将进入${item.title}`;
|
||||
this.showModal = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ===== 页面容器 - 主流三段式布局 ===== */
|
||||
.page-container {
|
||||
/* 固定定位,占满整个视口 */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
/* Flex 布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* 背景色 */
|
||||
background-color: #f5f6fa;
|
||||
|
||||
/* 防止溢出 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ===== 头部导航 ===== */
|
||||
.page-header {
|
||||
/* 不收缩,固定高度 */
|
||||
flex-shrink: 0;
|
||||
|
||||
/* 层级 */
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* ===== 内容区域 ===== */
|
||||
.page-main {
|
||||
/* 占据剩余空间 */
|
||||
flex: 1;
|
||||
|
||||
/* 重要:防止 flex 子元素溢出 */
|
||||
height: 0;
|
||||
|
||||
/* 允许滚动 */
|
||||
overflow-y: auto;
|
||||
|
||||
/* iOS 滚动优化 */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* ===== 内容内层 ===== */
|
||||
.main-content {
|
||||
/* 内边距 */
|
||||
padding: 10px;
|
||||
|
||||
/* 底部留出 TabBar 空间 + 安全区域 */
|
||||
padding-bottom: calc(50px + env(safe-area-inset-bottom) + 10px);
|
||||
|
||||
/* 最小高度(确保可以滚动) */
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* ===== 底部导航 ===== */
|
||||
.page-tabbar {
|
||||
/* 不收缩,固定高度 */
|
||||
flex-shrink: 0;
|
||||
|
||||
/* 层级 */
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.consultation-item {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.consultation-item:active {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-right: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 50px 20px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -1,26 +1,23 @@
|
|||
<template>
|
||||
<view class="admissions-container">
|
||||
<header-bar title="在线咨询" @leftClick="handleLeftClick"></header-bar>
|
||||
<view class="header">
|
||||
<div class="header-left">
|
||||
<u-icon
|
||||
class="header-left-icon"
|
||||
name="arrow-left"
|
||||
@click="handleLeftClick"
|
||||
></u-icon>
|
||||
</div>
|
||||
<text class="header-title">招办在线</text>
|
||||
<div></div>
|
||||
</view>
|
||||
|
||||
<view class="admissions-content">
|
||||
<!-- 自定义tab -->
|
||||
<view class="custom-tab">
|
||||
<view
|
||||
v-for="tab in tabList"
|
||||
:key="tab.id"
|
||||
:class="['tab-item', { active: activeTab === tab.id }]"
|
||||
@click="switchTab(tab.id)"
|
||||
>
|
||||
<text class="tab-text">{{ tab.name }}</text>
|
||||
<view class="tab-underline" v-if="activeTab === tab.id"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="teacher-list">
|
||||
<!-- 教师列表 -->
|
||||
<view
|
||||
class="teacher-item"
|
||||
v-for="(teacher, index) in currentList"
|
||||
v-for="(teacher, index) in teacherList"
|
||||
:key="index"
|
||||
>
|
||||
<image class="teacher-avatar" :src="teacher.avatar"></image>
|
||||
|
|
@ -35,9 +32,9 @@
|
|||
</view>
|
||||
<view class="teacher-department">{{ teacher.department }}</view>
|
||||
</view>
|
||||
<view class="ask-button" @click="handleAskQuestion(teacher)">
|
||||
立即提问
|
||||
</view>
|
||||
<view class="ask-button" @click="handleAskQuestion(teacher)">{{
|
||||
teacher.online ? "立即提问" : "留言"
|
||||
}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -53,21 +50,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import HeaderBar from "@/components/HeaderBar.vue"; // 导入头部组件
|
||||
import LeaveMessage from "@/components/LeaveMessage.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HeaderBar, // 注册头部组件
|
||||
LeaveMessage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: "1", // 当前激活的tab
|
||||
tabList: [
|
||||
{ id: "1", name: "招生在线", key: "admissions" },
|
||||
{ id: "2", name: "迎新在线", key: "welcome" },
|
||||
],
|
||||
showLeaveMessage: false,
|
||||
teacherList: [
|
||||
{
|
||||
|
|
@ -99,41 +89,10 @@ export default {
|
|||
online: false,
|
||||
},
|
||||
],
|
||||
welcomeList: [
|
||||
{
|
||||
id: 5,
|
||||
name: "李老师",
|
||||
department: "学生处",
|
||||
avatar: "/static/common/images/avatar.png",
|
||||
online: true,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "张老师",
|
||||
department: "宿管中心",
|
||||
avatar: "/static/common/images/student.png",
|
||||
online: true,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "迎新办陈老师",
|
||||
department: "后勤服务中心",
|
||||
avatar: "/static/common/images/student.png",
|
||||
online: false,
|
||||
},
|
||||
],
|
||||
currentTeacher: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentList() {
|
||||
return this.activeTab === "1" ? this.teacherList : this.welcomeList;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
switchTab(tab) {
|
||||
this.activeTab = tab;
|
||||
},
|
||||
handleLeftClick() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
|
|
@ -200,44 +159,6 @@ export default {
|
|||
}
|
||||
|
||||
.admissions-content {
|
||||
.custom-tab {
|
||||
padding: 0 30rpx;
|
||||
margin: 24rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12rpx 0;
|
||||
margin-right: 60rpx;
|
||||
cursor: pointer;
|
||||
|
||||
.tab-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #505866;
|
||||
letter-spacing: 0.04rpx;
|
||||
}
|
||||
|
||||
&.active .tab-text {
|
||||
color: #4f6aff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-underline {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
right: 0;
|
||||
width: 80rpx;
|
||||
height: 4rpx;
|
||||
background-color: #4f6aff;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.teacher-list {
|
||||
.teacher-item {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,655 +0,0 @@
|
|||
<template>
|
||||
<view class="history-page">
|
||||
<header-bar title="历史记录" @leftClick="handleLeftClick"></header-bar>
|
||||
|
||||
<view class="tab-container">
|
||||
<view class="custom-tab">
|
||||
<view
|
||||
v-for="tab in tabList"
|
||||
:key="tab.id"
|
||||
:class="['tab-item', { active: activeTab === tab.id }]"
|
||||
@click="switchTab(tab.id)"
|
||||
>
|
||||
<text class="tab-text">{{ tab.name }}</text>
|
||||
<view class="tab-underline" v-if="activeTab === tab.id"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="more-icon-container" @click="toggleMoreMenu">
|
||||
<image
|
||||
class="more-icon"
|
||||
src="/static/common/images/icon-more.png"
|
||||
mode="scaleToFill"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
<!-- 右上角弹出菜单:批量删除 -->
|
||||
<view v-if="isMoreMenuVisible" class="more-menu" @click.stop>
|
||||
<view class="menu-card" @click="handleBatchDeleteClick">
|
||||
<!-- <image
|
||||
class="menu-icon"
|
||||
src="/static/common/images/icon_delete.png"
|
||||
mode="widthFix"
|
||||
/> -->
|
||||
<u-icon name="trash" color="#FF647D" size="28"></u-icon>
|
||||
<text class="menu-text">批量删除</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 点击其他区域关闭弹层 -->
|
||||
<view v-if="isMoreMenuVisible" class="overlay" @click="hideMoreMenu"></view>
|
||||
|
||||
<!-- 内容区域包装器 -->
|
||||
<view class="content-wrapper">
|
||||
<!-- 历史记录列表(按日期分组,日期固定在上方) -->
|
||||
<view class="history-list">
|
||||
<view
|
||||
class="date-group"
|
||||
v-for="group in groupedHistoryList"
|
||||
:key="group.header"
|
||||
>
|
||||
<view class="date-header">{{ group.header }}</view>
|
||||
|
||||
<view class="history-item" v-for="item in group.items" :key="item.id">
|
||||
<!-- 批量删除模式下的选择框 -->
|
||||
<view
|
||||
class="item-checkbox"
|
||||
v-if="isBatchDeleteMode"
|
||||
@click="toggleItemSelection(item.id)"
|
||||
>
|
||||
<view
|
||||
class="checkbox"
|
||||
:class="{ checked: selectedItems.includes(item.id) }"
|
||||
>
|
||||
<text class="checkmark" v-if="selectedItems.includes(item.id)"
|
||||
>✓</text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 历史记录内容 -->
|
||||
<view class="item-content">
|
||||
<view class="item-header">
|
||||
<view class="item-icon">
|
||||
<text class="icon-text">B</text>
|
||||
</view>
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
</view>
|
||||
<view class="item-description">{{ item.content }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 批量删除操作栏 -->
|
||||
<view class="batch-actions" v-if="isBatchDeleteMode">
|
||||
<view class="select-all-container" @click="toggleSelectAll">
|
||||
<view class="checkbox" :class="{ checked: selectAll }">
|
||||
<text class="checkmark" v-if="selectAll">✓</text>
|
||||
</view>
|
||||
<text class="select-all-text">全选</text>
|
||||
</view>
|
||||
<view class="function-btn">
|
||||
<view class="cancel-btn" @click="cancelBatchDelete">取消</view>
|
||||
<view class="delete-btn" @click="batchDelete">删除</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提示 -->
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
HeaderBar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: "1", // 当前激活的tab
|
||||
tabList: [
|
||||
{ id: "1", name: "AI咨询" },
|
||||
{ id: "2", name: "人工咨询" },
|
||||
],
|
||||
isBatchDeleteMode: false, // 是否为批量删除模式
|
||||
isMoreMenuVisible: false, // 右上角更多菜单是否可见
|
||||
selectedItems: [], // 选中的项目
|
||||
selectAll: false, // 是否全选
|
||||
historyList: [
|
||||
{
|
||||
id: 1,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime(), // 今天
|
||||
type: "ai",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime(), // 今天
|
||||
type: "ai",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime(), // 今天
|
||||
type: "ai",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime(), // 今天
|
||||
type: "ai",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime() - 24 * 60 * 60 * 1000 * 2, // 2天前
|
||||
type: "ai",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime() - 365 * 24 * 60 * 60 * 1000, // 去年
|
||||
type: "human",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "学校哪些专业比较好?",
|
||||
content:
|
||||
"学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?学校哪些专业比较好?",
|
||||
time: new Date().getTime() - 24 * 60 * 60 * 1000 * 5, // 5天前
|
||||
type: "human",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 根据当前tab过滤历史记录
|
||||
filteredHistoryList() {
|
||||
return this.historyList.filter((item) => {
|
||||
if (this.activeTab === "1") {
|
||||
return item.type === "ai";
|
||||
} else {
|
||||
return item.type === "human";
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 按日期分组后的历史记录(日期标题固定在上方)
|
||||
groupedHistoryList() {
|
||||
// 先按时间倒序
|
||||
const list = [...this.filteredHistoryList].sort(
|
||||
(a, b) => b.time - a.time
|
||||
);
|
||||
const groups = [];
|
||||
let currentHeader = null;
|
||||
let currentItems = [];
|
||||
|
||||
list.forEach((item) => {
|
||||
const header = this.formatTime(item.time);
|
||||
if (header !== currentHeader) {
|
||||
if (currentItems.length) {
|
||||
groups.push({ header: currentHeader, items: currentItems });
|
||||
}
|
||||
currentHeader = header;
|
||||
currentItems = [item];
|
||||
} else {
|
||||
currentItems.push(item);
|
||||
}
|
||||
});
|
||||
if (currentItems.length) {
|
||||
groups.push({ header: currentHeader, items: currentItems });
|
||||
}
|
||||
return groups;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 格式化时间显示
|
||||
formatTime(timestamp) {
|
||||
const now = new Date();
|
||||
const date = new Date(timestamp);
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const itemDate = new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate()
|
||||
);
|
||||
|
||||
// 计算时间差
|
||||
const diffTime = today.getTime() - itemDate.getTime();
|
||||
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
||||
|
||||
// 今天
|
||||
if (diffDays === 0) {
|
||||
return `${String(date.getMonth() + 1).padStart(2, "0")}/${String(
|
||||
date.getDate()
|
||||
).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// 本周内(1-6天前)
|
||||
if (diffDays > 0 && diffDays <= 6) {
|
||||
const weekdays = [
|
||||
"周日",
|
||||
"周一",
|
||||
"周二",
|
||||
"周三",
|
||||
"周四",
|
||||
"周五",
|
||||
"周六",
|
||||
];
|
||||
return weekdays[date.getDay()];
|
||||
}
|
||||
|
||||
// 今年但不是本周
|
||||
if (date.getFullYear() === now.getFullYear()) {
|
||||
return `${String(date.getMonth() + 1).padStart(2, "0")}/${String(
|
||||
date.getDate()
|
||||
).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// 往年
|
||||
return `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(
|
||||
2,
|
||||
"0"
|
||||
)}/${String(date.getDate()).padStart(2, "0")}`;
|
||||
},
|
||||
|
||||
handleLeftClick() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
|
||||
switchTab(tab) {
|
||||
this.activeTab = tab;
|
||||
},
|
||||
|
||||
// 显示/隐藏右上角更多菜单
|
||||
toggleMoreMenu() {
|
||||
this.isMoreMenuVisible = !this.isMoreMenuVisible;
|
||||
},
|
||||
// 点击蒙层隐藏更多菜单
|
||||
hideMoreMenu() {
|
||||
this.isMoreMenuVisible = false;
|
||||
},
|
||||
// 取消批量删除模式
|
||||
cancelBatchDelete() {
|
||||
this.isBatchDeleteMode = false;
|
||||
},
|
||||
|
||||
// 点击“批量删除”菜单项,进入批量删除模式
|
||||
handleBatchDeleteClick() {
|
||||
this.isMoreMenuVisible = false;
|
||||
if (!this.isBatchDeleteMode) {
|
||||
this.toggleBatchDeleteMode();
|
||||
}
|
||||
},
|
||||
|
||||
// 切换批量删除模式
|
||||
toggleBatchDeleteMode() {
|
||||
this.isBatchDeleteMode = !this.isBatchDeleteMode;
|
||||
if (!this.isBatchDeleteMode) {
|
||||
// 退出批量删除模式时清空选中项
|
||||
this.selectedItems = [];
|
||||
this.selectAll = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 切换单个项目选中状态
|
||||
toggleItemSelection(itemId) {
|
||||
const index = this.selectedItems.indexOf(itemId);
|
||||
if (index > -1) {
|
||||
this.selectedItems.splice(index, 1);
|
||||
} else {
|
||||
this.selectedItems.push(itemId);
|
||||
}
|
||||
// 更新全选状态
|
||||
this.selectAll =
|
||||
this.selectedItems.length === this.filteredHistoryList.length;
|
||||
},
|
||||
|
||||
// 切换全选状态
|
||||
toggleSelectAll() {
|
||||
this.selectAll = !this.selectAll;
|
||||
if (this.selectAll) {
|
||||
this.selectedItems = this.filteredHistoryList.map((item) => item.id);
|
||||
} else {
|
||||
this.selectedItems = [];
|
||||
}
|
||||
},
|
||||
|
||||
// 批量删除选中项
|
||||
batchDelete() {
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.$refs.uToast.show({
|
||||
title: "请选择要删除的项目",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除选中的项目
|
||||
this.historyList = this.historyList.filter(
|
||||
(item) => !this.selectedItems.includes(item.id)
|
||||
);
|
||||
|
||||
// 重置状态
|
||||
this.selectedItems = [];
|
||||
this.selectAll = false;
|
||||
this.isBatchDeleteMode = false;
|
||||
|
||||
this.$refs.uToast.show({
|
||||
title: "删除成功",
|
||||
type: "success",
|
||||
});
|
||||
},
|
||||
|
||||
handleDelete(item, callback) {
|
||||
console.log("handleDelete", item);
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.uToast.show({
|
||||
title: "撤回成功",
|
||||
type: "success",
|
||||
});
|
||||
callback(true);
|
||||
}, 1500);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.history-page {
|
||||
height: 100vh;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
padding-top: 88rpx;
|
||||
|
||||
.tab-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-tab {
|
||||
padding: 0 30rpx;
|
||||
margin: 24rpx 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.tab-item {
|
||||
position: relative;
|
||||
padding: 12rpx 0;
|
||||
margin-right: 80rpx;
|
||||
cursor: pointer;
|
||||
|
||||
.tab-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #505866;
|
||||
letter-spacing: 0.04rpx;
|
||||
}
|
||||
|
||||
&.active .tab-text {
|
||||
color: #4f6aff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-underline {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
right: 0;
|
||||
width: 80rpx;
|
||||
height: 4rpx;
|
||||
background-color: #4f6aff;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-icon-container {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
margin-right: 30rpx;
|
||||
|
||||
.more-icon {
|
||||
width: 50rpx;
|
||||
height: 10rpx;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 右上角更多菜单弹层 */
|
||||
.more-menu {
|
||||
position: absolute;
|
||||
top: 80rpx;
|
||||
right: 30rpx;
|
||||
z-index: 100;
|
||||
background-color: #ffffff;
|
||||
border: 1rpx solid rgba(79, 106, 255, 0.12);
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
padding: 24rpx 28rpx;
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
|
||||
// .menu-icon {
|
||||
// color: #FF647D;
|
||||
// width: 32rpx;
|
||||
// height: 32rpx;
|
||||
// margin-right: 12rpx;
|
||||
// }
|
||||
|
||||
.menu-text {
|
||||
font-size: 28rpx;
|
||||
color: #ff647d;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 点击其它区域关闭的遮罩(透明) */
|
||||
.overlay {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding: 30rpx;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
.date-group {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.date-header {
|
||||
font-size: 26rpx;
|
||||
color: #9aa0a8;
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 24rpx 0;
|
||||
|
||||
.item-checkbox {
|
||||
margin-right: 24rpx;
|
||||
padding-top: 8rpx;
|
||||
cursor: pointer;
|
||||
width: 48rpx;
|
||||
flex: 0 0 48rpx; /* 固定宽度,避免内容因显隐而抖动 */
|
||||
|
||||
.checkbox {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 2rpx solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #ffffff;
|
||||
|
||||
&.checked {
|
||||
background-color: #ff4757;
|
||||
border-color: #ff4757;
|
||||
|
||||
.checkmark {
|
||||
color: #ffffff;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.item-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background-color: #4f6aff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16rpx;
|
||||
|
||||
.icon-text {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.item-description {
|
||||
letter-spacing: 0.04rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 12rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.batch-actions {
|
||||
height: 130rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 30rpx;
|
||||
border-top: 1rpx solid #eeeeee;
|
||||
|
||||
.select-all-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
.checkbox {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 2rpx solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #ffffff;
|
||||
|
||||
&.checked {
|
||||
background-color: #ff4757;
|
||||
border-color: #ff4757;
|
||||
|
||||
.checkmark {
|
||||
color: #ffffff;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select-all-text {
|
||||
margin-left: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.function-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #e5e3e3e4;
|
||||
color: #333333;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 28rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
.delete-btn {
|
||||
background-color: #ff4757;
|
||||
color: #ffffff;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 28rpx;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
>
|
||||
</view>
|
||||
|
||||
<view class="qa-section">
|
||||
<!-- <view class="qa-section">
|
||||
<view class="qa-header">
|
||||
<text class="qa-title">大家都在问</text>
|
||||
<view class="more-link">
|
||||
|
|
@ -37,6 +37,16 @@
|
|||
</view>
|
||||
</view>
|
||||
|
||||
<button class="chat-button" @click="isChat = true">
|
||||
<image
|
||||
class="chat-icon"
|
||||
src="/static/common/images/icon_chat.png"
|
||||
></image>
|
||||
<text class="chat-text">开启对话</text>
|
||||
</button>
|
||||
</view> -->
|
||||
|
||||
<view class="start-chat">
|
||||
<button class="chat-button" @click="handleStartChat">
|
||||
<image
|
||||
class="chat-icon"
|
||||
|
|
@ -51,9 +61,6 @@
|
|||
class="feature-item"
|
||||
v-for="(item, index) in features"
|
||||
:key="index"
|
||||
:style="{
|
||||
background: item.background,
|
||||
}"
|
||||
@click="handleFeatureClick(item)"
|
||||
>
|
||||
<image :src="item.icon" class="feature-icon"></image>
|
||||
|
|
@ -104,7 +111,7 @@
|
|||
<view class="loading-more" v-if="isLoading">
|
||||
<u-loading mode="circle" color="#4370fe"></u-loading>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 到顶部提示 -->
|
||||
<view class="no-more-data" v-if="noMoreData">
|
||||
<text>已经到顶了</text>
|
||||
|
|
@ -272,7 +279,7 @@ export default {
|
|||
isLoadingMore: false, // 是否正在加载更多的标志位
|
||||
noMoreData: false, // 是否已加载全部历史消息
|
||||
isSwitchingConversation: false, // 是否正在切换对话的标志位
|
||||
|
||||
|
||||
pageQuery: {
|
||||
PageIndex: 1,
|
||||
PageSize: 20,
|
||||
|
|
@ -287,20 +294,18 @@ export default {
|
|||
],
|
||||
features: [
|
||||
{
|
||||
title: "在线咨询",
|
||||
title: "招办在线",
|
||||
icon: "/static/common/images/icon_admissions.png",
|
||||
path: "/pages/home/admissions/index",
|
||||
background: "linear-gradient(0deg, #F4FBFE 0%, #F4FBFE 100%)",
|
||||
},
|
||||
// {
|
||||
// title: "留言板",
|
||||
// icon: "/static/common/images/icon_messageBoard.png",
|
||||
// path: "/pages/home/messageBoard/index",
|
||||
// },
|
||||
{
|
||||
title: "留言板",
|
||||
icon: "/static/common/images/icon_messageBoard.png",
|
||||
path: "/pages/home/messageBoard/index",
|
||||
},
|
||||
{
|
||||
title: "电话咨询",
|
||||
icon: "/static/common/images/icon_phone.png",
|
||||
background: "linear-gradient(0deg, #F4FBF9 0%, #F4FBF9 100%)",
|
||||
},
|
||||
],
|
||||
popupShow: false,
|
||||
|
|
@ -500,7 +505,7 @@ export default {
|
|||
// 添加到消息列表
|
||||
this.messageGroups.push(userMessage);
|
||||
this.messageValue = "";
|
||||
|
||||
|
||||
// 立即添加一个AI回复的加载状态消息
|
||||
const loadingMessage = {
|
||||
id: "loading_" + Math.random().toString(36).substring(2, 15),
|
||||
|
|
@ -514,7 +519,7 @@ export default {
|
|||
displayTime: "",
|
||||
isLoading: true, // 标记为加载状态
|
||||
};
|
||||
|
||||
|
||||
// 添加加载状态消息到列表
|
||||
this.messageGroups.push(loadingMessage);
|
||||
|
||||
|
|
@ -554,12 +559,12 @@ export default {
|
|||
})
|
||||
.catch((error) => {
|
||||
console.error("API请求失败:", error);
|
||||
|
||||
|
||||
// 从消息列表中移除加载状态消息
|
||||
this.messageGroups = this.messageGroups.filter(
|
||||
(msg) => !msg.isLoading
|
||||
);
|
||||
|
||||
|
||||
// 添加错误消息
|
||||
const errorMessage = {
|
||||
id: "error_" + Math.random().toString(36).substring(2, 15),
|
||||
|
|
@ -572,7 +577,7 @@ export default {
|
|||
timeLabel: 0,
|
||||
displayTime: "",
|
||||
};
|
||||
|
||||
|
||||
this.messageGroups.push(errorMessage);
|
||||
});
|
||||
},
|
||||
|
|
@ -640,18 +645,18 @@ export default {
|
|||
// 关闭弹窗
|
||||
this.popupShow = false;
|
||||
this.isChat = true;
|
||||
|
||||
|
||||
// 设置切换对话标志位,防止触发上拉刷新
|
||||
this.isSwitchingConversation = true;
|
||||
|
||||
|
||||
this.currentConversationId = "";
|
||||
this.messageGroups = [];
|
||||
|
||||
|
||||
// 重置分页和加载状态
|
||||
this.pageQuery.PageIndex = 1;
|
||||
this.isLoadingMore = false;
|
||||
this.noMoreData = false;
|
||||
|
||||
|
||||
// 延迟重置切换对话标志位
|
||||
setTimeout(() => {
|
||||
this.isSwitchingConversation = false;
|
||||
|
|
@ -661,18 +666,18 @@ export default {
|
|||
// 开始新对话
|
||||
handleStartChat() {
|
||||
this.isChat = true;
|
||||
|
||||
|
||||
// 设置切换对话标志位,防止触发上拉刷新
|
||||
this.isSwitchingConversation = true;
|
||||
|
||||
|
||||
this.currentConversationId = "";
|
||||
this.messageGroups = [];
|
||||
|
||||
|
||||
// 重置分页和加载状态
|
||||
this.pageQuery.PageIndex = 1;
|
||||
this.isLoadingMore = false;
|
||||
this.noMoreData = false;
|
||||
|
||||
|
||||
// 延迟重置切换对话标志位
|
||||
setTimeout(() => {
|
||||
this.isSwitchingConversation = false;
|
||||
|
|
@ -687,7 +692,7 @@ export default {
|
|||
// 滚动到顶部事件处理
|
||||
onScrollToUpper() {
|
||||
console.log("触发上拉刷新");
|
||||
|
||||
|
||||
// 如果已经没有更多数据或正在切换对话,不再触发上拉刷新
|
||||
if (this.noMoreData || this.isSwitchingConversation) {
|
||||
return;
|
||||
|
|
@ -766,7 +771,7 @@ export default {
|
|||
|
||||
.main-content {
|
||||
padding: 30rpx;
|
||||
padding-top: 60rpx;
|
||||
padding-top: 100rpx;
|
||||
|
||||
.welcome-message {
|
||||
// display: inline-block;
|
||||
|
|
@ -779,8 +784,8 @@ export default {
|
|||
|
||||
.avatar {
|
||||
display: inline-block;
|
||||
width: 50rpx;
|
||||
height: 44rpx;
|
||||
width: 42rpx;
|
||||
height: 34rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
|
|
@ -889,19 +894,20 @@ export default {
|
|||
gap: 30rpx;
|
||||
|
||||
.feature-item {
|
||||
height: 150rpx;
|
||||
height: 210rpx;
|
||||
border-radius: 16rpx;
|
||||
background-color: #fafafc;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-left: 30rpx;
|
||||
gap: 20rpx;
|
||||
justify-content: center;
|
||||
// gap: 20rpx;
|
||||
flex: 1;
|
||||
|
||||
.feature-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-top: 16rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.feature-text {
|
||||
|
|
@ -976,7 +982,7 @@ export default {
|
|||
text-align: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
|
||||
.no-more-data {
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
|
|
@ -1024,7 +1030,7 @@ export default {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
width: 12rpx;
|
||||
|
|
@ -1034,20 +1040,20 @@ export default {
|
|||
margin: 0 6rpx;
|
||||
opacity: 0.6;
|
||||
animation: dot-flashing 1.5s infinite linear alternate;
|
||||
|
||||
|
||||
&:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
|
||||
&:nth-child(3) {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes dot-flashing {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,425 @@
|
|||
<template>
|
||||
<view class="flex-col page">
|
||||
<!-- <image src="/static/common/img/16530269846087107196.png" class="image"/> -->
|
||||
<u-navbar title="重置密码"></u-navbar>
|
||||
<view class="flex-col group" v-if="state === 1">
|
||||
<text class="text">重置密码</text>
|
||||
<view class="flex-col group_7">
|
||||
<view class="flex-col">
|
||||
<!-- <text class="text_1">手机号</text> -->
|
||||
<view class="text-wrapper flex-row view">
|
||||
<u-input
|
||||
v-model="phone"
|
||||
:disabled="disabled"
|
||||
placeholder="请输入手机号"
|
||||
type="text"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_6">
|
||||
<!-- <text class="text_1">短信验证码</text> -->
|
||||
<view class="text-wrapper flex-row view" style="padding: 0">
|
||||
<u-input
|
||||
v-model="code"
|
||||
placeholder="请输入短信验证码"
|
||||
type="text"
|
||||
/>
|
||||
<u-button
|
||||
:disabled="!(send == '发送验证码')"
|
||||
class="custom-style"
|
||||
style="background: transparent"
|
||||
@click="sendCode"
|
||||
>{{ send }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <navigator class="text_6" url="/pages/login/login/login">登录</navigator> -->
|
||||
<u-button
|
||||
class="flex-col items-center text-wrapper_1"
|
||||
shape="circle"
|
||||
type="primary"
|
||||
@click="login"
|
||||
>
|
||||
<text class="text_7">重置密码</text>
|
||||
</u-button>
|
||||
</view>
|
||||
<view class="flex-col group" v-if="state === 2">
|
||||
<text class="text">遇见身边的校友</text>
|
||||
<view class="flex-col group_7">
|
||||
<view class="flex-col">
|
||||
<u-input
|
||||
v-model="passWord"
|
||||
class="view text-wrapper"
|
||||
placeholder="请输入新密码"
|
||||
type="password"
|
||||
/>
|
||||
</view>
|
||||
<view class="flex-col group_6">
|
||||
<u-input
|
||||
v-model="passWordTwo"
|
||||
class="view text-wrapper"
|
||||
placeholder="请确认新密码"
|
||||
type="password"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tips">
|
||||
<image src="/static/common/img/infoCircle.png"></image>
|
||||
提示:密码长度应该大于5,必须包含字母、数字、特殊字符,且不能包含中文!
|
||||
</view>
|
||||
<u-button
|
||||
class="flex-col items-center text-wrapper_1"
|
||||
shape="circle"
|
||||
throttle-time="1000"
|
||||
type="primary"
|
||||
@click="login"
|
||||
>
|
||||
<text class="text_7">立即重置</text>
|
||||
</u-button>
|
||||
<u-button
|
||||
class="flex-col items-center text-wrapper_1"
|
||||
shape="circle"
|
||||
style=" margin-top: 10px"
|
||||
type="primary"
|
||||
@click="state = 1"
|
||||
>
|
||||
<text class="text_7">返回</text>
|
||||
</u-button>
|
||||
</view>
|
||||
<u-top-tips ref="uTips" :navbar-height="0"></u-top-tips>
|
||||
<u-toast ref="uToast"/>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import md5 from "js-md5";
|
||||
import {aesEncrypt} from "@/utils/encrypt.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
phone: "",
|
||||
passWord: "",
|
||||
code: "",
|
||||
passWordTwo: "",
|
||||
agreement: false,
|
||||
disabled: false,
|
||||
send: "发送验证码",
|
||||
state: 1,
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
},
|
||||
methods: {
|
||||
// 提示
|
||||
tips(title, type, time) {
|
||||
this.$refs.uToast.show({
|
||||
title: title ? title : "",
|
||||
type: type ? type : "success",
|
||||
duration: time ? time + "" : "1500",
|
||||
});
|
||||
},
|
||||
// 发送验证码
|
||||
sendCode() {
|
||||
var reg = new RegExp("^[1][3,4,5,6,7,8,9][0-9]{9}$", "g"); //手机号
|
||||
if (this.phone === "" || !reg.test(this.phone)) {
|
||||
// this.$tips("请输入正确的手机号码", "error");
|
||||
this.$refs.uToast.show({
|
||||
title: '请输入正确的手机号码',
|
||||
type: 'error',
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = Date.now();
|
||||
const base64Key = btoa('xiaoyout!#@12345');
|
||||
const signStr = this.phone + timestamp + base64Key;
|
||||
const sign = md5(signStr);
|
||||
|
||||
this.$u.apiList
|
||||
.GetAppValidateCode({
|
||||
phone: this.phone,
|
||||
t: timestamp.toString(),
|
||||
sign: sign,
|
||||
type: 2,
|
||||
})
|
||||
.then((res) => {
|
||||
//中间数字加密
|
||||
// let result =this.userName.replace(this.userName.substring(3,7),"****")
|
||||
// this.userName = result
|
||||
this.disabled = true;
|
||||
// this.$tips("发送成功");
|
||||
this.$refs.uToast.show({
|
||||
title: '发送成功',
|
||||
type: 'success',
|
||||
})
|
||||
var second = 60;
|
||||
this.send = second + "秒后重试";
|
||||
var time = setInterval(() => {
|
||||
this.send = second + "秒后重试";
|
||||
second--;
|
||||
if (second <= 0) {
|
||||
this.disabled = false;
|
||||
this.send = "发送验证码";
|
||||
clearInterval(time);
|
||||
}
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
// this.$tips(err.error, "error");
|
||||
this.$refs.uToast.show({
|
||||
title: err.error,
|
||||
type: 'error',
|
||||
})
|
||||
});
|
||||
},
|
||||
login() {
|
||||
if (this.state == 1) {
|
||||
if (this.phone === "") {
|
||||
return this.tips("请输入手机号", "error");
|
||||
}
|
||||
if (this.code === "") {
|
||||
return this.tips("请输入验证码", "error");
|
||||
}
|
||||
this.$u.apiList.ForgotPasswordValidate({
|
||||
phone: this.phone,
|
||||
code: this.code,
|
||||
}).then((res) => {
|
||||
console.info("🚀 ~ file:ForgetPassword method: line:189 -----", res)
|
||||
this.state = 2;
|
||||
}).catch((err) => {
|
||||
this.tips(err.error, "error");
|
||||
});
|
||||
|
||||
return
|
||||
}
|
||||
if (this.state == 2) {
|
||||
if (this.passWord === "") {
|
||||
return this.tips("请输入新密码", "error");
|
||||
}
|
||||
let reg = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[\W_]).{5,}$/;
|
||||
if (!reg.test(this.passWord)) {
|
||||
/* return this.$tips(
|
||||
"密码必须包含字母、数字、特殊字符,且不能包含中文!",
|
||||
"error"
|
||||
); */
|
||||
return this.$refs.uToast.show({
|
||||
title: "密码必须包含字母、数字、特殊字符,且不能包含中文!",
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
if (this.passWordTwo === "") {
|
||||
return this.tips("请确认新密码", "error");
|
||||
}
|
||||
if (this.passWord != this.passWordTwo) {
|
||||
return this.tips("请检查两次密码是否一致", "error");
|
||||
}
|
||||
}
|
||||
const req = {
|
||||
phone: this.phone,
|
||||
code: this.code,
|
||||
newPassWord: aesEncrypt(this.passWord),
|
||||
};
|
||||
console.log(req, "res-");
|
||||
// return
|
||||
this.$u.apiList
|
||||
.ForgotPassword(req)
|
||||
.then((res) => {
|
||||
console.log(res, "res---");
|
||||
uni.showToast({
|
||||
title: "重置成功",
|
||||
duration: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: "/",
|
||||
});
|
||||
}, 2000);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err, "err---");
|
||||
// this.$tips(err.error, "error");
|
||||
this.$refs.uToast.show({
|
||||
title: err.error,
|
||||
type: 'error',
|
||||
})
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-style {
|
||||
color: #3cb5fb !important;
|
||||
padding: 0 !important;
|
||||
height: 70rpx !important;
|
||||
line-height: 60rpx !important;
|
||||
}
|
||||
|
||||
.custom-style::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding-bottom: 0.015rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
|
||||
.image {
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
width: 102%;
|
||||
}
|
||||
|
||||
.group {
|
||||
padding: 1.0rem 0.29rem;
|
||||
|
||||
.text {
|
||||
align-self: left;
|
||||
// color: rgb(46, 155, 255);
|
||||
color: #000;
|
||||
font-size: 0.22rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.17rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
margin-top: 0.29rem;
|
||||
|
||||
.text-wrapper {
|
||||
border: solid 2rpx #dcdfe6;
|
||||
border-radius: 100rpx;
|
||||
background: #f6f8fa;
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
margin-left: 0.01rem;
|
||||
align-self: flex-start;
|
||||
color: rgb(51, 51, 51);
|
||||
font-size: 0.15rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.14rem;
|
||||
}
|
||||
|
||||
.view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx 50rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
margin-left: 0.01rem;
|
||||
margin-top: 0.2rem;
|
||||
align-self: flex-start;
|
||||
color: #1F2232;
|
||||
font-size: 0.13rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
|
||||
.text-wrapper_1 {
|
||||
margin: 50rpx 0rpx 40rpx;
|
||||
padding: 50rpx 0 50rpx;
|
||||
// background-image: linear-gradient(90deg, rgb(135, 230, 254) 0%, rgb(91, 192, 254) 52%, rgb(46, 155, 255) 100%);
|
||||
background: #3cb4fb;
|
||||
box-shadow: 0px 6rpx 9rpx rgba(38, 122, 199, 0.34);
|
||||
border-radius: 100rpx;
|
||||
|
||||
.text_7 {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.group_1 {
|
||||
margin-top: 0.12rem;
|
||||
|
||||
.text_8 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-left: 0.035rem;
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.14rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
margin-top: 0.36rem;
|
||||
justify-content: center;
|
||||
|
||||
.section_1 {
|
||||
flex-shrink: 0;
|
||||
width: 0.17rem;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-left: 0.11rem;
|
||||
height: 0.18rem;
|
||||
line-height: 0.18rem;
|
||||
font-size: 0;
|
||||
|
||||
.text_10 {
|
||||
color: rgb(153, 153, 153);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_13 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 24rpx;
|
||||
color: #EF3920;
|
||||
display: flex;
|
||||
margin-top: 20rpx;
|
||||
|
||||
image {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 10rpx;
|
||||
margin-top: 5rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
<template>
|
||||
<view class="authentication-page">
|
||||
<u-navbar title="身份认证"> </u-navbar>
|
||||
|
||||
<view class="title">请填写您的真实信息</view>
|
||||
|
||||
<view class="form-container">
|
||||
<view class="input-item">
|
||||
<text>姓名</text>
|
||||
<input
|
||||
v-model="name"
|
||||
maxlength="20"
|
||||
type="text"
|
||||
placeholder="请输入"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="input-item">
|
||||
<text>身份证号</text>
|
||||
<input
|
||||
v-model="idCard"
|
||||
maxlength="18"
|
||||
type="text"
|
||||
placeholder="请输入"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="btn-container">
|
||||
<button class="next-btn" @click="handleNext">下一步</button>
|
||||
</view>
|
||||
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import test from "@/uview-ui/libs/function/test.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
name: "",
|
||||
idCard: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleNext() {
|
||||
// 调用后端API获取token
|
||||
this.getTokenFromBackend();
|
||||
|
||||
if (!this.name) {
|
||||
return this.$refs.uToast.show({
|
||||
title: "请输入姓名",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
if (!this.idCard) {
|
||||
return this.$refs.uToast.show({
|
||||
title: "请输入身份证号",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
// 验证身份证号码格式
|
||||
if (!test.idCard(this.idCard)) {
|
||||
return this.$refs.uToast.show({
|
||||
title: "请输入正确的身份证号码",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
// 处理下一步逻辑
|
||||
// console.log("提交信息", this.name, this.idCard);
|
||||
},
|
||||
|
||||
// 获取token
|
||||
getTokenFromBackend() {
|
||||
this.$u.api.getAPIToken().then((res) => {
|
||||
const token = res.verifyToken
|
||||
|
||||
// 跳转到百度人脸核验页面
|
||||
this.redirectToBaiduVerification(token);
|
||||
});
|
||||
},
|
||||
|
||||
redirectToBaiduVerification(token) {
|
||||
// 获取当前域名
|
||||
const currentDomain = window.location.origin;
|
||||
|
||||
// 编码回调URL
|
||||
// const callbackUrl = encodeURIComponent(`${currentDomain}/success.html`);
|
||||
const callbackUrl = "/pages/login/recognitionResult/recognitionResult";
|
||||
|
||||
// 构建跳转URL
|
||||
const verifyUrl = `https://brain.baidu.com/face/print/verify/verify?token=${token}&successUrl=${callbackUrl}&failedUrl=${callbackUrl}`;
|
||||
|
||||
const verifyObj = {
|
||||
url: verifyUrl,
|
||||
name:'身份认证'
|
||||
};
|
||||
|
||||
// 在uni-app中跳转
|
||||
this.$u.route({
|
||||
url: `/pages/webview/index?data=${encodeURIComponent(
|
||||
JSON.stringify(verifyObj)
|
||||
)}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.authentication-page {
|
||||
padding: 0 30rpx;
|
||||
|
||||
.title {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: bold;
|
||||
font-size: 32rpx;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
line-height: 52rpx;
|
||||
text-align: left;
|
||||
margin-top: 30rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 30rpx;
|
||||
margin-bottom: 60rpx;
|
||||
|
||||
.input-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
.next-btn {
|
||||
background-color: #45b5ff;
|
||||
color: #ffffff;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
border-radius: 45rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
<template>
|
||||
<view class="flex-col page">
|
||||
<!-- <image src="/static/common/img/16530269846087107196.png" class="image" /> -->
|
||||
<u-navbar title="确认密码"></u-navbar>
|
||||
<view class="flex-col group">
|
||||
<text class="text">确认密码后登录</text>
|
||||
<view class="flex-col group_7">
|
||||
<view class="flex-col">
|
||||
<view class="flex-row" style="padding: 0">
|
||||
<u-input type="password" v-model="user.passWord" placeholder="请输入密码" class="view text-wrapper"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_6">
|
||||
<u-input type="password" v-model="isPassWord" placeholder="请确认密码" class="view text-wrapper" />
|
||||
</view>
|
||||
<view class="tips">
|
||||
<image src="/static/common/img/infoCircle.png"></image> 提示:密码长度应该大于5,必须包含字母、数字、特殊字符,且不能包含中文!
|
||||
</view>
|
||||
</view>
|
||||
<u-button class="flex-col items-center text-wrapper_1" throttle-time="1000" type="primary" shape="circle"
|
||||
@click="login"><text class="text_7">确定</text></u-button>
|
||||
<!-- 此次接入人脸验证 登录 ==> 确定 -->
|
||||
</view>
|
||||
<u-toast ref="uToast" />
|
||||
<u-top-tips ref="uTips" :navbar-height="0"></u-top-tips>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {aesEncrypt} from '@/utils/encrypt.js'
|
||||
import md5 from "js-md5";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
userName: "",
|
||||
isPassWord: "",
|
||||
agreement: false,
|
||||
user: {
|
||||
phone: '',
|
||||
passWord: '',
|
||||
code:''
|
||||
}
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
if (e.phone) {
|
||||
this.user.phone = e.phone
|
||||
this.user.code = e.code
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: "../register/register",
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
login() {
|
||||
if (this.user.passWord === "") {
|
||||
return this.$refs.uToast.show({
|
||||
title: '请输入密码',
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
if (this.user.passWord.length < 5) {
|
||||
// return this.$tips("您的密码长度应该大于5", "error");
|
||||
return this.$refs.uToast.show({
|
||||
title: '您的密码长度应该大于5',
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
if (this.isPassWord === "") {
|
||||
return this.$refs.uToast.show({
|
||||
title: '请确认密码',
|
||||
type: 'error',
|
||||
})
|
||||
// return this.$tips("请确认密码", "error");
|
||||
}
|
||||
if (this.isPassWord !== this.user.passWord) {
|
||||
return this.$refs.uToast.show({
|
||||
title: '请确认两次密码是否一致',
|
||||
type: 'error',
|
||||
})
|
||||
// return this.$tips("请确认两次密码是否一致", "error");
|
||||
}
|
||||
uni.showLoading({
|
||||
title: "注册中",
|
||||
});
|
||||
const req = {...this.user}
|
||||
req.passWord = aesEncrypt(this.user.passWord)
|
||||
// 生成13位时间戳
|
||||
const timestamp = Date.now();
|
||||
// 生成签名
|
||||
const base64Key = btoa('xiaoyout!#@12345');
|
||||
const signStr = this.user.phone + timestamp + base64Key;
|
||||
const sign = md5(signStr);
|
||||
req.timeStamp = timestamp.toString();
|
||||
req.sign = sign;
|
||||
console.log(req,'req')
|
||||
// return
|
||||
this.$u.api.RegisterUser(req).then((res) => {
|
||||
const newReq = {...this.user}
|
||||
newReq.passWord = aesEncrypt(this.user.passWord)
|
||||
this.$u.api.LoginApp(newReq).then((ress) => {
|
||||
uni.hideLoading();
|
||||
// 保存登录后得到的用户数据
|
||||
this.$u.vuex('vuex_user', ress.user)
|
||||
this.$u.vuex('vuex_token', ress.token)
|
||||
this.$refs.uToast.show({
|
||||
title: '注册成功',
|
||||
type: 'success',
|
||||
})
|
||||
// uni.navigateTo({
|
||||
// url: "../perfect/perfect",
|
||||
// });
|
||||
setTimeout(()=>{
|
||||
// 这里改为进行人脸识别
|
||||
this.toBaiduApi();
|
||||
return
|
||||
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/roleSelection",
|
||||
});
|
||||
},300)
|
||||
|
||||
}).catch((e) => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
|
||||
}).catch((e) => {
|
||||
uni.hideLoading();
|
||||
// this.$tips( e.error, 'error')
|
||||
this.$refs.uToast.show({
|
||||
title: e.error,
|
||||
type: 'error',
|
||||
})
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
// 跳转至人脸验证
|
||||
toBaiduApi(){
|
||||
this.$u.api.getAPIToken().then((res) => {
|
||||
const token = res.result.verify_token;
|
||||
|
||||
// 跳转到百度人脸核验页面
|
||||
this.redirectToBaiduVerification(token);
|
||||
});
|
||||
},
|
||||
redirectToBaiduVerification(token) {
|
||||
// 获取当前域名
|
||||
const currentDomain = window.location.origin;
|
||||
|
||||
// 编码回调URL - 使用相对路径,让uni-app处理路由
|
||||
// 百度人脸核验会在回调URL后附加verify_result和verify_info参数
|
||||
const successUrl = encodeURIComponent(
|
||||
`${currentDomain}/#/pages/login/recognitionResult/recognitionResult?token=${token}`
|
||||
);
|
||||
const failedUrl = encodeURIComponent(
|
||||
`${currentDomain}/#/pages/login/recognitionResult/recognitionFailed?token=${token}`
|
||||
);
|
||||
|
||||
// 构建跳转URL
|
||||
const verifyUrl = `https://brain.baidu.com/face/print/verify/verify?token=${token}&successUrl=${successUrl}&failedUrl=${failedUrl}`;
|
||||
|
||||
console.log("跳转到百度人脸核验页面:", verifyUrl);
|
||||
|
||||
// 直接跳转到百度人脸核验页面
|
||||
window.location.href = verifyUrl;
|
||||
return;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-style {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.custom-style::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding-bottom: 0.015rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
|
||||
.image {
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
width: 102%;
|
||||
}
|
||||
|
||||
.group {
|
||||
padding: 0.15rem 0.29rem;
|
||||
|
||||
.text {
|
||||
font-family: PingFang;
|
||||
font-weight: bold;
|
||||
font-size: 48rpx;
|
||||
color: #000000;
|
||||
margin-top: 200rpx;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
margin-top: 0.29rem;
|
||||
|
||||
.text-wrapper {
|
||||
border: solid 2rpx #dcdfe6;
|
||||
border-radius: 100rpx;
|
||||
background: #f6f8fa;
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
margin-left: 0.01rem;
|
||||
align-self: flex-start;
|
||||
color: rgb(51, 51, 51);
|
||||
font-size: 0.15rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.14rem;
|
||||
}
|
||||
|
||||
.view {
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx 50rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-left: 0.01rem;
|
||||
margin-top: 0.13rem;
|
||||
align-self: flex-start;
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.13rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
|
||||
.text-wrapper_1 {
|
||||
margin: 0.35rem 0rem 0;
|
||||
padding: 20rpx 50rpx;
|
||||
height: 108rpx;
|
||||
/* background-image: linear-gradient(90deg,
|
||||
rgb(135, 230, 254) 0%,
|
||||
rgb(91, 192, 254) 52%,
|
||||
rgb(46, 155, 255) 100%);
|
||||
box-shadow: 0px 0.03rem 0.09rem rgba(38, 122, 199, 0.34); */
|
||||
background: #3cb5fb;
|
||||
border-radius: 0.23rem;
|
||||
|
||||
.text_7 {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.16rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.15rem;
|
||||
}
|
||||
}
|
||||
|
||||
.group_1 {
|
||||
margin-top: 0.12rem;
|
||||
|
||||
.text_8 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 0.14rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-left: 0.035rem;
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.14rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
margin-top: 0.36rem;
|
||||
justify-content: center;
|
||||
|
||||
.section_1 {
|
||||
flex-shrink: 0;
|
||||
width: 0.17rem;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-left: 0.11rem;
|
||||
height: 0.18rem;
|
||||
line-height: 0.18rem;
|
||||
font-size: 0;
|
||||
|
||||
.text_10 {
|
||||
color: rgb(153, 153, 153);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_13 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tips{
|
||||
font-size: 24rpx;
|
||||
color: #EF3920;
|
||||
display: flex;
|
||||
margin-top: 20rpx;
|
||||
image{
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -357,9 +357,6 @@ export default {
|
|||
|
||||
// 登录
|
||||
login() {
|
||||
console.log("登录...");
|
||||
|
||||
return;
|
||||
// const res = {
|
||||
// data: {
|
||||
// // token:
|
||||
|
|
@ -369,18 +366,18 @@ export default {
|
|||
// },
|
||||
// };
|
||||
|
||||
const token = res.data.token;
|
||||
// 解析获取用户信息
|
||||
const userInfo = getUserInfoFromJWT(token);
|
||||
// 保存登录后得到的用户数据
|
||||
this.$u.vuex("vuex_token", token);
|
||||
this.$u.vuex("vuex_user", userInfo);
|
||||
// const token = res.data.token;
|
||||
// // 解析获取用户信息
|
||||
// const userInfo = getUserInfoFromJWT(token);
|
||||
// // 保存登录后得到的用户数据
|
||||
// this.$u.vuex("vuex_token", token);
|
||||
// this.$u.vuex("vuex_user", userInfo);
|
||||
|
||||
// 跳转至首页
|
||||
uni.reLaunch({
|
||||
url: "/pages/home/index/index",
|
||||
});
|
||||
return;
|
||||
// // 跳转至首页
|
||||
// uni.reLaunch({
|
||||
// url: "/pages/home/index/index",
|
||||
// });
|
||||
// return;
|
||||
|
||||
// 校验手机号
|
||||
if (!this.validatePhone()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,532 @@
|
|||
<template>
|
||||
<view class="flex-col page">
|
||||
<!-- <image src="/static/common/img/loginBg.png" class="image" /> -->
|
||||
<view class="img-box">
|
||||
|
||||
</view>
|
||||
<view class="login-box">
|
||||
<view class="tab-list">
|
||||
<view v-for="tab in tabList" :key="tab.id" class="tab-item"
|
||||
:class="activeTab === tab.id ? 'tab-selected' : 'not-selected'" @click="onTab(tab.id)">
|
||||
<text class="tab-text">{{tab.label}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group">
|
||||
<text class="text"></text>
|
||||
|
||||
<view class="flex-col group_7">
|
||||
<view class="flex-col">
|
||||
<!-- <text class="text_1">手机号</text> -->
|
||||
<!-- @input="changeInput($event)" -->
|
||||
<!-- <u-input type="number" v-model="userName" placeholder="请输入手机号" class="view text-wrapper" /> -->
|
||||
<u-input type="number" v-model="userName" placeholder="请输入手机号" class="view text-wrapper" maxlength="11"/>
|
||||
</view>
|
||||
<view class="error-tips" v-if="errorPhone"><image class="error-img" src="/static/common/img/infoCircle.png"></image> 请输入正确手机号</view>
|
||||
<view style="height: 0;width: 0;border: 0;padding: 0;margin: 0;overflow: hidden;">
|
||||
<u-input placeholder='' />
|
||||
</view>
|
||||
<view class="flex-col group_6">
|
||||
<!-- <text class="text_1">密码</text> -->
|
||||
<u-input type="password" v-model="passWord" placeholder="请输入密码" class="view text-wrapper" />
|
||||
</view>
|
||||
<!-- 密码必须包含字母、数字、特殊字符,且不能包含中文 -->
|
||||
<view class="error-tips" v-if="errorPwd"><image class="error-img" src="/static/common/img/infoCircle.png"></image> {{errormsg}}</view>
|
||||
|
||||
</view>
|
||||
<navigator class="text_6" style="width: 100%; text-align: right;" url="/pages/login/ForgetPassword/ForgetPassword">忘记密码?</navigator>
|
||||
<u-button class="flex-col items-center text-wrapper_1" throttle-time='1000' type="primary" shape="circle"
|
||||
@click="login"><text class="text_7">登录</text></u-button>
|
||||
<view class="justify-center group_1">
|
||||
<text class="text_8">还没有账号?</text>
|
||||
<navigator class="text_9" url="/pages/login/register/register">立即注册</navigator>
|
||||
</view>
|
||||
<!-- <view class="flex-row group_2">
|
||||
<u-checkbox-group class="section_1" :size='34'>
|
||||
<u-checkbox v-model="agreement"> </u-checkbox>
|
||||
</u-checkbox-group>
|
||||
<view class="group_3">
|
||||
<text class="text_10">已阅读并同意</text>
|
||||
<text class="text_11">《用户服务协议》</text>
|
||||
<text class="text_12">&</text>
|
||||
<text class="text_13">《用户隐私保护政策》</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<u-top-tips ref="uTips" :navbar-height='0'></u-top-tips>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
toBaiduApi
|
||||
} from '@/utils/faceVerify.js'
|
||||
import {
|
||||
aesEncrypt
|
||||
} from '@/utils/encrypt.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
userName: "",
|
||||
passWord: "",
|
||||
agreement: true,
|
||||
tabList: [{
|
||||
id: 1,
|
||||
label: '密码登录'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '验证码登录'
|
||||
}
|
||||
],
|
||||
activeTab: 1,
|
||||
errorPhone: false,
|
||||
errorPwd: false,
|
||||
errormsg: "",
|
||||
};
|
||||
},
|
||||
onLoad(e) {},
|
||||
onHide(){
|
||||
this.userName= ""
|
||||
this.passWord= ""
|
||||
},
|
||||
onShow() {
|
||||
this.userName= ""
|
||||
this.passWord= ""
|
||||
setTimeout(() => {
|
||||
const lifeData = uni.getStorageSync('lifeData');
|
||||
if (lifeData.vuex_user && lifeData.vuex_token) {
|
||||
this.$u.api.getUser().then(res => {
|
||||
// if(!this.vuex_user.isCard){
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/Face/index/index'
|
||||
// });
|
||||
// return
|
||||
// }
|
||||
uni.switchTab({
|
||||
url: '../../main/index/index'
|
||||
});
|
||||
})
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
methods: {
|
||||
changeInput(e){
|
||||
setTimeout(() => {
|
||||
this.userName = e.target.value
|
||||
}, 0);
|
||||
},
|
||||
onTab(id) {
|
||||
if(id == 1){
|
||||
this.activeTab = id
|
||||
}else{
|
||||
/* uni.showToast({
|
||||
title: '暂未开放',
|
||||
icon: 'none'
|
||||
}) */
|
||||
this.$refs.uToast.show({
|
||||
title: '暂未开放',
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
login() {
|
||||
// this.$u.vuex("vuex_msgList", '');
|
||||
// this.$u.vuex('vuex_user', '')
|
||||
// this.$u.vuex('vuex_token', '')
|
||||
// uni.clearStorage();
|
||||
if (!/^1[3-9]\d{9}$/.test(this.userName)) {
|
||||
// 校验手机号格式是否正确
|
||||
this.errorPhone = true;
|
||||
// return this.$tips("请输入正确的手机号", "error");
|
||||
return;
|
||||
} else {
|
||||
this.errorPhone = false;
|
||||
}
|
||||
// 校验密码格式先注释掉 因为重置密码的时候密码是1q2w3e
|
||||
// if (!/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&.])[A-Za-z\d@$!%*?&.]{5,}$/.test(this.passWord)) {
|
||||
/* if (this.passWord.length < 6) {
|
||||
// 校验密码是否包含字母、数字、特殊字符,且长度至少为6
|
||||
this.errorPwd = true;
|
||||
// return this.$tips("密码必须包含字母、数字、特殊字符,且不能包含中文", "error");
|
||||
return;
|
||||
} else {
|
||||
this.errorPwd = false;
|
||||
} */
|
||||
/* if (!this.agreement) {
|
||||
return this.$tips("请同意用户服务协议及用户隐私保护政策", "error");
|
||||
} */
|
||||
if (this.passWord.length===0) {
|
||||
this.errorPwd = true;
|
||||
this.errormsg = "请输入密码";
|
||||
console.log("请输入密码");
|
||||
return
|
||||
}else if (this.passWord.length>0&&this.passWord.length < 6) {
|
||||
this.errorPwd = true;
|
||||
this.errormsg = "密码长度不能少于6位";
|
||||
console.log("密码长度不能少于6位");
|
||||
return
|
||||
}else{
|
||||
this.errorPwd = false;
|
||||
this.errormsg = "";
|
||||
}
|
||||
uni.showLoading({
|
||||
title: "登录中",
|
||||
});
|
||||
//
|
||||
let data = {
|
||||
phone: this.userName,
|
||||
password: aesEncrypt(this.passWord),
|
||||
}
|
||||
this.$u.api.LoginApp(data).then((res) => {
|
||||
uni.hideLoading();
|
||||
if (!res.succeed && !res.token) {
|
||||
/* uni.showToast({
|
||||
title: res.error,
|
||||
duration: 2000,
|
||||
icon: 'none'
|
||||
}); */
|
||||
// this.$tips(res.error, "error");
|
||||
this.$refs.uToast.show({
|
||||
title: err.error,
|
||||
type: "error",
|
||||
});
|
||||
return
|
||||
}
|
||||
// this.$tips("登录成功", "success")
|
||||
this.$refs.uToast.show({
|
||||
title: "登录成功",
|
||||
type: "success",
|
||||
});
|
||||
/* uni.showToast({
|
||||
title: "登录成功",
|
||||
duration: 2000,
|
||||
}); */
|
||||
// 保存登录后得到的用户数据
|
||||
this.$u.vuex('vuex_user', res.user)
|
||||
this.$u.vuex('vuex_token', res.token)
|
||||
this.$u.vuex('vuex_glyType', res.glyType)
|
||||
// if(!res.user.isCard){
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/Face/index/index'
|
||||
// });
|
||||
// return
|
||||
// }
|
||||
if (res.user.isFill) {
|
||||
uni.switchTab({
|
||||
url: '/pages/home/home/home'
|
||||
});
|
||||
} else {
|
||||
// uni.navigateTo({
|
||||
// // url: "../perfect/perfect",
|
||||
// url: "../roleSelection",
|
||||
// });
|
||||
|
||||
// 没认证先前往人脸识别
|
||||
toBaiduApi(this);
|
||||
|
||||
return
|
||||
// 这里先注释
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/roleSelection?source=login",
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
// this.$tips(err.error, "error");
|
||||
this.$refs.uToast.show({
|
||||
title: err.error,
|
||||
type: "error",
|
||||
});
|
||||
/* uni.showToast({
|
||||
title: err.error,
|
||||
icon: 'none'
|
||||
}) */
|
||||
})
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page {
|
||||
padding-bottom: 30rpx;
|
||||
background-color: rgb(255, 255, 255);
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
background: url(/static/common/img/loginBg.png);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
|
||||
.img-box {
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.image {
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
width: 102%;
|
||||
}
|
||||
|
||||
.group {
|
||||
background: #fff;
|
||||
padding: 20rpx 25rpx;
|
||||
width: calc(100% - 50rpx);
|
||||
margin: 0 auto;
|
||||
border-radius: 30rpx;
|
||||
|
||||
.text {
|
||||
align-self: center;
|
||||
color: rgb(46, 155, 255);
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
margin-top: 40rpx;
|
||||
|
||||
.text-wrapper {
|
||||
border: solid 2rpx #dcdfe6;
|
||||
border-radius: 100rpx;
|
||||
background: #f6f8fa;
|
||||
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
margin-left: 10rpx;
|
||||
align-self: flex-start;
|
||||
color: rgb(51, 51, 51);
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.view {
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx 50rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-left: 5rpx;
|
||||
margin-top: 50rpx;
|
||||
align-self: flex-start;
|
||||
// color: rgb(25, 140, 255);
|
||||
color:#1F2232;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
|
||||
.text-wrapper_1 {
|
||||
margin: 50rpx 0rpx 40rpx;
|
||||
padding: 50rpx 0 50rpx;
|
||||
// background-image: linear-gradient(90deg, rgb(135, 230, 254) 0%, rgb(91, 192, 254) 52%, rgb(46, 155, 255) 100%);
|
||||
background: #3cb4fb;
|
||||
box-shadow: 0px 6rpx 9rpx rgba(38, 122, 199, 0.34);
|
||||
border-radius: 100rpx;
|
||||
|
||||
.text_7 {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.group_1 {
|
||||
margin-top: 24rpx;
|
||||
|
||||
.text_8 {
|
||||
color: #8697AC;
|
||||
font-size: 30rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 26rpx;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-left: 0.035rem;
|
||||
color: #2E9CFE;
|
||||
font-size: 30rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
margin-top: 40rpx;
|
||||
justify-content: center;
|
||||
|
||||
.section_1 {
|
||||
flex-shrink: 0;
|
||||
width: 34rpx;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-left: 24rpx;
|
||||
height: 24rpx;
|
||||
font-size: 0;
|
||||
|
||||
.text_10 {
|
||||
color: rgb(153, 153, 153);
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_13 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-box{
|
||||
background: #fff;
|
||||
width: calc(100% - 50rpx);
|
||||
margin: 0 auto;
|
||||
border-radius: 30rpx;
|
||||
// height: calc(100% - 350rpx);
|
||||
overflow: hidden;
|
||||
padding-bottom:100rpx;
|
||||
|
||||
}
|
||||
|
||||
$tab-height: 52px;
|
||||
$active-color: #fff;
|
||||
$default-color: #90ecf1;
|
||||
$primary-color: #666;
|
||||
|
||||
.tab-list {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
background-color: $default-color;
|
||||
overflow: hidden; // 重点
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
height: $tab-height;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
// opacity: 0.65; // 暂时删除,不选中样式需要重新编写
|
||||
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.tab-selected {
|
||||
color:#3cb5fb;
|
||||
opacity: 1;
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
box-shadow: 48rpx 80rpx 0 $active-color, -34rpx 90rpx 0 0 $active-color; // 重点
|
||||
.tab-text::after{
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 160rpx;
|
||||
bottom: 10rpx;
|
||||
width: 32rpx;
|
||||
height: 8rpx;
|
||||
background: #3CB5FB;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-selected::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -12rpx;
|
||||
bottom: 0;
|
||||
width: 24rpx;
|
||||
height: $tab-height;
|
||||
border-top-left-radius: 48rpx;
|
||||
background-color: $active-color;
|
||||
transform: skewX(-15deg); // 重点
|
||||
}
|
||||
|
||||
.tab-selected::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -12rpx;
|
||||
bottom: 0;
|
||||
width: 24rpx;
|
||||
height: $tab-height;
|
||||
border-top-right-radius: 24rpx;
|
||||
background-color: $active-color;
|
||||
transform: skewX(15deg); // 重点
|
||||
}
|
||||
.not-selected {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.not-selected::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12rpx;
|
||||
bottom: 0;
|
||||
width: 24rpx;
|
||||
height: $tab-height;
|
||||
background: $default-color;
|
||||
border-bottom-left-radius: 24rpx;
|
||||
transform: skewX(15deg); // 重点
|
||||
}
|
||||
|
||||
.not-selected::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 12rpx;
|
||||
bottom: 0;
|
||||
width: 24rpx;
|
||||
height: $tab-height;
|
||||
background: $default-color;
|
||||
border-bottom-right-radius: 24rpx;
|
||||
transform: skewX(-15deg); // 重点
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
.error-tips{
|
||||
font-size: 24rpx;
|
||||
color: #EF3920;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
margin-top: 10rpx;
|
||||
// padding-left: 30rpx;
|
||||
.error-img{
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 5rpx;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
<template>
|
||||
<view class="flex-col page">
|
||||
<u-navbar :border-bottom="false" :background="background"> </u-navbar>
|
||||
<view class="flex-col group_3">
|
||||
<h3 class="text_3">填写学校信息</h3>
|
||||
<view class="flex-col group_4">
|
||||
<view class="justify-between list-item group_5">
|
||||
<text class="text_8">学历</text>
|
||||
<u-input v-model="form.educational" placeholder="请选择学历" type="select" @click="show = true" />
|
||||
<u-action-sheet :list="educationalList" v-model="show" @click="educationalSelect()" ></u-action-sheet>
|
||||
</view>
|
||||
<view class="justify-between list-item">
|
||||
<text class="text_8">学校</text>
|
||||
<u-input v-model="form.shool" placeholder="请选择学校" type="select" @click="show = true" />
|
||||
</view>
|
||||
<view class="justify-between list-item">
|
||||
<text class="text_8">学院</text>
|
||||
<u-input v-model="form.college" placeholder="请选择学院" type="select" @click="show = true" />
|
||||
</view>
|
||||
<view class="justify-between list-item">
|
||||
<text class="text_8">专业</text>
|
||||
<u-input v-model="form.major" placeholder="请选择专业" type="select" @click="show = true" />
|
||||
<u-action-sheet :list="actionSheetList" v-model="show" @click="actionSheetCallback()" ></u-action-sheet>
|
||||
</view>
|
||||
<view class="justify-between list-item">
|
||||
<text class="text_8">时间</text>
|
||||
<u-input v-model="form.time" placeholder="请选择时间" type="select" @click="show = true" />
|
||||
</view>
|
||||
</view>
|
||||
<u-button shape="circle" class="button text-wrapper_1" type="primary"
|
||||
>完成</u-button
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uButton from "../../../uview-ui/components/u-button/u-button.vue";
|
||||
export default {
|
||||
components: { uButton },
|
||||
data() {
|
||||
return {
|
||||
form:{
|
||||
educational:'',//学历
|
||||
shool:'',//学校
|
||||
college:'',//学院
|
||||
major:'',//专业
|
||||
time:'',//时间
|
||||
},
|
||||
background: {
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
value: "",
|
||||
show: false,
|
||||
educationalList:[//学历选择列表
|
||||
{
|
||||
text:'专科',
|
||||
},
|
||||
{
|
||||
text:'本科',
|
||||
},
|
||||
{
|
||||
text:'专科',
|
||||
}
|
||||
],
|
||||
actionSheetList: [
|
||||
{
|
||||
text: "男",
|
||||
},
|
||||
{
|
||||
text: "女",
|
||||
},
|
||||
{
|
||||
text: "保密",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
;
|
||||
},
|
||||
methods: {
|
||||
educationalSelect(index){
|
||||
this.form.educational = this.actionSheetList[index].text;
|
||||
},
|
||||
actionSheetCallback(index) {
|
||||
|
||||
this.form.major = this.actionSheetList[index].text;
|
||||
},
|
||||
save() {
|
||||
uni.switchTab({
|
||||
url: "/pages/main/index/index",
|
||||
});
|
||||
},
|
||||
skip() {
|
||||
uni.switchTab({
|
||||
url: "/pages/main/index/index",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page {
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
.text-wrapper_1 {
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
background-image: linear-gradient(
|
||||
90deg,
|
||||
#87e6fe 0%,
|
||||
#5bc0fe 52%,
|
||||
#2e9bff 100%
|
||||
);
|
||||
box-shadow: 0px 0.03rem 0.09rem rgba(38, 122, 199, 0.34);
|
||||
border-radius: 0.23rem;
|
||||
}
|
||||
.button {
|
||||
margin: 0.6rem 0.15rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
padding-left: 0.16rem;
|
||||
padding-top: 0.3rem;
|
||||
.text_3 {
|
||||
align-self: center;
|
||||
color: rgb(51, 51, 51);
|
||||
font-size: 0.18rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.17rem;
|
||||
}
|
||||
.group_4 {
|
||||
margin-top: 0.44rem;
|
||||
.list-item {
|
||||
padding: 0.1rem 0.2rem 0.09rem 0;
|
||||
border-bottom: solid 0.005rem rgb(199, 199, 204);
|
||||
.text_8 {
|
||||
color: #000;
|
||||
font-size: 0.18rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.38rem;
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
.text_26 {
|
||||
color: #000;
|
||||
font-size: 0.17rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.16rem;
|
||||
}
|
||||
}
|
||||
.group_5 {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<template>
|
||||
<view class="recognition-result-page">
|
||||
<u-navbar title="认证结果" :is-back="false"></u-navbar>
|
||||
|
||||
<view class="result-container">
|
||||
<image
|
||||
:src="isSuccess ? resultSuccess : resultError"
|
||||
class="result-image"
|
||||
mode="widthFix"
|
||||
></image>
|
||||
<text class="result-text">{{
|
||||
isSuccess ? "认证通过" : "认证未通过"
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<view class="btn-container">
|
||||
<button class="next-btn" @click="handleNext">
|
||||
{{ isSuccess ? "进入系统" : "重新识别" }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import resultSuccess from "@/static/common/img/result-success.png";
|
||||
import resultError from "@/static/common/img/result-error.png";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
resultSuccess,
|
||||
resultError,
|
||||
isSuccess: false,
|
||||
verifyResult: null,
|
||||
verifyInfo: null,
|
||||
token: null,
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
// 获取token参数
|
||||
if (options && options.token) {
|
||||
this.token = options.token;
|
||||
console.log("获取到token:", this.token);
|
||||
// 使用token查询认证结果
|
||||
this.queryVerificationResult();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 查询认证结果
|
||||
queryVerificationResult() {
|
||||
if (!this.token) {
|
||||
console.error("token为空,无法查询认证结果");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用API查询认证结果
|
||||
this.$u.api
|
||||
.getVerifyInfo(this.token)
|
||||
.then((res) => {})
|
||||
.catch((err) => {
|
||||
console.log("API调用过程中出错:", err);
|
||||
});
|
||||
},
|
||||
handleNext() {
|
||||
if (this.isSuccess) {
|
||||
uni.switchTab({
|
||||
url: "/pages/main/index/index",
|
||||
});
|
||||
} else {
|
||||
this.toBaiduApi();
|
||||
}
|
||||
},
|
||||
|
||||
toBaiduApi() {
|
||||
this.$u.api.getAPIToken().then((res) => {
|
||||
const token = res.result.verify_token;
|
||||
|
||||
// 跳转到百度人脸核验页面
|
||||
this.redirectToBaiduVerification(token);
|
||||
});
|
||||
},
|
||||
redirectToBaiduVerification(token) {
|
||||
// 获取当前域名
|
||||
const currentDomain = window.location.origin;
|
||||
|
||||
// 编码回调URL - 使用相对路径,让uni-app处理路由
|
||||
// 百度人脸核验会在回调URL后附加verify_result和verify_info参数
|
||||
const successUrl = encodeURIComponent(
|
||||
`${currentDomain}/#/pages/login/recognitionResult/recognitionResult?token=${token}`
|
||||
);
|
||||
const failedUrl = encodeURIComponent(
|
||||
`${currentDomain}/#/pages/login/recognitionResult/recognitionFailed?token=${token}`
|
||||
);
|
||||
|
||||
// 构建跳转URL
|
||||
const verifyUrl = `https://brain.baidu.com/face/print/verify/verify?token=${token}&successUrl=${successUrl}&failedUrl=${failedUrl}`;
|
||||
|
||||
console.log("跳转到百度人脸核验页面:", verifyUrl);
|
||||
|
||||
// 直接跳转到百度人脸核验页面
|
||||
window.location.href = verifyUrl;
|
||||
|
||||
return;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.recognition-result-page {
|
||||
padding: 0 24rpx;
|
||||
|
||||
.result-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 130rpx 0 36rpx;
|
||||
text-align: center;
|
||||
|
||||
.result-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
.next-btn {
|
||||
background-color: #45b5ff;
|
||||
color: #ffffff;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
border-radius: 45rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<template>
|
||||
<view class="recognition-result-page">
|
||||
<u-navbar title="认证结果" :is-back="false"></u-navbar>
|
||||
|
||||
<view class="result-container">
|
||||
<image
|
||||
:src="isSuccess ? resultSuccess : resultError"
|
||||
class="result-image"
|
||||
mode="widthFix"
|
||||
></image>
|
||||
<text class="result-text">{{
|
||||
isSuccess ? "认证通过" : "认证未通过"
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
<view class="btn-container">
|
||||
<button class="next-btn" @click="handleNext">
|
||||
{{ isSuccess ? "进入系统" : "重新识别" }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import resultSuccess from "@/static/common/img/result-success.png";
|
||||
import resultError from "@/static/common/img/result-error.png";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
resultSuccess,
|
||||
resultError,
|
||||
isSuccess: true,
|
||||
token: null,
|
||||
userInfo: {
|
||||
realName: "", // 姓名
|
||||
// idCard: "", // 身份证号
|
||||
},
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
// 获取token参数
|
||||
if (options && options.token) {
|
||||
this.token = options.token;
|
||||
console.log("获取到token:", this.token);
|
||||
// 使用token查询认证结果
|
||||
this.queryVerificationResult();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 查询认证结果
|
||||
queryVerificationResult() {
|
||||
if (!this.token) {
|
||||
console.log("token为空,无法查询认证结果");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用API查询认证结果
|
||||
this.$u.api.getVerifyInfo(this.token).then((res) => {
|
||||
const result = res.result.idcard_confirm;
|
||||
this.userInfo.realName = result.name;
|
||||
// this.userInfo.idCard = result.idcard_number;
|
||||
// console.log("this.userInfo", this.userInfo);
|
||||
|
||||
if (result.idcard_number) {
|
||||
this.getSchoolInfo(result.idcard_number);
|
||||
this.updateCardFn(result.idcard_number);
|
||||
} else {
|
||||
this.$u.vuex("vuex_userInfo", this.userInfo);
|
||||
}
|
||||
});
|
||||
// .catch((err) => {
|
||||
// console.log("API调用过程中出错:", err);
|
||||
// });
|
||||
},
|
||||
|
||||
// 根据身份证号获取学校信息
|
||||
getSchoolInfo(card) {
|
||||
this.$u.api
|
||||
.getInfoByCard({
|
||||
card,
|
||||
// card: "362429199209242513",
|
||||
})
|
||||
.then((res) => {
|
||||
// console.log("返回结果:", res);
|
||||
if (res.length > 0) {
|
||||
this.userInfo = {
|
||||
...this.userInfo,
|
||||
...res[0],
|
||||
};
|
||||
}
|
||||
|
||||
this.$u.vuex("vuex_userInfo", this.userInfo);
|
||||
});
|
||||
},
|
||||
// 更新idCard
|
||||
updateCardFn(card) {
|
||||
this.$u.api
|
||||
.updateCard({
|
||||
userId: this.vuex_user.id,
|
||||
idCode: card,
|
||||
})
|
||||
.then((res) => {
|
||||
console.log("返回结果:", res);
|
||||
});
|
||||
},
|
||||
|
||||
handleNext() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/roleSelection",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.recognition-result-page {
|
||||
padding: 0 24rpx;
|
||||
|
||||
.result-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 130rpx 0 36rpx;
|
||||
text-align: center;
|
||||
|
||||
.result-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
.next-btn {
|
||||
background-color: #45b5ff;
|
||||
color: #ffffff;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
border-radius: 45rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,633 @@
|
|||
<template>
|
||||
<view class="flex-col page">
|
||||
<!-- <image src="/static/common/img/16530269846087107196.png" class="image" /> -->
|
||||
<u-navbar title="注册"></u-navbar>
|
||||
<view class="flex-col group">
|
||||
<text class="text">手机号注册</text>
|
||||
<view class="flex-col group_7">
|
||||
<view class="flex-col">
|
||||
<!-- <text class="text_1">手机号</text> -->
|
||||
<view class="text-wrapper flex-row view" style="padding: 0">
|
||||
<u-input type="number" v-model="userName" placeholder="请输入手机号" maxlength="11" :disabled="disabled" />
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_6" v-show="showCap">
|
||||
<view class="text-wrapper flex-row view" style="padding: 0">
|
||||
<u-input type="text" v-model="captchaCode" placeholder="请输入图形验证码" />
|
||||
<image :src="captchaImg" class="captcha-img" @click="refreshCaptcha" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_6">
|
||||
<!-- <text class="text_1">短信验证码</text> -->
|
||||
<view class="text-wrapper flex-row view" style="padding: 0">
|
||||
<u-input type="text" v-model="passWord" placeholder="请输入短信验证码" />
|
||||
<u-button style='background:transparent;' class="custom-style" :disabled="!(send == '获取验证码')" @click="sendCode">{{ send }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-button class="flex-col items-center text-wrapper_1" throttle-time="1000" type="primary" shape="circle"
|
||||
@click="login"><text class="text_7">注册</text></u-button>
|
||||
<view class="justify-center group_1">
|
||||
<text class="text_8">已有账号</text>
|
||||
<navigator class="text_9" url="/pages/login/login/login">立即登录</navigator>
|
||||
</view>
|
||||
<!-- <view class="flex-row group_2">
|
||||
<u-checkbox-group class="section_1" :size="34">
|
||||
<u-checkbox v-model="agreement"> </u-checkbox>
|
||||
</u-checkbox-group>
|
||||
<view class="group_3">
|
||||
<text class="text_10">已阅读并同意</text>
|
||||
<text class="text_11">《用户服务协议》</text>
|
||||
<text class="text_12">&</text>
|
||||
<text class="text_13">《用户隐私保护政策》</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
<u-top-tips ref="uTips" :navbar-height="0"></u-top-tips>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import md5 from "js-md5";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
userName: "",
|
||||
passWord: "",
|
||||
agreement: false,
|
||||
send: "获取验证码",
|
||||
disabled: false,
|
||||
captchaCode: "", // 图形验证码
|
||||
captchaImg: "", // 验证码图片
|
||||
captchaId: "", // 验证码ID
|
||||
showCap: false,
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
// this.refreshCaptcha();
|
||||
},
|
||||
methods: {
|
||||
// 刷新验证码
|
||||
refreshCaptcha() {
|
||||
const timestamp = Date.now();
|
||||
this.captchaId = timestamp;
|
||||
uni.request({
|
||||
url: 'http://sl.vrgon.com:8003/api/Token/Captcha?id=' + timestamp,
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
headers: {
|
||||
'Accept': '*/*',
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data) {
|
||||
// res.data 是 ArrayBuffer 类型,将其转换为 Blob
|
||||
const blob = new Blob([res.data], { type: 'image/gif' }); // 根据实际图片类型调整
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
// 将 Blob 读取为 Data URL (Base64 编码的图片)
|
||||
this.captchaImg = reader.result;
|
||||
};
|
||||
reader.onerror = (e) => {
|
||||
console.error("FileReader error:", e);
|
||||
this.$refs.uToast.show({
|
||||
title: '图片转换失败',
|
||||
type: "error",
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
} else {
|
||||
console.error('获取验证码失败,状态码或数据异常:', res);
|
||||
this.$refs.uToast.show({
|
||||
title: '获取验证码失败',
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('验证码请求失败:', err);
|
||||
this.$refs.uToast.show({
|
||||
title: '获取验证码失败,请检查网络或CORS设置',
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取验证码
|
||||
sendCode() {
|
||||
var reg = new RegExp("^[1][3,4,5,6,7,8,9][0-9]{9}$", "g"); //手机号
|
||||
if (this.userName === "" || !reg.test(this.userName)) {
|
||||
this.$refs.uToast.show({
|
||||
title: "请输入正确的手机号码",
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// if (!this.captchaCode) {
|
||||
// this.$refs.uToast.show({
|
||||
// title: "请输入图形验证码",
|
||||
// type: "error",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 生成13位时间戳
|
||||
const timestamp = Date.now();
|
||||
// 生成签名
|
||||
const base64Key = btoa('xiaoyout!#@12345');
|
||||
const signStr = this.userName + timestamp + base64Key;
|
||||
const sign = md5(signStr);
|
||||
|
||||
this.$u.apiList.GetPhoneValidateCode({
|
||||
phone: this.userName,
|
||||
t: timestamp.toString(),
|
||||
sign: sign,
|
||||
captchaCode: this.showCap ? this.captchaCode : '' ,
|
||||
captchaId: this.showCap ? this.captchaId.toString() : ''
|
||||
}).then((res)=>{
|
||||
//中间数字加密
|
||||
// let result =this.userName.replace(this.userName.substring(3,7),"****")
|
||||
// this.userName = result
|
||||
this.disabled = true
|
||||
// this.$tips("发送成功");
|
||||
this.$refs.uToast.show({
|
||||
title: "发送成功",
|
||||
type: "success",
|
||||
});
|
||||
var second = 60;
|
||||
this.send = second + "秒后重试";
|
||||
var time = setInterval(() => {
|
||||
this.send = second + "秒后重试";
|
||||
second--;
|
||||
if (second <= 0) {
|
||||
this.disabled = false
|
||||
this.send = "获取验证码";
|
||||
clearInterval(time);
|
||||
}
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log('err-----',err)
|
||||
if(err?.data?.needcap && !this.showCap) {
|
||||
this.showCap = true
|
||||
this.$refs.uToast.show({
|
||||
title: '请输入图形验证码',
|
||||
type: "error",
|
||||
});
|
||||
this.refreshCaptcha()
|
||||
return
|
||||
}
|
||||
this.$refs.uToast.show({
|
||||
title: err.error,
|
||||
type: "error",
|
||||
});
|
||||
this.refreshCaptcha()
|
||||
})
|
||||
},
|
||||
|
||||
login() {
|
||||
// uni.navigateTo({
|
||||
// url: "/pages/login/confirmPwd/confirmPwd?phone="+this.userName,
|
||||
// });
|
||||
// return
|
||||
var reg = new RegExp("^[1][3,4,5,7,8,9][0-9]{9}$", "g"); //手机号
|
||||
if (this.userName === "" || !reg.test(this.userName)) {
|
||||
// this.$tips("请输入正确的手机号码", "error");
|
||||
this.$refs.uToast.show({
|
||||
title: "请输入正确的手机号码",
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.passWord === "") {
|
||||
// return this.$tips("请输入验证码", "error");
|
||||
return this.$refs.uToast.show({
|
||||
title: "请输入验证码",
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
// if (!this.agreement) {
|
||||
// return this.$tips("请同意用户服务协议及用户隐私保护政策", "error");
|
||||
// }
|
||||
const data = {
|
||||
phone:this.userName,
|
||||
code : this.passWord
|
||||
}
|
||||
this.$u.apiList.IsPhoneCode(data).then((res)=>{
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/confirmPwd/confirmPwd?phone="+this.userName+"&code="+this.passWord,
|
||||
});
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err)=>{
|
||||
// this.$tips(err.error, "error");
|
||||
this.$refs.uToast.show({
|
||||
title: err.error,
|
||||
type: "error",
|
||||
});
|
||||
})
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-style {
|
||||
color: #3CB5FB !important;
|
||||
padding: 0 !important;
|
||||
height: 70rpx !important;
|
||||
line-height: 60rpx !important;
|
||||
}
|
||||
|
||||
.custom-style::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding-bottom: 0.015rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
|
||||
.image {
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
width: 102%;
|
||||
}
|
||||
|
||||
.group {
|
||||
padding: 0.7rem 0.29rem;
|
||||
|
||||
.text {
|
||||
align-self: left;
|
||||
// color: rgb(46, 155, 255);
|
||||
color: #000;
|
||||
font-size: 0.22rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.17rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
margin-top: 0.29rem;
|
||||
|
||||
.text-wrapper {
|
||||
border: solid 2rpx #dcdfe6;
|
||||
border-radius: 100rpx;
|
||||
background: #f6f8fa;
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
margin-left: 0.01rem;
|
||||
align-self: flex-start;
|
||||
color: rgb(51, 51, 51);
|
||||
font-size: 0.15rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.14rem;
|
||||
}
|
||||
|
||||
.view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
padding: 20rpx 50rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-left: 0.01rem;
|
||||
margin-top: 0.13rem;
|
||||
align-self: flex-start;
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.13rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
|
||||
.text-wrapper_1 {
|
||||
margin: 50rpx 0rpx 40rpx;
|
||||
padding: 50rpx 0 50rpx;
|
||||
// background-image: linear-gradient(90deg, rgb(135, 230, 254) 0%, rgb(91, 192, 254) 52%, rgb(46, 155, 255) 100%);
|
||||
background: #3cb4fb;
|
||||
box-shadow: 0px 6rpx 9rpx rgba(38, 122, 199, 0.34);
|
||||
border-radius: 100rpx;
|
||||
|
||||
.text_7 {
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.group_1 {
|
||||
margin-top: 0.12rem;
|
||||
|
||||
.text_8 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 0.14rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-left: 0.035rem;
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.14rem;
|
||||
font-family: PingFang;
|
||||
line-height: 0.13rem;
|
||||
}
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
margin-top: 0.36rem;
|
||||
justify-content: center;
|
||||
|
||||
.section_1 {
|
||||
flex-shrink: 0;
|
||||
width: 0.17rem;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-left: 0.11rem;
|
||||
height: 0.18rem;
|
||||
line-height: 0.18rem;
|
||||
font-size: 0;
|
||||
|
||||
.text_10 {
|
||||
color: rgb(153, 153, 153);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
|
||||
.text_13 {
|
||||
color: rgb(25, 140, 255);
|
||||
font-size: 0.11rem;
|
||||
font-family: PingFang;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.captcha-img {
|
||||
width: 200rpx;
|
||||
height: 70rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!-- <template>
|
||||
<view class="flex-col page">
|
||||
<view class="flex-col group">
|
||||
<text class="text">注册账号</text>
|
||||
<view class="flex-col group_1">
|
||||
<view class="flex-col group_2">
|
||||
<u-select
|
||||
@click="show = true"
|
||||
:mode="'single-column'"
|
||||
v-model="show"
|
||||
:list="shoolList"
|
||||
@confirm="confirm"
|
||||
@cancel="cancel"
|
||||
></u-select>
|
||||
<view class="justify-between section_1" @click="show = true">
|
||||
<text
|
||||
:style="{ color: this.shoolname !== '' ? '#000' : '#c1c4cc' }"
|
||||
>{{ this.shoolname != "" ? this.shoolname : "请选择学校" }}</text
|
||||
>
|
||||
<image
|
||||
src="/static/common/img/select.png"
|
||||
class="image_1"
|
||||
/>
|
||||
</view>
|
||||
<view class="text-wrapper flex-col">
|
||||
<u-input
|
||||
v-model="userName"
|
||||
type="text"
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
</view>
|
||||
<view class="text-wrapper flex-col view_1">
|
||||
<u-input
|
||||
v-model="passWord"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
/>
|
||||
</view>
|
||||
<view class="text-wrapper flex-col">
|
||||
<u-input
|
||||
v-model="twopassWord"
|
||||
type="password"
|
||||
placeholder="请确认密码"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row group_3">
|
||||
<view class="flex-col text-wrapper_1">
|
||||
<u-input
|
||||
v-model="Code"
|
||||
type="text"
|
||||
placeholder="请输入验证码"
|
||||
/>
|
||||
<text class="text_6"></text>
|
||||
</view>
|
||||
<view class="flex-col items-end text-wrapper_2">
|
||||
<text>6789</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<u-button class="flex-col items-center text-wrapper_3" @click="register" :throttle-time='1000'>注册</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
userName: "",
|
||||
passWord: "",
|
||||
twopassWord: "",
|
||||
Code: "",
|
||||
shoolname: "",
|
||||
shoolList: [
|
||||
{
|
||||
value: "1",
|
||||
label: "学校一",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "学校二",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "学校三",
|
||||
},
|
||||
{
|
||||
value: "4",
|
||||
label: "学校四",
|
||||
}
|
||||
],
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
;
|
||||
},
|
||||
methods: {
|
||||
confirm(e) {
|
||||
this.shoolname = "";
|
||||
e.map((val, index) => {
|
||||
this.shoolname += this.shoolname == "" ? val.label : "-" + val.label;
|
||||
});
|
||||
},
|
||||
cancel(e) {
|
||||
;
|
||||
},
|
||||
register(){
|
||||
uni.showLoading({
|
||||
title: '注册中'
|
||||
});
|
||||
|
||||
setTimeout(()=>{
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '注册成功',
|
||||
duration: 2000
|
||||
});
|
||||
},1000)
|
||||
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="css">
|
||||
.text-wrapper {
|
||||
margin-top: 0.095rem;
|
||||
padding: 0.03rem 0.17rem;
|
||||
background-color: rgb(246, 247, 250);
|
||||
border-radius: 0.1rem;
|
||||
}
|
||||
.text_2 {
|
||||
margin-left: 0.17rem;
|
||||
}
|
||||
.page {
|
||||
padding: 0.045rem 0 1.5rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.image {
|
||||
margin-left: 0.26rem;
|
||||
margin-right: 0.23rem;
|
||||
width: 3.27rem;
|
||||
height: 0.09rem;
|
||||
}
|
||||
.group {
|
||||
margin-top: 0.72rem;
|
||||
padding-left: 0.34rem;
|
||||
padding-right: 0.31rem;
|
||||
}
|
||||
.text {
|
||||
margin-left: 0.045rem;
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 0.24rem;
|
||||
line-height: 0.23rem;
|
||||
letter-spacing: 0.024rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.group_1 {
|
||||
margin-top: 0.67rem;
|
||||
}
|
||||
.text-wrapper_3 {
|
||||
margin-top: 0.54rem;
|
||||
padding: 0.13rem 0;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.03rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(46, 155, 255, 0.5);
|
||||
border-radius: 0.1rem;
|
||||
}
|
||||
.group_2 {
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.group_3 {
|
||||
margin-top: 0.08rem;
|
||||
}
|
||||
.section_1 {
|
||||
padding: 0.13rem 0.17rem;
|
||||
background-color: rgb(246, 247, 250);
|
||||
border-radius: 0.1rem;
|
||||
}
|
||||
.view_1 {
|
||||
margin-top: 0.09rem;
|
||||
}
|
||||
.text-wrapper_1 {
|
||||
padding: 0 0.17rem;
|
||||
flex: 1 1 auto;
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgb(246, 247, 250);
|
||||
border-radius: 0.1rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
.text-wrapper_2 {
|
||||
margin-left: 0.08rem;
|
||||
padding: 0.14rem 0.1rem ;
|
||||
color: rgb(106, 134, 241);
|
||||
font-size: 0.18rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.036rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(211, 220, 255, 0.5);
|
||||
border-radius: 0.1rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
.image_1 {
|
||||
margin-right: 0.035rem;
|
||||
margin-top: 0.05rem;
|
||||
width: 0.14rem;
|
||||
height: 0.09rem;
|
||||
}
|
||||
.text_6 {
|
||||
margin-left: 0.17rem;
|
||||
}
|
||||
</style> -->
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
<template>
|
||||
<view class="flex-col page">
|
||||
<u-navbar
|
||||
:custom-back="backFn"
|
||||
:background="background"
|
||||
:border-bottom="false"
|
||||
title=""
|
||||
></u-navbar>
|
||||
<view>
|
||||
<view class="title">
|
||||
<h3>请选择登录角色</h3>
|
||||
<text class="tips"
|
||||
>温馨提示:因为系统中存在多个角色,请选择角色后再登录</text
|
||||
>
|
||||
</view>
|
||||
<view class="selectList">
|
||||
<view
|
||||
class="item flex-row"
|
||||
:class="{ 'item-selected': [0,2].includes(vuex_user.userType) }"
|
||||
@click="goStudent"
|
||||
>
|
||||
<view class="name">
|
||||
<text class="text">我是学生</text>
|
||||
<text class="text text-en">Student</text>
|
||||
</view>
|
||||
<view class="student"></view>
|
||||
</view>
|
||||
<view
|
||||
class="item flex-row"
|
||||
:class="{ 'item-selected': vuex_user.userType == '1' }"
|
||||
@click="goTeacher"
|
||||
>
|
||||
<view class="name">
|
||||
<text class="text">我是教师</text>
|
||||
<text class="text text-en">Teacher</text>
|
||||
</view>
|
||||
<view class="teacher"></view>
|
||||
</view>
|
||||
<!-- <view
|
||||
class="item flex-row"
|
||||
:class="{ 'item-selected': vuex_user.userType == '2' }"
|
||||
@click="goGraduate"
|
||||
>
|
||||
<view class="name">
|
||||
<text class="text">我是毕业生</text>
|
||||
<text class="text text-en">Graduate</text>
|
||||
</view>
|
||||
<view class="graduate"></view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uButton from "../../uview-ui/components/u-button/u-button.vue";
|
||||
|
||||
export default {
|
||||
components: { uButton },
|
||||
data() {
|
||||
return {
|
||||
background: {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
source: "",
|
||||
userRole: uni.getStorageSync("role"),
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
console.log(e, "e");
|
||||
this.source = e.source;
|
||||
},
|
||||
methods: {
|
||||
backFn() {
|
||||
console.log(this.source, "source---");
|
||||
if (this.source === "my") {
|
||||
uni.switchTab({
|
||||
url: "/pages/my/my/my",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.source === "login") {
|
||||
this.$u.vuex("vuex_msgList", "");
|
||||
this.$u.vuex("vuex_user", "");
|
||||
this.$u.vuex("vuex_token", "");
|
||||
this.$u.vuex("vuex_userInfo", "");
|
||||
uni.clearStorage();
|
||||
this.$u.route({
|
||||
url: "/pages/login/login/login",
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateBack();
|
||||
},
|
||||
async goTeacher() {
|
||||
const res = await this.$u.apiList.SelectUserTypeApi();
|
||||
console.log(res, "res---");
|
||||
const jzgFlag = res.jzgFlag;
|
||||
// const studentFlag = res.studentFlag
|
||||
if (jzgFlag === false) {
|
||||
uni.setStorageSync("role", "teacher");
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/teacherCertification",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const req = {
|
||||
userType: 1,
|
||||
};
|
||||
const result = await this.$u.apiList.UpdateUserTypeApi(req);
|
||||
console.log(result, "result---");
|
||||
this.vuex_user.userType = 1;
|
||||
uni.switchTab({
|
||||
url: "/pages/my/my/my",
|
||||
});
|
||||
},
|
||||
async goStudent() {
|
||||
const res = await this.$u.apiList.SelectUserTypeApi();
|
||||
console.log(res, "res---");
|
||||
// const jzgFlag = res.jzgFlag;
|
||||
const studentFlag = res.studentFlag;
|
||||
if (studentFlag === false) {
|
||||
uni.setStorageSync("role", "student");
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/perfect/perfect",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const req = {
|
||||
userType: 0,
|
||||
};
|
||||
const result = await this.$u.apiList.UpdateUserTypeApi(req);
|
||||
console.log(result, "result---");
|
||||
this.vuex_user.userType = 0;
|
||||
uni.switchTab({
|
||||
url: "/pages/my/my/my",
|
||||
});
|
||||
},
|
||||
async goGraduate() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/graduateCertification/graduateCertification",
|
||||
});
|
||||
// uni.$u.toast("功能开发中");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(80, 123, 244, 0) 80%,
|
||||
rgba(95, 147, 238, 0.3) 100%
|
||||
);
|
||||
height: 100%;
|
||||
.button {
|
||||
margin: 0.6rem 0.15rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.selectList {
|
||||
width: calc(100% - 30rpx);
|
||||
margin: 0 auto;
|
||||
.item {
|
||||
height: 278rpx;
|
||||
padding: 15rpx;
|
||||
margin-bottom: 15rpx;
|
||||
box-shadow: 0rpx 20rpx 40rpx 0rpx rgba(0, 0, 0, 0.05);
|
||||
border-radius: 32rpx;
|
||||
border: 4rpx solid #e7ecf0;
|
||||
.name {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
// align-items: center;
|
||||
flex-direction: column;
|
||||
padding-top: 20rpx;
|
||||
padding-left: 100rpx;
|
||||
.text {
|
||||
margin-top: 10rpx;
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
// margin-right: 200rpx;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.text-en {
|
||||
font-size: 40rpx;
|
||||
color: #666;
|
||||
letter-spacing: 2rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-selected {
|
||||
border: 4rpx solid #8ad3fd;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
width: calc(100% - 60rpx);
|
||||
margin: 70rpx auto 100rpx;
|
||||
h3 {
|
||||
font-weight: 800;
|
||||
font-size: 48rpx;
|
||||
color: #000;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
.tips {
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
.teacher {
|
||||
width: 256rpx;
|
||||
height: 256rpx;
|
||||
-moz-background-image: url("/static/common/img/teacher.png");
|
||||
-webkit-background-image: url("/static/common/img/teacher.png");
|
||||
background-image: url("/static/common/img/teacher.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
.student {
|
||||
width: 256rpx;
|
||||
height: 256rpx;
|
||||
-moz-background-image: url("/static/common/img/student.png");
|
||||
-webkit-background-image: url("/static/common/img/student.png");
|
||||
background-image: url("/static/common/img/student.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
.graduate {
|
||||
width: 256rpx;
|
||||
height: 256rpx;
|
||||
-moz-background-image: url("/static/common/img/graduate.png");
|
||||
-webkit-background-image: url("/static/common/img/graduate.png");
|
||||
background-image: url("/static/common/img/graduate.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar title="管理列表" :border-bottom="false"></u-navbar>
|
||||
<scroll-view scroll-y="true" class="scroll">
|
||||
<view class="flex-col list" v-if='list.length'>
|
||||
<view class="flex-row list_item" v-for="(item, i) in list" @click="GoChat(item)" :key="i">
|
||||
<u-avatar class="image" :show-sex="vuex_msgList.indexOf(item.fromUserId+'admin,')>=0" sex-icon=""
|
||||
sex-bg-color="red" :src="$u.http.config.imgUrl+item.fromUserHead"></u-avatar>
|
||||
<view class="right-section flex-col">
|
||||
<view class="top-group justify-between view_1">
|
||||
<view class="flex-row">
|
||||
<text class="text_1">{{item.fromUserName}}</text>
|
||||
<view class="right-text-wrapper flex-col items-end">
|
||||
<text class="text_3">{{item.schoolName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text
|
||||
v-if='new Date(item.sendDate) - 0 + (3600000*24)> new Date()&&new Date(item.sendDate).getDate()==new Date().getDate()'
|
||||
class="text_5">{{ item.sendDate.slice(10,16)}}</text>
|
||||
<text v-else class="text_5">{{ item.sendDate.slice(0,10)}}</text>
|
||||
</view>
|
||||
<view class="bottom-group justify-between">
|
||||
<text class="text_7">{{item.message}}</text>
|
||||
<text class="text_9" v-if="item.messageState == 0">未读</text>
|
||||
<text class="text_9" v-if="item.messageState == 1">已读</text>
|
||||
<text class="text_10" v-if="item.messageState == 2">待回复</text>
|
||||
<text class="text_10" v-if="item.messageState == 3">回复中...</text>
|
||||
<text class="text_10" v-if="item.messageState == 4">已回复</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else style='padding-top:30vh'>
|
||||
<!-- <u-empty text="暂无消息列表" mode="message"></u-empty> -->
|
||||
<no-data text="暂无消息列表"></no-data>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NoData from 'components/NoData.vue'
|
||||
export default {
|
||||
components: {
|
||||
NoData
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
};
|
||||
},
|
||||
onLoad() {},
|
||||
onShow() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
// 去聊天
|
||||
GoChat(data) {
|
||||
//管理员聊天绑定
|
||||
this.$u.api.toBind({
|
||||
manageUser: this.vuex_user.id,
|
||||
lockUser: data.fromUserId
|
||||
}).then(res => {
|
||||
uni.navigateTo({
|
||||
url: '../dialogBox/dialogBox?id=admin' + '&shoolid=' + data.schoolId + '&fromid=' + data
|
||||
.fromUserId
|
||||
});
|
||||
}).catch(err=>{
|
||||
/* uni.showToast({
|
||||
title: '已有老师回复',
|
||||
duration: 2000,
|
||||
icon:'none'
|
||||
}); */
|
||||
this.$refs.uToast.show({
|
||||
title: '已有老师回复',
|
||||
type: 'warning',
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
||||
},
|
||||
getList() {
|
||||
this.$u.api.getAdminList().then(res => {
|
||||
this.list = res
|
||||
})
|
||||
},
|
||||
//返回上一级
|
||||
router() {
|
||||
uni.switchTab({
|
||||
url: '../../message/msgList/msgList'
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .u-avatar__sex {
|
||||
width: 0.1rem !important;
|
||||
height: 0.1rem !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
background-color: rgb(246, 247, 250);
|
||||
width: 100%;
|
||||
height: calc(100vh - 0.44rem);
|
||||
}
|
||||
|
||||
.list_item {
|
||||
padding-left: 0.2rem;
|
||||
background: #fff;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
border-bottom: 1px solid rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image {
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.right-section {
|
||||
margin-left: 0.14rem;
|
||||
padding: 0.13rem 0.015rem 0.15rem;
|
||||
flex: 1 1 auto;
|
||||
height: 0.66rem;
|
||||
|
||||
}
|
||||
|
||||
.list_item:last-child .right-section {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.top-group {
|
||||
margin-top: 0.09rem;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.bottom-group {
|
||||
margin-top: 0.05rem;
|
||||
}
|
||||
|
||||
.text_5 {
|
||||
text-align: right;
|
||||
margin-right: 0.1rem;
|
||||
margin-top: 0.02rem;
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.095rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
min-width: 0.5rem;
|
||||
}
|
||||
|
||||
.text_7 {
|
||||
margin-top: 0.035rem;
|
||||
color: rgb(177, 179, 182);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-right: 0.085rem;
|
||||
margin-bottom: 0.035rem;
|
||||
color: rgb(46, 155, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
min-width: 0.5rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text_10 {
|
||||
color: rgb(177, 179, 182);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
min-width: 0.5rem;
|
||||
text-align: right;
|
||||
margin-right: 0.1rem;
|
||||
margin-top: 0.035rem;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
margin: 0.02rem 0;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
max-width: 0.65rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
margin-left: 0.045rem;
|
||||
padding: 0.035rem 0.08rem;
|
||||
color: rgb(115, 129, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
height: 0.18rem;
|
||||
}
|
||||
|
||||
.text_3 {
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding-top: 0.01rem;
|
||||
}
|
||||
|
||||
.view_1 {
|
||||
margin-top: initial;
|
||||
width: initial;
|
||||
height: initial;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,673 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar back-text="" title="新增关注"></u-navbar>
|
||||
<scroll-view scroll-y="true" class="scroll">
|
||||
<view class="guanzhu">
|
||||
<view class="flex-col renzheng" v-if="!vuex_user.isAttestationXY&& false">
|
||||
<view class="flex-col section_4">
|
||||
<view class="flex-row section_5">
|
||||
<text class="text_3">待认证,请先完成认证~</text>
|
||||
<text class="text_4" @click="onAuto">去认证 >></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_3">
|
||||
<view class="flex-col">
|
||||
<image src="/static/common/img/16535374500948048054.png" class="image_1" />
|
||||
</view>
|
||||
<text class="text_5">暂无数据,请尽快认证补充资料~</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_2" v-else>
|
||||
<view class="flex-col list">
|
||||
<view class="list-item flex-row group_3" v-for="(item, index) in UserFollowList" :key='index'
|
||||
@click='toDetail(item.carewUserId, item.userRole, item.carewUserHead)'>
|
||||
<u-avatar :src="$u.http.config.imgUrl + item.carewUserHead" class="text_3 image_1"></u-avatar>
|
||||
<view class="bottom-text-wrapper justify-between view_1">
|
||||
<view class="">
|
||||
<text class="text_5 text_7"><text>{{ item.carewUserName }}</text><text>{{
|
||||
"(" + item.carewName +
|
||||
")" }}</text></text>
|
||||
<view class="center-text-wrapper flex-col items-end">
|
||||
<text class="text_8">{{ item.school }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if='item.carewType == 2 && item.carewName != "管理员"'
|
||||
class="right-text-wrapper flex-col items-center">
|
||||
<text class="text_10">互相关注</text>
|
||||
</view>
|
||||
<view v-if='item.carewType == 1 && item.carewName != "管理员"'
|
||||
class="right-text-wrapper flex-col items-center view_21">
|
||||
<text class="text_10">已关注</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!UserFollowList.length"
|
||||
style="margin-top:20vh;display: flex;justify-content: center;align-items: center;flex-direction: column;">
|
||||
<image src="/static/common/img/empty.png" class="image" />
|
||||
<view style="color:grey;margin-top: -5vh;">暂无关注</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import noData from '@/components/NoData.vue';
|
||||
export default {
|
||||
components: {
|
||||
noData
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
UserFollowList: [],
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// this.getList()
|
||||
//接收系统消息
|
||||
this.$connection.on("SystemMessage", (title, content, time) => {
|
||||
if (this.route.indexOf('sysList') >= 0) {
|
||||
this.list.unshift({
|
||||
messageDate: time,
|
||||
title: title,
|
||||
message: content
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
this.FollowList()
|
||||
|
||||
if (this.vuex_msgList) {
|
||||
var msgList = this.vuex_msgList;
|
||||
msgList = msgList.replace('SystemMessage,', '')
|
||||
this.$u.vuex('vuex_msgList', msgList)
|
||||
if (!msgList) {
|
||||
var tab = this.vuex_tabbar;
|
||||
tab[1].isDot = false;
|
||||
this.$u.vuex('vuex_tabbar', tab);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
FollowList() {
|
||||
this.$u.api.getFollowList().then(res => {
|
||||
|
||||
this.UserFollowList = res
|
||||
})
|
||||
},
|
||||
toDetail(id, role, head) {
|
||||
if (role == 0) {
|
||||
this.$u.route({
|
||||
url: '/pages/AlumniCircle/userDetail/userDetail?id=' + id + '&type=0'
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: "../dialogBox/dialogBox?id=" + id + '&chatType=1&type=0&head=' + head,
|
||||
});
|
||||
}
|
||||
},
|
||||
getList() {
|
||||
this.$u.api.getSysList().then(res => {
|
||||
this.list = res
|
||||
})
|
||||
},
|
||||
|
||||
//返回上一级
|
||||
router() {
|
||||
uni.switchTab({
|
||||
url: '../../message/msgList/msgList'
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll {
|
||||
// background-color: rgb(246, 247, 250);
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: calc(100vh - 0.44rem);
|
||||
}
|
||||
|
||||
.list-item {
|
||||
margin-top: 0.1rem;
|
||||
padding: 20rpx;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.items-title {
|
||||
display: flex;
|
||||
padding-bottom: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-bottom: 2rpx solid #F6F8F9;
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.title-time {
|
||||
margin-left: 0.1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 5rpx 0;
|
||||
// align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
// align-self: center;
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.095rem;
|
||||
// letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
.text_3 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 28rpx;
|
||||
line-height: 0.16rem;
|
||||
// letter-spacing: 0.018rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.015rem;
|
||||
margin-top: 0.1rem;
|
||||
color: #908a8a;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
// letter-spacing: 0.01rem;
|
||||
// text-indent: 0.3rem;
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-top: 0.1rem;
|
||||
font-size: 0.16rem;
|
||||
line-height: 0.16rem;
|
||||
letter-spacing: 0.01rem;
|
||||
|
||||
text {
|
||||
margin-left: 0.015rem;
|
||||
text-indent: 0.3rem;
|
||||
display: block;
|
||||
color: #908a8a;
|
||||
font-size: 0.14rem;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0rem 0.15rem;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.guanzhu {
|
||||
.list-item {
|
||||
padding: 0.14rem 0 0.1rem;
|
||||
align-self: flex-end;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
width: 3.02rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text_3 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bottom-text-wrapper {
|
||||
margin-top: 0.1rem;
|
||||
padding: 0.035rem 0;
|
||||
// color: rgb(115, 129, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
// background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
color: #3CB5FB;
|
||||
background-color: rgba(60,181,251,0.1);
|
||||
}
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.1rem;
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.center-text-wrapper {
|
||||
padding: 0.035rem 0.08rem;
|
||||
color: #3CB5FB;
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(60,181,251,0.1);
|
||||
border-radius: 0.09rem;
|
||||
display: inline-block;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
// margin-right: 0.06rem;
|
||||
// margin-top: 0.03rem;
|
||||
// margin-left: 0.06rem;
|
||||
// padding: 0.085rem 0.08rem;
|
||||
color: #3CB5FB;
|
||||
font-size: 24rpx;
|
||||
line-height: 58rpx;
|
||||
white-space: nowrap;
|
||||
// background-color: rgb(46, 155, 255);
|
||||
border-radius: 0.15rem;
|
||||
border: 2rpx #3CB5FB solid;
|
||||
width: 120rpx;
|
||||
height: 64rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text_8 {
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.text_10 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
margin-left: 0.05rem;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_14 {
|
||||
margin-left: 0.1rem;
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
// padding: 0.11rem 0 4.94rem;
|
||||
// flex: 1 1 auto;
|
||||
// overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
// margin-left: 0.17rem;
|
||||
padding: 0.11rem 0 0.12rem;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.18rem;
|
||||
line-height: 0.17rem;
|
||||
letter-spacing: 0.018rem;
|
||||
white-space: nowrap;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
margin-top: 0.08rem;
|
||||
background: #6574fc;
|
||||
border-radius: 50%;
|
||||
|
||||
text {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.list {}
|
||||
|
||||
.text_2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
// box-shadow: 0px 0.005rem #eaeaea;
|
||||
// padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
.group_4 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
margin-top: 0.015rem;
|
||||
}
|
||||
|
||||
.group_5 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_8 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_9 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
// margin-top: 0.12rem;
|
||||
margin-top: 26rpx;
|
||||
}
|
||||
|
||||
.view_1 {
|
||||
margin-top: initial;
|
||||
// padding: 0.1rem 0;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.08rem;
|
||||
margin-right: 0.04rem;
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.image_2 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_4 {
|
||||
margin-top: initial;
|
||||
padding: 0.14rem 0 0.1rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_3 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_8 {
|
||||
margin-top: initial;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.13rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_4 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_12 {
|
||||
margin-top: initial;
|
||||
padding: 0.17rem 0 0.08rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_5 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_16 {
|
||||
margin-top: initial;
|
||||
padding: 0.15rem 0 0.11rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.16rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.image_6 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_19 {
|
||||
margin-top: initial;
|
||||
padding: 0.18rem 0 0.18rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.16rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
height: 0.67rem;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.text_7 {
|
||||
margin-left: initial;
|
||||
margin-right: initial;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 28rpx;
|
||||
line-height: 0.14rem;
|
||||
// letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
|
||||
text:nth-child(1) {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
max-width: 0.9rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.text_26 {
|
||||
margin-left: initial;
|
||||
margin-right: initial;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view_20 {
|
||||
left: -0.03rem;
|
||||
bottom: 0.075rem;
|
||||
}
|
||||
|
||||
.view_21 {
|
||||
// margin-right: 0.06rem;
|
||||
// margin-top: initial;
|
||||
// padding: 0.09rem 0 0.06rem;
|
||||
color: rgb(73, 76, 87);
|
||||
background-color: initial;
|
||||
border: solid 0.01rem rgb(185, 185, 185);
|
||||
}
|
||||
}
|
||||
|
||||
.renzheng {
|
||||
padding-bottom: 0rem;
|
||||
|
||||
.section_4 {
|
||||
padding: 0.33rem 0 0.34rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
|
||||
.section_5 {
|
||||
margin-left: 0.18rem;
|
||||
margin-right: 0.24rem;
|
||||
padding: 0.2rem 0.18rem 0.18rem 0.24rem;
|
||||
background-color: rgb(246, 247, 250);
|
||||
justify-between: space-between;
|
||||
|
||||
.text_3 {
|
||||
color: rgb(160, 162, 172);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_4 {
|
||||
margin-left: 0.05rem;
|
||||
color: rgb(46, 155, 255);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-top: 0.76rem;
|
||||
padding-left: 0.8rem;
|
||||
padding-right: 0.69rem;
|
||||
color: rgb(163, 182, 202);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
white-space: nowrap;
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.02rem;
|
||||
margin-top: 0.13rem;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
margin-right: 0.14rem;
|
||||
width: 2.12rem;
|
||||
height: 1.89rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,438 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar title="互动消息" :border-bottom="false"></u-navbar>
|
||||
<u-tabs-swiper ref="uTabs" :list="tabList" :current="current" :is-scroll="false"
|
||||
:active-item-style="{ color: '#3CB5FB', fontSize: '0.15rem' }" :bar-style="{ background: '#3CB5FB' }"
|
||||
@change="tabsChange"></u-tabs-swiper>
|
||||
<scroll-view scroll-y="true" class="scroll">
|
||||
<view class="flex-col list" v-if='list.length'>
|
||||
<template v-for="(item, i) in list">
|
||||
<view v-if='item.interType != 3' class="list-item flex-col section_4" :key="i">
|
||||
<view class="image_2 flex-row view_14">
|
||||
<u-avatar :src="$u.http.config.imgUrl + item.userHead"
|
||||
@click='toDetil(item.interUserId)' class="image_2"></u-avatar>
|
||||
<view class="flex-col group_2">
|
||||
<view class="flex-row group_3">
|
||||
<text class="text_16">{{ item.interUserName }}</text>
|
||||
<text class="text_18">{{ item.interDate }}</text>
|
||||
</view>
|
||||
<view class="right-text-wrapper">
|
||||
<text v-if="item.interType == 0">点赞了你的作品</text>
|
||||
<text v-if="item.interType == 1">收藏了你的作品</text>
|
||||
<text v-if="item.interType == 2">转发了你的作品</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-section flex-row">
|
||||
<image v-if='item.dynamicHead' :src="$u.http.config.imgUrl + item.dynamicHead.split(',')[0]"
|
||||
class="image_4" />
|
||||
<text v-if="item.interType == 0" class="text_6">{{ item.commentContent }}</text>
|
||||
<text v-if="item.interType == 1" class="text_6">{{ item.dynamicTitle }}</text>
|
||||
<text v-if="item.interType == 2" class="text_6">{{ item.dynamicTitle }}</text>
|
||||
</view>
|
||||
<view class="flex-row group_4">
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="list-item flex-col section_4">
|
||||
<view class="top-group flex-row view_13">
|
||||
<view class="image_2 flex-row view_14">
|
||||
<u-avatar :src="$u.http.config.imgUrl + item.userHead"
|
||||
@click='toDetil(item.interUserId)' class="image_2"></u-avatar>
|
||||
<view class="flex-col group_2">
|
||||
<view class="flex-row group_3">
|
||||
<text class="text_16">{{ item.interUserName }}</text>
|
||||
<!-- <view class="flex-col items-center text-wrapper_1">
|
||||
<text>{{ item.commentContent == '【我的评论】' ? '评论' : '回复' }}了</text>
|
||||
</view> -->
|
||||
<text class="text_18">{{ item.interDate }}</text>
|
||||
</view>
|
||||
<text class="text_20">{{ item.commentContent == '【我的评论】' ? '评论' :
|
||||
'回复' }}了你:{{ item.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="right-group flex-col items-center view_15">
|
||||
<text class="top-group_2">回复</text>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<view class="bottom-section flex-row">
|
||||
<text v-if="item.commentContent == '【我的评论】'" class="text_6">{{ item.dynamicTitle }}</text>
|
||||
<text v-else class="text_6">{{ item.commentContent }}</text>
|
||||
</view>
|
||||
<view class="flex-row group_4">
|
||||
<!-- <image
|
||||
src="https://codefun-proj-user-res-1256085488.cos.ap-guangzhou.myqcloud.com/6216dee45a7e3f031061d0f1/621c59e162a7d90011002985/16460348611151830106.png"
|
||||
class="image_11" />
|
||||
<text class="text_22">还有10条评论 ></text> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<view v-else style='padding-top:30vh'>
|
||||
<!-- <u-empty text="暂无消息列表" mode="message"></u-empty> -->
|
||||
<no-data text="暂无消息列表"></no-data>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NoData from 'components/NoData.vue'
|
||||
export default {
|
||||
components: {
|
||||
NoData
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
tabList: [
|
||||
{
|
||||
name: "全部",
|
||||
},
|
||||
{
|
||||
name: "评论",
|
||||
},
|
||||
{
|
||||
name: "点赞",
|
||||
},
|
||||
{
|
||||
name: "收藏",
|
||||
},
|
||||
{
|
||||
name: "转发",
|
||||
},
|
||||
],
|
||||
current:0,
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
this.getList(-1)
|
||||
//接收互动消息
|
||||
this.$connection.on("InteractMessage", (data, type) => {
|
||||
if (this.route.indexOf('interactionList') >= 0) {
|
||||
this.list.unshift(data.data)
|
||||
}
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
if (this.vuex_msgList) {
|
||||
var msgList = this.vuex_msgList;
|
||||
msgList = msgList.replace('InteractMessage,', '')
|
||||
this.$u.vuex('vuex_msgList', msgList)
|
||||
if (!msgList) {
|
||||
var tab = this.vuex_tabbar;
|
||||
tab[1].isDot = false;
|
||||
this.$u.vuex('vuex_tabbar', tab);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// tabschange
|
||||
tabsChange(i) {
|
||||
// this.current 0=全部 1=评论 2=点赞 3=收藏 4=转发
|
||||
if (this.current !== i) {
|
||||
this.current = i;
|
||||
if (this.current == 0) {
|
||||
this.getList(-1)
|
||||
} else if (this.current == 1) {
|
||||
this.getList(3)
|
||||
} else {
|
||||
this.getList(this.current - 2);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
//type 0=点赞 1=收藏 2=转发 3=评论
|
||||
getList(type) {
|
||||
this.$u.api.getinteractionList(type).then(res => {
|
||||
this.list = res
|
||||
})
|
||||
},
|
||||
//返回上一级
|
||||
router() {
|
||||
uni.switchTab({
|
||||
url: '../../message/msgList/msgList'
|
||||
})
|
||||
},
|
||||
toDetil(id) {
|
||||
this.$u.route({
|
||||
url: '/pages/AlumniCircle/userDetail/userDetail?id=' + id
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll {
|
||||
background-color: rgb(246, 247, 250);
|
||||
width: 100%;
|
||||
height: calc(100vh - 0.44rem);
|
||||
}
|
||||
|
||||
.list-item {
|
||||
padding: 0 0.14rem 0.17rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 0.1rem;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.list-item:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.top-group {
|
||||
padding: 0.12rem 0 0.085rem;
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
margin-top: 10rpx;
|
||||
margin-right: 0.055rem;
|
||||
padding: 0.07rem 0.025rem 0.08rem 0.14rem;
|
||||
color: rgb(54, 54, 54);
|
||||
font-size: 0.12rem;
|
||||
// letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
background-color:#F6F8F9;
|
||||
border-radius: 24rpx;
|
||||
// margin-left: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image_2 {
|
||||
margin-bottom: 0.025rem;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.right-group {
|
||||
// margin-left: 0.12rem;
|
||||
// margin-top: 0.05rem;
|
||||
}
|
||||
|
||||
.image_4 {
|
||||
width: 0.35rem;
|
||||
height: 0.35rem;
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-left: 0.025rem;
|
||||
align-self: center;
|
||||
overflow: hidden;
|
||||
max-width: 84%;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.top-group_1 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_4 {
|
||||
margin-left: 0.02rem;
|
||||
margin-top: 0.085rem;
|
||||
color: rgb(177, 179, 182);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
// margin: 0.02rem 0;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
// line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
// margin-left: 0.015rem;
|
||||
padding: 0.03rem 0 0.04rem;
|
||||
// color: rgb(46, 155, 255);
|
||||
font-size: 0.12rem;
|
||||
// line-height: 0.12rem;
|
||||
// letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
// background-color: rgba(46, 155, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
// width: 0.65rem;
|
||||
height: 0.18rem;
|
||||
color: #000;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0.14rem 0.14rem 0.14rem 0.15rem;
|
||||
}
|
||||
|
||||
.view_2 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.top-group {
|
||||
padding: 0.12rem 0 0.085rem;
|
||||
}
|
||||
|
||||
.image_2 {
|
||||
margin-bottom: 0.025rem;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.right-group {
|
||||
// margin-left: 0.12rem;
|
||||
// margin-top: 0.05rem;
|
||||
}
|
||||
|
||||
.section_4 {
|
||||
padding: 0.15rem 0.16rem 0;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.view_13 {
|
||||
padding: initial;
|
||||
}
|
||||
|
||||
.text_20 {
|
||||
margin-right: initial;
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
background-color: initial;
|
||||
// margin-left: 0.53rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 80%;
|
||||
font-size: 24rpx;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
|
||||
}
|
||||
|
||||
.text-wrapper_2 {
|
||||
margin-right: 0.05rem;
|
||||
margin-top: 0.1rem;
|
||||
padding: 0.18rem 0 0.2rem;
|
||||
color: rgb(54, 54, 54);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgb(246, 247, 250);
|
||||
max-width: 84%;
|
||||
min-width: 84%;
|
||||
margin-left: 0.53rem;
|
||||
}
|
||||
|
||||
.group_4 {
|
||||
padding: 0.1rem 0.52rem 0.1rem;
|
||||
color: rgb(177, 179, 182);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view_14 {
|
||||
margin-bottom: initial;
|
||||
width: initial;
|
||||
height: initial;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.view_15 {
|
||||
margin-left: 0.2rem;
|
||||
margin-top: initial;
|
||||
margin-right: 0.07rem;
|
||||
padding: 0.07rem 0;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgb(46, 155, 255);
|
||||
border-radius: 0.13rem;
|
||||
width: 0.56rem;
|
||||
height: 0.25rem;
|
||||
}
|
||||
|
||||
.view_15:active {
|
||||
background: #fff;
|
||||
color: rgb(46, 155, 255);
|
||||
border: 1px solid rgb(46, 155, 255);
|
||||
}
|
||||
|
||||
.text_21 {
|
||||
margin-left: 0.14rem;
|
||||
margin-right: 0.12rem;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image_11 {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
}
|
||||
|
||||
.text_22 {
|
||||
margin: 0.05rem 0 0.04rem 0.09rem;
|
||||
}
|
||||
|
||||
.image_10 {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
margin-left: 0.11rem;
|
||||
margin-bottom: 0.045rem;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
padding-left: 0.01rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text_18 {
|
||||
// margin-top: 0.055rem;
|
||||
color: rgb(177, 179, 182);
|
||||
font-size: 0.12rem;
|
||||
// line-height: 0.12rem;
|
||||
// letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.text_16 {
|
||||
margin-bottom: 0.03rem;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size:28rpx;
|
||||
// line-height: 0.14rem;
|
||||
// letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text-wrapper_1 {
|
||||
margin-left: 0.04rem;
|
||||
padding: 0.03rem 0 0.04rem;
|
||||
color: rgb(46, 155, 255);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(46, 155, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
width: 0.65rem;
|
||||
height: 0.18rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,938 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar :is-back="false" title="">
|
||||
<view class="slot-wrap">
|
||||
<u-tabs-swiper ref="uTabs" :list="navList" :current="current" :is-scroll="false"
|
||||
:active-item-style="{ color: '#3CB5FB', fontSize: '0.18rem' }"
|
||||
:bar-style="{ background: '#3CB5FB' }" @change="tabsChange"></u-tabs-swiper>
|
||||
</view>
|
||||
</u-navbar>
|
||||
<swiper :current="swiperCurrent" @transition="transition" @animationfinish="animationfinish"
|
||||
style="height: calc(100vh - 0.95rem); width: 100%">
|
||||
<swiper-item class="swiper-item" v-for="(item, index) in navList" :key="index"
|
||||
style="height: calc(100vh - 0.95rem); width: 100%">
|
||||
<scroll-view scroll-y="true" style="height: calc(100vh - 0.95rem); width: 100%"
|
||||
@scrolltolower="onreachBottom">
|
||||
<!-- 消息 -->
|
||||
<view class="msg" v-if="index == 0">
|
||||
<view class="flex-col renzheng" v-if="!vuex_user.isAttestationXY&&false">
|
||||
<view class="flex-col section_4">
|
||||
<view class="flex-row section_5">
|
||||
<text class="text_3">待认证,请先完成认证~</text>
|
||||
<text class="text_4" @click="onAuto">去认证 >></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_3">
|
||||
<view class="flex-col">
|
||||
<image src="/static/common/img/16535374500948048054.png" class="image_1" />
|
||||
</view>
|
||||
<text class="text_5">暂无数据,请尽快认证补充资料~</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_2">
|
||||
<view class="flex-col list">
|
||||
<navigator url="../adminList/adminList" class="list-item flex-row"
|
||||
style='box-shadow: 0px 0.005rem #eaeaea;' v-if='vuex_user.isAttestationGLY'>
|
||||
<u-avatar class="image_1" :show-sex="vuex_msgList.indexOf('admin')>=0" sex-icon=""
|
||||
size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
src="/static/common/img/adminHeaderImg.png">
|
||||
</u-avatar>
|
||||
<view class="right-section flex-col">
|
||||
<view class="top-group justify-between">
|
||||
<text class="text_2">管理列表</text>
|
||||
<!-- <text class="text_4">8:52</text> -->
|
||||
</view>
|
||||
<text class="text_6" v-if="vuex_msgList.indexOf('admin')>=0">你有新通知</text>
|
||||
<text class="text_6" v-else>暂无新通知</text>
|
||||
</view>
|
||||
</navigator>
|
||||
<navigator v-if='interInfo' url="../interactionList/interactionList"
|
||||
class="list-item flex-row" style='box-shadow: 0px 0.005rem #eaeaea;'>
|
||||
<u-avatar class="image_1" :show-sex="vuex_msgList.indexOf('InteractMessage')>=0"
|
||||
sex-icon="" size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
src="/static/common/img/interactionhiderimg.png"></u-avatar>
|
||||
<view class="right-section flex-col">
|
||||
<view class="top-group justify-between">
|
||||
<text class="text_2">互动消息</text>
|
||||
<view style='display:none'>
|
||||
{{interInfo.interDate?interInfo.interDate = interInfo.interDate.replace(/-/g, "/"):''}}
|
||||
</view>
|
||||
<text
|
||||
v-if='new Date(interInfo.interDate) - 0 + (3600000*24)> new Date()&&new Date(interInfo.interDate).getDate()==new Date().getDate()'
|
||||
class="text_4">{{interInfo.interDate.slice(10,16)}}</text>
|
||||
<text v-else class="text_4">{{ interInfo.interDate.slice(5,10)}}</text>
|
||||
<!-- <text class="text_4">{{interInfo.interDate}}</text> -->
|
||||
</view>
|
||||
<text v-if='interInfo.interType==0'
|
||||
class="text_4 text_15">{{interInfo.interUserName+' 点赞了 '+interInfo.dynamicTitle}}</text>
|
||||
<text v-if='interInfo.interType==1'
|
||||
class="text_4 text_15">{{interInfo.interUserName+' 收藏了 '+interInfo.dynamicTitle}}</text>
|
||||
<text v-if='interInfo.interType==2'
|
||||
class="text_4 text_15">{{interInfo.interUserName+' 转发了 '+interInfo.dynamicTitle}}</text>
|
||||
<text v-if='interInfo.interType==3'
|
||||
class="text_4 text_15">{{interInfo.interUserName+' 评论了 '+interInfo.dynamicTitle}}</text>
|
||||
</view>
|
||||
</navigator>
|
||||
<navigator v-if='sysInfo' url="../sysList/sysList" class="list-item flex-row group_4">
|
||||
<u-avatar class="image_1" :show-sex="vuex_msgList.indexOf('SystemMessage')>=0"
|
||||
sex-icon="" size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
src="/static/common/img/systemHeaderimg.png">
|
||||
</u-avatar>
|
||||
<view class="right-section flex-col view_2">
|
||||
<view class="top-group justify-between view_3">
|
||||
<text class="text_2 text_8">系统消息</text>
|
||||
<view style='display:none'>
|
||||
{{sysInfo.messageDate?sysInfo.messageDate = sysInfo.messageDate.replace(/-/g, "/"):''}}
|
||||
</view>
|
||||
<text
|
||||
v-if='new Date(sysInfo.messageDate) - 0 + (3600000*24)> new Date()&&new Date(sysInfo.messageDate).getDate()==new Date().getDate()'
|
||||
class="text_6 text_16">{{ sysInfo.messageDate.slice(10,16)}}</text>
|
||||
<text v-else class="text_6 text_16">{{
|
||||
sysInfo.messageDate.slice(5,10)}}</text>
|
||||
</view>
|
||||
<text class="text_4 text_15">{{sysInfo.title}} </text>
|
||||
</view>
|
||||
</navigator>
|
||||
<view v-if="!sysInfo" style="margin-top:20vh;display: flex;justify-content: center;align-items: center;flex-direction: column;" >
|
||||
<image src="/static/common/img/empty.png" class="image" />
|
||||
<view style="color:grey;margin-top: -5vh;">暂无消息</view>
|
||||
</view>
|
||||
<template v-for="(v, i) in UserMsgList">
|
||||
<view class="list-item flex-row group_5" :key="i"
|
||||
@click="GoChat(v.userId,v.chatType,v.userHead)">
|
||||
<u-avatar v-if='v.chatType==0' class="image_1"
|
||||
:show-sex="vuex_msgList.indexOf(v.userId+',')>=0" sex-icon="" size="0.4rem"
|
||||
sex-bg-color="red" mode="circle" :src="$u.http.config.imgUrl+v.userHead">
|
||||
</u-avatar>
|
||||
<u-avatar v-else class="image_1" :show-sex="vuex_msgList.indexOf('admin,')>=0"
|
||||
sex-icon="" size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
:src="$u.http.config.imgUrl+v.userHead">
|
||||
</u-avatar>
|
||||
<view class="right-section justify-between view_4">
|
||||
<view class="top-group flex-col view_5">
|
||||
<view class="text_2 flex-row view_6">
|
||||
<text class="text_11">{{v.userNetName}}</text>
|
||||
<view class="right-text-wrapper flex-col items-end">
|
||||
<text class="text_13">{{ v.userSchoolName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="text_4 text_15">{{v.message}}</text>
|
||||
</view>
|
||||
<view style='display:none'>
|
||||
{{v.sendDate?v.sendDate = v.sendDate.replace(/-/g, "/"):''}}</view>
|
||||
<text
|
||||
v-if='new Date(v.sendDate) - 0 + (3600000*24)> new Date()&&new Date(v.sendDate).getDate()==new Date().getDate()'
|
||||
class="text_6 text_16">{{ v.sendDate.slice(10,16)}}</text>
|
||||
<text v-else class="text_6 text_16">{{ v.sendDate.slice(5,10)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<!-- 关注 -->
|
||||
<view class="guanzhu" v-else>
|
||||
<view class="flex-col renzheng" v-if="!vuex_user.isAttestationXY&&false">
|
||||
<view class="flex-col section_4">
|
||||
<view class="flex-row section_5">
|
||||
<text class="text_3">待认证,请先完成认证~</text>
|
||||
<text class="text_4" @click="onAuto">去认证 >></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_3">
|
||||
<view class="flex-col">
|
||||
<image src="/static/common/img/16535374500948048054.png" class="image_1" />
|
||||
</view>
|
||||
<text class="text_5">暂无数据,请尽快认证补充资料~</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_2">
|
||||
<view class="flex-col list">
|
||||
<!-- <view class="list-item flex-row group_3">
|
||||
<view class="flex-col items-end text-wrapper text_3">
|
||||
<text class="text_2">浙</text>
|
||||
</view>
|
||||
<view class="bottom-text-wrapper justify-between view_1">
|
||||
<text class="text_5 text_7">学校管理员</text>
|
||||
<view class="center-text-wrapper flex-col items-end">
|
||||
<text class="text_8">浙江大学</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<view class="list-item flex-row group_3" v-for="(item,index) in UserFollowList"
|
||||
:key='index' @click='toDetail(item.carewUserId,item.userRole,item.carewUserHead)'>
|
||||
<u-avatar :src="$u.http.config.imgUrl+item.carewUserHead" class="text_3 image_1"></u-avatar>
|
||||
<view class="bottom-text-wrapper justify-between view_1">
|
||||
<view class="">
|
||||
<text
|
||||
class="text_5 text_7"><text>{{item.carewUserName}}</text><text>{{"("+item.carewName+")"}}</text></text>
|
||||
<view class="center-text-wrapper flex-col items-end">
|
||||
<text class="text_8">{{item.school}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if='item.carewType==2&&item.carewName!="管理员"'
|
||||
class="right-text-wrapper flex-col items-center">
|
||||
<text class="text_10">互相关注</text>
|
||||
</view>
|
||||
<view v-if='item.carewType==1&&item.carewName!="管理员"'
|
||||
class="right-text-wrapper flex-col items-center view_21">
|
||||
<text class="text_10">已关注</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!UserFollowList.length" style="margin-top:20vh;display: flex;justify-content: center;align-items: center;flex-direction: column;" >
|
||||
<image src="/static/common/img/empty.png" class="image" />
|
||||
<view style="color:grey;margin-top: -5vh;">暂无关注</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<u-tabbar :list="vuex_tabbar" :class="{phone:vuex_iPhone}"></u-tabbar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tabbar: "",
|
||||
swiperCurrent: 0,
|
||||
current: 0,
|
||||
UserMsgList: [],
|
||||
UserFollowList: [],
|
||||
navList: [{
|
||||
name: "消息",
|
||||
},
|
||||
{
|
||||
name: "关注",
|
||||
},
|
||||
],
|
||||
interInfo: '',
|
||||
sysInfo: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
current() {
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// 和onShow 重复
|
||||
// this.getList()
|
||||
},
|
||||
onLoad() {
|
||||
//接收数据
|
||||
this.$connection.on("ReceiveMessage", (user, message) => {
|
||||
this.getList()
|
||||
});
|
||||
this.$connection.on("InteractMessage", (data, type) => {
|
||||
this.getList()
|
||||
});
|
||||
this.$connection.on("SystemMessage", (title, content, time) => {
|
||||
this.getList()
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
this.getList()
|
||||
|
||||
},
|
||||
methods: {
|
||||
toDetail(id, role, head) {
|
||||
if (role == 0) {
|
||||
this.$u.route({
|
||||
url: '/pages/AlumniCircle/userDetail/userDetail?id=' + id + '&type=0'
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: "../dialogBox/dialogBox?id=" + id + '&chatType=1&type=0&head=' + head,
|
||||
});
|
||||
}
|
||||
},
|
||||
getList() {
|
||||
if (this.current == 0) {
|
||||
this.charList();
|
||||
} else {
|
||||
this.FollowList();
|
||||
}
|
||||
},
|
||||
onAuto() {
|
||||
this.$u.route({
|
||||
url: "pages/my/ShoolList/ShoolList"
|
||||
})
|
||||
},
|
||||
charList() {
|
||||
const data = {
|
||||
id: this.vuex_user.id,
|
||||
userRole: this.vuex_user.isAttestationGLY ? 1 : 0
|
||||
}
|
||||
this.$u.api.getcharList(data).then(res => {
|
||||
this.UserMsgList = res
|
||||
})
|
||||
this.$u.api.getinteractionList().then(res => {
|
||||
this.interInfo = res[0]
|
||||
})
|
||||
this.$u.api.getSysList().then(res => {
|
||||
this.sysInfo = res[0]
|
||||
})
|
||||
},
|
||||
FollowList() {
|
||||
this.$u.api.getFollowList().then(res => {
|
||||
|
||||
this.UserFollowList = res
|
||||
})
|
||||
},
|
||||
// 去聊天
|
||||
GoChat(id, chatType, head) {
|
||||
uni.navigateTo({
|
||||
url: "../dialogBox/dialogBox?id=" + id + '&chatType=' + chatType + '&type=0&head=' + head,
|
||||
});
|
||||
},
|
||||
// tabs通知swiper切换
|
||||
tabsChange(index) {
|
||||
this.swiperCurrent = index;
|
||||
},
|
||||
// swiper-item左右移动,通知tabs的滑块跟随移动
|
||||
transition(e) {
|
||||
let dx = e.detail.dx;
|
||||
this.$refs.uTabs.setDx(dx);
|
||||
},
|
||||
// 由于swiper的内部机制问速切题,快换swiper不会触发dx的连续变化,需要在结束时重置状态
|
||||
// swiper滑动结束,分别设置tabs和swiper的状态
|
||||
animationfinish(e) {
|
||||
let current = e.detail.current;
|
||||
this.$refs.uTabs.setFinishCurrent(current);
|
||||
this.swiperCurrent = current;
|
||||
this.current = current;
|
||||
},
|
||||
// scroll-view到底部加载更多
|
||||
onreachBottom() {},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .u-avatar__sex {
|
||||
width: 0.1rem !important;
|
||||
height: 0.1rem !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.msg {
|
||||
.list-item {
|
||||
padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.right-section {
|
||||
margin-left: 0.15rem;
|
||||
padding: 0.14rem 0 0.18rem;
|
||||
flex: 1 1 auto;
|
||||
height: 0.66rem;
|
||||
}
|
||||
|
||||
.top-group {
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-top: 0.09rem;
|
||||
color: rgb(180, 182, 189);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_2 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_4 {
|
||||
margin-bottom: 0.05rem;
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.095rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
margin-bottom: 0.03rem;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
max-width: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
padding: 0.035rem 0.08rem;
|
||||
color: rgb(115, 129, 255);
|
||||
margin-left: 0.1rem;
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
height: 0.18rem;
|
||||
}
|
||||
|
||||
.text_13 {
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
// .group_2 {
|
||||
// padding-bottom: 0.15rem;
|
||||
// flex: 1 1 auto;
|
||||
// overflow-y: auto;
|
||||
// }
|
||||
|
||||
.group_4 {
|
||||
padding: 0 0.01rem;
|
||||
padding-left: 0.2rem;
|
||||
box-shadow: 0px 0.005rem #eaeaea;
|
||||
}
|
||||
|
||||
.group_5 {
|
||||
padding-left: 0.2rem;
|
||||
margin-top: 0.015rem;
|
||||
box-shadow: 0px 0.005rem #eaeaea;
|
||||
}
|
||||
|
||||
.group_4:active,
|
||||
.group_5:active {
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.group_8 {
|
||||
padding-left: initial;
|
||||
}
|
||||
|
||||
.view_2 {
|
||||
margin-left: 0.15rem;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.view_4 {
|
||||
margin-left: 0.16rem;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.view_3 {
|
||||
margin-right: initial;
|
||||
}
|
||||
|
||||
.view_5 {
|
||||
margin-right: initial;
|
||||
}
|
||||
|
||||
.text_16 {
|
||||
margin-top: 0.0rem;
|
||||
color: rgb(193, 196, 204);
|
||||
line-height: 0.095rem;
|
||||
margin-right: 0.095rem;
|
||||
}
|
||||
|
||||
.text_28 {
|
||||
margin-top: initial;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0.17rem;
|
||||
}
|
||||
|
||||
.text_8 {
|
||||
line-height: 0.14rem;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-right: 0.025rem;
|
||||
}
|
||||
|
||||
.view_6 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.text_15 {
|
||||
margin-bottom: initial;
|
||||
color: rgb(180, 182, 189);
|
||||
line-height: 1.2;
|
||||
margin-top: 0.08rem;
|
||||
width: 50%;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view_19 {
|
||||
margin-left: 0.045rem;
|
||||
}
|
||||
}
|
||||
|
||||
.guanzhu {
|
||||
.list-item {
|
||||
padding: 0.14rem 0 0.1rem;
|
||||
align-self: flex-end;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
width: 3.02rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text_3 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bottom-text-wrapper {
|
||||
margin-top: 0.1rem;
|
||||
padding: 0.035rem 0;
|
||||
color: rgb(115, 129, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
}
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.1rem;
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.center-text-wrapper {
|
||||
padding: 0.035rem 0.08rem;
|
||||
color: rgb(115, 129, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
display: inline-block;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
margin-right: 0.06rem;
|
||||
margin-top: 0.03rem;
|
||||
margin-left: 0.06rem;
|
||||
padding: 0.085rem 0.08rem;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.13rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgb(46, 155, 255);
|
||||
border-radius: 0.15rem;
|
||||
width: 0.85rem;
|
||||
height: 0.3rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text_8 {
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.text_10 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
margin-left: 0.05rem;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_14 {
|
||||
margin-left: 0.1rem;
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
// padding: 0.11rem 0 4.94rem;
|
||||
// flex: 1 1 auto;
|
||||
// overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
// margin-left: 0.17rem;
|
||||
padding: 0.11rem 0 0.12rem;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.18rem;
|
||||
line-height: 0.17rem;
|
||||
letter-spacing: 0.018rem;
|
||||
white-space: nowrap;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
margin-top: 0.08rem;
|
||||
background: #6574fc;
|
||||
border-radius: 50%;
|
||||
|
||||
text {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.list {}
|
||||
|
||||
.text_2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
box-shadow: 0px 0.005rem #eaeaea;
|
||||
padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
.group_4 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
margin-top: 0.015rem;
|
||||
}
|
||||
|
||||
.group_5 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_8 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_9 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-top: 0.12rem;
|
||||
}
|
||||
|
||||
.view_1 {
|
||||
margin-top: initial;
|
||||
padding: 0.1rem 0;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
margin-right: 0.04rem;
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image_2 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_4 {
|
||||
margin-top: initial;
|
||||
padding: 0.14rem 0 0.1rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_3 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_8 {
|
||||
margin-top: initial;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.13rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_4 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_12 {
|
||||
margin-top: initial;
|
||||
padding: 0.17rem 0 0.08rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_5 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_16 {
|
||||
margin-top: initial;
|
||||
padding: 0.15rem 0 0.11rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.16rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.image_6 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_19 {
|
||||
margin-top: initial;
|
||||
padding: 0.18rem 0 0.18rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.16rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
height: 0.67rem;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.text_7 {
|
||||
margin-left: initial;
|
||||
margin-right: initial;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
|
||||
text:nth-child(1) {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
max-width: 0.9rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.text_26 {
|
||||
margin-left: initial;
|
||||
margin-right: initial;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view_20 {
|
||||
left: -0.03rem;
|
||||
bottom: 0.075rem;
|
||||
}
|
||||
|
||||
.view_21 {
|
||||
margin-right: 0.06rem;
|
||||
margin-top: initial;
|
||||
padding: 0.09rem 0 0.06rem;
|
||||
color: rgb(73, 76, 87);
|
||||
background-color: initial;
|
||||
border: solid 0.01rem rgb(185, 185, 185);
|
||||
}
|
||||
}
|
||||
|
||||
.renzheng {
|
||||
padding-bottom: 0rem;
|
||||
|
||||
.section_4 {
|
||||
padding: 0.33rem 0 0.34rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
|
||||
.section_5 {
|
||||
margin-left: 0.18rem;
|
||||
margin-right: 0.24rem;
|
||||
padding: 0.2rem 0.18rem 0.18rem 0.24rem;
|
||||
background-color: rgb(246, 247, 250);
|
||||
justify-between: space-between;
|
||||
|
||||
.text_3 {
|
||||
color: rgb(160, 162, 172);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_4 {
|
||||
margin-left: 0.05rem;
|
||||
color: rgb(46, 155, 255);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-top: 0.76rem;
|
||||
padding-left: 0.8rem;
|
||||
padding-right: 0.69rem;
|
||||
color: rgb(163, 182, 202);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
white-space: nowrap;
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.02rem;
|
||||
margin-top: 0.13rem;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
margin-right: 0.14rem;
|
||||
width: 2.12rem;
|
||||
height: 1.89rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,908 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="page-title">消息</view>
|
||||
<view class="slot-wrap">
|
||||
<!-- <u-tabs-swiper ref="uTabs" :list="navList" :current="current" :is-scroll="false"
|
||||
:active-item-style="{ color: '#3CB5FB', fontSize: '0.18rem' }"
|
||||
:bar-style="{ background: '#3CB5FB' }" @change="tabsChange"></u-tabs-swiper> -->
|
||||
|
||||
<view class="tabs-item" v-for="(item, index) in navList" :key="index" @click="tabsClick(item.type)">
|
||||
<image :src="item.image"></image>
|
||||
<text>{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <swiper :current="swiperCurrent" @transition="transition" @animationfinish="animationfinish"
|
||||
style="height: calc(100vh - 2.1rem); width: 100%">
|
||||
<swiper-item class="swiper-item" v-for="(item, index) in navList" :key="index"
|
||||
style="height: calc(100vh - 2.1rem); width: 100%"> -->
|
||||
<scroll-view scroll-y="true" style="height: calc(100vh - 2.5rem - 56px); width: 100%"
|
||||
@scrolltolower="onreachBottom">
|
||||
<!-- 消息 -->
|
||||
<view class="msg" >
|
||||
<view class="flex-col renzheng" v-if="!vuex_user.isAttestationXY && false">
|
||||
<view class="flex-col section_4">
|
||||
<view class="flex-row section_5">
|
||||
<text class="text_3">待认证,请先完成认证~</text>
|
||||
<text class="text_4" @click="onAuto">去认证 >></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_3">
|
||||
<view class="flex-col">
|
||||
<image src="/static/common/img/16535374500948048054.png" class="image_1" />
|
||||
</view>
|
||||
<text class="text_5">暂无数据,请尽快认证补充资料~</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-col group_2">
|
||||
<view class="flex-col list">
|
||||
<navigator url="../adminList/adminList" class="list-item flex-row"
|
||||
style='box-shadow: 0px 0.005rem #eaeaea;' v-if='vuex_user.isAttestationGLY'>
|
||||
<u-avatar class="image_1" :show-sex="vuex_msgList.indexOf('admin') >= 0" sex-icon=""
|
||||
size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
src="/static/common/img/adminHeaderImg.png">
|
||||
</u-avatar>
|
||||
<view class="right-section flex-col">
|
||||
<view class="top-group justify-between">
|
||||
<text class="text_2">管理列表</text>
|
||||
<!-- <text class="text_4">8:52</text> -->
|
||||
</view>
|
||||
<text class="text_6" v-if="vuex_msgList.indexOf('admin') >= 0">你有新通知</text>
|
||||
<text class="text_6" v-else>暂无管理消息</text>
|
||||
</view>
|
||||
</navigator>
|
||||
<!-- <view v-if="!sysInfo"
|
||||
style="margin-top:20vh;display: flex;justify-content: center;align-items: center;flex-direction: column;">
|
||||
<image src="/static/common/img/empty.png" class="image" />
|
||||
<view style="color:grey;margin-top: -5vh;">暂无消息</view>
|
||||
</view> -->
|
||||
<view v-if="UserMsgList.length">
|
||||
<view v-for="(v, i) in UserMsgList" :key="i" class="list-item flex-row group_5"
|
||||
@click="GoChat(v.userId, v.chatType, v.userHead)">
|
||||
<u-avatar v-if='v.chatType == 0' class="image_1"
|
||||
:show-sex="vuex_msgList.indexOf(v.userId + ',') >= 0" sex-icon=""
|
||||
size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
:src="$u.http.config.imgUrl + v.userHead">
|
||||
</u-avatar>
|
||||
<u-avatar v-else class="image_1" :show-sex="vuex_msgList.indexOf('admin,') >= 0"
|
||||
sex-icon="" size="0.4rem" sex-bg-color="red" mode="circle"
|
||||
:src="$u.http.config.imgUrl + v.userHead">
|
||||
</u-avatar>
|
||||
<view class="right-section justify-between view_4">
|
||||
<view class="top-group flex-col view_5">
|
||||
<view class="text_2 flex-row view_6">
|
||||
<text class="text_11">{{ v.userNetName }}</text>
|
||||
<view class="right-text-wrapper flex-col items-end">
|
||||
<text class="text_13">{{ v.userSchoolName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="text_4 text_15">{{ v.message }}</text>
|
||||
</view>
|
||||
<view style='display:none'>
|
||||
{{ v.sendDate ? v.sendDate = v.sendDate.replace(/-/g, "/") : '' }}
|
||||
</view>
|
||||
<text
|
||||
v-if='new Date(v.sendDate) - 0 + (3600000 * 24) > new Date() && new Date(v.sendDate).getDate() == new Date().getDate()'
|
||||
class="text_6 text_16">{{ v.sendDate.slice(10, 16) }}</text>
|
||||
<text v-else class="text_6 text_16">{{ v.sendDate.slice(5, 10) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<no-data v-else type="message"></no-data>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- </swiper-item>
|
||||
</swiper> -->
|
||||
|
||||
<!-- 使用自定义TabBar -->
|
||||
<custom-tab-bar></custom-tab-bar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import noData from '@/components/NoData.vue'
|
||||
import CustomTabBar from '@/components/custom-tab-bar/custom-tab-bar.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
noData,
|
||||
CustomTabBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabbar: "",
|
||||
swiperCurrent: 0,
|
||||
current: 0,
|
||||
UserMsgList: [],
|
||||
UserFollowList: [],
|
||||
navList: [{
|
||||
name: "互动消息",
|
||||
image: "/static/common/img/message/interactive.png",
|
||||
type: 0
|
||||
},
|
||||
{
|
||||
name: "系统消息",
|
||||
image: "/static/common/img/message/system.png",
|
||||
type: 1
|
||||
},
|
||||
{
|
||||
name: "新增关注",
|
||||
image: "/static/common/img/message/attention.png",
|
||||
type: 2
|
||||
},
|
||||
],
|
||||
interInfo: '',
|
||||
sysInfo: '',
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
current() {
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// 和onShow 重复
|
||||
// this.getList()
|
||||
},
|
||||
onLoad() {
|
||||
//接收数据
|
||||
this.$connection.on("ReceiveMessage", (user, message) => {
|
||||
this.getList()
|
||||
});
|
||||
this.$connection.on("InteractMessage", (data, type) => {
|
||||
this.getList()
|
||||
});
|
||||
this.$connection.on("SystemMessage", (title, content, time) => {
|
||||
this.getList()
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
this.getList()
|
||||
|
||||
},
|
||||
methods: {
|
||||
tabsClick(type) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
this.$u.route({url:'/pages/message/interactionList/interactionList'})
|
||||
break;
|
||||
case 1:
|
||||
this.$u.route({url:'/pages/message/sysList/sysList'})
|
||||
break;
|
||||
case 2:
|
||||
this.$u.route({url:'/pages/message/attentionList/attentionList'})
|
||||
break;
|
||||
|
||||
}
|
||||
},
|
||||
toDetail(id, role, head) {
|
||||
if (role == 0) {
|
||||
this.$u.route({
|
||||
url: '/pages/AlumniCircle/userDetail/userDetail?id=' + id + '&type=0'
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: "../dialogBox/dialogBox?id=" + id + '&chatType=1&type=0&head=' + head,
|
||||
});
|
||||
}
|
||||
},
|
||||
getList() {
|
||||
if (this.current == 0) {
|
||||
this.charList();
|
||||
} else {
|
||||
this.FollowList();
|
||||
}
|
||||
},
|
||||
onAuto() {
|
||||
this.$u.route({
|
||||
url: "pages/my/ShoolList/ShoolList"
|
||||
})
|
||||
},
|
||||
charList() {
|
||||
const data = {
|
||||
id: this.vuex_user.id,
|
||||
userRole: this.vuex_user.isAttestationGLY ? 1 : 0
|
||||
}
|
||||
this.$u.api.getcharList(data).then(res => {
|
||||
this.UserMsgList = res
|
||||
})
|
||||
this.$u.api.getinteractionList(-1).then(res => {
|
||||
this.interInfo = res[0]
|
||||
})
|
||||
this.$u.api.getSysList().then(res => {
|
||||
this.sysInfo = res[0]
|
||||
})
|
||||
},
|
||||
FollowList() {
|
||||
this.$u.api.getFollowList().then(res => {
|
||||
|
||||
this.UserFollowList = res
|
||||
})
|
||||
},
|
||||
// 去聊天
|
||||
GoChat(id, chatType, head) {
|
||||
uni.navigateTo({
|
||||
url: "../dialogBox/dialogBox?id=" + id + '&chatType=' + chatType + '&type=0&head=' + head,
|
||||
});
|
||||
},
|
||||
// tabs通知swiper切换
|
||||
tabsChange(index) {
|
||||
this.swiperCurrent = index;
|
||||
},
|
||||
// swiper-item左右移动,通知tabs的滑块跟随移动
|
||||
transition(e) {
|
||||
let dx = e.detail.dx;
|
||||
this.$refs.uTabs.setDx(dx);
|
||||
},
|
||||
// 由于swiper的内部机制问速切题,快换swiper不会触发dx的连续变化,需要在结束时重置状态
|
||||
// swiper滑动结束,分别设置tabs和swiper的状态
|
||||
animationfinish(e) {
|
||||
let current = e.detail.current;
|
||||
this.$refs.uTabs.setFinishCurrent(current);
|
||||
this.swiperCurrent = current;
|
||||
this.current = current;
|
||||
},
|
||||
// scroll-view到底部加载更多
|
||||
onreachBottom() { },
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .u-avatar__sex {
|
||||
width: 0.1rem !important;
|
||||
height: 0.1rem !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.msg {
|
||||
.list-item {
|
||||
padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.right-section {
|
||||
margin-left: 0.15rem;
|
||||
padding: 0.14rem 0 0.18rem;
|
||||
flex: 1 1 auto;
|
||||
height: 0.66rem;
|
||||
}
|
||||
|
||||
.top-group {
|
||||
margin-right: 0.1rem;
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-top: 0.09rem;
|
||||
color: rgb(180, 182, 189);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.12rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_2 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_4 {
|
||||
margin-bottom: 0.05rem;
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.095rem;
|
||||
letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_11 {
|
||||
// margin-bottom: 0.03rem;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 36rpx;
|
||||
// letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
max-width: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
padding: 0.035rem 0.08rem;
|
||||
color: #3CB5FB;
|
||||
margin-left: 0.1rem;
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(60,181,251,0.1);
|
||||
border-radius: 0.09rem;
|
||||
height: 0.18rem;
|
||||
}
|
||||
|
||||
.text_13 {
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
// .group_2 {
|
||||
// padding-bottom: 0.15rem;
|
||||
// flex: 1 1 auto;
|
||||
// overflow-y: auto;
|
||||
// }
|
||||
|
||||
.group_4 {
|
||||
padding: 0 0.01rem;
|
||||
padding-left: 0.2rem;
|
||||
box-shadow: 0px 0.005rem #eaeaea;
|
||||
}
|
||||
|
||||
.group_5 {
|
||||
padding-left: 0.2rem;
|
||||
margin-top: 0.015rem;
|
||||
box-shadow: 0px 0.005rem #eaeaea;
|
||||
}
|
||||
|
||||
.group_4:active,
|
||||
.group_5:active {
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.group_8 {
|
||||
padding-left: initial;
|
||||
}
|
||||
|
||||
.view_2 {
|
||||
margin-left: 0.15rem;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.view_4 {
|
||||
margin-left: 0.16rem;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.view_3 {
|
||||
margin-right: initial;
|
||||
}
|
||||
|
||||
.view_5 {
|
||||
margin-right: initial;
|
||||
}
|
||||
|
||||
.text_16 {
|
||||
margin-top: 0.0rem;
|
||||
color: rgb(193, 196, 204);
|
||||
line-height: 0.095rem;
|
||||
margin-right: 0.095rem;
|
||||
}
|
||||
|
||||
.text_28 {
|
||||
margin-top: initial;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0.17rem;
|
||||
}
|
||||
|
||||
.text_8 {
|
||||
line-height: 0.14rem;
|
||||
}
|
||||
|
||||
.text_9 {
|
||||
margin-right: 0.025rem;
|
||||
}
|
||||
|
||||
.view_6 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.text_15 {
|
||||
margin-bottom: initial;
|
||||
color: rgb(180, 182, 189);
|
||||
line-height: 1.2;
|
||||
margin-top: 0.08rem;
|
||||
width: 50%;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view_19 {
|
||||
margin-left: 0.045rem;
|
||||
}
|
||||
}
|
||||
|
||||
.guanzhu {
|
||||
.list-item {
|
||||
padding: 0.14rem 0 0.1rem;
|
||||
align-self: flex-end;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
width: 3.02rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text_3 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bottom-text-wrapper {
|
||||
margin-top: 0.1rem;
|
||||
padding: 0.035rem 0;
|
||||
color: rgb(115, 129, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
}
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.1rem;
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.center-text-wrapper {
|
||||
padding: 0.035rem 0.08rem;
|
||||
color: rgb(115, 129, 255);
|
||||
font-size: 0.11rem;
|
||||
line-height: 0.11rem;
|
||||
letter-spacing: 0.011rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgba(115, 129, 255, 0.1);
|
||||
border-radius: 0.09rem;
|
||||
display: inline-block;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.right-text-wrapper {
|
||||
margin-right: 0.06rem;
|
||||
margin-top: 0.03rem;
|
||||
margin-left: 0.06rem;
|
||||
padding: 0.085rem 0.08rem;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.13rem;
|
||||
white-space: nowrap;
|
||||
background-color: rgb(46, 155, 255);
|
||||
border-radius: 0.15rem;
|
||||
width: 0.85rem;
|
||||
height: 0.3rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text_8 {
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.text_10 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.text_12 {
|
||||
margin-left: 0.05rem;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_14 {
|
||||
margin-left: 0.1rem;
|
||||
margin-right: 0.03rem;
|
||||
}
|
||||
|
||||
.group_2 {
|
||||
// padding: 0.11rem 0 4.94rem;
|
||||
// flex: 1 1 auto;
|
||||
// overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-wrapper {
|
||||
// margin-left: 0.17rem;
|
||||
padding: 0.11rem 0 0.12rem;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 0.18rem;
|
||||
line-height: 0.17rem;
|
||||
letter-spacing: 0.018rem;
|
||||
white-space: nowrap;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
margin-top: 0.08rem;
|
||||
background: #6574fc;
|
||||
border-radius: 50%;
|
||||
|
||||
text {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.list {}
|
||||
|
||||
.text_2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
box-shadow: 0px 0.005rem #eaeaea;
|
||||
padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
.group_4 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
margin-top: 0.015rem;
|
||||
}
|
||||
|
||||
.group_5 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_6 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_7 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_8 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.group_9 {
|
||||
padding: initial;
|
||||
align-self: initial;
|
||||
box-shadow: initial;
|
||||
width: initial;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-top: 0.12rem;
|
||||
}
|
||||
|
||||
.view_1 {
|
||||
margin-top: initial;
|
||||
padding: 0.1rem 0;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
margin-right: 0.04rem;
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image_2 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_4 {
|
||||
margin-top: initial;
|
||||
padding: 0.14rem 0 0.1rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_3 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_8 {
|
||||
margin-top: initial;
|
||||
padding: 0.15rem 0 0.14rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.13rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_4 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_12 {
|
||||
margin-top: initial;
|
||||
padding: 0.17rem 0 0.08rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.12rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
}
|
||||
|
||||
.image_5 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_16 {
|
||||
margin-top: initial;
|
||||
padding: 0.15rem 0 0.11rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.16rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
height: 0.67rem;
|
||||
}
|
||||
|
||||
.image_6 {
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
align-self: center;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
}
|
||||
|
||||
.view_19 {
|
||||
margin-top: initial;
|
||||
padding: 0.18rem 0 0.18rem;
|
||||
color: initial;
|
||||
font-size: initial;
|
||||
line-height: initial;
|
||||
letter-spacing: initial;
|
||||
white-space: initial;
|
||||
background-color: initial;
|
||||
border-radius: initial;
|
||||
margin-left: 0.16rem;
|
||||
flex: 1 1 auto;
|
||||
box-shadow: 0px 0.005rem rgb(234, 234, 234);
|
||||
height: 0.67rem;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.text_7 {
|
||||
margin-left: initial;
|
||||
margin-right: initial;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
|
||||
text:nth-child(1) {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
max-width: 0.9rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.text_26 {
|
||||
margin-left: initial;
|
||||
margin-right: initial;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.14rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.view_20 {
|
||||
left: -0.03rem;
|
||||
bottom: 0.075rem;
|
||||
}
|
||||
|
||||
.view_21 {
|
||||
margin-right: 0.06rem;
|
||||
margin-top: initial;
|
||||
padding: 0.09rem 0 0.06rem;
|
||||
color: rgb(73, 76, 87);
|
||||
background-color: initial;
|
||||
border: solid 0.01rem rgb(185, 185, 185);
|
||||
}
|
||||
}
|
||||
|
||||
.renzheng {
|
||||
padding-bottom: 0rem;
|
||||
|
||||
.section_4 {
|
||||
padding: 0.33rem 0 0.34rem;
|
||||
background-color: rgb(255, 255, 255);
|
||||
|
||||
.section_5 {
|
||||
margin-left: 0.18rem;
|
||||
margin-right: 0.24rem;
|
||||
padding: 0.2rem 0.18rem 0.18rem 0.24rem;
|
||||
background-color: rgb(246, 247, 250);
|
||||
justify-between: space-between;
|
||||
|
||||
.text_3 {
|
||||
color: rgb(160, 162, 172);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
letter-spacing: 0.015rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_4 {
|
||||
margin-left: 0.05rem;
|
||||
color: rgb(46, 155, 255);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group_3 {
|
||||
margin-top: 0.76rem;
|
||||
padding-left: 0.8rem;
|
||||
padding-right: 0.69rem;
|
||||
color: rgb(163, 182, 202);
|
||||
font-size: 0.15rem;
|
||||
line-height: 0.15rem;
|
||||
white-space: nowrap;
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.02rem;
|
||||
margin-top: 0.13rem;
|
||||
}
|
||||
|
||||
.image_1 {
|
||||
margin-right: 0.14rem;
|
||||
width: 2.12rem;
|
||||
height: 1.89rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.page-title{
|
||||
padding: 50rpx ;
|
||||
color: #000;
|
||||
font-size: 36rpx;
|
||||
font-weight: 800;
|
||||
background: #ceedff; /* fallback for old browsers */
|
||||
background: -webkit-linear-gradient(to top,#f6f7fa, #ceedff); /* Chrome 10-25, Safari 5.1-6 */
|
||||
background: linear-gradient(to top,#f6f7fa, #ceedff); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
||||
|
||||
}
|
||||
|
||||
.slot-wrap {
|
||||
// height: 220rpx;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
padding: 0 60rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.tabs-item {
|
||||
width: 112rpx;
|
||||
height: 168rpx;
|
||||
// padding-top: 50rpx;
|
||||
// margin-bottom: 50rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
image {
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<template>
|
||||
<view>
|
||||
<u-navbar back-text="" title="系统消息"></u-navbar>
|
||||
<scroll-view scroll-y="true" class="scroll">
|
||||
<view class="flex-col list" v-if='list.length'>
|
||||
<view class="list-item flex-col group_1" :key="i" v-for="(item, i) in list">
|
||||
<view class="items-title">
|
||||
<image src="/static/common/img/message/system.png" mode=""></image>
|
||||
<view class="title-time">
|
||||
<text class="title-text">系统消息</text>
|
||||
<text class="text_1">{{ item.messageDate }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-section flex-col">
|
||||
<text class="text_3">{{ item.title }}</text>
|
||||
<text class="text_5">{{ item.message.split(',未通过原因:')[0] }}</text>
|
||||
<text v-if="item.message.split(',未通过原因:').length >= 2" class="text_6">驳回理由:<text>{{
|
||||
item.message.split(',未通过原因:')[1] }}</text></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else style='padding-top:30vh'>
|
||||
<no-data text="暂无系统消息"></no-data>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import noData from '@/components/NoData.vue';
|
||||
export default {
|
||||
components: {
|
||||
noData
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: []
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
this.getList()
|
||||
//接收系统消息
|
||||
this.$connection.on("SystemMessage", (title, content, time) => {
|
||||
if (this.route.indexOf('sysList') >= 0) {
|
||||
this.list.unshift({
|
||||
messageDate: time,
|
||||
title: title,
|
||||
message: content
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
onShow() {
|
||||
if (this.vuex_msgList) {
|
||||
var msgList = this.vuex_msgList;
|
||||
msgList = msgList.replace('SystemMessage,', '')
|
||||
this.$u.vuex('vuex_msgList', msgList)
|
||||
if (!msgList) {
|
||||
var tab = this.vuex_tabbar;
|
||||
tab[1].isDot = false;
|
||||
this.$u.vuex('vuex_tabbar', tab);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.$u.api.getSysList().then(res => {
|
||||
this.list = res
|
||||
})
|
||||
},
|
||||
//返回上一级
|
||||
router() {
|
||||
uni.switchTab({
|
||||
url: '../../message/msgList/msgList'
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scroll {
|
||||
background-color: rgb(246, 247, 250);
|
||||
width: 100%;
|
||||
height: calc(100vh - 0.44rem);
|
||||
}
|
||||
|
||||
.list-item {
|
||||
margin-top: 0.1rem;
|
||||
padding: 20rpx;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.items-title {
|
||||
display: flex;
|
||||
padding-bottom: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-bottom: 2rpx solid #F6F8F9;
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.title-time {
|
||||
margin-left: 0.1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 5rpx 0;
|
||||
// align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
.text_1 {
|
||||
// align-self: center;
|
||||
color: rgb(193, 196, 204);
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.095rem;
|
||||
// letter-spacing: 0.012rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bottom-section {
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
.text_3 {
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 28rpx;
|
||||
line-height: 0.16rem;
|
||||
// letter-spacing: 0.018rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text_5 {
|
||||
margin-left: 0.015rem;
|
||||
margin-top: 0.1rem;
|
||||
color: #908a8a;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.5;
|
||||
// letter-spacing: 0.01rem;
|
||||
// text-indent: 0.3rem;
|
||||
}
|
||||
|
||||
.text_6 {
|
||||
margin-top: 0.1rem;
|
||||
font-size: 0.16rem;
|
||||
line-height: 0.16rem;
|
||||
letter-spacing: 0.01rem;
|
||||
|
||||
text {
|
||||
margin-left: 0.015rem;
|
||||
text-indent: 0.3rem;
|
||||
display: block;
|
||||
color: #908a8a;
|
||||
font-size: 0.14rem;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0.18rem 0.15rem;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,99 +1,89 @@
|
|||
<template>
|
||||
<view class="page-container">
|
||||
<view class="page-header">
|
||||
<PageHeader
|
||||
title="我的"
|
||||
:is-back="false"
|
||||
:border-bottom="false"
|
||||
:background="headerBackground"
|
||||
/>
|
||||
</view>
|
||||
<div class="my-page">
|
||||
<PageHeader
|
||||
title="我的"
|
||||
:is-back="false"
|
||||
:border-bottom="false"
|
||||
:background="headerBackground"
|
||||
/>
|
||||
|
||||
<scroll-view
|
||||
class="page-main"
|
||||
scroll-y
|
||||
enable-back-to-top
|
||||
>
|
||||
<view class="main-content">
|
||||
<div class="user-info">
|
||||
<div class="avatar">
|
||||
<!-- <img src="" alt="用户头像" /> -->
|
||||
<div class="content-wrapper">
|
||||
<div class="user-info">
|
||||
<div class="avatar">
|
||||
<!-- <img src="" alt="用户头像" /> -->
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="name">{{ teacherInfo.name }}</div>
|
||||
<div class="tag">
|
||||
<image
|
||||
class="tag-icon"
|
||||
src="@/static/notes/collage-icon.png"
|
||||
></image>
|
||||
{{ teacherInfo.collegeName }}
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="name">{{ teacherInfo.name }}</div>
|
||||
<div class="tag">
|
||||
<image
|
||||
class="tag-icon"
|
||||
src="@/static/notes/collage-icon.png"
|
||||
></image>
|
||||
{{ teacherInfo.collegeName }}
|
||||
</div>
|
||||
<div class="tag">
|
||||
<image class="tag-icon" src="@/static/notes/major-icon.png"></image>
|
||||
{{ teacherInfo.professionalName }}
|
||||
</div>
|
||||
<div class="tag">
|
||||
<image class="tag-icon" src="@/static/notes/major-icon.png"></image>
|
||||
{{ teacherInfo.professionalName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="statistics">
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">36</div>
|
||||
<div class="stat-label">总答题</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">10</div>
|
||||
<div class="stat-label">已完成</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">26</div>
|
||||
<div class="stat-label">未回复</div>
|
||||
</div>
|
||||
<div class="statistics">
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">36</div>
|
||||
<div class="stat-label">总答题</div>
|
||||
</div>
|
||||
|
||||
<div class="banner">
|
||||
<img src="@/static/notes/banner.png" alt="banner" />
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">10</div>
|
||||
<div class="stat-label">已完成</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-list">
|
||||
<div class="menu-item" @click="navigateTo('personal-info')">
|
||||
<div class="menu-icon">
|
||||
<image src="@/static/notes/menu1.png" class="menu-icon-img"></image>
|
||||
</div>
|
||||
<div class="menu-text">个人信息</div>
|
||||
<view class="arrow-icon">
|
||||
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
|
||||
</view>
|
||||
</div>
|
||||
<div class="menu-item" @click="navigateTo('change-password')">
|
||||
<div class="menu-icon">
|
||||
<image src="@/static/notes/menu2.png" class="menu-icon-img"></image>
|
||||
</div>
|
||||
<div class="menu-text">修改密码</div>
|
||||
<view class="arrow-icon">
|
||||
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
|
||||
</view>
|
||||
</div>
|
||||
<div class="menu-item" @click="navigateTo('logout-records')">
|
||||
<div class="menu-icon">
|
||||
<image src="@/static/notes/menu3.png" class="menu-icon-img"></image>
|
||||
</div>
|
||||
<div class="menu-text">退出登录</div>
|
||||
<view class="arrow-icon">
|
||||
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
|
||||
</view>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-num">26</div>
|
||||
<div class="stat-label">未回复</div>
|
||||
</div>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</div>
|
||||
|
||||
<view class="page-tabbar">
|
||||
<TabBar :currentPath="'/pages/my/index'" @change="handleTabChange" />
|
||||
</view>
|
||||
</view>
|
||||
<div class="banner">
|
||||
<img src="@/static/notes/banner.png" alt="banner" />
|
||||
</div>
|
||||
|
||||
<div class="menu-list">
|
||||
<div class="menu-item" @click="navigateTo('personal-info')">
|
||||
<div class="menu-icon">
|
||||
<image src="@/static/notes/menu1.png" class="menu-icon-img"></image>
|
||||
</div>
|
||||
<div class="menu-text">个人信息</div>
|
||||
<view class="arrow-icon">
|
||||
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
|
||||
</view>
|
||||
</div>
|
||||
<div class="menu-item" @click="navigateTo('change-password')">
|
||||
<div class="menu-icon">
|
||||
<image src="@/static/notes/menu2.png" class="menu-icon-img"></image>
|
||||
</div>
|
||||
<div class="menu-text">修改密码</div>
|
||||
<view class="arrow-icon">
|
||||
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
|
||||
</view>
|
||||
</div>
|
||||
<div class="menu-item" @click="navigateTo('logout-records')">
|
||||
<div class="menu-icon">
|
||||
<image src="@/static/notes/menu3.png" class="menu-icon-img"></image>
|
||||
</div>
|
||||
<div class="menu-text">退出登录</div>
|
||||
<view class="arrow-icon">
|
||||
<u-icon name="arrow-right" color="#999" size="24"></u-icon>
|
||||
</view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabBar :currentPath="'/pages/my/index'" @change="handleTabChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabBar from "@/components/TabBar-optimized.vue";
|
||||
import TabBar from "@/components/TabBar.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
|
||||
export default {
|
||||
|
|
@ -159,69 +149,24 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ===== 页面容器 - 主流三段式布局 ===== */
|
||||
.page-container {
|
||||
/* 固定定位,占满整个视口 */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
/* Flex 布局 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* 背景图片(保留特色) */
|
||||
.my-page {
|
||||
height: 100vh;
|
||||
/* background-color: #f5f6fa; */
|
||||
background-image: url("@/static/notes/bg.png");
|
||||
background-position: center top;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% auto;
|
||||
|
||||
/* 防止溢出 */
|
||||
overflow: hidden;
|
||||
background-size: 100% auto; /* 以宽度为基准等比例缩放 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ===== 头部导航 ===== */
|
||||
.page-header {
|
||||
/* 不收缩,固定高度 */
|
||||
flex-shrink: 0;
|
||||
|
||||
/* 层级 */
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* ===== 内容区域 ===== */
|
||||
.page-main {
|
||||
/* 占据剩余空间 */
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
|
||||
/* 重要:防止 flex 子元素溢出 */
|
||||
height: 0;
|
||||
|
||||
/* 允许滚动 */
|
||||
overflow-y: auto;
|
||||
|
||||
/* iOS 滚动优化 */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* ===== 内容内层 ===== */
|
||||
.main-content {
|
||||
/* 底部留出 TabBar 空间 + 安全区域 */
|
||||
padding-bottom: calc(50px + env(safe-area-inset-bottom));
|
||||
|
||||
/* 最小高度(确保可以滚动) */
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* ===== 底部导航 ===== */
|
||||
.page-tabbar {
|
||||
/* 不收缩,固定高度 */
|
||||
flex-shrink: 0;
|
||||
|
||||
/* 层级 */
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 60px; /* 为底部导航栏预留空间 */
|
||||
overflow: hidden; /* 防止滚动条出现 */
|
||||
}
|
||||
|
||||
.user-info {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,466 @@
|
|||
<template>
|
||||
<view class="notes-page">
|
||||
<PageHeader title="留言板" :is-back="false" :border-bottom="false" />
|
||||
<view class="custom-tabs-box">
|
||||
<u-tabs
|
||||
:list="tabList"
|
||||
:current="tabCurrent"
|
||||
@change="onTabChange"
|
||||
:is-scroll="false"
|
||||
:height="80"
|
||||
:font-size="28"
|
||||
active-color="#4A6CF7"
|
||||
inactive-color="#333333"
|
||||
:bar-width="120"
|
||||
:bar-height="4"
|
||||
bg-color="#ffffff"
|
||||
:item-width="200"
|
||||
></u-tabs>
|
||||
<view class="space"></view>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域包装器 -->
|
||||
<view class="content-wrapper">
|
||||
<!-- 留言列表 -->
|
||||
<scroll-view class="message-list" scroll-y>
|
||||
<view v-for="(item, index) in currentMessages" :key="index">
|
||||
<u-swipe-action
|
||||
:options="swipeOptions"
|
||||
:show="item.show"
|
||||
:index="index"
|
||||
@click="handleSwipeClick"
|
||||
@open="(e) => handleSwipeOpen(e, index)"
|
||||
@close="(e) => handleSwipeClose(e, index)"
|
||||
@content-click="() => closeOther(index)"
|
||||
:btn-width="80"
|
||||
:disabled="false"
|
||||
>
|
||||
<view class="message-item">
|
||||
<view class="message-header">
|
||||
<view class="user-info">
|
||||
<image
|
||||
class="avatar"
|
||||
:src="item.avatar || defaultAvatar"
|
||||
></image>
|
||||
<text class="username">{{ item.username }}</text>
|
||||
</view>
|
||||
<view class="message-time">{{ item.time }}</view>
|
||||
</view>
|
||||
|
||||
<view class="message-content">
|
||||
<view class="question">
|
||||
<view class="question-icon">问</view>
|
||||
<view class="question-text">{{ item.question }}</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="reply-button"
|
||||
@click.stop="handleReply(item)"
|
||||
v-if="!item.reply"
|
||||
>
|
||||
<u-icon name="chat" color="#4a6cf7" size="16"></u-icon>
|
||||
<text>回复</text>
|
||||
</view>
|
||||
|
||||
<view class="reply-content" v-if="item.reply">
|
||||
<view class="reply-header">
|
||||
<view class="reply-icon">答</view>
|
||||
<view class="reply-info">
|
||||
<text>{{ item.replyUser }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="reply-text"
|
||||
:class="{ expanded: item.expanded }"
|
||||
>{{ item.reply }}</view
|
||||
>
|
||||
<view
|
||||
class="expand-button"
|
||||
v-if="item.reply.length > 100"
|
||||
@click.stop="toggleExpand(item)"
|
||||
>
|
||||
{{ item.expanded ? "收起" : "展开" }}
|
||||
<u-icon
|
||||
:name="item.expanded ? 'arrow-up' : 'arrow-down'"
|
||||
size="12"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="reply-button"
|
||||
@click.stop="handleReply(item)"
|
||||
v-if="item.reply"
|
||||
>
|
||||
<u-icon name="chat" color="#4a6cf7" size="16"></u-icon>
|
||||
<text>回复</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-swipe-action>
|
||||
</view>
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<view class="empty-tip" v-if="currentMessages.length === 0">
|
||||
暂无留言数据
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 使用TabBar组件 -->
|
||||
<TabBar :currentPath="'/pages/notes/index'" @change="handleTabChange" />
|
||||
|
||||
<!-- 开发中提示弹窗 -->
|
||||
<u-modal
|
||||
v-model="showDevModal"
|
||||
:show-cancel-button="false"
|
||||
title="提示"
|
||||
content="该功能正在开发中,敬请期待!"
|
||||
@confirm="showDevModal = false"
|
||||
></u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabBar from "@/components/TabBar.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
|
||||
export default {
|
||||
name: "NotesPage",
|
||||
components: {
|
||||
TabBar,
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabList: [{ name: "未回复" }, { name: "已回复" }],
|
||||
tabCurrent: 0,
|
||||
activeTab: "unread",
|
||||
showDevModal: false,
|
||||
defaultAvatar: "/static/avatar/default-avatar.png",
|
||||
unreadMessages: [
|
||||
{
|
||||
id: 1,
|
||||
avatar: "",
|
||||
username: "浙江理工生13024",
|
||||
time: "2023/6/26 15:45:12",
|
||||
question: "学校在录取时有没有一些专业会有特殊要求?",
|
||||
reply: "",
|
||||
expanded: false,
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
avatar: "",
|
||||
username: "理工13024",
|
||||
time: "2023/6/26 15:45:12",
|
||||
question: "在录取时有没有一些专业会有特殊要求?",
|
||||
reply: "",
|
||||
expanded: false,
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
repliedMessages: [
|
||||
{
|
||||
id: 3,
|
||||
avatar: "",
|
||||
username: "浙江理工生13024",
|
||||
time: "2023/6/26 15:45:12",
|
||||
question: "学校在录取时有没有一些专业会有特殊要求?",
|
||||
reply:
|
||||
"学生与体健康状况必须符合教育部、卫生部、中国残疾人联合会印发的《普通高等学校招生体检工作指导意见》和人力资源社会保障部、原卫生部、原教育部、原公安部、原国家人口计划生育委员会制定的有关规定。原卫生部、原教育部、原公安部、原国家人口计划生育委员会制定的有关规定。",
|
||||
replyUser: "系统回复",
|
||||
expanded: false,
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
swipeOptions: [
|
||||
{
|
||||
text: "删除",
|
||||
style: {
|
||||
backgroundColor: "#fa3534",
|
||||
color: "#ffffff",
|
||||
fontSize: "15px",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentMessages() {
|
||||
return this.activeTab === "unread"
|
||||
? this.unreadMessages
|
||||
: this.repliedMessages;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onTabChange(index) {
|
||||
this.tabCurrent = index;
|
||||
// 根据索引设置activeTab值
|
||||
this.activeTab = index === 0 ? "unread" : "replied";
|
||||
// 切换选项卡时关闭所有打开的滑动按钮
|
||||
this.closeAllSwipe();
|
||||
},
|
||||
switchTab(tab) {
|
||||
this.activeTab = tab;
|
||||
// 更新tabCurrent以匹配activeTab
|
||||
this.tabCurrent = tab === "unread" ? 0 : 1;
|
||||
// 切换选项卡时关闭所有打开的滑动按钮
|
||||
this.closeAllSwipe();
|
||||
},
|
||||
handleTabChange(path, index) {
|
||||
console.log("切换到标签页:", path, index);
|
||||
},
|
||||
handleReply(item) {
|
||||
this.showDevModal = true;
|
||||
},
|
||||
toggleExpand(item) {
|
||||
item.expanded = !item.expanded;
|
||||
},
|
||||
handleSwipeClick(e) {
|
||||
const { index } = e; // 当前点击的滑动单元格索引
|
||||
const btnIndex = e.index; // 点击的按钮索引
|
||||
|
||||
if (btnIndex === 0) {
|
||||
// 删除按钮
|
||||
// 删除当前消息
|
||||
if (this.activeTab === "unread") {
|
||||
this.unreadMessages.splice(index, 1);
|
||||
} else {
|
||||
this.repliedMessages.splice(index, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleSwipeOpen(e, index) {
|
||||
// 打开滑动按钮时,关闭其他打开的滑动按钮
|
||||
this.closeOther(index);
|
||||
},
|
||||
handleSwipeClose(e, index) {
|
||||
// 处理滑动关闭事件
|
||||
if (this.activeTab === "unread") {
|
||||
this.unreadMessages[index].show = false;
|
||||
} else {
|
||||
this.repliedMessages[index].show = false;
|
||||
}
|
||||
},
|
||||
closeOther(index) {
|
||||
// 关闭除当前外的所有滑动按钮
|
||||
if (this.activeTab === "unread") {
|
||||
this.unreadMessages.forEach((item, idx) => {
|
||||
if (idx !== index) {
|
||||
item.show = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.repliedMessages.forEach((item, idx) => {
|
||||
if (idx !== index) {
|
||||
item.show = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
closeAllSwipe() {
|
||||
// 关闭所有滑动按钮
|
||||
this.unreadMessages.forEach((item) => {
|
||||
item.show = false;
|
||||
});
|
||||
this.repliedMessages.forEach((item) => {
|
||||
item.show = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notes-page {
|
||||
height: 100vh;
|
||||
background-color: #f5f6fa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.custom-tabs-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.space {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.custom-tabs-box >>> .u-tabs__wrapper__nav__item {
|
||||
padding: 0 20px 0 0 !important; /* 调整标签项的padding */
|
||||
}
|
||||
|
||||
.custom-tabs-box >>> .u-tabs__wrapper__nav__line {
|
||||
bottom: 0 !important; /* 确保下划线位于底部 */
|
||||
height: 4px !important; /* 设置下划线高度 */
|
||||
border-radius: 2px !important; /* 圆角 */
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 60px; /* 为底部导航栏预留空间 */
|
||||
overflow-y: auto; /* 允许内容滚动 */
|
||||
}
|
||||
|
||||
/* 留言列表样式 */
|
||||
.message-list {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
background-color: #fff;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.question {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.question-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: #4a6cf7;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.question-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.reply-button {
|
||||
height: 32px;
|
||||
background-color: #f0f2fd;
|
||||
color: #4a6cf7;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reply-button text {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.reply-content {
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.reply-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.reply-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: #ff9500;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.reply-info {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.reply-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
margin-left: 28px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.reply-text.expanded {
|
||||
-webkit-line-clamp: unset;
|
||||
}
|
||||
|
||||
.expand-button {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
color: #4a6cf7;
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
<template>
|
||||
<view class="page-container">
|
||||
<view class="page-header">
|
||||
<PageHeader title="人工转接" :is-back="false" :border-bottom="false" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="page-main"
|
||||
scroll-y
|
||||
enable-back-to-top
|
||||
>
|
||||
<view class="main-content">
|
||||
<!-- 服务状态卡片 -->
|
||||
<view class="status-card">
|
||||
<view class="status-title">人工服务</view>
|
||||
<view class="status-content">
|
||||
<view class="status-badge" :class="isOnline ? 'online' : 'offline'">
|
||||
{{ isOnline ? '在线' : '离线' }}
|
||||
</view>
|
||||
<view class="status-desc">
|
||||
{{ isOnline ? '客服正在为您服务' : '当前暂无客服在线' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 人工客服列表 -->
|
||||
<view class="section-title">在线客服</view>
|
||||
<view class="agent-list">
|
||||
<view class="agent-item" v-for="(agent, index) in agentList" :key="index" @click="handleTransfer(agent)">
|
||||
<view class="agent-avatar-wrapper">
|
||||
<image class="agent-avatar" :src="agent.avatar || defaultAvatar"></image>
|
||||
<view class="online-dot" v-if="agent.online"></view>
|
||||
</view>
|
||||
<view class="agent-info">
|
||||
<view class="agent-name">{{ agent.name }}</view>
|
||||
<view class="agent-status">{{ agent.statusText }}</view>
|
||||
</view>
|
||||
<view class="agent-action">
|
||||
<u-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
:disabled="!agent.online"
|
||||
@click.stop="handleTransfer(agent)"
|
||||
>
|
||||
{{ agent.online ? '转接' : '离线' }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<view class="empty-tip" v-if="agentList.length === 0">
|
||||
暂无客服在线
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="page-tabbar">
|
||||
<TabBar :currentPath="'/pages/manual-transfer/index'" @change="handleTabChange" />
|
||||
</view>
|
||||
|
||||
<u-modal
|
||||
v-model="showModal"
|
||||
:show-cancel-button="true"
|
||||
title="人工转接"
|
||||
:content="modalContent"
|
||||
@confirm="confirmTransfer"
|
||||
@cancel="showModal = false"
|
||||
></u-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TabBar from "@/components/TabBar-optimized.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
|
||||
export default {
|
||||
name: "TransferPage",
|
||||
components: {
|
||||
TabBar,
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
modalContent: '',
|
||||
selectedAgent: null,
|
||||
defaultAvatar: "/static/avatar/default-avatar.png",
|
||||
isOnline: true,
|
||||
agentList: [
|
||||
{
|
||||
id: 1,
|
||||
name: "客服小王",
|
||||
avatar: "",
|
||||
online: true,
|
||||
statusText: "空闲中",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "客服小李",
|
||||
avatar: "",
|
||||
online: true,
|
||||
statusText: "忙碌中",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "客服小张",
|
||||
avatar: "",
|
||||
online: false,
|
||||
statusText: "离线",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleTabChange(path, index) {
|
||||
console.log("切换到标签页:", path, index);
|
||||
},
|
||||
handleTransfer(agent) {
|
||||
if (!agent.online) {
|
||||
uni.showToast({
|
||||
title: '该客服当前离线',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.selectedAgent = agent;
|
||||
this.modalContent = `确认转接到${agent.name}吗?`;
|
||||
this.showModal = true;
|
||||
},
|
||||
confirmTransfer() {
|
||||
// 这里可以执行实际的转接逻辑
|
||||
uni.showToast({
|
||||
title: '正在为您转接...',
|
||||
icon: 'none'
|
||||
});
|
||||
this.showModal = false;
|
||||
|
||||
// 模拟跳转到聊天页面
|
||||
setTimeout(() => {
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/message/dialogBox/dialogBox?agentId=' + this.selectedAgent.id
|
||||
// });
|
||||
}, 1000);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ===== 页面容器 - 主流三段式布局 ===== */
|
||||
.page-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f5f6fa;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ===== 头部导航 ===== */
|
||||
.page-header {
|
||||
flex-shrink: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* ===== 内容区域 ===== */
|
||||
.page-main {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* ===== 内容内层 ===== */
|
||||
.main-content {
|
||||
padding: 10px;
|
||||
padding-bottom: calc(50px + env(safe-area-inset-bottom) + 10px);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* ===== 底部导航 ===== */
|
||||
.page-tabbar {
|
||||
flex-shrink: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.status-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.status-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-badge.online {
|
||||
background-color: #52c41a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.status-badge.offline {
|
||||
background-color: #999;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
font-size: 13px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 15px 0 10px 0;
|
||||
}
|
||||
|
||||
.agent-list {
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.agent-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.agent-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.agent-avatar-wrapper {
|
||||
position: relative;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.agent-avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.online-dot {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #52c41a;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.agent-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.agent-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.agent-status {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.agent-action {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 50px 20px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
<!-- 图标文件内容,这是二进制文件,实际使用时需要上传真实图片 -->
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
// 这是一个占位文件,需要替换为实际的AI头像图标
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
// 这是一个占位文件,需要替换为实际的聊天图标
|
||||
|
After Width: | Height: | Size: 541 B |
|
After Width: | Height: | Size: 654 B |
|
After Width: | Height: | Size: 703 B |
|
After Width: | Height: | Size: 637 B |
|
After Width: | Height: | Size: 745 B |
|
After Width: | Height: | Size: 749 B |
|
After Width: | Height: | Size: 574 B |
|
After Width: | Height: | Size: 707 B |
|
After Width: | Height: | Size: 742 B |
|
After Width: | Height: | Size: 565 B |
|
After Width: | Height: | Size: 597 B |
|
After Width: | Height: | Size: 662 B |
|
After Width: | Height: | Size: 659 B |
|
After Width: | Height: | Size: 761 B |
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
After Width: | Height: | Size: 789 B |
|
After Width: | Height: | Size: 375 B |
|
After Width: | Height: | Size: 352 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 405 B |
|
After Width: | Height: | Size: 404 B |
|
After Width: | Height: | Size: 338 B |
|
After Width: | Height: | Size: 382 B |
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 313 B |
|
After Width: | Height: | Size: 358 B |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 377 B |
|
After Width: | Height: | Size: 399 B |
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
Before Width: | Height: | Size: 310 B |
|
Before Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 412 B |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 679 B |
|
After Width: | Height: | Size: 615 B |
|
After Width: | Height: | Size: 511 B |
|
After Width: | Height: | Size: 796 B |
|
After Width: | Height: | Size: 419 B |
|
After Width: | Height: | Size: 743 B |
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 514 B |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 239 B |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 402 B |
|
After Width: | Height: | Size: 18 KiB |