第一次打包失败
This commit is contained in:
@@ -850,4 +850,91 @@
|
||||
color: #999;
|
||||
margin-bottom: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ===== 加载动画 ===== */
|
||||
.loading-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border: 4rpx solid #e8e8e8;
|
||||
border-top-color: #3b86ff;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* ===== 数据库标签 ===== */
|
||||
.db-tag {
|
||||
display: inline-block;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
padding: 2rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-left: 10rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.db-tag.tag-personal {
|
||||
color: #3b86ff;
|
||||
background: rgba(59, 134, 255, 0.08);
|
||||
border: 1rpx solid rgba(59, 134, 255, 0.2);
|
||||
}
|
||||
|
||||
.db-tag.tag-team {
|
||||
color: #67c23a;
|
||||
background: rgba(103, 194, 58, 0.08);
|
||||
border: 1rpx solid rgba(103, 194, 58, 0.2);
|
||||
}
|
||||
|
||||
/* ===== 上传数据库弹窗 ===== */
|
||||
.upload-db-hint {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-bottom: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-file-area {
|
||||
padding: 40rpx;
|
||||
border: 2rpx dashed #d0d0d0;
|
||||
border-radius: 16rpx;
|
||||
background: #f9fafb;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.upload-file-area:active {
|
||||
border-color: #3b86ff;
|
||||
background: rgba(59, 134, 255, 0.03);
|
||||
}
|
||||
|
||||
.selected-file {
|
||||
font-size: 26rpx;
|
||||
color: #3b86ff;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
font-size: 24rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
@@ -150,24 +150,39 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 新建文件夹弹窗 ===== -->
|
||||
<view v-if="showNewTable" class="popup-overlay" @click="closeNewTableModal">
|
||||
<!-- ===== 新建数据库弹窗 ===== -->
|
||||
<view v-if="showNewDb" class="popup-overlay" @click="closeNewDbModal">
|
||||
<view class="popup-card" @click.stop>
|
||||
<view class="close-popup-card" @click="closeNewTableModal">
|
||||
<view class="close-popup-card" @click="closeNewDbModal">
|
||||
<view class="iconfont icon-quxiao"></view>
|
||||
</view>
|
||||
<view class="popup-title">新建{{ currentCategory === 'user' ? '个人' : '团队' }}数据表</view>
|
||||
<view class="new-folder-name">表名称</view>
|
||||
<view class="popup-title">新建个人数据库</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="输入表描述(可选)" />
|
||||
<input v-model="newDbName" 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 class="opt-btn opt-cancel" @click="closeNewDbModal">取消</view>
|
||||
<view class="opt-btn opt-confirm" @click="confirmCreateDb">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ===== 上传数据库文件弹窗 ===== -->
|
||||
<view v-if="showUploadDb" class="popup-overlay" @click="closeUploadDbModal">
|
||||
<view class="popup-card" @click.stop>
|
||||
<view class="close-popup-card" @click="closeUploadDbModal">
|
||||
<view class="iconfont icon-quxiao"></view>
|
||||
</view>
|
||||
<view class="popup-title">上传数据库文件</view>
|
||||
<view class="upload-db-hint">支持 .db / .sqlite / .sqlite3 格式的 SQLite 数据库文件</view>
|
||||
<view class="upload-file-area" @click="handlePickDbFile">
|
||||
<view v-if="uploadDbFileName" class="selected-file">{{ uploadDbFileName }}</view>
|
||||
<view v-else class="upload-hint">点击选择数据库文件</view>
|
||||
</view>
|
||||
<view class="option-wrapper">
|
||||
<view class="opt-btn opt-cancel" @click="closeUploadDbModal">取消</view>
|
||||
<view class="opt-btn opt-confirm" @click="confirmUploadDb">确认上传</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -182,15 +197,15 @@
|
||||
</view>
|
||||
<view class="navbar-title">{{ navbarTitle }}</view>
|
||||
<view class="navbar-right">
|
||||
<view v-if="isSelectFolder" class="navbar-right-text" @click="handleSelectFolder">完成</view>
|
||||
<!-- <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>
|
||||
@click="handleMenu"></view> -->
|
||||
</view>
|
||||
<!-- <view v-if="isMenuOpen" class="menu-card">
|
||||
<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>
|
||||
|
||||
<!-- 个人/团队 分类切换 -->
|
||||
@@ -258,31 +273,46 @@
|
||||
<view class="section-title">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>云端数据库</text>
|
||||
<text class="file-count">{{ !isDbLoading && personalDatabases.length > 0 ? '(' + personalDatabases.length + ')' : '' }}</text>
|
||||
</view>
|
||||
<view class="section-actions">
|
||||
<view class="action-btn" @click="openNewTableModal">
|
||||
<view class="action-btn" @click="openUploadDbModal">
|
||||
<uni-icons type="upload" size="16"></uni-icons>
|
||||
<text>上传数据库</text>
|
||||
</view>
|
||||
<view class="action-btn" @click="openNewDbModal">
|
||||
<uni-icons type="plus" size="16"></uni-icons>
|
||||
<text>新建表</text>
|
||||
<text>新建数据库</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view v-if="personalTables.length === 0" class="empty-tip">
|
||||
<!-- 加载中 -->
|
||||
<view v-if="isDbLoading" class="loading-area">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="personalDatabases.length === 0" class="empty-tip">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>暂无数据表</text>
|
||||
<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-item" v-for="db in personalDatabases" :key="db.database_id"
|
||||
@click="handleDbClick(db)" @longpress="handleDeleteDb(db)">
|
||||
<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 class="table-name">
|
||||
{{ db.database_name }}
|
||||
<text class="db-tag" :class="db.team_id ? 'tag-team' : 'tag-personal'">
|
||||
{{ db.team_id ? '团队共享' : '个人私有' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="table-desc">{{ db.table_count || 0 }} 张表 · {{ getAccessLevelLabel(db.access_level) }}</view>
|
||||
</view>
|
||||
<view class="table-meta">
|
||||
<text class="table-rows">{{ table.rowCount || 0 }} 条记录</text>
|
||||
<text class="table-rows">{{ formatDbTime(db.modified_time) }}</text>
|
||||
<uni-icons type="right" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
@@ -376,30 +406,32 @@
|
||||
<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 v-if="isDbLoading" class="loading-area">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="teamDatabases.length === 0" class="empty-tip">
|
||||
<view class="iconfont icon-shujuku"></view>
|
||||
<text>暂无团队数据表</text>
|
||||
<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-item" v-for="db in teamDatabases" :key="db.database_id"
|
||||
@click="handleDbClick(db)">
|
||||
<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 class="table-name">
|
||||
{{ db.database_name }}
|
||||
<text class="db-tag tag-team">团队共享</text>
|
||||
</view>
|
||||
<view class="table-desc">{{ db.table_count || 0 }} 张表 · {{ getAccessLevelLabel(db.access_level) }}</view>
|
||||
</view>
|
||||
<view class="table-meta">
|
||||
<text class="table-rows">{{ table.rowCount || 0 }} 条记录</text>
|
||||
<text class="table-rows">{{ formatDbTime(db.modified_time) }}</text>
|
||||
<uni-icons type="right" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
@@ -420,6 +452,7 @@
|
||||
import {
|
||||
getToken
|
||||
} from '@/utils/user-info.js';
|
||||
import { chooseFile } from '@/uni_modules/lime-choose-file';
|
||||
import {
|
||||
getUserInfo,
|
||||
getCloudFileList,
|
||||
@@ -436,7 +469,11 @@
|
||||
removeTeamMember,
|
||||
uploadCloudFile,
|
||||
deleteCloudFile,
|
||||
deleteTeam
|
||||
deleteTeam,
|
||||
getCloudDbList,
|
||||
createCloudDb,
|
||||
uploadCloudDbFile,
|
||||
deleteCloudDb
|
||||
} from '@/utils/cloud-api.js';
|
||||
|
||||
const userToken = ref('');
|
||||
@@ -1075,31 +1112,7 @@
|
||||
|
||||
// 选择文件并上传
|
||||
const chooseAndUploadFile = () => {
|
||||
// #ifdef APP-PLUS
|
||||
// App 端使用系统文件选择器
|
||||
plus.io.chooseFile(
|
||||
(res) => {
|
||||
// plus.io.chooseFile 返回格式可能是 { files: [...] } 或直接是数组
|
||||
const files = Array.isArray(res) ? res : (res.files || []);
|
||||
if (files.length === 0) {
|
||||
uni.showToast({
|
||||
title: '未选择文件',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
doUploadFiles(files);
|
||||
},
|
||||
(err) => {
|
||||
if (err.code !== 11) { // 11 是用户取消
|
||||
console.error('选择文件失败:', err);
|
||||
}
|
||||
}
|
||||
);
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
// 小程序/H5 端使用 uni.chooseFile(H5 可能不支持)
|
||||
uni.chooseFile({
|
||||
chooseFile({
|
||||
count: 10,
|
||||
type: 'all',
|
||||
success: (res) => {
|
||||
@@ -1120,7 +1133,6 @@
|
||||
console.error('选择文件失败:', err);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
};
|
||||
|
||||
// 执行上传
|
||||
@@ -1152,88 +1164,160 @@
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 数据库表 ==========
|
||||
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 allDatabases = ref([]); // API 返回的全部数据库列表
|
||||
const isDbLoading = ref(false); // 数据库加载中
|
||||
// 个人展示全部数据库(个人私有 + 归属团队的),团队只展示属于当前选中团队的
|
||||
const personalDatabases = computed(() => allDatabases.value);
|
||||
const teamDatabases = computed(() => allDatabases.value.filter(db => db.team_id && String(db.team_id) === String(currentTeamId.value)));
|
||||
|
||||
const showNewTable = ref(false);
|
||||
const newTableName = ref('');
|
||||
const newTableDesc = ref('');
|
||||
const showNewDb = ref(false);
|
||||
const newDbName = ref('');
|
||||
|
||||
const openNewTableModal = () => {
|
||||
showNewTable.value = true;
|
||||
newTableName.value = '';
|
||||
newTableDesc.value = '';
|
||||
// 获取数据库列表
|
||||
const fetchDbList = async () => {
|
||||
isDbLoading.value = true;
|
||||
try {
|
||||
const token = getToken();
|
||||
const list = await getCloudDbList(token);
|
||||
allDatabases.value = list || [];
|
||||
} catch (error) {
|
||||
console.error('获取数据库列表失败:', error);
|
||||
allDatabases.value = [];
|
||||
} finally {
|
||||
isDbLoading.value = false;
|
||||
}
|
||||
};
|
||||
const closeNewTableModal = () => {
|
||||
showNewTable.value = false;
|
||||
newTableName.value = '';
|
||||
newTableDesc.value = '';
|
||||
|
||||
const openNewDbModal = () => {
|
||||
showNewDb.value = true;
|
||||
newDbName.value = '';
|
||||
};
|
||||
const confirmCreateTable = () => {
|
||||
const name = newTableName.value.trim();
|
||||
const closeNewDbModal = () => {
|
||||
showNewDb.value = false;
|
||||
newDbName.value = '';
|
||||
};
|
||||
const confirmCreateDb = async () => {
|
||||
const name = newDbName.value.trim();
|
||||
if (!name) {
|
||||
uni.showToast({
|
||||
title: '请输入表名称',
|
||||
icon: 'none'
|
||||
});
|
||||
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.showLoading({ title: '创建中...' });
|
||||
try {
|
||||
const token = getToken();
|
||||
await createCloudDb(token, name);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '创建成功', icon: 'success' });
|
||||
closeNewDbModal();
|
||||
await fetchDbList();
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('创建数据库失败:', error);
|
||||
uni.showToast({ title: '创建失败,请重试', icon: 'error' });
|
||||
}
|
||||
uni.showToast({
|
||||
title: '创建成功',
|
||||
icon: 'success'
|
||||
});
|
||||
closeNewTableModal();
|
||||
};
|
||||
|
||||
const handleTableClick = (table) => {
|
||||
uni.showToast({
|
||||
title: `进入数据表:${table.name}`,
|
||||
icon: 'none'
|
||||
// ========== 上传数据库文件 ==========
|
||||
const showUploadDb = ref(false);
|
||||
const uploadDbFileName = ref('');
|
||||
const uploadDbFilePath = ref('');
|
||||
|
||||
const openUploadDbModal = () => {
|
||||
showUploadDb.value = true;
|
||||
uploadDbFileName.value = '';
|
||||
uploadDbFilePath.value = '';
|
||||
};
|
||||
const closeUploadDbModal = () => {
|
||||
showUploadDb.value = false;
|
||||
};
|
||||
|
||||
const handlePickDbFile = () => {
|
||||
chooseFile({
|
||||
count: 1,
|
||||
type: 'all',
|
||||
success: (res) => {
|
||||
const file = res.tempFiles[0];
|
||||
uploadDbFileName.value = file.name || 'unknown';
|
||||
uploadDbFilePath.value = file.path;
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择文件失败:', err);
|
||||
uni.showToast({ title: '选择文件失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const confirmUploadDb = async () => {
|
||||
if (!uploadDbFilePath.value) {
|
||||
uni.showToast({ title: '请选择数据库文件', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
// 校验文件后缀
|
||||
const ext = uploadDbFileName.value.split('.').pop()?.toLowerCase();
|
||||
const allowedExts = ['db', 'sqlite', 'sqlite3'];
|
||||
if (!allowedExts.includes(ext)) {
|
||||
uni.showToast({ title: '仅支持 .db / .sqlite / .sqlite3 文件', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
uni.showLoading({ title: '上传中...' });
|
||||
try {
|
||||
const token = getToken();
|
||||
await uploadCloudDbFile(token, uploadDbFilePath.value);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '上传成功', icon: 'success' });
|
||||
closeUploadDbModal();
|
||||
await fetchDbList();
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('上传数据库失败:', error);
|
||||
uni.showToast({ title: '上传失败,请重试', icon: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteDb = (db) => {
|
||||
uni.showModal({
|
||||
title: '删除数据库',
|
||||
content: `确定要删除数据库"${db.database_name}"吗?此操作不可恢复。`,
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({ title: '删除中...' });
|
||||
try {
|
||||
const token = getToken();
|
||||
await deleteCloudDb(token, db.database_id);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '删除成功', icon: 'success' });
|
||||
await fetchDbList();
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('删除数据库失败:', error);
|
||||
uni.showToast({ title: '删除失败,请重试', icon: 'error' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDbClick = (db) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/CloudDbDetail/CloudDbDetail?databaseId=${db.database_id}&databaseName=${encodeURIComponent(db.database_name)}&teamId=${db.team_id || ''}`
|
||||
});
|
||||
};
|
||||
|
||||
const formatDbTime = (timestamp) => {
|
||||
if (!timestamp) return '';
|
||||
const date = new Date(timestamp * 1000);
|
||||
const y = date.getFullYear();
|
||||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(date.getDate()).padStart(2, '0');
|
||||
return `${y}-${m}-${d}`;
|
||||
};
|
||||
|
||||
const getAccessLevelLabel = (level) => {
|
||||
const labels = { 0: '无权限', 1: '只读', 2: '读写', 3: '管理' };
|
||||
return labels[level] || '未知';
|
||||
};
|
||||
|
||||
// ========== 团队管理 ==========
|
||||
const showTeamManage = ref(false);
|
||||
const teamGroups = ref([]);
|
||||
@@ -1568,6 +1652,7 @@
|
||||
onMounted(async () => {
|
||||
await fetchFileList();
|
||||
await fetchTeamList();
|
||||
await fetchDbList();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user