| | |
| | | <div class="info-item"> |
| | | <span class="item-label">附件</span> |
| | | <el-upload |
| | | action="#" |
| | | :auto-upload="false" |
| | | :show-file-list="false" |
| | | @change="handleFileChange" |
| | | :action="uploadUrl" |
| | | :headers="uploadHeaders" |
| | | :on-success="handleUploadSuccess" |
| | | :on-remove="handleRemove" |
| | | :file-list="form.attachmentList" |
| | | name="files" |
| | | multiple |
| | | > |
| | | <el-button type="primary">上传附件</el-button> |
| | | </el-upload> |
| | |
| | | </div> |
| | | |
| | | <!-- 步骤配置表格 --> |
| | | <p class="top-tip">请按照顺序配置项目阶段,拖拽<b>步骤</b>排序即可</p> |
| | | <div class="step-table-container"> |
| | | <el-table :data="form.savePlanNodeList" border style="width: 100%"> |
| | | <el-table-column label="步骤" width="80" align="center"> |
| | | <el-table |
| | | :data="form.savePlanNodeList" |
| | | border |
| | | style="width: 100%" |
| | | row-key="id" |
| | | class="drag-table" |
| | | > |
| | | <el-table-column label="步骤" width="80" align="center" class-name="drag-handle"> |
| | | <template #default="scope"> |
| | | <div class="step-index" style="cursor: move;"> |
| | | {{ scope.$index + 1 }} |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | |
| | | <el-input v-model="scope.row.workContent" placeholder="请输入" /> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="操作" width="180" align="center"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | :disabled="scope.$index === 0" |
| | | @click="moveStep(scope.$index, -1)" |
| | | >上移</el-button> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | :disabled="scope.$index === form.savePlanNodeList.length - 1" |
| | | @click="moveStep(scope.$index, 1)" |
| | | >下移</el-button> |
| | | <el-button |
| | | link |
| | | type="danger" |
| | | @click="removeStep(scope.$index)" |
| | | >删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div class="add-row-btn" @click="addStep"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, watch, onMounted } from 'vue'; |
| | | import { ref, watch, onMounted, nextTick } from 'vue'; |
| | | import { Plus, QuestionFilled } from '@element-plus/icons-vue'; |
| | | import { userListNoPageByTenantId } from '@/api/system/user'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import { getToken } from '@/utils/auth'; |
| | | import Sortable from 'sortablejs'; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: Boolean, |
| | |
| | | const visible = ref(false); |
| | | const formRef = ref(null); |
| | | const userOptions = ref([]); |
| | | const uploadHeaders = { Authorization: "Bearer " + getToken() }; |
| | | // 上传地址 |
| | | const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/basic/customer-follow/upload"; |
| | | let sortable = null; |
| | | |
| | | const form = ref({ |
| | | id: undefined, |
| | | name: '', |
| | | description: '', |
| | | attachmentIds: [], |
| | | attachmentList: [], |
| | | savePlanNodeList: [] |
| | | }); |
| | | |
| | |
| | | visible.value = val; |
| | | if (val) { |
| | | if (props.data) { |
| | | // 编辑模式 |
| | | form.value = JSON.parse(JSON.stringify(props.data)); |
| | | if (!form.value.savePlanNodeList || form.value.savePlanNodeList.length === 0) { |
| | | // 编辑模式 - 回显数据 |
| | | form.value = { |
| | | id: props.data.id, |
| | | name: props.data.name, |
| | | description: props.data.description, |
| | | attachmentIds: [], |
| | | attachmentList: props.data.attachmentList || [], |
| | | savePlanNodeList: [] |
| | | }; |
| | | |
| | | // 回显附件ID |
| | | if (form.value.attachmentList && form.value.attachmentList.length > 0) { |
| | | form.value.attachmentIds = form.value.attachmentList.map(item => item.id); |
| | | } |
| | | |
| | | // 回显步骤节点 |
| | | if (props.data.planNodeList && props.data.planNodeList.length > 0) { |
| | | form.value.savePlanNodeList = props.data.planNodeList.map(node => ({ |
| | | id: node.id, |
| | | projectManagementPlanId: node.projectManagementPlanId, |
| | | sort: node.sort, |
| | | name: node.name, |
| | | leaderId: node.leaderId, |
| | | leaderName: node.leaderName, |
| | | estimatedDuration: node.estimatedDuration, |
| | | hourlyRate: node.hourlyRate, |
| | | workContent: node.workContent |
| | | })); |
| | | } else { |
| | | form.value.savePlanNodeList = [createDefaultNode()]; |
| | | } |
| | | } else { |
| | | // 新增模式 |
| | | resetForm(); |
| | | } |
| | | // 初始化拖拽 |
| | | nextTick(() => { |
| | | initSortable(); |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | watch(visible, (val) => { |
| | | emit('update:modelValue', val); |
| | | }); |
| | | |
| | | /** 初始化拖拽 */ |
| | | function initSortable() { |
| | | const el = document.querySelector('.drag-table .el-table__body-wrapper tbody'); |
| | | if (!el) return; |
| | | |
| | | if (sortable) { |
| | | sortable.destroy(); |
| | | } |
| | | |
| | | sortable = Sortable.create(el, { |
| | | handle: '.drag-handle', |
| | | animation: 150, |
| | | onEnd: ({ newIndex, oldIndex }) => { |
| | | const targetRow = form.value.savePlanNodeList.splice(oldIndex, 1)[0]; |
| | | form.value.savePlanNodeList.splice(newIndex, 0, targetRow); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** 创建默认节点对象 */ |
| | | function createDefaultNode() { |
| | |
| | | name: '', |
| | | description: '', |
| | | attachmentIds: [], |
| | | attachmentList: [], |
| | | savePlanNodeList: [createDefaultNode()] |
| | | }; |
| | | if (formRef.value) { |
| | |
| | | } |
| | | } |
| | | |
| | | /** 处理文件变化 */ |
| | | function handleFileChange(file) { |
| | | // 这里实现文件上传逻辑,获取 attachmentId |
| | | ElMessage.info('正在上传: ' + file.name); |
| | | // 模拟上传成功 |
| | | // form.value.attachmentIds.push(newId); |
| | | /** 处理文件上传成功 */ |
| | | function handleUploadSuccess(response, file, fileList) { |
| | | if (response.code === 200) { |
| | | ElMessage.success('上传成功'); |
| | | // 假设后端返回的数据结构中包含文件ID和URL等信息 |
| | | // 这里需要根据实际接口返回结构进行调整 |
| | | // 通常 response.data 包含文件信息 |
| | | const newFile = response.data; |
| | | if (newFile && newFile.id) { |
| | | form.value.attachmentIds.push(newFile.id); |
| | | form.value.attachmentList.push({ |
| | | name: file.name, |
| | | url: newFile.url, |
| | | id: newFile.id |
| | | }); |
| | | } |
| | | } else { |
| | | ElMessage.error(response.msg || '上传失败'); |
| | | } |
| | | } |
| | | |
| | | /** 处理文件移除 */ |
| | | function handleRemove(file) { |
| | | const index = form.value.attachmentList.findIndex(item => item.name === file.name); |
| | | if (index !== -1) { |
| | | const fileId = form.value.attachmentList[index].id; |
| | | form.value.attachmentList.splice(index, 1); |
| | | const idIndex = form.value.attachmentIds.indexOf(fileId); |
| | | if (idIndex !== -1) { |
| | | form.value.attachmentIds.splice(idIndex, 1); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 添加步骤 */ |
| | |
| | | try { |
| | | const valid = await formRef.value.validate(); |
| | | if (valid) { |
| | | // 提交前自动填充 sort 字段,按当前数组顺序排序 |
| | | form.value.savePlanNodeList.forEach((node, index) => { |
| | | node.sort = index; |
| | | }); |
| | | emit('submit', form.value); |
| | | } |
| | | } catch (error) { |
| | |
| | | gap: 15px; |
| | | padding-top: 10px; |
| | | } |
| | | .top-tip { |
| | | |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin:0 0 10px 10px; |
| | | } |
| | | </style> |