|
|
@@ -1,123 +1,88 @@
|
|
|
<template>
|
|
|
<div class="upload-container">
|
|
|
- <!-- 顶部导航栏 -->
|
|
|
- <nav class="navbar">
|
|
|
- <div class="navbar-logo">
|
|
|
- <img src="https://picsum.photos/60/40" alt="金水信息" class="logo-img" />
|
|
|
- <span class="logo-text">北京金水信息技术发展有限公司</span>
|
|
|
+ <div class="header">
|
|
|
+ <div class="logo">
|
|
|
+ <img src="@/assets/images/log.png" alt="logo" />
|
|
|
+ <span>北京金水信息技术发展有限公司</span>
|
|
|
</div>
|
|
|
- <div class="navbar-title">项目文件管理系统</div>
|
|
|
- </nav>
|
|
|
+ <div class="title">上传项目/功能文件</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 主容器 -->
|
|
|
- <div class="main-card">
|
|
|
- <!-- 步骤导航 -->
|
|
|
- <div class="step-nav">
|
|
|
- <div class="step-item" :class="{ active: step >= 1 }">
|
|
|
- <div class="step-dot">1</div>
|
|
|
- <div class="step-text">选择上传类型</div>
|
|
|
- </div>
|
|
|
- <div class="step-line"></div>
|
|
|
- <div class="step-item" :class="{ active: step >= 2 }">
|
|
|
- <div class="step-dot">2</div>
|
|
|
- <div class="step-text">选择/创建项目</div>
|
|
|
- </div>
|
|
|
- <div class="step-line"></div>
|
|
|
- <div class="step-item" :class="{ active: step >= 3 }">
|
|
|
- <div class="step-dot">3</div>
|
|
|
- <div class="step-text">上传功能文件</div>
|
|
|
- </div>
|
|
|
+ <div class="upload-step">
|
|
|
+ <!-- 上传类型选择 -->
|
|
|
+ <div class="step-item">
|
|
|
+ <div class="step-title">选择上传类型</div>
|
|
|
+ <el-radio-group v-model="uploadType" @change="handleUploadTypeChange">
|
|
|
+ <el-radio label="new_project">新建项目并上传文件</el-radio>
|
|
|
+ <el-radio label="add_file">给已有项目补充上传文件</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 步骤1:选择上传类型 -->
|
|
|
- <div class="form-section" v-if="step === 1">
|
|
|
- <div class="section-title">请选择上传方式</div>
|
|
|
- <div class="radio-group">
|
|
|
- <el-radio-group v-model="uploadType" @change="goToStep(2)">
|
|
|
- <el-radio
|
|
|
- label="new_project"
|
|
|
- class="radio-item"
|
|
|
- >
|
|
|
- <div class="radio-card">
|
|
|
- <el-icon class="radio-icon"><FolderAdd /></el-icon>
|
|
|
- <div class="radio-label">新建项目并上传文件</div>
|
|
|
- <div class="radio-desc">适用于首次上传的新项目</div>
|
|
|
- </div>
|
|
|
- </el-radio>
|
|
|
- <el-radio
|
|
|
- label="add_file"
|
|
|
- class="radio-item"
|
|
|
- >
|
|
|
- <div class="radio-card">
|
|
|
- <el-icon class="radio-icon"><UploadFilled /></el-icon>
|
|
|
- <div class="radio-label">补充文件到已有项目</div>
|
|
|
- <div class="radio-desc">适用于给现有项目添加文件</div>
|
|
|
- </div>
|
|
|
- </el-radio>
|
|
|
- </el-radio-group>
|
|
|
+ <!-- 分支1:新建项目 -->
|
|
|
+ <div v-if="uploadType === 'new_project'" class="new-project-section">
|
|
|
+ <!-- 步骤1:选择分类 -->
|
|
|
+ <div class="step-item">
|
|
|
+ <div class="step-title">步骤1:选择项目分类</div>
|
|
|
+ <el-select
|
|
|
+ v-model="activeBigCategoryId"
|
|
|
+ placeholder="选择大分类(数字孪生/业务项目)"
|
|
|
+ @change="handleBigCategoryChange"
|
|
|
+ style="width: 300px; margin-bottom: 15px;"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="category in categories"
|
|
|
+ :key="category.parent_id"
|
|
|
+ :label="category.name"
|
|
|
+ :value="category.parent_id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <el-select
|
|
|
+ v-model="activeSmallCategoryId"
|
|
|
+ placeholder="选择小分类"
|
|
|
+ style="width: 300px;"
|
|
|
+ :disabled="!activeBigCategoryId"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="child in currentSmallCategories"
|
|
|
+ :key="child.id"
|
|
|
+ :label="child.name"
|
|
|
+ :value="child.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 步骤2:选择/创建项目 -->
|
|
|
- <div class="form-section" v-if="step === 2">
|
|
|
- <div class="section-title">
|
|
|
- {{ uploadType === 'new_project' ? '创建新项目' : '选择已有项目' }}
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 新建项目 -->
|
|
|
- <div v-if="uploadType === 'new_project'" class="form-content">
|
|
|
- <el-form :model="newProjectForm" label-width="100px" class="form">
|
|
|
- <el-form-item label="项目分类" prop="categoryId">
|
|
|
- <el-select
|
|
|
- v-model="newProjectForm.categoryId"
|
|
|
- placeholder="请选择项目分类"
|
|
|
- class="form-select"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="category in categories"
|
|
|
- :key="category.id"
|
|
|
- :label="category.name"
|
|
|
- :value="category.id"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="项目名称" prop="name">
|
|
|
- <el-input
|
|
|
- v-model="newProjectForm.name"
|
|
|
- placeholder="请输入项目名称(如:智慧园区数字孪生)"
|
|
|
- class="form-input"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="项目简介" prop="summary">
|
|
|
- <el-input
|
|
|
- v-model="newProjectForm.summary"
|
|
|
- type="textarea"
|
|
|
- placeholder="请简要描述项目背景(选填)"
|
|
|
- rows="3"
|
|
|
- class="form-input"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- @click="createProject"
|
|
|
- :disabled="!newProjectForm.categoryId || !newProjectForm.name"
|
|
|
- class="btn-primary"
|
|
|
- >
|
|
|
- 创建项目并下一步
|
|
|
- </el-button>
|
|
|
- <el-button @click="goToStep(1)" class="btn-cancel">返回</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
+ <!-- 步骤2:创建项目 -->
|
|
|
+ <div class="step-item" style="margin-top: 30px;">
|
|
|
+ <div class="step-title">步骤2:填写项目信息</div>
|
|
|
+ <el-input
|
|
|
+ v-model="projectName"
|
|
|
+ placeholder="输入项目名称"
|
|
|
+ style="width: 300px; margin-bottom: 15px;"
|
|
|
+ />
|
|
|
+ <el-input
|
|
|
+ v-model="projectSummary"
|
|
|
+ type="textarea"
|
|
|
+ placeholder="输入项目背景简介"
|
|
|
+ style="width: 500px; height: 100px; margin-bottom: 15px;"
|
|
|
+ />
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="createProject"
|
|
|
+ :disabled="!activeBigCategoryId || !activeSmallCategoryId || !projectName"
|
|
|
+ >
|
|
|
+ 创建项目
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 选择已有项目 -->
|
|
|
- <div v-else class="form-content">
|
|
|
- <el-select
|
|
|
- v-model="selectedProjectId"
|
|
|
- placeholder="请选择要补充文件的项目"
|
|
|
- class="project-select"
|
|
|
- filterable
|
|
|
+ <!-- 分支2:给已有项目上传文件 -->
|
|
|
+ <div v-if="uploadType === 'add_file'" class="add-file-section">
|
|
|
+ <div class="step-item">
|
|
|
+ <div class="step-title">选择已有项目</div>
|
|
|
+ <el-select
|
|
|
+ v-model="selectedProjectId"
|
|
|
+ placeholder="选择要补充文件的项目"
|
|
|
+ style="width: 300px; margin-bottom: 15px;"
|
|
|
@change="getProjectInfo"
|
|
|
>
|
|
|
<el-option
|
|
|
@@ -127,194 +92,111 @@
|
|
|
:value="project.id"
|
|
|
/>
|
|
|
</el-select>
|
|
|
-
|
|
|
- <div v-if="selectedProjectInfo" class="project-card">
|
|
|
- <div class="project-card-header">
|
|
|
- <span class="project-name">{{ selectedProjectInfo.name }}</span>
|
|
|
- <span class="project-category">{{ selectedProjectInfo.categoryName }}</span>
|
|
|
- </div>
|
|
|
- <div class="project-card-body">
|
|
|
- <div class="project-info-item">
|
|
|
- <span class="info-label">功能数量:</span>
|
|
|
- <span class="info-value">{{ selectedProjectInfo.funcNames.length }}个</span>
|
|
|
- </div>
|
|
|
- <div class="project-info-item">
|
|
|
- <span class="info-label">已有功能:</span>
|
|
|
- <span class="info-value">{{ selectedProjectInfo.funcNames.join('、') || '暂无' }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="btn-group" style="margin-top: 20px;">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- @click="goToStep(3)"
|
|
|
- :disabled="!selectedProjectId"
|
|
|
- class="btn-primary"
|
|
|
- >
|
|
|
- 确认选择并下一步
|
|
|
- </el-button>
|
|
|
- <el-button @click="goToStep(1)" class="btn-cancel">返回</el-button>
|
|
|
+ <!-- 显示选中项目的信息 -->
|
|
|
+ <div v-if="selectedProjectInfo" class="project-info">
|
|
|
+ <div class="info-label">项目名称:{{ selectedProjectInfo.name }}</div>
|
|
|
+ <div class="info-label">所属分类:{{ selectedProjectInfo.categoryName }}</div>
|
|
|
+ <div class="info-label">现有功能数:{{ selectedProjectInfo.funcNames.length }}个</div>
|
|
|
+ <div class="info-label">现有功能:{{ selectedProjectInfo.funcNames.join('、') }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 步骤3:上传功能文件 -->
|
|
|
- <div class="form-section" v-if="step === 3">
|
|
|
- <div class="section-title">上传功能文件</div>
|
|
|
- <div class="form-content">
|
|
|
- <div class="func-name-selector">
|
|
|
- <label class="func-label">功能名称:</label>
|
|
|
- <el-select
|
|
|
- v-model="funcName"
|
|
|
- placeholder="选择已有功能 / 输入新功能名称"
|
|
|
- filterable
|
|
|
- allow-create
|
|
|
- @create="handleCreateNewFunc"
|
|
|
- class="func-select"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="func in projectExistFuncs"
|
|
|
- :key="func"
|
|
|
- :label="func"
|
|
|
- :value="func"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- <div class="func-tip">提示:输入新名称按回车可创建新功能</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 文件上传区域 -->
|
|
|
- <div class="upload-area">
|
|
|
- <el-upload
|
|
|
- class="uploader"
|
|
|
- :auto-upload="false"
|
|
|
- :on-change="handleFileChange"
|
|
|
- :on-remove="handleFileRemove"
|
|
|
- :file-list="fileList"
|
|
|
- multiple
|
|
|
- accept=".jpg,.png,.mp4,.avi,.mov"
|
|
|
- drag
|
|
|
- >
|
|
|
- <div class="upload-dragger">
|
|
|
- <el-icon class="upload-icon"><Upload /></el-icon>
|
|
|
- <div class="upload-text">点击或拖拽文件到此处上传</div>
|
|
|
- <div class="upload-hint">支持多选,格式:JPG/PNG/MP4/AVI/MOV,单文件最大2GB</div>
|
|
|
- </div>
|
|
|
- </el-upload>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 已选文件列表 -->
|
|
|
- <div v-if="fileList.length > 0" class="file-list-container">
|
|
|
- <div class="file-list-header">
|
|
|
- <span>已选择文件({{ fileList.length }}个)</span>
|
|
|
- <el-button
|
|
|
- type="text"
|
|
|
- text-color="#ff4d4f"
|
|
|
- @click="fileList = []"
|
|
|
- size="small"
|
|
|
- >
|
|
|
- 清空所有
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- <div class="file-card" v-for="(file, index) in fileList" :key="index">
|
|
|
- <div class="file-icon">
|
|
|
- <el-icon v-if="file.raw.type.includes('image')"><Picture /></el-icon>
|
|
|
- <el-icon v-else><VideoPlay /></el-icon>
|
|
|
- </div>
|
|
|
- <div class="file-info">
|
|
|
- <div class="file-name">{{ file.name }}</div>
|
|
|
- <div class="file-size">{{ formatFileSize(file.size) }}</div>
|
|
|
- </div>
|
|
|
- <el-button
|
|
|
- type="text"
|
|
|
- icon="Delete"
|
|
|
- text-color="#ff4d4f"
|
|
|
- size="small"
|
|
|
- @click="handleFileRemove(file, fileList)"
|
|
|
- >
|
|
|
- 删除
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 操作按钮 -->
|
|
|
- <div class="btn-group">
|
|
|
- <el-button
|
|
|
- type="success"
|
|
|
- @click="uploadFuncFile"
|
|
|
- :disabled="!funcName || fileList.length === 0"
|
|
|
- class="btn-success"
|
|
|
- icon="UploadFilled"
|
|
|
- >
|
|
|
- 批量上传选中文件
|
|
|
- </el-button>
|
|
|
- <el-button @click="goToStep(2)" class="btn-cancel">返回</el-button>
|
|
|
- </div>
|
|
|
+ <!-- 通用:上传功能文件(核心修复:按钮启用逻辑) -->
|
|
|
+ <div class="step-item" style="margin-top: 30px;" v-if="projectId || selectedProjectId">
|
|
|
+ <div class="step-title">
|
|
|
+ {{ uploadType === 'new_project' ? '步骤3' : '步骤2' }}:上传功能文件
|
|
|
+ <span v-if="uploadType === 'new_project'">(项目:{{ projectName }})</span>
|
|
|
+ <span v-if="uploadType === 'add_file'">(项目:{{ selectedProjectInfo?.name }})</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 功能名称输入框(强制要求输入/选择) -->
|
|
|
+ <div style="margin-bottom: 10px;">
|
|
|
+ <label style="font-size: 14px; color: #333; margin-right: 10px;">功能名称:</label>
|
|
|
+ <el-select
|
|
|
+ v-model="funcName"
|
|
|
+ placeholder="必须选择/输入功能名称"
|
|
|
+ style="width: 300px;"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ @create="handleCreateNewFunc"
|
|
|
+ @change="checkUploadButtonStatus"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="func in projectExistFuncs"
|
|
|
+ :key="func"
|
|
|
+ :label="func"
|
|
|
+ :value="func"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 上传成功提示 -->
|
|
|
- <el-dialog
|
|
|
- v-model="uploadSuccessVisible"
|
|
|
- title="上传成功"
|
|
|
- width="400px"
|
|
|
- center
|
|
|
- >
|
|
|
- <div class="success-content">
|
|
|
- <el-icon class="success-icon"><CircleCheck /></el-icon>
|
|
|
- <div class="success-text">
|
|
|
- 成功上传 {{ successCount }} 个文件到「{{ funcName }}」功能下!
|
|
|
+ <!-- 调试信息(可选:帮助排查问题) -->
|
|
|
+ <div style="color: #666; font-size: 12px; margin-bottom: 15px;">
|
|
|
+ 调试:功能名称={{ funcName || '空' }} | 已选文件数={{ fileList.length }} | 按钮禁用={{ !funcName || fileList.length === 0 }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 文件选择(修复:简化文件验证,确保文件能被收集) -->
|
|
|
+ <el-upload
|
|
|
+ class="upload-file"
|
|
|
+ :auto-upload="false"
|
|
|
+ :on-change="handleFileChange"
|
|
|
+ :on-remove="handleFileRemove"
|
|
|
+ :file-list="fileList"
|
|
|
+ multiple
|
|
|
+ accept=".jpg,.png,.mp4,.avi,.mov"
|
|
|
+ style="margin-bottom: 15px;"
|
|
|
+ >
|
|
|
+ <el-button type="primary">选择图片/视频(可多选)</el-button>
|
|
|
+ </el-upload>
|
|
|
+
|
|
|
+ <!-- 已选文件列表(明确显示) -->
|
|
|
+ <div v-if="fileList.length > 0" class="selected-files">
|
|
|
+ <div class="selected-title">已选择文件({{ fileList.length }}个):</div>
|
|
|
+ <div class="file-tag" v-for="(file, index) in fileList" :key="index">
|
|
|
+ {{ file.name }} ({{ formatFileSize(file.size) }})
|
|
|
</div>
|
|
|
</div>
|
|
|
- <template #footer>
|
|
|
- <el-button @click="resetPage">返回首页</el-button>
|
|
|
- <el-button type="primary" @click="continueUpload">继续上传</el-button>
|
|
|
- </template>
|
|
|
- </el-dialog>
|
|
|
+ <div v-else style="color: #999; font-size: 12px; margin-bottom: 15px;">
|
|
|
+ 暂无选中文件,请选择图片/视频文件
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 上传按钮(修复:禁用条件更宽松,增加点击提示) -->
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ @click="uploadFuncFile"
|
|
|
+ :disabled="!funcName || fileList.length === 0"
|
|
|
+ @mouseenter="checkUploadButtonStatus"
|
|
|
+ >
|
|
|
+ 批量上传选中文件
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 页脚 -->
|
|
|
- <footer class="footer">
|
|
|
- <div class="footer-text">© 2025 北京金水信息技术发展有限公司 版权所有</div>
|
|
|
- </footer>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted } from 'vue';
|
|
|
-import { ElMessage } from 'element-plus'; // 移除未使用的ElMessageBox
|
|
|
-import {
|
|
|
- FolderAdd, UploadFilled, Upload, Picture, VideoPlay,
|
|
|
- CircleCheck // 移除未使用的Delete图标
|
|
|
-} from '@element-plus/icons-vue';
|
|
|
+import { ref, onMounted, watch } from 'vue';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
import axios from 'axios';
|
|
|
|
|
|
-const baseURL = 'http://localhost:3001';
|
|
|
+const baseURL = 'http://localhost:3001'; // 后端地址
|
|
|
|
|
|
// 响应式变量
|
|
|
-const step = ref(1); // 当前步骤:1-选类型 2-选/创项目 3-传文件
|
|
|
const uploadType = ref('new_project');
|
|
|
const categories = ref([]);
|
|
|
+const activeBigCategoryId = ref('');
|
|
|
+const currentSmallCategories = ref([]);
|
|
|
+const activeSmallCategoryId = ref('');
|
|
|
+const projectName = ref('');
|
|
|
+const projectSummary = ref('');
|
|
|
+const projectId = ref('');
|
|
|
const allProjects = ref([]);
|
|
|
-
|
|
|
-// 新建项目表单
|
|
|
-const newProjectForm = ref({
|
|
|
- categoryId: '',
|
|
|
- name: '',
|
|
|
- summary: ''
|
|
|
-});
|
|
|
-const projectId = ref(''); // 创建后的项目ID
|
|
|
-
|
|
|
-// 选择已有项目
|
|
|
const selectedProjectId = ref('');
|
|
|
const selectedProjectInfo = ref(null);
|
|
|
const projectExistFuncs = ref([]);
|
|
|
-
|
|
|
-// 文件上传
|
|
|
const funcName = ref('');
|
|
|
-const fileList = ref([]);
|
|
|
-const uploadSuccessVisible = ref(false);
|
|
|
-const successCount = ref(0);
|
|
|
+const fileList = ref([]); // 确保初始化为空数组
|
|
|
|
|
|
// 格式化文件大小
|
|
|
const formatFileSize = (size) => {
|
|
|
@@ -329,12 +211,18 @@ const formatFileSize = (size) => {
|
|
|
return `${fileSize.toFixed(2)}${units[unitIndex]}`;
|
|
|
};
|
|
|
|
|
|
-// 步骤导航
|
|
|
-const goToStep = (targetStep) => {
|
|
|
- step.value = targetStep;
|
|
|
+// 检查上传按钮状态(调试用)
|
|
|
+const checkUploadButtonStatus = () => {
|
|
|
+ if (!funcName.value) {
|
|
|
+ ElMessage.warning('请先输入/选择功能名称!');
|
|
|
+ } else if (fileList.value.length === 0) {
|
|
|
+ ElMessage.warning('请选择要上传的文件!');
|
|
|
+ } else {
|
|
|
+ ElMessage.success('可以上传了!');
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-// 获取分类列表
|
|
|
+// 获取所有分类
|
|
|
const getCategories = async () => {
|
|
|
try {
|
|
|
const res = await axios.get(`${baseURL}/api/categories`);
|
|
|
@@ -344,13 +232,55 @@ const getCategories = async () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 获取所有项目
|
|
|
+// 获取所有已有项目
|
|
|
const getAllProjects = async () => {
|
|
|
try {
|
|
|
+ const categoryRes = await axios.get(`${baseURL}/api/categories`);
|
|
|
+ const allCats = categoryRes.data.data;
|
|
|
+
|
|
|
const res = await axios.get(`${baseURL}/api/projects?big_category_id=all&category_id=all`);
|
|
|
- allProjects.value = res.data.data;
|
|
|
+ const projects = res.data.data;
|
|
|
+
|
|
|
+ const projectsWithInfo = [];
|
|
|
+ for (const project of projects) {
|
|
|
+ let categoryName = '';
|
|
|
+ allCats.forEach(bigCat => {
|
|
|
+ const smallCat = bigCat.children.find(child => child.id === project.category_id);
|
|
|
+ if (smallCat) {
|
|
|
+ categoryName = `${bigCat.name}-${smallCat.name}`;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ projectsWithInfo.push({
|
|
|
+ ...project,
|
|
|
+ categoryName,
|
|
|
+ funcNames: project.funcNames || []
|
|
|
+ });
|
|
|
+ }
|
|
|
+ allProjects.value = projectsWithInfo;
|
|
|
} catch (err) {
|
|
|
- ElMessage.error('获取项目列表失败:' + err.message);
|
|
|
+ ElMessage.error('获取已有项目失败:' + err.message);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 切换大分类
|
|
|
+const handleBigCategoryChange = () => {
|
|
|
+ const currentBigCategory = categories.value.find(
|
|
|
+ item => item.parent_id === activeBigCategoryId.value
|
|
|
+ );
|
|
|
+ currentSmallCategories.value = currentBigCategory?.children || [];
|
|
|
+ activeSmallCategoryId.value = '';
|
|
|
+};
|
|
|
+
|
|
|
+// 切换上传类型
|
|
|
+const handleUploadTypeChange = () => {
|
|
|
+ projectId.value = '';
|
|
|
+ selectedProjectId.value = '';
|
|
|
+ selectedProjectInfo.value = null;
|
|
|
+ projectExistFuncs.value = [];
|
|
|
+ funcName.value = '';
|
|
|
+ fileList.value = [];
|
|
|
+ if (uploadType.value === 'add_file') {
|
|
|
+ getAllProjects();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -358,13 +288,13 @@ const getAllProjects = async () => {
|
|
|
const createProject = async () => {
|
|
|
try {
|
|
|
const res = await axios.post(`${baseURL}/api/project`, {
|
|
|
- name: newProjectForm.value.name,
|
|
|
- category_id: newProjectForm.value.categoryId,
|
|
|
- summary: newProjectForm.value.summary
|
|
|
+ name: projectName.value,
|
|
|
+ category_id: activeSmallCategoryId.value,
|
|
|
+ summary: projectSummary.value
|
|
|
});
|
|
|
projectId.value = res.data.data.project_id;
|
|
|
+ projectExistFuncs.value = [];
|
|
|
ElMessage.success('项目创建成功!');
|
|
|
- goToStep(3);
|
|
|
} catch (err) {
|
|
|
ElMessage.error('创建项目失败:' + err.message);
|
|
|
}
|
|
|
@@ -373,477 +303,216 @@ const createProject = async () => {
|
|
|
// 获取选中项目信息
|
|
|
const getProjectInfo = async () => {
|
|
|
if (!selectedProjectId.value) return;
|
|
|
- try {
|
|
|
- const res = await axios.get(`${baseURL}/api/project/${selectedProjectId.value}`);
|
|
|
- selectedProjectInfo.value = res.data.data;
|
|
|
- projectExistFuncs.value = res.data.data.funcNames || [];
|
|
|
- } catch (err) {
|
|
|
- ElMessage.error('获取项目信息失败:' + err.message);
|
|
|
+ const project = allProjects.value.find(item => item.id == selectedProjectId.value);
|
|
|
+ if (project) {
|
|
|
+ selectedProjectInfo.value = project;
|
|
|
+ projectExistFuncs.value = [...project.funcNames];
|
|
|
+ funcName.value = '';
|
|
|
+ fileList.value = [];
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 创建新功能名称
|
|
|
+// 处理新增功能名称
|
|
|
const handleCreateNewFunc = (newFuncName) => {
|
|
|
if (!projectExistFuncs.value.includes(newFuncName)) {
|
|
|
projectExistFuncs.value.push(newFuncName);
|
|
|
}
|
|
|
+ // 创建后自动赋值,确保funcName不为空
|
|
|
funcName.value = newFuncName;
|
|
|
+ checkUploadButtonStatus();
|
|
|
};
|
|
|
|
|
|
-// 文件选择/移除
|
|
|
+// 核心修复:简化文件处理逻辑,确保文件能被收集
|
|
|
const handleFileChange = (file, newFileList) => {
|
|
|
+ // 移除严格验证(先确保文件能被收集,验证放到上传时)
|
|
|
fileList.value = newFileList.filter(item => item.status !== 'removed');
|
|
|
+ // 实时检查按钮状态
|
|
|
+ checkUploadButtonStatus();
|
|
|
};
|
|
|
+
|
|
|
+// 处理文件移除
|
|
|
const handleFileRemove = (file, newFileList) => {
|
|
|
fileList.value = newFileList;
|
|
|
+ checkUploadButtonStatus();
|
|
|
};
|
|
|
|
|
|
-// 批量上传文件
|
|
|
+// 批量上传文件(上传时再做文件验证)
|
|
|
const uploadFuncFile = async () => {
|
|
|
try {
|
|
|
- const targetId = projectId.value || selectedProjectId.value;
|
|
|
- if (!targetId) return ElMessage.error('请先选择/创建项目!');
|
|
|
+ const targetProjectId = projectId.value || selectedProjectId.value;
|
|
|
+ if (!targetProjectId) {
|
|
|
+ ElMessage.error('请先选择/创建项目!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!funcName.value.trim()) {
|
|
|
+ ElMessage.error('功能名称不能为空!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (fileList.value.length === 0) {
|
|
|
+ ElMessage.error('请选择要上传的文件!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ElMessage.info(`开始上传${fileList.value.length}个文件,请稍候...`);
|
|
|
|
|
|
- ElMessage.info('开始上传文件,请稍候...');
|
|
|
- let success = 0;
|
|
|
+ let successCount = 0;
|
|
|
let failFiles = [];
|
|
|
-
|
|
|
- for (const file of fileList.value) {
|
|
|
+ for (const fileItem of fileList.value) {
|
|
|
try {
|
|
|
+ // 上传时做文件验证
|
|
|
+ const fileType = fileItem.raw.type;
|
|
|
+ const allowImageTypes = ['image/jpeg', 'image/png', 'image/gif'];
|
|
|
+ const allowVideoTypes = ['video/mp4', 'video/avi', 'video/quicktime'];
|
|
|
+ const allowTypes = [...allowImageTypes, ...allowVideoTypes];
|
|
|
+
|
|
|
+ if (!allowTypes.includes(fileType)) {
|
|
|
+ failFiles.push(`${fileItem.name}(类型不支持)`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const maxSize = 2 * 1024 * 1024 * 1024;
|
|
|
+ if (fileItem.raw.size > maxSize) {
|
|
|
+ failFiles.push(`${fileItem.name}(大小超过2GB)`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const illegalChars = /[\\/:*?"<>|]/;
|
|
|
+ if (illegalChars.test(fileItem.name)) {
|
|
|
+ failFiles.push(`${fileItem.name}(文件名含非法字符)`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证通过,上传文件
|
|
|
const formData = new FormData();
|
|
|
- formData.append('file', file.raw);
|
|
|
- formData.append('project_id', targetId);
|
|
|
- formData.append('func_name', funcName.value);
|
|
|
+ formData.append('file', fileItem.raw);
|
|
|
+ formData.append('project_id', targetProjectId);
|
|
|
+ formData.append('func_name', funcName.value.trim());
|
|
|
|
|
|
await axios.post(`${baseURL}/api/project/file`, formData, {
|
|
|
headers: { 'Content-Type': 'multipart/form-data' }
|
|
|
});
|
|
|
- success++;
|
|
|
+ successCount++;
|
|
|
} catch (err) {
|
|
|
- failFiles.push(file.name);
|
|
|
+ failFiles.push(`${fileItem.name}(上传失败:${err.message})`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- successCount.value = success;
|
|
|
- uploadSuccessVisible.value = true;
|
|
|
-
|
|
|
+ // 上传结果提示
|
|
|
+ if (successCount > 0) {
|
|
|
+ ElMessage.success(`成功上传${successCount}个文件!`);
|
|
|
+ }
|
|
|
if (failFiles.length > 0) {
|
|
|
- ElMessage.error(`以下文件上传失败:${failFiles.join('、')}`);
|
|
|
+ ElMessage.error(`上传失败的文件:${failFiles.join('、')}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置状态
|
|
|
+ funcName.value = '';
|
|
|
+ fileList.value = [];
|
|
|
+
|
|
|
+ if (uploadType.value === 'add_file') {
|
|
|
+ getProjectInfo();
|
|
|
}
|
|
|
} catch (err) {
|
|
|
- ElMessage.error('上传失败:' + err.message);
|
|
|
+ ElMessage.error('批量上传失败:' + err.message);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 重置页面/继续上传
|
|
|
-const resetPage = () => {
|
|
|
- step.value = 1;
|
|
|
- uploadSuccessVisible.value = false;
|
|
|
- fileList.value = [];
|
|
|
- funcName.value = '';
|
|
|
-};
|
|
|
-const continueUpload = () => {
|
|
|
- uploadSuccessVisible.value = false;
|
|
|
- fileList.value = [];
|
|
|
-};
|
|
|
+// 监听上传类型变化
|
|
|
+watch([uploadType], () => {
|
|
|
+ if (uploadType.value === 'new_project') {
|
|
|
+ getCategories();
|
|
|
+ } else {
|
|
|
+ getAllProjects();
|
|
|
+ }
|
|
|
+});
|
|
|
|
|
|
-// 初始化
|
|
|
onMounted(() => {
|
|
|
getCategories();
|
|
|
- getAllProjects();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-/* 全局样式 */
|
|
|
.upload-container {
|
|
|
+ width: 100%;
|
|
|
min-height: 100vh;
|
|
|
- background: linear-gradient(120deg, #f5f7fa 0%, #e4e8f0 100%);
|
|
|
- padding-bottom: 40px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ padding: 0 20px;
|
|
|
}
|
|
|
-
|
|
|
-/* 导航栏 */
|
|
|
-.navbar {
|
|
|
- background: #fff;
|
|
|
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
- padding: 0 40px;
|
|
|
- height: 70px;
|
|
|
+.header {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
- margin-bottom: 30px;
|
|
|
+ padding: 15px 0;
|
|
|
+ border-bottom: 1px solid #e6e6e6;
|
|
|
}
|
|
|
-.navbar-logo {
|
|
|
+.logo {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 12px;
|
|
|
+ gap: 10px;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #0e48ad;
|
|
|
}
|
|
|
-.logo-img {
|
|
|
- width: 60px;
|
|
|
- height: 40px;
|
|
|
+
|
|
|
+.logo img {
|
|
|
+ width: 50px;
|
|
|
+ height: 30px;
|
|
|
object-fit: contain;
|
|
|
}
|
|
|
-.logo-text {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
-}
|
|
|
-.navbar-title {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 600;
|
|
|
- color: #409eff;
|
|
|
+.title {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
}
|
|
|
-
|
|
|
-/* 主卡片 */
|
|
|
-.main-card {
|
|
|
- width: 90%;
|
|
|
- max-width: 1000px;
|
|
|
- margin: 0 auto;
|
|
|
+.upload-step {
|
|
|
+ margin-top: 30px;
|
|
|
background: #fff;
|
|
|
- border-radius: 12px;
|
|
|
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
|
|
padding: 30px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 步骤导航 */
|
|
|
-.step-nav {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- margin-bottom: 40px;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-.step-item {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- z-index: 1;
|
|
|
-}
|
|
|
-.step-dot {
|
|
|
- width: 36px;
|
|
|
- height: 36px;
|
|
|
- border-radius: 50%;
|
|
|
- background: #e5e7eb;
|
|
|
- color: #9ca3af;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-weight: 600;
|
|
|
- margin-bottom: 8px;
|
|
|
- transition: all 0.3s ease;
|
|
|
-}
|
|
|
-.step-text {
|
|
|
- font-size: 14px;
|
|
|
- color: #9ca3af;
|
|
|
- transition: all 0.3s ease;
|
|
|
-}
|
|
|
-.step-item.active .step-dot {
|
|
|
- background: #409eff;
|
|
|
- color: #fff;
|
|
|
- box-shadow: 0 0 0 4px rgba(64, 158, 255, 0.2);
|
|
|
-}
|
|
|
-.step-item.active .step-text {
|
|
|
- color: #409eff;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-.step-line {
|
|
|
- width: 80px;
|
|
|
- height: 2px;
|
|
|
- background: #e5e7eb;
|
|
|
- margin: 0 10px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 表单区域 */
|
|
|
-.form-section {
|
|
|
- margin-bottom: 20px;
|
|
|
-}
|
|
|
-.section-title {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
- margin-bottom: 20px;
|
|
|
- padding-bottom: 10px;
|
|
|
- border-bottom: 1px solid #f3f4f6;
|
|
|
-}
|
|
|
-.form-content {
|
|
|
- padding: 0 20px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 步骤1:上传类型选择 */
|
|
|
-.radio-group {
|
|
|
- display: flex;
|
|
|
- gap: 20px;
|
|
|
- padding: 20px;
|
|
|
-}
|
|
|
-.radio-item {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-.radio-card {
|
|
|
- padding: 24px;
|
|
|
- border: 2px solid #e5e7eb;
|
|
|
- border-radius: 8px;
|
|
|
- text-align: center;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s ease;
|
|
|
-}
|
|
|
-.radio-item.is-checked .radio-card {
|
|
|
- border-color: #409eff;
|
|
|
- background: rgba(64, 158, 255, 0.05);
|
|
|
-}
|
|
|
-.radio-icon {
|
|
|
- font-size: 32px;
|
|
|
- color: #409eff;
|
|
|
- margin-bottom: 12px;
|
|
|
-}
|
|
|
-.radio-label {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- color: #1f2937;
|
|
|
- margin-bottom: 8px;
|
|
|
-}
|
|
|
-.radio-desc {
|
|
|
- font-size: 14px;
|
|
|
- color: #6b7280;
|
|
|
-}
|
|
|
-
|
|
|
-/* 表单样式 */
|
|
|
-.form {
|
|
|
- max-width: 600px;
|
|
|
-}
|
|
|
-.form-select, .form-input {
|
|
|
- width: 100%;
|
|
|
-}
|
|
|
-.project-select {
|
|
|
- width: 100%;
|
|
|
- max-width: 500px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 项目卡片 */
|
|
|
-.project-card {
|
|
|
- margin-top: 20px;
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
border-radius: 8px;
|
|
|
- padding: 16px;
|
|
|
- background: #f9fafb;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|
|
+ max-width: 800px;
|
|
|
}
|
|
|
-.project-card-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 12px;
|
|
|
- padding-bottom: 8px;
|
|
|
- border-bottom: 1px solid #e5e7eb;
|
|
|
-}
|
|
|
-.project-name {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2937;
|
|
|
+.step-title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ margin-bottom: 20px;
|
|
|
}
|
|
|
-.project-category {
|
|
|
- font-size: 14px;
|
|
|
- color: #6b7280;
|
|
|
- background: #f3f4f6;
|
|
|
- padding: 4px 8px;
|
|
|
+.project-info {
|
|
|
+ margin-top: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ background: #f5f7fa;
|
|
|
border-radius: 4px;
|
|
|
-}
|
|
|
-.project-info-item {
|
|
|
- display: flex;
|
|
|
- margin-bottom: 8px;
|
|
|
+ width: 300px;
|
|
|
}
|
|
|
.info-label {
|
|
|
- color: #6b7280;
|
|
|
- min-width: 80px;
|
|
|
-}
|
|
|
-.info-value {
|
|
|
- color: #1f2937;
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-
|
|
|
-/* 功能名称选择 */
|
|
|
-.func-name-selector {
|
|
|
- margin-bottom: 24px;
|
|
|
-}
|
|
|
-.func-label {
|
|
|
font-size: 14px;
|
|
|
- color: #1f2937;
|
|
|
- margin-right: 12px;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-.func-select {
|
|
|
- width: 100%;
|
|
|
- max-width: 400px;
|
|
|
-}
|
|
|
-.func-tip {
|
|
|
- font-size: 12px;
|
|
|
- color: #9ca3af;
|
|
|
- margin-top: 8px;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 5px;
|
|
|
}
|
|
|
-
|
|
|
-/* 文件上传区域 */
|
|
|
-.upload-area {
|
|
|
- margin-bottom: 24px;
|
|
|
-}
|
|
|
-.uploader {
|
|
|
- width: 100%;
|
|
|
-}
|
|
|
-.upload-dragger {
|
|
|
- width: 100%;
|
|
|
- height: 200px;
|
|
|
- border: 2px dashed #d1d5db;
|
|
|
- border-radius: 8px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background: #f9fafb;
|
|
|
- transition: all 0.3s ease;
|
|
|
-}
|
|
|
-.upload-dragger:hover {
|
|
|
- border-color: #409eff;
|
|
|
- background: rgba(64, 158, 255, 0.05);
|
|
|
-}
|
|
|
-.upload-icon {
|
|
|
- font-size: 48px;
|
|
|
- color: #9ca3af;
|
|
|
- margin-bottom: 12px;
|
|
|
-}
|
|
|
-.upload-text {
|
|
|
- font-size: 16px;
|
|
|
- color: #4b5563;
|
|
|
- margin-bottom: 8px;
|
|
|
-}
|
|
|
-.upload-hint {
|
|
|
- font-size: 12px;
|
|
|
- color: #9ca3af;
|
|
|
+.new-project-section, .add-file-section {
|
|
|
+ margin-top: 20px;
|
|
|
+ padding-top: 20px;
|
|
|
+ border-top: 1px dashed #e6e6e6;
|
|
|
}
|
|
|
|
|
|
-/* 文件列表 */
|
|
|
-.file-list-container {
|
|
|
- margin-bottom: 24px;
|
|
|
+/* 已选文件样式 */
|
|
|
+.selected-files {
|
|
|
+ margin-bottom: 15px;
|
|
|
}
|
|
|
-.file-list-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 12px;
|
|
|
- font-weight: 500;
|
|
|
- color: #1f2937;
|
|
|
-}
|
|
|
-.file-card {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 12px;
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
- border-radius: 6px;
|
|
|
+.selected-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
margin-bottom: 8px;
|
|
|
- background: #f9fafb;
|
|
|
}
|
|
|
-.file-icon {
|
|
|
- font-size: 24px;
|
|
|
+.file-tag {
|
|
|
+ display: inline-block;
|
|
|
+ padding: 4px 8px;
|
|
|
+ background: #e8f4ff;
|
|
|
color: #409eff;
|
|
|
- margin-right: 12px;
|
|
|
- width: 40px;
|
|
|
- text-align: center;
|
|
|
-}
|
|
|
-.file-info {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-.file-name {
|
|
|
- font-size: 14px;
|
|
|
- color: #1f2937;
|
|
|
- margin-bottom: 4px;
|
|
|
-}
|
|
|
-.file-size {
|
|
|
+ border-radius: 4px;
|
|
|
font-size: 12px;
|
|
|
- color: #9ca3af;
|
|
|
-}
|
|
|
-
|
|
|
-/* 按钮样式 */
|
|
|
-.btn-group {
|
|
|
- display: flex;
|
|
|
- gap: 12px;
|
|
|
-}
|
|
|
-.btn-primary {
|
|
|
- background: #409eff;
|
|
|
- border-color: #409eff;
|
|
|
- padding: 10px 24px;
|
|
|
- font-size: 14px;
|
|
|
- border-radius: 6px;
|
|
|
- transition: all 0.2s ease;
|
|
|
-}
|
|
|
-.btn-primary:hover {
|
|
|
- background: #3399ff;
|
|
|
-}
|
|
|
-.btn-success {
|
|
|
- background: #67c23a;
|
|
|
- border-color: #67c23a;
|
|
|
- padding: 10px 24px;
|
|
|
- font-size: 14px;
|
|
|
- border-radius: 6px;
|
|
|
- transition: all 0.2s ease;
|
|
|
-}
|
|
|
-.btn-success:hover {
|
|
|
- background: #5daf34;
|
|
|
-}
|
|
|
-.btn-cancel {
|
|
|
- border-color: #d9d9d9;
|
|
|
- color: #6b7280;
|
|
|
- padding: 10px 24px;
|
|
|
- font-size: 14px;
|
|
|
- border-radius: 6px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 成功弹窗 */
|
|
|
-.success-content {
|
|
|
- text-align: center;
|
|
|
- padding: 20px 0;
|
|
|
-}
|
|
|
-.success-icon {
|
|
|
- font-size: 64px;
|
|
|
- color: #67c23a;
|
|
|
- margin-bottom: 16px;
|
|
|
-}
|
|
|
-.success-text {
|
|
|
- font-size: 18px;
|
|
|
- color: #1f2937;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-
|
|
|
-/* 页脚 */
|
|
|
-.footer {
|
|
|
- text-align: center;
|
|
|
- margin-top: 40px;
|
|
|
- color: #9ca3af;
|
|
|
- font-size: 14px;
|
|
|
-}
|
|
|
-.footer-text {
|
|
|
- padding: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-/* 响应式适配 */
|
|
|
-@media (max-width: 768px) {
|
|
|
- .navbar {
|
|
|
- padding: 0 20px;
|
|
|
- height: 60px;
|
|
|
- }
|
|
|
- .logo-text {
|
|
|
- font-size: 16px;
|
|
|
- }
|
|
|
- .navbar-title {
|
|
|
- font-size: 18px;
|
|
|
- }
|
|
|
- .main-card {
|
|
|
- width: 95%;
|
|
|
- padding: 20px;
|
|
|
- }
|
|
|
- .radio-group {
|
|
|
- flex-direction: column;
|
|
|
- }
|
|
|
- .step-nav {
|
|
|
- flex-wrap: wrap;
|
|
|
- }
|
|
|
- .step-line {
|
|
|
- width: 40px;
|
|
|
- }
|
|
|
+ margin-right: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
}
|
|
|
</style>
|