云端文件下载
This commit is contained in:
14
pages.json
14
pages.json
@@ -87,7 +87,19 @@
|
||||
{
|
||||
"path" : "pages/text/text2",
|
||||
"style" : {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/CloudDatabase/CloudDatabase",
|
||||
"style": {
|
||||
"navigationBarTitleText": "云端数据",
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"titleNView": false,
|
||||
"bounce": "none",
|
||||
"softinputMode": "adjustResize"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"uniIdRouter" : {},
|
||||
"condition" : {
|
||||
|
||||
@@ -70,6 +70,9 @@
|
||||
<view class="head-btn" @click="goWorkSpace">
|
||||
<view class="iconfont icon-wenjianjia"></view>
|
||||
</view>
|
||||
<view class="head-btn" @click="goCloudDatabase">
|
||||
<view class="iconfont icon-cloud"></view>
|
||||
</view>
|
||||
<view class="head-btn log-out" @click="logOut">
|
||||
<view class="iconfont icon-tuichu"></view>
|
||||
</view>
|
||||
@@ -80,24 +83,46 @@
|
||||
<scroll-view class="chat-messages" direction="vertical" scroll-y :scroll-into-view="scrollToView"
|
||||
@scrolltoupper="loadMoreMessages" :upper-threshold="0" :scroll-with-animation="true"
|
||||
@scroll="onScroll">
|
||||
<!-- 与AI的对话内容展示 -->
|
||||
<view v-if="ChatType === 0" class="chat-message" v-for="(message, index) in currentMessages"
|
||||
:key="index" :id="'msg-' + index" :class="{'message-user':message.role==='user'}">
|
||||
<view v-if="message.role ==='user'" class="chat-avatar"
|
||||
<!-- 与AI的对话内容展示 -->
|
||||
<view v-if="ChatType === 0" class="chat-message" v-for="(message, index) in currentMessages"
|
||||
:key="index" :id="'msg-' + index" :class="{'message-user':message.role==='user'}">
|
||||
<!-- <view class="chat-avatar" :class="{'chat-avatar-user':message.role==='user'}">
|
||||
<image v-if="message.role === 'user' && UserData?.avatar" :src="UserData.avatar"
|
||||
class="friend-avatar" mode="aspectFill"></image>
|
||||
<view v-else-if="message.role === 'assistant'" class="iconfont icon-Robot"></view>
|
||||
<view v-else class="iconfont icon-yonghuziliao"></view>
|
||||
</view> -->
|
||||
<view v-if="message.role ==='user'" class="chat-avatar"
|
||||
:class="{'chat-avatar-user':message.role==='user'}">
|
||||
<image v-if="message.role === 'user' && UserData?.avatar" :src="UserData.avatar"
|
||||
class="friend-avatar" mode="aspectFill"></image>
|
||||
<view v-else class="iconfont icon-yonghuziliao"></view>
|
||||
<!-- <view v-if="message.role ==='assistant'" class="iconfont icon-Robot"></view> -->
|
||||
<!-- <view v-if="message.role ==='user'" class="iconfont icon-yonghuziliao"></view> -->
|
||||
</view>
|
||||
<view class="chat-content" :class="{'chat-content-user':message.role==='user'}"
|
||||
v-if="message.content && String(message.content).trim() !== ''">
|
||||
<!-- <view><rich-text :nodes="pareseMarkdown(message.content)"></rich-text></view> -->
|
||||
<view v-html="pareseMarkdown(message.content)"></view>
|
||||
<!-- <mp-html :content="pareseMarkdown(message.content)" /> -->
|
||||
</view>
|
||||
<view class="chat-content" :class="{'chat-content-user':message.role==='user'}"
|
||||
v-if="message.content && String(message.content).trim() !== ''">
|
||||
<!-- 文本内容 -->
|
||||
<view v-if="parseFileInfo(message.content).textContent"
|
||||
v-html="pareseMarkdown(parseFileInfo(message.content).textContent)"></view>
|
||||
<!-- 文件卡片列表 -->
|
||||
<view v-if="parseFileInfo(message.content).files.length > 0" class="message-file-list">
|
||||
<view v-for="(file, idx) in parseFileInfo(message.content).files" :key="idx" class="file-item">
|
||||
<!-- 图片:jpg/png/gif/webp 等 -->
|
||||
<image
|
||||
v-if="['jpg','jpeg','png','gif','webp','bmp'].includes(file.extendName.toLowerCase())"
|
||||
:src="file.url" mode="widthFix" class="message-image"
|
||||
@click="previewImage(file.url)"></image>
|
||||
<!-- 普通文件:doc/xlsx/zip/pdf 等 -->
|
||||
<view v-else class="message-file" @click="openFile(file.url)">
|
||||
<view class="file-icon">📄</view>
|
||||
<view class="file-name">{{ file.name }}</view>
|
||||
<view class="file-size">{{ formatFileSize(file.fileSize) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 与好友的对话内容展示 -->
|
||||
<view v-if="ChatType === 1" class="chat-message" v-for="(message, index) in currentMessages"
|
||||
:key="index" :id="'msg-' + index" :class="{'message-user':message.sender === UserId}">
|
||||
@@ -417,6 +442,22 @@
|
||||
return size.toFixed(2) + 'MB'
|
||||
}
|
||||
|
||||
// 从消息 content 中提取文件信息
|
||||
// 格式: [文件信息:[{...}]]
|
||||
const parseFileInfo = (content) => {
|
||||
if (!content || typeof content !== 'string') return { textContent: content || '', files: [] }
|
||||
const match = content.match(/\[文件信息:(\[.*?\])\]/)
|
||||
if (!match) return { textContent: content, files: [] }
|
||||
try {
|
||||
const files = JSON.parse(match[1])
|
||||
const textContent = content.replace(match[0], '').trim()
|
||||
return { textContent, files }
|
||||
} catch (e) {
|
||||
console.error('解析文件信息失败:', e)
|
||||
return { textContent: content, files: [] }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -483,6 +524,17 @@
|
||||
url: '/pages/WorkSpace/WorkSpace'
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转到云端数据库
|
||||
const goCloudDatabase = () => {
|
||||
// 保存当前会话id,确保从工作区返回时能恢复
|
||||
if (currentSessionId.value) {
|
||||
uni.setStorageSync('currentSessionId', currentSessionId.value)
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/CloudDatabase/CloudDatabase'
|
||||
})
|
||||
}
|
||||
|
||||
const logOut = () => {
|
||||
uni.reLaunch({
|
||||
@@ -821,7 +873,7 @@
|
||||
userToken.value = getToken();
|
||||
console.log("token:", userToken.value);
|
||||
UserConversations.value = await getUserConversations(userToken.value) || [];
|
||||
console.log("UserConversations:", UserConversations.value);
|
||||
// console.log("UserConversations:", JSON.stringify(UserConversations.value) );
|
||||
// 优先恢复已保存的会话id,避免刷新到列表第一个
|
||||
const savedSessionId = getCurrentSessionId();
|
||||
if (savedSessionId && UserConversations.value.some(c => c._id === savedSessionId)) {
|
||||
@@ -964,7 +1016,7 @@
|
||||
const takeConversationMessages = async () => {
|
||||
try {
|
||||
allmessages.value = await getConversationMessages(userToken.value, currentSessionId.value) || [];
|
||||
// console.log("AI消息:", allmessages.value);
|
||||
// console.log("AI消息:", JSON.stringify(allmessages.value) );
|
||||
// currentMessages.value = allmessages.value.slice(-pageInfoNumber);
|
||||
// 数据获取后执行滚动
|
||||
scrollToBottom();
|
||||
|
||||
669
pages/CloudDatabase/CloudDatabase.css
Normal file
669
pages/CloudDatabase/CloudDatabase.css
Normal file
@@ -0,0 +1,669 @@
|
||||
|
||||
.cloud-db-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background-color: #f5f6fa;
|
||||
}
|
||||
|
||||
.cloud-db-header {
|
||||
flex-shrink: 0;
|
||||
background-color: #ffffff;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.custom-navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 160rpx;
|
||||
}
|
||||
|
||||
.custom-navbar-icon {
|
||||
font-size: 44rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
width: auto;
|
||||
min-width: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.navbar-before-title {
|
||||
font-size: 26rpx;
|
||||
color: #3b86ff;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.navbar-right-text {
|
||||
font-size: 28rpx;
|
||||
color: #3b86ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 菜单 */
|
||||
.menu-open {
|
||||
color: #3b86ff;
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
position: absolute;
|
||||
top: 80rpx;
|
||||
right: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
|
||||
padding: 12rpx 0;
|
||||
z-index: 100;
|
||||
min-width: 200rpx;
|
||||
}
|
||||
|
||||
.menu-card-item {
|
||||
padding: 20rpx 32rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.menu-card-item:active {
|
||||
background: #f5f6fa;
|
||||
}
|
||||
|
||||
.solid-line {
|
||||
height: 1rpx;
|
||||
background: #eee;
|
||||
margin: 4rpx 20rpx;
|
||||
}
|
||||
|
||||
.file-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 分类切换 Tab */
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
padding: 10rpx 30rpx 0;
|
||||
gap: 40rpx;
|
||||
border-bottom: 2rpx solid #eee;
|
||||
}
|
||||
|
||||
.category-tab {
|
||||
font-size: 30rpx;
|
||||
color: #999;
|
||||
padding-bottom: 20rpx;
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.category-tab.active {
|
||||
color: #3b86ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.category-tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 48rpx;
|
||||
height: 6rpx;
|
||||
background: #3b86ff;
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.cloud-db-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 20rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 区块 */
|
||||
.section-block {
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.section-title .iconfont {
|
||||
font-size: 36rpx;
|
||||
color: #3b86ff;
|
||||
}
|
||||
|
||||
.section-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
background: rgba(59, 134, 255, 0.08);
|
||||
color: #3b86ff;
|
||||
border-radius: 30rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
height: 2rpx;
|
||||
margin: 4rpx 0 20rpx;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 0;
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.empty-tip .iconfont {
|
||||
font-size: 80rpx;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
/* 文件网格 */
|
||||
.file-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
width: calc(33.33% - 12rpx);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx 10rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f8f9fc;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.file-item:active {
|
||||
border-color: #3b86ff;
|
||||
background: rgba(59, 134, 255, 0.04);
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.file-icon.is-directory {
|
||||
background: rgba(255, 170, 0, 0.12);
|
||||
}
|
||||
|
||||
.file-icon.is-directory .iconfont {
|
||||
font-size: 48rpx;
|
||||
color: #ffa500;
|
||||
}
|
||||
|
||||
.file-icon.is-file {
|
||||
background: rgba(59, 134, 255, 0.1);
|
||||
}
|
||||
|
||||
.file-icon.is-file .iconfont {
|
||||
font-size: 44rpx;
|
||||
color: #3b86ff;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-type-tag {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
background: #f0f0f0;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.file-size {
|
||||
font-size: 20rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* 数据表列表 */
|
||||
.table-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.table-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
border-radius: 14rpx;
|
||||
background: #f8f9fc;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.table-item:active {
|
||||
border-color: #3b86ff;
|
||||
background: rgba(59, 134, 255, 0.03);
|
||||
}
|
||||
|
||||
.table-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(59, 134, 255, 0.08);
|
||||
border-radius: 14rpx;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.table-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.table-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.table-desc {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.table-rows {
|
||||
font-size: 22rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* 团队管理 */
|
||||
.team-manage-block {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.team-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.team-selector {
|
||||
padding: 0 0 20rpx;
|
||||
}
|
||||
|
||||
.team-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.team-chip {
|
||||
display: inline-block;
|
||||
padding: 12rpx 28rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
background: #f0f2f5;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.team-chip.active {
|
||||
color: #fff;
|
||||
background: #3b86ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.select-team-tip {
|
||||
padding: 80rpx 0;
|
||||
}
|
||||
|
||||
/* ===== 弹窗样式 ===== */
|
||||
.popup-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.popup-card {
|
||||
width: 600rpx;
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.close-popup-card {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-popup-card .iconfont {
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
margin-bottom: 30rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.new-folder-name {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.create-folder-name {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.create-folder-name input {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 16rpx;
|
||||
font-size: 28rpx;
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.create-folder-name input:focus {
|
||||
border-color: #3b86ff;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.nf-path-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.nf-path-display {
|
||||
font-size: 24rpx;
|
||||
color: #3b86ff;
|
||||
background: rgba(59, 134, 255, 0.06);
|
||||
padding: 12rpx 16rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.option-wrapper {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.opt-btn {
|
||||
padding: 16rpx 40rpx;
|
||||
border-radius: 30rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.opt-cancel {
|
||||
color: #999;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
|
||||
.opt-confirm {
|
||||
color: #fff;
|
||||
background: #3b86ff;
|
||||
}
|
||||
|
||||
/* 团队管理弹窗 */
|
||||
.team-manage-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-height: 500rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.member-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16rpx;
|
||||
border-radius: 14rpx;
|
||||
background: #f8f9fc;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-avatar image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.default-avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.member-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.member-label {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.member-actions {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-role {
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.member-role.owner {
|
||||
color: #e6a23c;
|
||||
background: rgba(230, 162, 60, 0.1);
|
||||
}
|
||||
|
||||
.member-role.admin {
|
||||
color: #3b86ff;
|
||||
background: rgba(59, 134, 255, 0.1);
|
||||
}
|
||||
|
||||
.member-actions-right {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.member-action-btn {
|
||||
font-size: 20rpx;
|
||||
padding: 8rpx 14rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.set-admin {
|
||||
color: #3b86ff;
|
||||
border: 1rpx solid #3b86ff;
|
||||
}
|
||||
|
||||
.remove-member {
|
||||
color: #f56c6c;
|
||||
border: 1rpx solid #f56c6c;
|
||||
}
|
||||
|
||||
.invite-section {
|
||||
border-top: 1rpx solid #eee;
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
.invite-input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.invite-input {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
padding: 18rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 30rpx;
|
||||
font-size: 26rpx;
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.invite-btn {
|
||||
background: #3b86ff;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
padding: 14rpx 32rpx;
|
||||
}
|
||||
|
||||
.folder-null {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 0;
|
||||
color: #bbb;
|
||||
font-size: 26rpx;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.folder-null .iconfont {
|
||||
font-size: 64rpx;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.text-btn {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 30rpx;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
982
pages/CloudDatabase/CloudDatabase.vue
Normal file
982
pages/CloudDatabase/CloudDatabase.vue
Normal file
@@ -0,0 +1,982 @@
|
||||
<template>
|
||||
<view class="status-bar"></view>
|
||||
|
||||
<!-- ===== 团队管理弹窗 ===== -->
|
||||
<view v-if="showTeamManage" class="popup-overlay" @click="closeTeamManage">
|
||||
<view class="popup-card" @click.stop>
|
||||
<view class="close-popup-card" @click="closeTeamManage">
|
||||
<view class="iconfont icon-quxiao"></view>
|
||||
</view>
|
||||
<view class="popup-title">团队管理</view>
|
||||
|
||||
<scroll-view class="team-manage-body" scroll-y>
|
||||
<view v-if="currentTeamMembers.length === 0" class="folder-null">暂无团队成员</view>
|
||||
<view v-else class="member-list">
|
||||
<view class="member-item" v-for="member in currentTeamMembers" :key="member.id || member.receiver">
|
||||
<view class="member-avatar">
|
||||
<image v-if="member.avatar" :src="member.avatar" mode="aspectFill"></image>
|
||||
<view v-else class="default-avatar">
|
||||
<uni-icons type="person-filled" size="60rpx" color="#ffffff"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="member-info">
|
||||
<view class="member-name">{{ member.friendNickName || member.username || member.name }}</view>
|
||||
<view class="member-label">{{ member.email || member.sessionId || '' }}</view>
|
||||
</view>
|
||||
<view class="member-actions">
|
||||
<view v-if="member.role === 'owner'" class="member-role owner">创建者</view>
|
||||
<view v-else-if="member.role === 'admin'" class="member-role admin">管理员</view>
|
||||
<view v-else class="member-actions-right">
|
||||
<view class="member-action-btn set-admin" @click="setTeamAdmin(member)">设为管理</view>
|
||||
<view class="member-action-btn remove-member" @click="removeTeamMember(member)">移除</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 邀请成员 -->
|
||||
<view class="invite-section">
|
||||
<view class="invite-input-wrapper">
|
||||
<input class="invite-input" placeholder="输入用户ID或名称邀请" v-model="inviteKeyword" />
|
||||
<view class="invite-btn text-btn" @click="inviteTeamMember">邀请</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 新建文件夹弹窗 ===== -->
|
||||
<view v-if="showNewFolder" class="popup-overlay" @click="closeNewFolderModal">
|
||||
<view class="popup-card" @click.stop>
|
||||
<view class="close-popup-card" @click="closeNewFolderModal">
|
||||
<view class="iconfont icon-quxiao"></view>
|
||||
</view>
|
||||
<view class="popup-title">新建{{ currentCategory === 'user' ? '个人' : '团队' }}目录</view>
|
||||
<view class="new-folder-name">目录名称</view>
|
||||
<view class="create-folder-name">
|
||||
<input v-model="newFolderName" type="text" placeholder="输入目录名称" />
|
||||
</view>
|
||||
<view class="nf-path-label">当前路径</view>
|
||||
<view class="nf-path-display">{{ currentFolderDisplayPath }}</view>
|
||||
<view class="option-wrapper">
|
||||
<view class="opt-btn opt-cancel" @click="closeNewFolderModal">取消</view>
|
||||
<view class="opt-btn opt-confirm" @click="confirmCreateFolder">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 新建数据库表弹窗 ===== -->
|
||||
<view v-if="showNewTable" class="popup-overlay" @click="closeNewTableModal">
|
||||
<view class="popup-card" @click.stop>
|
||||
<view class="close-popup-card" @click="closeNewTableModal">
|
||||
<view class="iconfont icon-quxiao"></view>
|
||||
</view>
|
||||
<view class="popup-title">新建{{ currentCategory === 'user' ? '个人' : '团队' }}数据表</view>
|
||||
<view class="new-folder-name">表名称</view>
|
||||
<view class="create-folder-name">
|
||||
<input v-model="newTableName" type="text" placeholder="输入表名称,如:users" />
|
||||
</view>
|
||||
<view class="new-folder-name">描述</view>
|
||||
<view class="create-folder-name">
|
||||
<input v-model="newTableDesc" type="text" placeholder="输入表描述(可选)" />
|
||||
</view>
|
||||
<view class="option-wrapper">
|
||||
<view class="opt-btn opt-cancel" @click="closeNewTableModal">取消</view>
|
||||
<view class="opt-btn opt-confirm" @click="confirmCreateTable">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 主页面 ===== -->
|
||||
<view class="cloud-db-container page-container">
|
||||
<view class="cloud-db-header">
|
||||
<view class="custom-navbar">
|
||||
<view class="navbar-left" @click="handleBackOrUp">
|
||||
<view class="iconfont icon-fanhui custom-navbar-icon"></view>
|
||||
<view v-if="currentFolderPath.length > 0" class="navbar-before-title">{{ parentFolderName }}</view>
|
||||
</view>
|
||||
<view class="navbar-title">{{ navbarTitle }}</view>
|
||||
<view class="navbar-right">
|
||||
<view v-if="isSelectFolder" class="navbar-right-text" @click="handleSelectFolder">完成</view>
|
||||
<view v-else class="iconfont icon-gengduo" :class="isMenuOpen ? 'menu-open' : 'custom-navbar-icon'"
|
||||
@click="handleMenu"></view>
|
||||
</view>
|
||||
<view v-if="isMenuOpen" class="menu-card">
|
||||
<view class="menu-card-item" @click="handleSelectFolder(); handleMenu()">选择</view>
|
||||
<view class="solid-line"></view>
|
||||
<view class="menu-card-item" @click="openNewFolderModal(); handleMenu()">新建文件夹</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 个人/团队 分类切换 -->
|
||||
<view class="category-tabs">
|
||||
<view class="category-tab" :class="{ active: currentCategory === 'user' }"
|
||||
@click="switchCategory('user')">
|
||||
个人
|
||||
</view>
|
||||
<view class="category-tab" :class="{ active: currentCategory === 'team' }"
|
||||
@click="switchCategory('team')">
|
||||
团队
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="cloud-db-content" scroll-y>
|
||||
<!-- ========== 个人分类 ========== -->
|
||||
<template v-if="currentCategory === 'user'">
|
||||
<!-- 云端文件区域 -->
|
||||
<view class="section-block">
|
||||
<view class="section-header">
|
||||
<view class="section-title">
|
||||
<view class="iconfont icon-wenjianjia"></view>
|
||||
<text>云端文件</text>
|
||||
<text class="file-count">{{ personalFileCount > 0 ? '(' + personalFileCount + ')' : '' }}</text>
|
||||
</view>
|
||||
<view class="section-actions">
|
||||
<view class="action-btn" @click="openNewFolderModal">
|
||||
<uni-icons type="plus" size="16"></uni-icons>
|
||||
<text>新建目录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view v-if="personalFiles.length === 0" class="empty-tip">
|
||||
<view class="iconfont icon-wenjianjia"></view>
|
||||
<text>暂无云端文件</text>
|
||||
</view>
|
||||
<view v-else class="file-grid">
|
||||
<view class="file-item" v-for="file in personalFiles" :key="file.id"
|
||||
@click="handleFileClick(file)" @longpress="handleFileLongPress(file)">
|
||||
<view class="file-icon" :class="isFolder(file) ? 'is-directory' : 'is-file'">
|
||||
<view class="iconfont" :class="getFileIcon(file)"></view>
|
||||
</view>
|
||||
<view class="file-name">{{ file.name }}</view>
|
||||
<view class="file-meta">
|
||||
<text class="file-type-tag">{{ isFolder(file) ? '目录' : '文件' }}</text>
|
||||
<text v-if="file.size" class="file-size">{{ formatFileSize(file.size) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-divider"></view>
|
||||
|
||||
<!-- 云端数据库区域 -->
|
||||
<view class="section-block">
|
||||
<view class="section-header">
|
||||
<view class="section-title">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>云端数据库</text>
|
||||
</view>
|
||||
<view class="section-actions">
|
||||
<view class="action-btn" @click="openNewTableModal">
|
||||
<uni-icons type="plus" size="16"></uni-icons>
|
||||
<text>新建表</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view v-if="personalTables.length === 0" class="empty-tip">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>暂无数据表</text>
|
||||
</view>
|
||||
<view v-else class="table-list">
|
||||
<view class="table-item" v-for="table in personalTables" :key="table.id"
|
||||
@click="handleTableClick(table)">
|
||||
<view class="table-icon">
|
||||
<uni-icons type="compose" size="22" color="#3b86ff"></uni-icons>
|
||||
</view>
|
||||
<view class="table-info">
|
||||
<view class="table-name">{{ table.name }}</view>
|
||||
<view class="table-desc">{{ table.desc || '无描述' }}</view>
|
||||
</view>
|
||||
<view class="table-meta">
|
||||
<text class="table-rows">{{ table.rowCount || 0 }} 条记录</text>
|
||||
<uni-icons type="right" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- ========== 团队分类 ========== -->
|
||||
<template v-if="currentCategory === 'team'">
|
||||
<!-- 团队管理入口 -->
|
||||
<view class="section-block team-manage-block" @click="openTeamManage">
|
||||
<view class="section-header">
|
||||
<view class="section-title">
|
||||
<view class="iconfont icon-qunliao"></view>
|
||||
<text>团队管理</text>
|
||||
</view>
|
||||
<view class="section-actions">
|
||||
<text class="team-count">{{ teamGroups.length }} 个团队</text>
|
||||
<uni-icons type="right" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-divider"></view>
|
||||
|
||||
<!-- 团队选择器 -->
|
||||
<view v-if="teamGroups.length > 0" class="team-selector">
|
||||
<scroll-view scroll-x class="team-scroll">
|
||||
<view class="team-chip" :class="{ active: currentTeamId === team.id }"
|
||||
v-for="team in teamGroups" :key="team.id" @click="selectTeam(team.id)">
|
||||
{{ team.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view v-if="!currentTeamId && teamGroups.length > 0" class="empty-tip select-team-tip">
|
||||
<view class="iconfont icon-qunliao"></view>
|
||||
<text>请选择一个团队</text>
|
||||
</view>
|
||||
|
||||
<!-- 团队云端文件区域 -->
|
||||
<view v-if="currentTeamId" class="section-block">
|
||||
<view class="section-header">
|
||||
<view class="section-title">
|
||||
<view class="iconfont icon-wenjianjia"></view>
|
||||
<text>团队云端文件</text>
|
||||
</view>
|
||||
<view class="section-actions">
|
||||
<view class="action-btn" @click="openNewFolderModal">
|
||||
<uni-icons type="plus" size="16"></uni-icons>
|
||||
<text>新建目录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view v-if="teamFiles.length === 0" class="empty-tip">
|
||||
<view class="iconfont icon-wenjianjia"></view>
|
||||
<text>暂无团队云端文件</text>
|
||||
</view>
|
||||
<view v-else class="file-grid">
|
||||
<view class="file-item" v-for="file in teamFiles" :key="file.id"
|
||||
@click="handleFileClick(file)" @longpress="handleFileLongPress(file)">
|
||||
<view class="file-icon" :class="isFolder(file) ? 'is-directory' : 'is-file'">
|
||||
<view class="iconfont" :class="getFileIcon(file)"></view>
|
||||
</view>
|
||||
<view class="file-name">{{ file.name }}</view>
|
||||
<view class="file-meta">
|
||||
<text class="file-type-tag">{{ isFolder(file) ? '目录' : '文件' }}</text>
|
||||
<text v-if="file.size" class="file-size">{{ formatFileSize(file.size) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="currentTeamId" class="section-divider"></view>
|
||||
|
||||
<!-- 团队云端数据库区域 -->
|
||||
<view v-if="currentTeamId" class="section-block">
|
||||
<view class="section-header">
|
||||
<view class="section-title">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>团队云端数据库</text>
|
||||
</view>
|
||||
<view class="section-actions">
|
||||
<view class="action-btn" @click="openNewTableModal">
|
||||
<uni-icons type="plus" size="16"></uni-icons>
|
||||
<text>新建表</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view v-if="teamTables.length === 0" class="empty-tip">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>暂无团队数据表</text>
|
||||
</view>
|
||||
<view v-else class="table-list">
|
||||
<view class="table-item" v-for="table in teamTables" :key="table.id"
|
||||
@click="handleTableClick(table)">
|
||||
<view class="table-icon">
|
||||
<uni-icons type="compose" size="22" color="#3b86ff"></uni-icons>
|
||||
</view>
|
||||
<view class="table-info">
|
||||
<view class="table-name">{{ table.name }}</view>
|
||||
<view class="table-desc">{{ table.desc || '无描述' }}</view>
|
||||
</view>
|
||||
<view class="table-meta">
|
||||
<text class="table-rows">{{ table.rowCount || 0 }} 条记录</text>
|
||||
<uni-icons type="right" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import { getToken } from '@/utils/user-info.js';
|
||||
import {
|
||||
getUserInfo,
|
||||
getCloudFileList,
|
||||
getCloudFileUrl,
|
||||
createWorkspaceFolder,
|
||||
daleteWorkspace,
|
||||
getWorkspaceFileURL
|
||||
} from '@/utils/cloud-api.js';
|
||||
import { getGroup } from '@/utils/friend-api.js';
|
||||
|
||||
const userToken = ref('');
|
||||
const userId = ref('');
|
||||
const workspaceId = ref('');
|
||||
|
||||
// 分类:personal / team
|
||||
const currentCategory = ref('user');
|
||||
const switchCategory = (category) => {
|
||||
currentCategory.value = category;
|
||||
// 切换分类时重置文件夹导航
|
||||
currentFolderPath.value = [];
|
||||
// 切换分类时重新加载文件
|
||||
loadCurrentFiles();
|
||||
};
|
||||
|
||||
// ========== 导航栏 ==========
|
||||
const navbarTitle = ref('云端数据');
|
||||
const isMenuOpen = ref(false);
|
||||
const isSelectFolder = ref(false);
|
||||
const selectFileList = ref([]);
|
||||
|
||||
const handleMenu = () => {
|
||||
isMenuOpen.value = !isMenuOpen.value;
|
||||
};
|
||||
const handleSelectFolder = () => {
|
||||
isSelectFolder.value = !isSelectFolder.value;
|
||||
if (!isSelectFolder.value) {
|
||||
selectFileList.value = [];
|
||||
}
|
||||
};
|
||||
const parentFolderName = computed(() => {
|
||||
if (currentFolderPath.value.length === 0) return '';
|
||||
if (currentFolderPath.value.length === 1) return '云端数据';
|
||||
return currentFolderPath.value[currentFolderPath.value.length - 2]?.name || '云端数据';
|
||||
});
|
||||
|
||||
// 返回 / 上级目录
|
||||
const handleBackOrUp = () => {
|
||||
if (currentFolderPath.value.length > 0) {
|
||||
currentFolderPath.value.pop();
|
||||
navbarTitle.value = currentFolderPath.value.length > 0
|
||||
? currentFolderPath.value[currentFolderPath.value.length - 1].name
|
||||
: '云端数据';
|
||||
loadCurrentFiles();
|
||||
} else {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
fail() {
|
||||
uni.reLaunch({ url: '/pages/Chat/Chat' });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 云端文件 ==========
|
||||
// 原始完整数据(从 API 返回的扁平列表)
|
||||
const rawPersonalFileData = ref([]);
|
||||
const rawTeamFileData = ref([]);
|
||||
// 当前显示的文件夹列表(扁平)
|
||||
const personalFiles = ref([]);
|
||||
const teamFiles = ref([]);
|
||||
// 文件夹导航栈
|
||||
const currentFolderPath = ref([]);
|
||||
|
||||
// 判断是否为文件夹
|
||||
const isFolder = (file) => {
|
||||
return file.type === 'folder';
|
||||
};
|
||||
|
||||
// 获取文件扩展名
|
||||
const getFileExtension = (file) => {
|
||||
if (isFolder(file)) return 'folder';
|
||||
const name = file.name || '';
|
||||
const dotIndex = name.lastIndexOf('.');
|
||||
if (dotIndex === -1) return '';
|
||||
return name.slice(dotIndex).toLowerCase();
|
||||
};
|
||||
|
||||
// 获取文件图标(根据文件对象综合判断)
|
||||
const getFileIcon = (file) => {
|
||||
if (isFolder(file)) return 'icon-a-wenjianjiawenjian';
|
||||
|
||||
const ext = getFileExtension(file);
|
||||
const mimeType = (file.type || '').toLowerCase();
|
||||
|
||||
const extMap = {
|
||||
'.png': 'icon-wenjiantupian', '.jpg': 'icon-wenjiantupian', '.jpeg': 'icon-wenjiantupian',
|
||||
'.gif': 'icon-wenjiantupian', '.svg': 'icon-SVG', '.bmp': 'icon-wenjiantupian',
|
||||
'.webp': 'icon-wenjiantupian', '.mp4': 'icon-wenjianshipin', '.avi': 'icon-wenjianshipin',
|
||||
'.mov': 'icon-wenjianshipin', '.wmv': 'icon-wenjianshipin', '.flv': 'icon-wenjianshipin',
|
||||
'.mkv': 'icon-wenjianshipin', '.mp3': 'icon-wenjianyinpin', '.wav': 'icon-wenjianyinpin',
|
||||
'.flac': 'icon-wenjianyinpin', '.aac': 'icon-wenjianyinpin', '.pdf': 'icon-PDF',
|
||||
'.doc': 'icon-DOC', '.docx': 'icon-DOC', '.xls': 'icon-XLS', '.xlsx': 'icon-XLS',
|
||||
'.ppt': 'icon-PPT', '.pptx': 'icon-PPT', '.txt': 'icon-TXT', '.md': 'icon-file-markdown-fill',
|
||||
'.zip': 'icon-wenjianyasuo', '.rar': 'icon-wenjianyasuo', '.7z': 'icon-wenjianyasuo',
|
||||
'.tar': 'icon-wenjianyasuo', '.gz': 'icon-wenjianyasuo', '.js': 'icon-JS',
|
||||
'.ts': 'icon-daimawenjia', '.vue': 'icon-daimawenjia', '.html': 'icon-HTML',
|
||||
'.css': 'icon-CSS', '.py': 'icon-daimawenjian', '.java': 'icon-daimawenjian', '.json': 'icon-JSON',
|
||||
};
|
||||
if (ext && extMap[ext]) return extMap[ext];
|
||||
|
||||
// 根据 MIME 类型推断
|
||||
if (mimeType.startsWith('image/')) return 'icon-wenjiantupian';
|
||||
if (mimeType.startsWith('video/')) return 'icon-wenjianshipin';
|
||||
if (mimeType.startsWith('audio/')) return 'icon-wenjianyinpin';
|
||||
if (mimeType.includes('spreadsheet') || mimeType.includes('excel')) return 'icon-XLS';
|
||||
if (mimeType.includes('document') || mimeType.includes('word')) return 'icon-DOC';
|
||||
if (mimeType.includes('presentation') || mimeType.includes('powerpoint')) return 'icon-PPT';
|
||||
if (mimeType.includes('pdf')) return 'icon-PDF';
|
||||
if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('tar') || mimeType.includes('gzip'))
|
||||
return 'icon-wenjianyasuo';
|
||||
|
||||
return 'icon-qitawenjian';
|
||||
};
|
||||
|
||||
const formatFileSize = (bytes) => {
|
||||
if (!bytes) return '';
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
||||
};
|
||||
|
||||
const currentFolderDisplayPath = computed(() => {
|
||||
if (currentFolderPath.value.length === 0) return '根目录 (/)';
|
||||
return '/' + currentFolderPath.value.map(f => f.name).join('/');
|
||||
});
|
||||
|
||||
// 文件统计
|
||||
const personalFileCount = computed(() => rawPersonalFileData.value.length);
|
||||
|
||||
// 从 API 数据中递归查找指定路径的文件夹内容
|
||||
const findFolderByPath = (dataList, targetPath) => {
|
||||
for (const item of dataList) {
|
||||
if (isFolder(item) && item.path === targetPath) {
|
||||
return item.children || [];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// 加载当前路径下的文件列表
|
||||
const loadCurrentFiles = () => {
|
||||
const rawData = currentCategory.value === 'user' ? rawPersonalFileData.value : rawTeamFileData.value;
|
||||
|
||||
if (currentFolderPath.value.length === 0) {
|
||||
// 根目录:API 返回的 data 数组本身就是根目录下的所有顶层项
|
||||
// 不需要过滤 path,因为每个顶层项的 path 是其自身路径(文件夹的 path 为 /folderName/,文件的 path 为 /)
|
||||
if (currentCategory.value === 'user') {
|
||||
personalFiles.value = rawData;
|
||||
} else {
|
||||
teamFiles.value = rawData;
|
||||
}
|
||||
} else {
|
||||
// 子目录:找到当前文件夹的 children
|
||||
const currentFolder = currentFolderPath.value[currentFolderPath.value.length - 1];
|
||||
const result = currentFolder.children || [];
|
||||
if (currentCategory.value === 'user') {
|
||||
personalFiles.value = result;
|
||||
} else {
|
||||
teamFiles.value = result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取云端文件列表
|
||||
const fetchFileList = async () => {
|
||||
try {
|
||||
userToken.value = getToken();
|
||||
const userInfo = await getUserInfo(userToken.value);
|
||||
userId.value = userInfo._id;
|
||||
workspaceId.value = uni.getStorageSync('workspace_id') || '';
|
||||
|
||||
// 获取个人文件
|
||||
const personalData = await getCloudFileList(userToken.value, 'user');
|
||||
rawPersonalFileData.value = personalData || [];
|
||||
|
||||
// 默认显示个人文件根目录(API 返回的 data 数组即为根目录下的所有顶层项)
|
||||
if (currentCategory.value === 'user') {
|
||||
personalFiles.value = personalData || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取文件列表失败:', error);
|
||||
rawPersonalFileData.value = [];
|
||||
personalFiles.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 获取团队文件
|
||||
const fetchTeamFileList = async (teamId) => {
|
||||
try {
|
||||
const teamData = await getCloudFileList(userToken.value, 'team', '/', teamId);
|
||||
console.log('团队云端文件:', JSON.stringify(teamData));
|
||||
rawTeamFileData.value = teamData || [];
|
||||
teamFiles.value = teamData || [];
|
||||
} catch (error) {
|
||||
console.error('获取团队文件列表失败:', error);
|
||||
rawTeamFileData.value = [];
|
||||
teamFiles.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 文件点击:文件夹进入,文件下载
|
||||
const handleFileClick = (file) => {
|
||||
if (isFolder(file)) {
|
||||
// 进入文件夹
|
||||
currentFolderPath.value.push({
|
||||
path: file.path,
|
||||
name: file.name,
|
||||
children: file.children || []
|
||||
});
|
||||
navbarTitle.value = file.name;
|
||||
loadCurrentFiles();
|
||||
} else {
|
||||
handleDownload(file);
|
||||
}
|
||||
};
|
||||
|
||||
// 文件长按操作
|
||||
const handleFileLongPress = (file) => {
|
||||
if (isSelectFolder.value) {
|
||||
const idx = selectFileList.value.indexOf(file.id);
|
||||
if (idx !== -1) {
|
||||
selectFileList.value.splice(idx, 1);
|
||||
} else {
|
||||
selectFileList.value.push(file.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const menuItems = isFolder(file) ? ['重命名', '删除'] : ['下载', '重命名', '删除'];
|
||||
uni.showActionSheet({
|
||||
itemList: menuItems,
|
||||
success: (res) => {
|
||||
if (isFolder(file)) {
|
||||
switch (res.tapIndex) {
|
||||
case 0: handleRename(file); break;
|
||||
case 1: handleDeleteFile(file); break;
|
||||
}
|
||||
} else {
|
||||
switch (res.tapIndex) {
|
||||
case 0: handleDownload(file); break;
|
||||
case 1: handleRename(file); break;
|
||||
case 2: handleDeleteFile(file); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 下载文件:通过文件 ID 获取下载 URL
|
||||
const handleDownload = async (file) => {
|
||||
if (isFolder(file)) {
|
||||
uni.showToast({ title: '暂不支持下载目录', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 通过文件 ID 获取下载 URL
|
||||
uni.showLoading({ title: '获取下载链接...' });
|
||||
const downloadUrl = await getCloudFileUrl(userToken.value, file.id);
|
||||
uni.hideLoading();
|
||||
|
||||
if (!downloadUrl) {
|
||||
uni.showToast({ title: '获取下载链接失败', icon: 'error' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
uni.showLoading({ title: '下载中...' });
|
||||
uni.downloadFile({
|
||||
url: downloadUrl,
|
||||
success: (downloadRes) => {
|
||||
uni.hideLoading();
|
||||
if (downloadRes.statusCode === 200) {
|
||||
const tempPath = downloadRes.tempFilePath;
|
||||
// 下载成功,保存到 yxd 目录
|
||||
saveFileToYxd(file.name, tempPath);
|
||||
} else {
|
||||
uni.showToast({ title: '下载失败', icon: 'error' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '下载失败,请重试', icon: 'error' });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('下载失败:', error);
|
||||
uni.showToast({ title: '下载失败,请重试', icon: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
// 将下载的文件保存到 yxd 目录
|
||||
const saveFileToYxd = (fileName, tempPath) => {
|
||||
// #ifdef APP-PLUS
|
||||
// App 端:使用 plus.io 将文件复制到 /yxd/ 目录
|
||||
try {
|
||||
// 获取应用的私有文档目录作为 yxd 的父目录
|
||||
const yxdDir = `_doc/yxd/`;
|
||||
const targetPath = `${yxdDir}${fileName}`;
|
||||
|
||||
// 确保 yxd 目录存在
|
||||
plus.io.resolveLocalFileSystemURL(yxdDir, (dirEntry) => {
|
||||
// 目录已存在,复制文件
|
||||
copyFileToDir(tempPath, targetPath, fileName);
|
||||
}, () => {
|
||||
// 目录不存在,先创建
|
||||
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, (fs) => {
|
||||
fs.root.getDirectory('yxd', { create: true }, (dirEntry) => {
|
||||
copyFileToDir(tempPath, targetPath, fileName);
|
||||
}, (err) => {
|
||||
console.error('创建 yxd 目录失败:', err);
|
||||
uni.showToast({ title: '创建目录失败', icon: 'error' });
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('保存文件失败:', e);
|
||||
uni.showToast({ title: '保存失败', icon: 'error' });
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
// 小程序/H5 端:使用 uni.saveFile 保存
|
||||
uni.saveFile({
|
||||
tempFilePath: tempPath,
|
||||
success: (saveRes) => {
|
||||
openFileConfirm(fileName, saveRes.savedFilePath);
|
||||
},
|
||||
fail: () => {
|
||||
// saveFile 失败则直接确认打开
|
||||
openFileConfirm(fileName, tempPath);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
};
|
||||
|
||||
// App 端:将文件从临时路径复制到 yxd 目录
|
||||
const copyFileToDir = (tempPath, targetPath, fileName) => {
|
||||
plus.io.resolveLocalFileSystemURL(tempPath, (fileEntry) => {
|
||||
fileEntry.copyTo(
|
||||
plus.io.convertLocalFileSystemURL(targetPath).replace(fileName, ''),
|
||||
fileName,
|
||||
() => {
|
||||
console.log('文件已保存到 yxd 目录:', targetPath);
|
||||
// 保存成功后弹窗确认是否打开
|
||||
openFileConfirm(fileName, targetPath);
|
||||
},
|
||||
(err) => {
|
||||
console.error('复制文件失败:', err);
|
||||
// 复制失败,使用临时路径
|
||||
openFileConfirm(fileName, tempPath);
|
||||
}
|
||||
);
|
||||
}, () => {
|
||||
console.error('读取临时文件失败');
|
||||
openFileConfirm(fileName, tempPath);
|
||||
});
|
||||
};
|
||||
|
||||
// 下载成功后确认是否打开文件
|
||||
const openFileConfirm = (fileName, filePath) => {
|
||||
uni.showActionSheet({
|
||||
title: `${fileName} 下载完成`,
|
||||
itemList: ['打开文件', '选择其他应用打开', '仅保存'],
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0:
|
||||
// 打开文件(系统默认应用)
|
||||
openFile(filePath);
|
||||
break;
|
||||
case 1:
|
||||
// 选择其他应用打开(showMenu 会弹出分享/打开方式菜单)
|
||||
openFileWithMenu(filePath);
|
||||
break;
|
||||
case 2:
|
||||
// 仅保存,不打开
|
||||
uni.showToast({ title: '文件已保存到 yxd 目录', icon: 'success' });
|
||||
break;
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
// 用户取消,默认不打开
|
||||
uni.showToast({ title: '文件已保存到 yxd 目录', icon: 'success' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 使用系统默认应用打开
|
||||
const openFile = (filePath) => {
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
success: () => uni.showToast({ title: '打开成功', icon: 'success' }),
|
||||
fail: (err) => {
|
||||
console.error('打开文件失败:', err);
|
||||
// 如果默认应用打开失败,尝试弹出选择菜单
|
||||
openFileWithMenu(filePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 弹出系统分享/打开方式菜单,让用户选择应用
|
||||
const openFileWithMenu = (filePath) => {
|
||||
// #ifdef APP-PLUS
|
||||
// App 端使用 plus.runtime 打开文件,可选择应用
|
||||
plus.runtime.openFile(filePath, {}, (e) => {
|
||||
console.log('文件打开成功');
|
||||
});
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
// 小程序/H5 端使用 openDocument + showMenu
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
showMenu: true,
|
||||
success: () => uni.showToast({ title: '打开成功', icon: 'success' }),
|
||||
fail: () => uni.showToast({ title: '无法打开此文件', icon: 'error' })
|
||||
});
|
||||
// #endif
|
||||
};
|
||||
|
||||
// 重命名
|
||||
const handleRename = (file) => {
|
||||
uni.showModal({
|
||||
title: '重命名',
|
||||
content: '请输入新名称',
|
||||
editable: true,
|
||||
placeholderText: file.name,
|
||||
success: (res) => {
|
||||
if (res.confirm && res.content) {
|
||||
uni.showToast({ title: '重命名成功', icon: 'success' });
|
||||
refreshCurrentFileList();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 删除文件
|
||||
const handleDeleteFile = (file) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: `确定要删除"${file.name}"吗?`,
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
const filePath = file.full_path && !file.full_path.startsWith('http')
|
||||
? file.full_path
|
||||
: file.path + file.name;
|
||||
if (workspaceId.value) {
|
||||
await daleteWorkspace(userToken.value, workspaceId.value, filePath);
|
||||
}
|
||||
uni.showToast({ title: '删除成功', icon: 'success' });
|
||||
refreshCurrentFileList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
uni.showToast({ title: '删除失败,请重试', icon: 'error' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 刷新当前文件列表
|
||||
const refreshCurrentFileList = async () => {
|
||||
if (currentCategory.value === 'user') {
|
||||
await fetchFileList();
|
||||
} else {
|
||||
await fetchTeamFileList(currentTeamId.value);
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 新建文件夹 ==========
|
||||
const showNewFolder = ref(false);
|
||||
const newFolderName = ref('');
|
||||
|
||||
const openNewFolderModal = () => {
|
||||
showNewFolder.value = true;
|
||||
newFolderName.value = '';
|
||||
};
|
||||
const closeNewFolderModal = () => {
|
||||
showNewFolder.value = false;
|
||||
newFolderName.value = '';
|
||||
};
|
||||
const confirmCreateFolder = async () => {
|
||||
const name = newFolderName.value.trim();
|
||||
if (!name) {
|
||||
uni.showToast({ title: '请输入目录名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (workspaceId.value) {
|
||||
const prefix = currentFolderDisplayPath.value === '根目录 (/)'
|
||||
? '/'
|
||||
: currentFolderDisplayPath.value;
|
||||
const dirPath = prefix + name;
|
||||
await createWorkspaceFolder(userToken.value, workspaceId.value, dirPath);
|
||||
}
|
||||
uni.showToast({ title: '创建成功', icon: 'success' });
|
||||
closeNewFolderModal();
|
||||
refreshCurrentFileList();
|
||||
} catch (error) {
|
||||
console.error('创建文件夹失败:', error);
|
||||
uni.showToast({ title: '创建失败,请重试', icon: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 数据库表 ==========
|
||||
const personalTables = ref([
|
||||
{ id: '1', name: 'users', desc: '用户信息表', rowCount: 128 },
|
||||
{ id: '2', name: 'messages', desc: '聊天记录表', rowCount: 2048 },
|
||||
{ id: '3', name: 'settings', desc: '用户配置表', rowCount: 56 }
|
||||
]);
|
||||
const teamTables = ref([
|
||||
{ id: 't1', name: 'team_tasks', desc: '团队任务表', rowCount: 32 },
|
||||
{ id: 't2', name: 'team_docs', desc: '团队文档表', rowCount: 15 }
|
||||
]);
|
||||
|
||||
const showNewTable = ref(false);
|
||||
const newTableName = ref('');
|
||||
const newTableDesc = ref('');
|
||||
|
||||
const openNewTableModal = () => {
|
||||
showNewTable.value = true;
|
||||
newTableName.value = '';
|
||||
newTableDesc.value = '';
|
||||
};
|
||||
const closeNewTableModal = () => {
|
||||
showNewTable.value = false;
|
||||
newTableName.value = '';
|
||||
newTableDesc.value = '';
|
||||
};
|
||||
const confirmCreateTable = () => {
|
||||
const name = newTableName.value.trim();
|
||||
if (!name) {
|
||||
uni.showToast({ title: '请输入表名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const newTable = {
|
||||
id: Date.now().toString(),
|
||||
name: name,
|
||||
desc: newTableDesc.value.trim() || '无描述',
|
||||
rowCount: 0
|
||||
};
|
||||
if (currentCategory.value === 'user') {
|
||||
personalTables.value.push(newTable);
|
||||
} else {
|
||||
teamTables.value.push(newTable);
|
||||
}
|
||||
uni.showToast({ title: '创建成功', icon: 'success' });
|
||||
closeNewTableModal();
|
||||
};
|
||||
|
||||
const handleTableClick = (table) => {
|
||||
uni.showToast({ title: `进入数据表:${table.name}`, icon: 'none' });
|
||||
};
|
||||
|
||||
// ========== 团队管理 ==========
|
||||
const showTeamManage = ref(false);
|
||||
const teamGroups = ref([]);
|
||||
const currentTeamId = ref('');
|
||||
const currentTeamMembers = ref([]);
|
||||
const inviteKeyword = ref('');
|
||||
|
||||
const fetchTeamList = async () => {
|
||||
try {
|
||||
if (!userId.value) {
|
||||
const token = getToken();
|
||||
const userInfo = await getUserInfo(token);
|
||||
userId.value = userInfo._id;
|
||||
}
|
||||
const groups = await getGroup(userId.value);
|
||||
teamGroups.value = groups || [];
|
||||
} catch (error) {
|
||||
console.error('获取团队列表失败:', error);
|
||||
teamGroups.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const selectTeam = async (teamId) => {
|
||||
currentTeamId.value = teamId;
|
||||
const team = teamGroups.value.find(g => g.id === teamId);
|
||||
if (team) {
|
||||
currentTeamMembers.value = [
|
||||
{ id: 'owner1', receiver: 'owner1', friendNickName: '创建者', username: 'Owner', role: 'owner' },
|
||||
{ id: 'm1', receiver: 'm1', friendNickName: '管理员A', username: 'AdminA', role: 'admin' },
|
||||
{ id: 'm2', receiver: 'm2', friendNickName: '成员B', username: 'MemberB', role: 'member' },
|
||||
{ id: 'm3', receiver: 'm3', friendNickName: '成员C', username: 'MemberC', role: 'member' }
|
||||
];
|
||||
} else {
|
||||
currentTeamMembers.value = [];
|
||||
}
|
||||
// 重置文件夹导航并加载团队文件
|
||||
currentFolderPath.value = [];
|
||||
navbarTitle.value = '云端数据';
|
||||
await fetchTeamFileList(teamId);
|
||||
};
|
||||
|
||||
const openTeamManage = () => {
|
||||
showTeamManage.value = true;
|
||||
if (teamGroups.value.length > 0 && !currentTeamId.value) {
|
||||
selectTeam(teamGroups.value[0].id);
|
||||
}
|
||||
};
|
||||
const closeTeamManage = () => {
|
||||
showTeamManage.value = false;
|
||||
inviteKeyword.value = '';
|
||||
};
|
||||
|
||||
const setTeamAdmin = (member) => {
|
||||
uni.showModal({
|
||||
title: '设置管理员',
|
||||
content: `确定将"${member.friendNickName || member.username}"设为管理员吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
member.role = 'admin';
|
||||
uni.showToast({ title: '已设为管理员', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeTeamMember = (member) => {
|
||||
uni.showModal({
|
||||
title: '移除成员',
|
||||
content: `确定要移除"${member.friendNickName || member.username}"吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const index = currentTeamMembers.value.findIndex(
|
||||
m => (m.id || m.receiver) === (member.id || member.receiver)
|
||||
);
|
||||
if (index !== -1) {
|
||||
currentTeamMembers.value.splice(index, 1);
|
||||
}
|
||||
uni.showToast({ title: '已移除', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const inviteTeamMember = () => {
|
||||
const keyword = inviteKeyword.value.trim();
|
||||
if (!keyword) {
|
||||
uni.showToast({ title: '请输入用户ID或名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
uni.showToast({ title: '邀请已发送', icon: 'success' });
|
||||
inviteKeyword.value = '';
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchFileList();
|
||||
await fetchTeamList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import url("CloudDatabase.css");
|
||||
</style>
|
||||
@@ -350,15 +350,27 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.agree-btn,
|
||||
.refuse-btn {
|
||||
.agree-btn {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border: 1px solid #666;
|
||||
margin-left: 10rpx;
|
||||
border: 1px solid #3b86ff;
|
||||
color: #3b86ff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 好友申请状态 */
|
||||
.friend-status {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.friend-status.agreed {
|
||||
color: #059669;
|
||||
background-color: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
|
||||
/* 通讯录 */
|
||||
.contact-list {
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
<view class="popup-title">{{isAgreeBeFriend ? '通过好友申请' : '申请添加朋友'}}</view>
|
||||
<view class="friend-card">
|
||||
<view class="friend-card-left">
|
||||
<view class="friend-avatar">{{currentAddFriend.avatar}}</view>
|
||||
<!-- <view class="friend-avatar">{{currentAddFriend.avatar}}</view> -->
|
||||
<image v-if="currentAddFriend.avatar" :src="currentAddFriend.avatar" class="friend-avatar"
|
||||
mode="aspectFill"></image>
|
||||
<!-- 如果没有头像,显示默认头像 -->
|
||||
<view v-else class="friend-avatar">
|
||||
<uni-icons type="person-filled" size="100rpx" color="#ffffff"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="friend-card-middle">
|
||||
<view class="friend-name">{{currentAddFriend.username}}</view>
|
||||
@@ -18,9 +24,9 @@
|
||||
</view>
|
||||
<view class="nickname-wrapper">
|
||||
<view>好友备注</view>
|
||||
<input class="input-nickname" placeholder="请输入好友名称" />
|
||||
<input class="input-nickname" placeholder="请输入好友名称" v-model="inputFriendNickname"/>
|
||||
</view>
|
||||
<view class="text-btn confirm-btn">{{isAgreeBeFriend ? '确认通过' : '发送申请'}}</view>
|
||||
<view class="text-btn confirm-btn" @click="handleFriendOperate">{{isAgreeBeFriend ? '确认通过' : '发送申请'}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -158,7 +164,7 @@
|
||||
<view class="search-warpper">
|
||||
<input class="search-file-input" :class="{active:isSearchFriend}"
|
||||
@focus="handleFriendSearchFocus" @blur="handleFriendSearchFocus"
|
||||
v-model="searchFriendName" />
|
||||
v-model="searchFriendName" confirm-type="search" @confirm="searchFriend"/>
|
||||
<view class="iconfont icon-sousuo" @click="searchFriend"></view>
|
||||
</view>
|
||||
<view v-if="!searchFriendName" class="search-tip">请输入用户名或邮箱进行搜索</view>
|
||||
@@ -167,13 +173,16 @@
|
||||
<view class="friend-list">
|
||||
<view class="friend-card" v-for="friend in searchResultList" :key="friend.id">
|
||||
<view class="friend-card-left">
|
||||
<view class="friend-avatar">{{friend.avatar}}</view>
|
||||
<!-- <view class="friend-avatar">{{friend.avatar}}</view> -->
|
||||
<view class="friend-avatar">
|
||||
<image class="friend-avatar" :src="friend.avatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="friend-card-middle">
|
||||
<view class="friend-name">{{friend.nickname}}</view>
|
||||
<view class="friend-label">{{friend.email}}</view>
|
||||
</view>
|
||||
<view class="friend-card-right" @click="addNewFriend(friend.id)">
|
||||
<view class="friend-card-right" @click="addNewFriend(friend._id)">
|
||||
<view class="add-btn text-btn">添加</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -218,7 +227,7 @@
|
||||
<view class="friend-name">friend.nickname</view>
|
||||
<view class="friend-label">friend.email</view>
|
||||
</view>
|
||||
<view class="friend-card-right" @click="addNewFriend(friend.id)">
|
||||
<view class="friend-card-right" @click="addMeetingMembers(friend.id)">
|
||||
<view class="add-btn text-btn">添加</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -258,24 +267,28 @@
|
||||
<view class="tabpage-title">好友申请</view>
|
||||
</view>
|
||||
<view class="tabpage-body">
|
||||
<scroll-view class="group-member" direction="vertical" scroll-y="true">
|
||||
<view v-if="!friendRequestList.length" class="no-result">暂无好友申请</view>
|
||||
<scroll-view v-else class="group-member" direction="vertical" scroll-y="true">
|
||||
<view class="friend-list">
|
||||
<view class="friend-card" v-for="friend in mockFriendDB" :key="friend.id">
|
||||
<view class="friend-card" v-for="request in friendRequestList" :key="request.id">
|
||||
<view class="friend-card-left">
|
||||
<view class="friend-avatar">{{friend.avatar}}</view>
|
||||
</view>
|
||||
<view class="friend-card-middle">
|
||||
<view class="friend-name">{{friend.nickname}}</view>
|
||||
<view class="friend-label">{{friend.email}}</view>
|
||||
</view>
|
||||
<view class="friend-card-right">
|
||||
<view class="friend-option">
|
||||
<view class="refuse-btn text-btn" @click="refuseBeFriend(friend)">拒绝</view>
|
||||
<view class="agree-btn text-btn" @click="agreeBeFriend(friend)">同意</view>
|
||||
<image v-if="request.avatar" :src="request.avatar" class="friend-avatar"
|
||||
mode="aspectFill"></image>
|
||||
<view v-else class="friend-avatar">
|
||||
<uni-icons type="person-filled" size="100rpx" color="#ffffff"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="friend-card-middle">
|
||||
<view class="friend-name">{{request.friendNickName || request.sendId}}</view>
|
||||
<view class="friend-label">{{request.message || '请求添加你为好友'}}</view>
|
||||
</view>
|
||||
<view class="friend-card-right">
|
||||
<view v-if="request.state === 0" class="friend-option">
|
||||
<view class="agree-btn text-btn" @click="agreeBeFriend(request)">同意</view>
|
||||
</view>
|
||||
<view v-else class="friend-status agreed">已同意</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
@@ -306,7 +319,10 @@
|
||||
} from '@/utils/user-info.js'
|
||||
import {
|
||||
getChatFriend,
|
||||
getGroup
|
||||
getGroup,
|
||||
sendFriendRequest,
|
||||
getFriendRequestList,
|
||||
saveFriend
|
||||
} from '@/utils/friend-api.js'
|
||||
import {
|
||||
getUserInfo,
|
||||
@@ -466,6 +482,7 @@
|
||||
onMounted(async () => {
|
||||
await takeFriendList()
|
||||
await takeGroupList()
|
||||
await takeFriendRequestList()
|
||||
handleFriendConnect()
|
||||
})
|
||||
// 群聊相关
|
||||
@@ -656,7 +673,7 @@
|
||||
// 模糊查询:遍历 mockFriendDB,匹配 nickname、username 或 email
|
||||
const keyword = searchFriendName.value.toLowerCase().trim();
|
||||
searchResultList.value = await searchUsers(userToken.value,keyword)
|
||||
console.log("searchResultList.value:",searchResultList.value);
|
||||
console.log("searchResultList.value:",JSON.stringify(searchResultList.value));
|
||||
// const results = mockFriendDB.value.filter(friend => {
|
||||
// const isMatch = friend.nickname.toLowerCase().includes(keyword) || friend.username.toLowerCase()
|
||||
// .includes(keyword) || friend.email.toLowerCase().includes(keyword);
|
||||
@@ -670,7 +687,7 @@
|
||||
|
||||
const currentAddFriend = ref(null)
|
||||
const addNewFriend = (id) => {
|
||||
const friend = mockFriendDB.value.find(friend => friend.id === id)
|
||||
const friend = searchResultList.value.find(friend => friend._id === id)
|
||||
if (friend) {
|
||||
// 创建副本避免引用问题
|
||||
currentAddFriend.value = {
|
||||
@@ -685,6 +702,8 @@
|
||||
currentAddFriend.value = null
|
||||
isAgreeBeFriend.value = false
|
||||
}
|
||||
|
||||
const inputFriendNickname = ref('')
|
||||
|
||||
// 视频会议相关
|
||||
const isVoice = ref(false);
|
||||
@@ -698,77 +717,97 @@
|
||||
|
||||
// 好友申请相关
|
||||
const isAgreeBeFriend = ref(false)
|
||||
const refuseBeFriend = (friend) => {
|
||||
|
||||
const friendRequestList = ref([])
|
||||
// 获取好友申请列表
|
||||
const takeFriendRequestList = async () => {
|
||||
try {
|
||||
if (!UserId.value) return
|
||||
const list = await getFriendRequestList(UserId.value)
|
||||
if (list && list.length) {
|
||||
friendRequestList.value = await takeRequestAvatar(list)
|
||||
} else {
|
||||
friendRequestList.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取好友申请列表失败:', error)
|
||||
friendRequestList.value = []
|
||||
}
|
||||
}
|
||||
const agreeBeFriend = (friend) => {
|
||||
// 获取好友申请人的头像
|
||||
const takeRequestAvatar = async (requestList) => {
|
||||
if (!requestList?.length) return []
|
||||
try {
|
||||
const requestIds = requestList.map(item => item.sendId)
|
||||
const avatarList = await getUserAvatar(userToken.value, requestIds)
|
||||
const userMap = new Map(avatarList.map(user => [user.user_id, user]) || [])
|
||||
return requestList.map(request => {
|
||||
const userInfo = userMap.get(request.sendId)
|
||||
return {
|
||||
...request,
|
||||
avatar: userInfo?.avatar || null
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('获取申请人头像失败', err)
|
||||
return requestList
|
||||
}
|
||||
}
|
||||
|
||||
const agreeBeFriend = (request) => {
|
||||
isAgreeBeFriend.value = true
|
||||
currentAddFriend.value = {
|
||||
...friend
|
||||
...request,
|
||||
username: request.friendNickName || request.sendId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 模拟好友数据库
|
||||
const mockFriendDB = ref([{
|
||||
id: 1,
|
||||
nickname: '张三',
|
||||
username: 'zhangsan',
|
||||
email: 'zhangsan@example.com',
|
||||
avatar: '👤'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
nickname: '李四',
|
||||
username: 'lisi',
|
||||
email: 'lisi@example.com',
|
||||
avatar: '👥'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
nickname: '王小明',
|
||||
username: 'wangxm',
|
||||
email: 'wang@example.com',
|
||||
avatar: '👪'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
nickname: '赵磊',
|
||||
username: 'zhaolei',
|
||||
email: 'zhao@example.com',
|
||||
avatar: '🗣️'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
nickname: '张三2',
|
||||
username: 'zhangsan',
|
||||
email: 'zhangsan@example.com',
|
||||
avatar: '👤'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
nickname: '李四2',
|
||||
username: 'lisi',
|
||||
email: 'lisi@example.com',
|
||||
avatar: '👥'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
nickname: '王小明2',
|
||||
username: 'wangxm',
|
||||
email: 'wang@example.com',
|
||||
avatar: '👪'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
nickname: '赵磊2',
|
||||
username: 'zhaolei',
|
||||
email: 'zhao@example.com',
|
||||
avatar: '🗣️'
|
||||
// 同意好友申请的确认操作
|
||||
const handleFriendOperate = async () => {
|
||||
if (isAgreeBeFriend.value) {
|
||||
// 通过好友申请(保存好友即同意)
|
||||
try {
|
||||
const friendId = currentAddFriend.value.sendId
|
||||
const friendNickName = inputFriendNickname.value || currentAddFriend.value.friendNickName || ''
|
||||
const sessionId = currentAddFriend.value.sessionId || Date.now()
|
||||
const friendRelationshipId = currentAddFriend.value.id
|
||||
await saveFriend([
|
||||
{
|
||||
friendNickName: friendNickName,
|
||||
sender: UserId.value,
|
||||
receiver: friendId,
|
||||
sessionId: sessionId,
|
||||
friendRelationshipId: friendRelationshipId
|
||||
},
|
||||
{
|
||||
friendNickName: '',
|
||||
sender: friendId,
|
||||
receiver: UserId.value,
|
||||
sessionId: sessionId,
|
||||
friendRelationshipId: friendRelationshipId
|
||||
}
|
||||
])
|
||||
uni.showToast({ title: '已同意好友申请', icon: 'none' })
|
||||
const target = friendRequestList.value.find(item => item.sendId === friendId)
|
||||
if (target) target.status = 1
|
||||
closeAddFriendCard()
|
||||
await takeFriendList()
|
||||
} catch (error) {
|
||||
console.error('同意好友申请失败:', error)
|
||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||||
}
|
||||
} else {
|
||||
// 申请好友(添加好友页面的发送申请)
|
||||
try {
|
||||
const friendId = currentAddFriend.value._id
|
||||
const msg = await sendFriendRequest(UserId.value, inputFriendNickname.value, friendId)
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
closeAddFriendCard()
|
||||
} catch (error) {
|
||||
console.error('发送好友申请失败:', error)
|
||||
uni.showToast({ title: '发送申请失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
])
|
||||
}
|
||||
|
||||
// 通讯录相关
|
||||
</script>
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
onMounted,
|
||||
ref
|
||||
} from 'vue';
|
||||
import { getUserInfo } from '@/utils/cloud-api.js'
|
||||
import {getToken} from '@/utils/user-info.js'
|
||||
import { getUserInfo, updateUserInfo } from '@/utils/cloud-api.js'
|
||||
import { getToken } from '@/utils/user-info.js'
|
||||
|
||||
const closeUserCard = () => {
|
||||
console.log("点击了返回")
|
||||
@@ -63,13 +63,39 @@
|
||||
})
|
||||
}
|
||||
|
||||
const saveUpdate = () => {
|
||||
uni.navigateBack({
|
||||
url: '/pages/Chat/Chat'
|
||||
})
|
||||
const saveUpdate = async() => {
|
||||
// 表单校验
|
||||
if (!editUsername.value.trim()) {
|
||||
uni.showToast({ title: '用户名不能为空', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({ title: '保存中...', mask: true })
|
||||
|
||||
try {
|
||||
await updateUserInfo(
|
||||
UserToken.value,
|
||||
UserId.value,
|
||||
editUsername.value.trim(),
|
||||
editEmail.value.trim(),
|
||||
editUserAvatar.value
|
||||
)
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '保存成功', icon: 'success' })
|
||||
// 保存成功后延迟返回上一页
|
||||
setTimeout(() => {
|
||||
closeUserCard()
|
||||
}, 800)
|
||||
} catch (err) {
|
||||
uni.hideLoading()
|
||||
const msg = typeof err === 'string' ? err : '保存失败,请稍后重试'
|
||||
uni.showToast({ title: msg, icon: 'none' })
|
||||
console.error('保存用户信息失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const UserToken = ref('')
|
||||
const UserId = ref('')
|
||||
const editUserAvatar = ref('')
|
||||
const editUsername = ref('')
|
||||
const editEmail = ref('')
|
||||
@@ -84,7 +110,7 @@
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
const tempFilePath = res.tempFilePaths[0]
|
||||
handlrImageSelected(tempFilePath)
|
||||
handleImageSelected(tempFilePath)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败', err);
|
||||
@@ -93,17 +119,22 @@
|
||||
}
|
||||
|
||||
// 处理选择的图片
|
||||
const handlrImageSelected = (filePath) => {
|
||||
const handleImageSelected = (filePath) => {
|
||||
editUserAvatar.value = filePath
|
||||
}
|
||||
|
||||
onMounted(async()=>{
|
||||
const token = getToken()
|
||||
const UserInfo = await getUserInfo(token)
|
||||
|
||||
editUserAvatar.value = UserInfo.avatar || ''
|
||||
editUsername.value = UserInfo.username || ''
|
||||
editEmail.value = UserInfo.email || ''
|
||||
UserToken.value = getToken()
|
||||
try {
|
||||
const UserInfo = await getUserInfo(UserToken.value)
|
||||
UserId.value = UserInfo._id || ''
|
||||
editUserAvatar.value = UserInfo.avatar || ''
|
||||
editUsername.value = UserInfo.username || ''
|
||||
editEmail.value = UserInfo.email || ''
|
||||
} catch (err) {
|
||||
console.error('获取用户信息失败:', err)
|
||||
uni.showToast({ title: '加载用户信息失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -54,6 +54,12 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">云端,云,云服务</div>
|
||||
<div class="code-name">&#xe988;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">其他文件</div>
|
||||
@@ -420,9 +426,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1780384681439') format('woff2'),
|
||||
url('iconfont.woff?t=1780384681439') format('woff'),
|
||||
url('iconfont.ttf?t=1780384681439') format('truetype');
|
||||
src: url('iconfont.woff2?t=1782200893170') format('woff2'),
|
||||
url('iconfont.woff?t=1782200893170') format('woff'),
|
||||
url('iconfont.ttf?t=1782200893170') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -448,6 +454,15 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-cloud"></span>
|
||||
<div class="name">
|
||||
云端,云,云服务
|
||||
</div>
|
||||
<div class="code-name">.icon-cloud
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qitawenjian"></span>
|
||||
<div class="name">
|
||||
@@ -997,6 +1012,14 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-cloud"></use>
|
||||
</svg>
|
||||
<div class="name">云端,云,云服务</div>
|
||||
<div class="code-name">#icon-cloud</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qitawenjian"></use>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4890805 */
|
||||
src: url('iconfont.woff2?t=1780384681439') format('woff2'),
|
||||
url('iconfont.woff?t=1780384681439') format('woff'),
|
||||
url('iconfont.ttf?t=1780384681439') format('truetype');
|
||||
src: url('iconfont.woff2?t=1782200893170') format('woff2'),
|
||||
url('iconfont.woff?t=1782200893170') format('woff'),
|
||||
url('iconfont.ttf?t=1782200893170') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-cloud:before {
|
||||
content: "\e988";
|
||||
}
|
||||
|
||||
.icon-qitawenjian:before {
|
||||
content: "\e7b6";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "18170211",
|
||||
"name": "云端,云,云服务",
|
||||
"font_class": "cloud",
|
||||
"unicode": "e988",
|
||||
"unicode_decimal": 59784
|
||||
},
|
||||
{
|
||||
"icon_id": "35959059",
|
||||
"name": "其他文件",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
;(function(){
|
||||
let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[];
|
||||
const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"uni-app","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"test1","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"5.07","entryPagePath":"pages/Login/Login","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"locales":{},"darkmode":false,"themeConfig":{}};
|
||||
const __uniRoutes = [{"path":"pages/Login/Login","meta":{"isQuit":true,"isEntry":true,"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"登录页面","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/Chat/Chat","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"聊天页面(主页面)","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/WorkSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"工作区文件管理","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/UserProfileModal/UserProfileModal","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/ContactPages/ContactPages","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/TemplateSpace/TemplateSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/text/text","meta":{"navigationBar":{"titleText":"","type":"default"},"isNVue":false}},{"path":"pages/text/text2","meta":{"navigationBar":{"type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
|
||||
const __uniRoutes = [{"path":"pages/Login/Login","meta":{"isQuit":true,"isEntry":true,"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"登录页面","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/Chat/Chat","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"聊天页面(主页面)","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/WorkSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"工作区文件管理","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/UserProfileModal/UserProfileModal","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/ContactPages/ContactPages","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/TemplateSpace/TemplateSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/text/text","meta":{"navigationBar":{"titleText":"","type":"default"},"isNVue":false}},{"path":"pages/text/text2","meta":{"navigationBar":{"type":"default"},"isNVue":false}},{"path":"pages/CloudDatabase/CloudDatabase","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"云端数据","style":"custom","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute));
|
||||
__uniConfig.styles=[];//styles
|
||||
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
|
||||
2268
unpackage/dist/dev/app-plus/app-service.js
vendored
2268
unpackage/dist/dev/app-plus/app-service.js
vendored
File diff suppressed because it is too large
Load Diff
9
unpackage/dist/dev/app-plus/app.css
vendored
9
unpackage/dist/dev/app-plus/app.css
vendored
@@ -28,9 +28,9 @@
|
||||
/*每个页面公共css */
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4890805 */
|
||||
src: url('static/iconfont/iconfont.woff2?t=1780384681439') format('woff2'),
|
||||
url('static/iconfont/iconfont.woff?t=1780384681439') format('woff'),
|
||||
url('static/iconfont/iconfont.ttf?t=1780384681439') format('truetype');
|
||||
src: url('static/iconfont/iconfont.woff2?t=1782200893170') format('woff2'),
|
||||
url('static/iconfont/iconfont.woff?t=1782200893170') format('woff'),
|
||||
url('static/iconfont/iconfont.ttf?t=1782200893170') format('truetype');
|
||||
}
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
@@ -39,6 +39,9 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.icon-cloud:before {
|
||||
content: "\e988";
|
||||
}
|
||||
.icon-qitawenjian:before {
|
||||
content: "\e7b6";
|
||||
}
|
||||
|
||||
1116
unpackage/dist/dev/app-plus/pages/CloudDatabase/CloudDatabase.css
vendored
Normal file
1116
unpackage/dist/dev/app-plus/pages/CloudDatabase/CloudDatabase.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -841,15 +841,26 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.agree-btn[data-v-976117c3],
|
||||
.refuse-btn[data-v-976117c3] {
|
||||
.agree-btn[data-v-976117c3] {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border: 1px solid #666;
|
||||
margin-left: 0.3125rem;
|
||||
border: 1px solid #3b86ff;
|
||||
color: #3b86ff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 好友申请状态 */
|
||||
.friend-status[data-v-976117c3] {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 0.25rem 0.625rem;
|
||||
border-radius: 0.9375rem;
|
||||
}
|
||||
.friend-status.agreed[data-v-976117c3] {
|
||||
color: #059669;
|
||||
background-color: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
/* 通讯录 */
|
||||
.contact-list[data-v-976117c3] {
|
||||
display: flex;
|
||||
|
||||
@@ -54,6 +54,12 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">云端,云,云服务</div>
|
||||
<div class="code-name">&#xe988;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">其他文件</div>
|
||||
@@ -420,9 +426,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1780384681439') format('woff2'),
|
||||
url('iconfont.woff?t=1780384681439') format('woff'),
|
||||
url('iconfont.ttf?t=1780384681439') format('truetype');
|
||||
src: url('iconfont.woff2?t=1782200893170') format('woff2'),
|
||||
url('iconfont.woff?t=1782200893170') format('woff'),
|
||||
url('iconfont.ttf?t=1782200893170') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -448,6 +454,15 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-cloud"></span>
|
||||
<div class="name">
|
||||
云端,云,云服务
|
||||
</div>
|
||||
<div class="code-name">.icon-cloud
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qitawenjian"></span>
|
||||
<div class="name">
|
||||
@@ -997,6 +1012,14 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-cloud"></use>
|
||||
</svg>
|
||||
<div class="name">云端,云,云服务</div>
|
||||
<div class="code-name">#icon-cloud</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qitawenjian"></use>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4890805 */
|
||||
src: url('iconfont.woff2?t=1780384681439') format('woff2'),
|
||||
url('iconfont.woff?t=1780384681439') format('woff'),
|
||||
url('iconfont.ttf?t=1780384681439') format('truetype');
|
||||
src: url('iconfont.woff2?t=1782200893170') format('woff2'),
|
||||
url('iconfont.woff?t=1782200893170') format('woff'),
|
||||
url('iconfont.ttf?t=1782200893170') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-cloud:before {
|
||||
content: "\e988";
|
||||
}
|
||||
|
||||
.icon-qitawenjian:before {
|
||||
content: "\e7b6";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "18170211",
|
||||
"name": "云端,云,云服务",
|
||||
"font_class": "cloud",
|
||||
"unicode": "e988",
|
||||
"unicode_decimal": 59784
|
||||
},
|
||||
{
|
||||
"icon_id": "35959059",
|
||||
"name": "其他文件",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
// 云端基础URL
|
||||
// const BASE_URL = 'https://cloud.yuxindazhineng.com'
|
||||
const BASE_URL = 'https://cloudtest.yuxindazhineng.com'
|
||||
const BASE_URL = 'https://cloud.yuxindazhineng.com'
|
||||
// const BASE_URL = 'https://cloudtest.yuxindazhineng.com'
|
||||
// const BASE_URL = 'https://yxdpqj.yuxindazhineng.com'
|
||||
|
||||
// 登录请求
|
||||
@@ -85,8 +85,10 @@ export const getConversationMessages = async (token, conversatio_id) => {
|
||||
if (res.statusCode === 200) {
|
||||
const messagesInfo = res.data
|
||||
if (messagesInfo) {
|
||||
console.log("工作区id为:", messagesInfo.workspace_id);
|
||||
uni.setStorageSync('workspace_id', messagesInfo.workspace_id)
|
||||
console.log("工作区id为:", messagesInfo.workspace_id || '(无)');
|
||||
if (messagesInfo.workspace_id) {
|
||||
uni.setStorageSync('workspace_id', messagesInfo.workspace_id)
|
||||
}
|
||||
resolve(messagesInfo.messages)
|
||||
} else {
|
||||
const msg = res.data.error || '获取消息出错啦'
|
||||
@@ -143,7 +145,7 @@ export const addMessageDict = async (token, role, conversation_id, message) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取工作区消息(非内容)
|
||||
// 获取工作区信息(非内容)
|
||||
export const getWorkspaceByConversation = async (token, conversation_id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
@@ -162,11 +164,11 @@ export const getWorkspaceByConversation = async (token, conversation_id) => {
|
||||
if (workspaceInfo?.success) {
|
||||
resolve(workspaceInfo.workspace_id)
|
||||
} else {
|
||||
const msg = res.data.error || '获取工作区消息出错啦'
|
||||
const msg = res.data.error || '获取工作区信息出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`获取工作区消息失败:${res.statusCode}`)
|
||||
reject(`获取工作区信息失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
@@ -437,7 +439,7 @@ export const daleteWorkspace = (token, workspacesId, path) => {
|
||||
"file_path": path
|
||||
},
|
||||
success: (res) => {
|
||||
if(res.statusCode === 200){
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond.success) {
|
||||
resolve(respond.message)
|
||||
@@ -445,7 +447,7 @@ export const daleteWorkspace = (token, workspacesId, path) => {
|
||||
const msg = respond?.error || '删除工作区文件出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
reject(`删除工作区文件失败:${res.statusCode}`)
|
||||
}
|
||||
}
|
||||
@@ -468,7 +470,7 @@ export const createWorkspaceFolder = (token, workspacesId, path) => {
|
||||
"dir_path": path
|
||||
},
|
||||
success: (res) => {
|
||||
if(res.statusCode === 200){
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond.success) {
|
||||
resolve(respond.message)
|
||||
@@ -476,7 +478,7 @@ export const createWorkspaceFolder = (token, workspacesId, path) => {
|
||||
const msg = respond?.error || '创建工作区文件夹出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
reject(`创建工作区文件夹失败:${res.statusCode}`)
|
||||
}
|
||||
}
|
||||
@@ -484,7 +486,7 @@ export const createWorkspaceFolder = (token, workspacesId, path) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取工作区文URL
|
||||
// 获取工作区文件的URL
|
||||
export const getWorkspaceFileURL = (token, workspacesId, path) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
@@ -499,7 +501,7 @@ export const getWorkspaceFileURL = (token, workspacesId, path) => {
|
||||
"file_path": path
|
||||
},
|
||||
success: (res) => {
|
||||
if(res.statusCode === 200){
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
// 兼容两种返回格式:{success: true, message: [...]} 或直接返回数组
|
||||
if (respond.success) {
|
||||
@@ -510,10 +512,179 @@ export const getWorkspaceFileURL = (token, workspacesId, path) => {
|
||||
const msg = respond?.error || '获取工作区文URL出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
reject(`获取工作区文URL失败:${res.statusCode}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 上传文件到工作区(手机弃用,仅支持pc本地前缀)
|
||||
// export const workspaceUploadFile = (workspacesId,file,path) => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// uni.request({
|
||||
// url: `${BASE_URL}/workspace/file/upload`,
|
||||
// method: "POST",
|
||||
// header: {
|
||||
// 'Content-Type': 'application/json'
|
||||
// },
|
||||
// data: {
|
||||
// workspaces_id: workspacesId,
|
||||
// file: file,
|
||||
// file_path: path,
|
||||
// overwrite: true
|
||||
// },
|
||||
// success: (res) => {
|
||||
// if(res.statusCode === 200){
|
||||
// const respond = res.data
|
||||
// if (respond.success) {
|
||||
// resolve(respond)Z
|
||||
// } else {
|
||||
// const msg = respond?.error || '上传文件到工作区出错啦'
|
||||
// reject(msg)
|
||||
// }
|
||||
// }else {
|
||||
// reject(`上传文件到工作区失败:${res.statusCode}`)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
// 上传用户头像
|
||||
export const uploadUserAvatar = (token, userId, username, email, avatarURL) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${BASE_URL}/cloud_api/user/file/upload`,
|
||||
method: "POST",
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
"access_token": token,
|
||||
"file": file,
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond.success) {
|
||||
resolve(respond.upload_url)
|
||||
} else {
|
||||
const msg = respond?.error || '上传用户头像出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`上传用户头像失败:${res.statusCode}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 修改用户信息
|
||||
export const updateUserInfo = (token, userId, username, email, avatarURL) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${BASE_URL}/cloud_api/update_user_profile`,
|
||||
method: "POST",
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
"access_token": token,
|
||||
"user_id": userId,
|
||||
"username": username,
|
||||
"email:": email,
|
||||
"avatar": avatarURL
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond.success) {
|
||||
resolve(respond.message)
|
||||
} else {
|
||||
const msg = respond?.error || '修改用户信息出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`修改用户信息失败:${res.statusCode}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ============================云端文件管理=========================
|
||||
// 获取云端个人/团队文件列表
|
||||
export const getCloudFileList = (token, type, path = "/", teamId = "") => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${BASE_URL}/cloud_api/file/list`,
|
||||
method: "POST",
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
"access_token": token,
|
||||
"path": path,
|
||||
"type": type,
|
||||
"team_id": teamId
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond.success) {
|
||||
resolve(respond.data)
|
||||
} else {
|
||||
const msg = respond?.error || '获取工作区文件目录出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`获取工作区文件目录失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
const msg = err.errMsg || '网络错误'
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 下载云端文件,通过文件id获取下载url
|
||||
export const getCloudFileUrl = (token, fileId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${BASE_URL}/cloud_api/file/download`,
|
||||
method: "POST",
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
"access_token": token,
|
||||
"file_id": fileId,
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond.success) {
|
||||
resolve(respond.data.download_url)
|
||||
} else {
|
||||
const msg = respond?.error || '获取云端文件url出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`获取云端文件url失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
const msg = err.errMsg || '网络错误'
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ============================云端数据库=========================
|
||||
|
||||
@@ -189,17 +189,17 @@ export const getGroupMemberList = async (groupId) => {
|
||||
"size": 100
|
||||
},
|
||||
success: (res) => {
|
||||
if(res.statusCode ===200){
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if(respond.code === 0){
|
||||
if (respond.code === 0) {
|
||||
const groupMemberList = respond.data.records
|
||||
resolve(groupMemberList)
|
||||
} else {
|
||||
const msg = res.data.error || '获取群成员出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
} else {
|
||||
reject(`请求群成员失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
@@ -211,7 +211,7 @@ export const getGroupMemberList = async (groupId) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
// 聊天上传文件
|
||||
export const uploadFileToServer = (filePath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
@@ -232,3 +232,100 @@ export const uploadFileToServer = (filePath) => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 添加好友请求
|
||||
export const sendFriendRequest = async (sendId, friendNickName, friendId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log("进入sendFriendRequest");
|
||||
uni.request({
|
||||
url: `${BASE_Friend_URL}/api/friendship/saveShip`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
sendId: sendId,
|
||||
friendNickName: friendNickName,
|
||||
recipientId: friendId
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond?.code === 0 || respond?.msg === '成功') {
|
||||
resolve("已成功发送好友申请")
|
||||
} else {
|
||||
const msg = res.data.msg || '发送好友申请出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`发送好友申请失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
const msg = err.errMsg || '网络错误'
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 保存好友关系(即同意好友,双向保存)
|
||||
export const saveFriend = async (friendList) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${BASE_Friend_URL}/api/chatList/save`,
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: friendList,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond?.code === 0 || respond?.msg === '成功') {
|
||||
resolve("保存好友成功")
|
||||
} else {
|
||||
const msg = respond?.msg || '保存好友出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`保存好友失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
const msg = err.errMsg || '网络错误'
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 获取添加我的好友
|
||||
export const getFriendRequestList = async (sendId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${BASE_Friend_URL}/api/friendship/getFriendship`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
"current": 1,
|
||||
"size": 100,
|
||||
"sendId": sendId
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
const respond = res.data
|
||||
if (respond?.code === 0 || respond?.msg === '成功') {
|
||||
const records = respond?.data?.records || []
|
||||
resolve(records)
|
||||
} else {
|
||||
const msg = res.data.msg || '获取添加我的好友出错啦'
|
||||
reject(msg)
|
||||
}
|
||||
} else {
|
||||
reject(`获取添加我的好友失败:${res.statusCode}`)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
const msg = err.errMsg || '网络错误'
|
||||
reject(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -34,6 +34,7 @@ class WebSocketManager {
|
||||
this.isManualClose = false
|
||||
// 关闭现有连接
|
||||
if (this.ws) {
|
||||
console.log('关闭现有连接');
|
||||
this.close()
|
||||
}
|
||||
try {
|
||||
@@ -92,7 +93,7 @@ class WebSocketManager {
|
||||
}
|
||||
})
|
||||
this.ws.onError((err) => {
|
||||
console.error('WebSocket 错误:', err)
|
||||
console.error('WebSocket 错误:', JSON.stringify(err))
|
||||
this.handleError(err)
|
||||
})
|
||||
this.ws.onClose((event) => {
|
||||
@@ -299,11 +300,12 @@ class WebSocketManager {
|
||||
|
||||
if (this.ws) {
|
||||
// 移除所有事件监听,避免内存泄漏
|
||||
// 注意:不能用 null 调用回调,否则会触发 onError(null) 产生假错误日志
|
||||
// #ifdef APP-PLUS || MP-WEIXIN
|
||||
if (this.ws.onOpen) this.ws.onOpen(null)
|
||||
if (this.ws.onMessage) this.ws.onMessage(null)
|
||||
if (this.ws.onError) this.ws.onError(null)
|
||||
if (this.ws.onClose) this.ws.onClose(null)
|
||||
this.ws.onOpen(() => {})
|
||||
this.ws.onMessage(() => {})
|
||||
this.ws.onError(() => {})
|
||||
this.ws.onClose(() => {})
|
||||
// #endif
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const WS_APP_URL = 'ws://cloud_test.yuxindazhineng.com'
|
||||
// const WS_APP_URL = 'ws://cloud_test.yuxindazhineng.com'
|
||||
const WS_APP_URL = 'ws://cloud.yuxindazhineng.com'
|
||||
// (工具类)连接、发送、接收、重连...
|
||||
class WebSocketManager {
|
||||
constructor() {
|
||||
|
||||
Reference in New Issue
Block a user