| | |
| | | </div> |
| | | |
| | | <div class="content-section" v-loading="loading"> |
| | | <div v-for="item in projectTypeList" :key="item.id" class="project-type-card"> |
| | | <div class="card-header"> |
| | | <div class="info-group"> |
| | | <span class="label">类型名称:</span> |
| | | <span class="value">{{ item.name }}</span> |
| | | </div> |
| | | <div class="info-group"> |
| | | <span class="label">备注:</span> |
| | | <span class="value">{{ item.description || '--' }}</span> |
| | | </div> |
| | | <div class="info-group"> |
| | | <span class="label">附件:</span> |
| | | <div class="attachment-info" v-if="item.attachment" @click="handleExpand(item)"> |
| | | <el-icon class="file-icon"><Document /></el-icon> |
| | | <span class="file-name">{{ item.attachment.name }}</span> |
| | | <el-icon class="download-icon" @click.stop="handleDownload(item.attachment)"><Download /></el-icon> |
| | | <span class="expand-link">{{ item.expanded ? '收起' : '展开' }}</span> |
| | | <el-icon class="arrow-icon" :class="{ 'is-reverse': item.expanded }"><ArrowDown /></el-icon> |
| | | <div class="card-list-scroll"> |
| | | <div v-for="item in projectTypeList" :key="item.id" class="project-type-card"> |
| | | <div class="card-header"> |
| | | <div class="info-group"> |
| | | <span class="label">类型名称:</span> |
| | | <span class="value">{{ item.name }}</span> |
| | | </div> |
| | | <span class="value" v-else>--</span> |
| | | </div> |
| | | <div class="actions"> |
| | | <el-button link type="primary" @click="handleUpdate(item)">编辑</el-button> |
| | | <el-button link type="primary" @click="handleCopy(item)">复制</el-button> |
| | | <el-button link type="danger" @click="handleDelete(item)">删除</el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-collapse-transition> |
| | | <div v-show="item.expanded" class="expanded-content"> |
| | | <div class="attachment-list"> |
| | | <div class="attachment-item"> |
| | | <el-icon><Document /></el-icon> |
| | | <span>{{ item.attachment?.name }}</span> |
| | | <el-button link type="primary" size="small" @click="handleDownload(item.attachment)">下载</el-button> |
| | | <div class="info-group"> |
| | | <span class="label">备注:</span> |
| | | <span class="value">{{ item.description || '--' }}</span> |
| | | </div> |
| | | <div class="info-group"> |
| | | <span class="label">附件:</span> |
| | | <div |
| | | class="attachment-info" |
| | | v-if="(item.attachmentList?.length || 0) > 0" |
| | | @click="handleExpand(item)" |
| | | > |
| | | {{ item.attachmentList[0]?.fileName || item.attachmentList[0]?.name }} |
| | | <span v-if="item.attachmentList.length > 1" class="file-count"> |
| | | +{{ item.attachmentList.length - 1 }} |
| | | </span> |
| | | <span class="expand-link">{{ item.expanded ? '收起' : '展开' }}</span> |
| | | </div> |
| | | <span class="value" v-else>--</span> |
| | | </div> |
| | | <div class="actions"> |
| | | <el-button link type="primary" @click="handleUpdate(item)">编辑</el-button> |
| | | <el-button link type="primary" @click="handleCopy(item)">复制</el-button> |
| | | <el-button link type="danger" @click="handleDelete(item)">删除</el-button> |
| | | </div> |
| | | </div> |
| | | </el-collapse-transition> |
| | | |
| | | <div class="card-body"> |
| | | <div class="workflow-container"> |
| | | <div v-for="(step, index) in item.steps" :key="index" class="workflow-step"> |
| | | <div class="step-main"> |
| | | <div class="step-circle">{{ index + 1 }}</div> |
| | | <div v-if="index < item.steps.length - 1" class="step-line"></div> |
| | | <el-collapse-transition> |
| | | <div v-show="item.expanded" class="expanded-content"> |
| | | <div class="attachment-list"> |
| | | <div |
| | | v-for="att in (item.attachmentList || [])" |
| | | :key="att.id || att.url || att.fileUrl || att.fileName || att.name" |
| | | class="attachment-item" |
| | | > |
| | | <el-icon><Document /></el-icon> |
| | | <span class="attachment-name">{{ att.fileName || att.name || '--' }}</span> |
| | | <el-button link type="primary" size="small" @click="handleDownload(att)">下载</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="step-label">{{ step.label }}</div> |
| | | </div> |
| | | </el-collapse-transition> |
| | | |
| | | <div class="card-body"> |
| | | <div class="workflow-container"> |
| | | <div v-for="(step, index) in item.steps" :key="index" class="workflow-step"> |
| | | <div class="step-main"> |
| | | <div class="step-circle">{{ index + 1 }}</div> |
| | | <div v-if="index < item.steps.length - 1" class="step-line"></div> |
| | | </div> |
| | | <div class="step-label">{{ step.label }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | projectTypeList.value = res.data.records.map(item => ({ |
| | | ...item, |
| | | expanded: false, |
| | | attachmentList: Array.isArray(item.attachmentList) ? item.attachmentList : [], |
| | | // 后端返回的节点列表可能是 planNodeList 或 savePlanNodeList |
| | | steps: (item.planNodeList || item.savePlanNodeList || []).map(node => ({ |
| | | label: node.name |
| | |
| | | id: 1, |
| | | name: 'A项目', |
| | | description: '', |
| | | attachment: { name: 'precaution...' }, |
| | | attachmentList: [{ id: 1, fileName: 'precaution...' }], |
| | | steps: [{ label: '立项' }, { label: '设计' }, { label: '采购' }, { label: '生产' }, { label: '出货' }], |
| | | expanded: false |
| | | } |
| | |
| | | const copyData = { |
| | | name: row.name + " - 副本", |
| | | description: row.description, |
| | | attachmentIds: row.attachmentIds || [], |
| | | attachmentIds: Array.isArray(row.attachmentIds) |
| | | ? row.attachmentIds |
| | | : (row.attachmentList || []).map(x => x.id).filter(Boolean), |
| | | savePlanNodeList: (row.planNodeList || row.savePlanNodeList || []).map(node => ({ |
| | | name: node.name, |
| | | leaderId: node.leaderId, |
| | |
| | | |
| | | /** 下载附件 */ |
| | | function handleDownload(attachment) { |
| | | // 实现下载逻辑 |
| | | ElMessage.info("开始下载: " + (attachment.name || "文件")); |
| | | const url = attachment?.url || attachment?.fileUrl || attachment?.tempPath || attachment?.fileName; |
| | | if (!url) { |
| | | ElMessage.warning("未找到可下载的文件地址"); |
| | | return; |
| | | } |
| | | proxy.$download.name(url); |
| | | } |
| | | |
| | | onMounted(() => { |
| | |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | background-color: #f5f7fa; |
| | | min-height: calc(100vh - 84px); |
| | | height: calc(100vh - 84px); |
| | | padding: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .header-section { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | |
| | | } |
| | | } |
| | | |
| | | .content-section{ |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | .card-list-scroll { |
| | | flex: 1; |
| | | overflow-y: auto; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .project-type-card { |
| | | background-color: #fff; |
| | | border-radius: 8px; |
| | | padding: 20px; |
| | | margin-bottom: 20px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | border: 1px solid #ebeef5; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .file-count { |
| | | margin-right: 8px; |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .download-icon { |
| | |
| | | .el-icon { |
| | | font-size: 16px; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .attachment-name { |
| | | flex: 1; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | .pagination-container { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-top: 20px; |
| | | padding: 10px 20px; |
| | | background-color: #fff; |
| | | border-top: 1px solid #ebeef5; |
| | | margin-top: 0; |
| | | } |
| | | |
| | | .step-config-item { |
| | |
| | | align-items: center; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | .content-section{ |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | } |
| | | </style> |