|
@@ -0,0 +1,408 @@
|
|
|
+<template>
|
|
|
+ <hc-new-dialog
|
|
|
+ v-model="insertDialog"
|
|
|
+ :loading="insertDialogLoading"
|
|
|
+ widths="65vw"
|
|
|
+ @close="insertDialogClose"
|
|
|
+ @save="insertDialogSave"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <div class="text-1xl mt-2 text-center font-bold">
|
|
|
+ 文件插卷
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="p-4 space-y-6">
|
|
|
+ <!-- 被插卷档号和插入位置选择区 -->
|
|
|
+ <div class="flex">
|
|
|
+ <div class="flex items-center space-x-2">
|
|
|
+ <span class="w-24 text-gray-700">
|
|
|
+ 被插卷档号:<span class="text-red-500">*</span>
|
|
|
+ </span>
|
|
|
+ <el-select
|
|
|
+ v-model="insertModal.fileNumber"
|
|
|
+ clearable
|
|
|
+ placeholder="请选择"
|
|
|
+ class="w-200px flex-1"
|
|
|
+ :class="{ 'border-red-500': validateForm.fileNumberError }"
|
|
|
+ @change="fileNumberChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in fileNumberOptions"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.fileNumber"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <span v-if="validateForm.fileNumberError" class="text-sm text-red-500">
|
|
|
+ 请选择被插卷档号
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="ml-8 flex items-center space-x-2">
|
|
|
+ <span class="w-30 text-gray-700">
|
|
|
+ 文件插入位置:<span class="text-red-500">*</span>
|
|
|
+ </span>
|
|
|
+ <el-select
|
|
|
+ v-model="insertModal.type"
|
|
|
+ placeholder="请选择"
|
|
|
+ class="w-150px flex-1"
|
|
|
+ :class="{ 'border-red-500': validateForm.typeError }"
|
|
|
+ >
|
|
|
+ <el-option value="1" label="前" />
|
|
|
+ <el-option value="0" label="后" />
|
|
|
+ </el-select>
|
|
|
+ <span v-if="validateForm.typeError" class="text-sm text-red-500">
|
|
|
+ 请选择插入位置
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 说明文字 -->
|
|
|
+ <div class="rounded bg-[#FEF1F1] p-2 text-sm text-red">
|
|
|
+ 点击下方文件行,将在该文件的{{ insertModal.type === '1' ? '前' : '后' }}插入选中的文件,可进行拖动、点图标进行排序调整
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 文件列表表格 -->
|
|
|
+ <div class="overflow-hidden border">
|
|
|
+ <HcTable
|
|
|
+ ref="tableRef"
|
|
|
+
|
|
|
+ :check-style="{ width: 29 }"
|
|
|
+ :column="tableColumn"
|
|
|
+ :datas="tableData"
|
|
|
+ :index-style="{ width: 70 }"
|
|
|
+ :is-arr-index="false"
|
|
|
+ :loading="tableLoading"
|
|
|
+ is-row-drop ui="hc-table-row-drop" is-new
|
|
|
+ :is-current-row="false"
|
|
|
+ :row-style="tableRowStyle"
|
|
|
+ @row-drop="sortTableRowDrop"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ @row-click="handleRowClick"
|
|
|
+ >
|
|
|
+ <!-- 排序操作按钮 -->
|
|
|
+ <template #action="{ index }">
|
|
|
+ <span class="text-link text-xl" @click="upSortClick(index)">
|
|
|
+ <HcIcon fill name="arrow-up" />
|
|
|
+ </span>
|
|
|
+ <span class="text-link ml-2 text-xl" @click="downSortClick(index)">
|
|
|
+ <HcIcon fill name="arrow-down" />
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </HcTable>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </hc-new-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { arrToId, deepClone, getArrValue } from 'js-fast-way'
|
|
|
+import { onMounted, reactive, ref, watch } from 'vue'
|
|
|
+import tuningApi from '~api/archiveConfig/tuning.js'
|
|
|
+import { useAppStore } from '~src/store'
|
|
|
+
|
|
|
+const useAppState = useAppStore()
|
|
|
+const projectId = ref(useAppState.getProjectId)
|
|
|
+const contractId = ref(useAppState.getContractId)
|
|
|
+// 弹窗控制变量
|
|
|
+const insertDialog = ref(false)
|
|
|
+const insertDialogLoading = ref(false)
|
|
|
+const insertModal = reactive({
|
|
|
+ fileNumber: '', // 被插卷档号
|
|
|
+ type: '', // 插入位置,初始化为空,确保必须选择
|
|
|
+})
|
|
|
+
|
|
|
+// 验证状态
|
|
|
+const validateForm = reactive({
|
|
|
+ fileNumberError: false,
|
|
|
+ typeError: false,
|
|
|
+})
|
|
|
+
|
|
|
+// 档号选项
|
|
|
+const fileNumberOptions = ref([])
|
|
|
+//获取档号
|
|
|
+const fileNumberLoad = ref(false)
|
|
|
+
|
|
|
+const getFileNumberOptions = async () => {
|
|
|
+ fileNumberLoad.value = true
|
|
|
+ const { error, code, data } = await tuningApi.pageByArchive({
|
|
|
+ size:5000,
|
|
|
+ current:1,
|
|
|
+ projectId: projectId.value,
|
|
|
+ contractId: contractId.value,
|
|
|
+ isArchive: 1,
|
|
|
+ nodeIds:nodeIds.value,
|
|
|
+ })
|
|
|
+ fileNumberLoad.value = false
|
|
|
+ if (!error && code === 200) {
|
|
|
+ fileNumberOptions.value = getArrValue(data?.records)
|
|
|
+ } else {
|
|
|
+ fileNumberOptions.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//获取表格数据
|
|
|
+const numberId = ref('')
|
|
|
+const fileNumberChange = (val)=>{
|
|
|
+ // 选择档号后清除错误状态
|
|
|
+ validateForm.fileNumberError = false
|
|
|
+ numberId.value = val
|
|
|
+if (val) {
|
|
|
+ loadTableData()
|
|
|
+}
|
|
|
+}
|
|
|
+
|
|
|
+// 监听插入位置变化,清除错误状态
|
|
|
+watch(() => insertModal.type, () => {
|
|
|
+ validateForm.typeError = false
|
|
|
+})
|
|
|
+
|
|
|
+// 表格配置
|
|
|
+const tableRef = ref(null)
|
|
|
+const tableLoading = ref(false)
|
|
|
+const tableData = ref([])
|
|
|
+
|
|
|
+// 表格列配置
|
|
|
+const tableColumn = ref([
|
|
|
+ { key: 'fileNumber', name: '文件编号', width: 180 },
|
|
|
+ { key: 'fileName', name: '文件名称' },
|
|
|
+ { key: 'action', name: '排序', width: 100 },
|
|
|
+])
|
|
|
+
|
|
|
+// 选中的文件
|
|
|
+const selectedFiles = ref([])
|
|
|
+// 当前点击的行ID
|
|
|
+const currentRowId = ref(null)
|
|
|
+
|
|
|
+// 根据ID获取行索引的方法
|
|
|
+const getRowIndexById = (id) => {
|
|
|
+ return tableData.value.findIndex(row => row.id === id)
|
|
|
+}
|
|
|
+
|
|
|
+// 表格选择变化
|
|
|
+const handleSelectionChange = (selection) => {
|
|
|
+ selectedFiles.value = selection
|
|
|
+}
|
|
|
+
|
|
|
+// 行点击事件 - 精确插入逻辑
|
|
|
+const handleRowClick = ({ row }) => {
|
|
|
+ if (!insertModal.type) {
|
|
|
+ window?.$message?.error('请选择插入位置')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 记录当前点击行的ID
|
|
|
+ currentRowId.value = row.id
|
|
|
+ tableRef.value?.toggleRowSelection(row)
|
|
|
+
|
|
|
+ // 如果有要插入的行数据,则执行插入操作
|
|
|
+ if (insertRows.value && insertRows.value.length > 0) {
|
|
|
+ // 获取当前行ID对应的索引
|
|
|
+ const index = getRowIndexById(row.id)
|
|
|
+
|
|
|
+ if (index === -1) {
|
|
|
+ window?.$message?.error('未找到指定行')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 深拷贝要插入的数据,避免引用问题
|
|
|
+ const rowsToInsert = JSON.parse(JSON.stringify(insertRows.value))
|
|
|
+
|
|
|
+ // 生成新的ID(实际项目中可能由后端生成)
|
|
|
+ const newRows = rowsToInsert.map(item => ({
|
|
|
+ ...item,
|
|
|
+ id: Date.now() + Math.floor(Math.random() * 1000), // 简单的ID生成策略
|
|
|
+ }))
|
|
|
+
|
|
|
+ // 根据选择的位置插入数据
|
|
|
+ if (insertModal.type === '1') {
|
|
|
+ // 插入到当前行之前
|
|
|
+ tableData.value.splice(index, 0, ...newRows)
|
|
|
+ window?.$message?.success(`已在第${index + 1}行的文件前插入 ${newRows.length} 个文件`)
|
|
|
+ } else {
|
|
|
+ // 插入到当前行之后
|
|
|
+ tableData.value.splice(index + 1, 0, ...newRows)
|
|
|
+ window?.$message?.success(`已在ID为${index + 1}的文件后插入 ${newRows.length} 个文件`)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空插入数据,避免重复插入
|
|
|
+ insertRows.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 移动行 - 上移
|
|
|
+const downSortClick = (index) => {
|
|
|
+ const indexs = index + 1
|
|
|
+ const data = [...tableData.value] // 创建副本避免直接修改源数据
|
|
|
+ if (indexs !== data.length) {
|
|
|
+ // 提取要移动的行(保留所有属性,包括inType)
|
|
|
+ const tmp = data.splice(indexs, 1)[0]
|
|
|
+ // 插入到新位置
|
|
|
+ tableData.value = [...data.slice(0, index), tmp, ...data.slice(index)]
|
|
|
+ } else {
|
|
|
+ window?.$message?.warning('已经处于置底,无法下移')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 移动行 - 下移
|
|
|
+const upSortClick = (index) => {
|
|
|
+ const data = [...tableData.value] // 创建副本避免直接修改源数据
|
|
|
+ if (index !== 0) {
|
|
|
+ // 提取要移动的行(保留所有属性,包括inType)
|
|
|
+ const tmp = data.splice(index - 1, 1)[0]
|
|
|
+ // 插入到新位置
|
|
|
+ tableData.value = [...data.slice(0, index), tmp, ...data.slice(index)]
|
|
|
+ } else {
|
|
|
+ window?.$message?.warning('已经处于置顶,无法上移')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 打开弹窗
|
|
|
+const insertRows = ref([])
|
|
|
+const nodeIds = ref('')
|
|
|
+const archiveId = ref('')
|
|
|
+const openInsertDialog = (rows, fromId, aId) => {
|
|
|
+ insertDialog.value = true
|
|
|
+ insertRows.value = rows || [] // 接收要插入的行数据
|
|
|
+ insertRows.value.forEach(row => {
|
|
|
+ row.inType = 1 // 标记为插入的数据
|
|
|
+ })
|
|
|
+ nodeIds.value = fromId
|
|
|
+ archiveId.value = aId
|
|
|
+
|
|
|
+ getFileNumberOptions() // 加载文件编号选项
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+//设置某一行的样式
|
|
|
+const tableRowStyle = ({ row }) => {
|
|
|
+ // 只根据inType属性判断样式,确保排序后依然有效
|
|
|
+ if (row?.inType === 1) {
|
|
|
+ return 'color: red;'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 加载表格数据
|
|
|
+const loadTableData = async () => {
|
|
|
+ tableLoading.value = true
|
|
|
+ const { error, code, data } = await tuningApi.getarchiveFilePage({
|
|
|
+ current: 1,
|
|
|
+ size: 5000,
|
|
|
+ total:0,
|
|
|
+ archiveId: numberId.value,
|
|
|
+ })
|
|
|
+ tableLoading.value = false
|
|
|
+ if (!error && code === 200) {
|
|
|
+ tableData.value = getArrValue(data['records'])
|
|
|
+ } else {
|
|
|
+ tableData.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭弹窗
|
|
|
+const insertDialogClose = () => {
|
|
|
+ tableData.value = []
|
|
|
+ insertDialog.value = false
|
|
|
+ resetForm()
|
|
|
+}
|
|
|
+
|
|
|
+// 表单验证方法
|
|
|
+const validateRequiredFields = () => {
|
|
|
+ let isValid = true
|
|
|
+
|
|
|
+ // 验证被插卷档号
|
|
|
+ if (!insertModal.fileNumber) {
|
|
|
+ validateForm.fileNumberError = true
|
|
|
+ isValid = false
|
|
|
+ } else {
|
|
|
+ validateForm.fileNumberError = false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证插入位置
|
|
|
+ if (!insertModal.type) {
|
|
|
+ validateForm.typeError = true
|
|
|
+ isValid = false
|
|
|
+ } else {
|
|
|
+ validateForm.typeError = false
|
|
|
+ }
|
|
|
+
|
|
|
+ return isValid
|
|
|
+}
|
|
|
+
|
|
|
+// 保存插卷设置
|
|
|
+const insertDialogSave = async () => {
|
|
|
+ // 先验证必填项
|
|
|
+ if (!validateRequiredFields()) {
|
|
|
+ window?.$message?.warning('请完善必填信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (selectedFiles.value.length === 0) {
|
|
|
+ window?.$message?.warning('请选择文件')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备提交的数据,保留inType信息
|
|
|
+ const submitData = {
|
|
|
+ targetId: insertModal.fileNumber,
|
|
|
+ sourceId: archiveId.value,
|
|
|
+ targetIndexFileId:currentRowId.value,
|
|
|
+ type: insertModal.type,
|
|
|
+ sourceArchiveFileIds: arrToId(selectedFiles.value),
|
|
|
+ sortFileList : arrToId(tableData.value),
|
|
|
+ }
|
|
|
+ insertDialogLoading.value = true
|
|
|
+ const { error, code, msg } = await tuningApi.insertArchives(submitData)
|
|
|
+ insertDialogLoading.value = false
|
|
|
+ tableLoading.value = false
|
|
|
+ if (!error && code === 200) {
|
|
|
+ window?.$message?.success(msg)
|
|
|
+ insertDialog.value = false
|
|
|
+ resetForm()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 重置表单
|
|
|
+const resetForm = () => {
|
|
|
+ insertModal.fileNumber = ''
|
|
|
+ insertModal.type = '' // 重置为为空
|
|
|
+ selectedFiles.value = []
|
|
|
+ insertRows.value = []
|
|
|
+ currentRowId.value = null
|
|
|
+ // 重置验证状态
|
|
|
+ validateForm.fileNumberError = false
|
|
|
+ validateForm.typeError = false
|
|
|
+ if (tableRef.value) {
|
|
|
+ tableRef.value.clearSelection()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 拖动完成 - 关键修复:确保拖动后保留inType属性
|
|
|
+const sortTableRowDrop = (rows) => {
|
|
|
+ // 拖动后,使用原数据中的inType属性更新新的排序结果
|
|
|
+ const updatedRows = rows.map(row => {
|
|
|
+ // 查找原数据中对应的行,获取inType属性
|
|
|
+ const originalRow = tableData.value.find(item => item.id === row.id)
|
|
|
+ return {
|
|
|
+ ...row,
|
|
|
+ // 保留原有的inType属性
|
|
|
+ inType: originalRow ? originalRow.inType : row.inType,
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ tableData.value = updatedRows
|
|
|
+}
|
|
|
+
|
|
|
+// 暴露方法供外部调用
|
|
|
+defineExpose({
|
|
|
+ openInsertDialog,
|
|
|
+})
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // 组件挂载时的初始化操作
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+
|
|
|
+</style>
|