From d9ed7cc8ec13a59b69bd643a9c3e2ccd907fabd1 Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期三, 20 八月 2025 15:36:51 +0800 Subject: [PATCH] 档案管理 --- src/views/fileManagement/document/index.vue | 1262 ++++++++++++++++++ src/views/fileManagement/bookshelf/detail.vue | 110 + src/api/fileManagement/borrow.js | 47 src/views/fileManagement/bookshelf/index.vue | 688 ++++++++++ src/views/fileManagement/return/index.vue | 599 ++++++++ src/views/fileManagement/borrow/index.vue | 584 ++++++++ src/api/fileManagement/return.js | 54 vite.config.js | 6 src/api/fileManagement/document.js | 164 ++ src/api/fileManagement/bookshelf.js | 128 + src/views/fileManagement/document/attachmentManager.vue | 426 ++++++ 11 files changed, 4,065 insertions(+), 3 deletions(-) diff --git a/src/api/fileManagement/bookshelf.js b/src/api/fileManagement/bookshelf.js new file mode 100644 index 0000000..0a4a748 --- /dev/null +++ b/src/api/fileManagement/bookshelf.js @@ -0,0 +1,128 @@ +import request from "@/utils/request"; + +/** + * 涔︽灦绠$悊鐩稿叧API鎺ュ彛 + * 鍖呭惈浠撳簱绠$悊銆佽揣鏋剁鐞嗐�佸浘涔︾鐞嗙瓑鍔熻兘鐨勬帴鍙� + */ + +/** + * 鑾峰彇浠撳簱鍒楄〃 + * @description 鑾峰彇鎵�鏈変粨搴撶殑鍩烘湰淇℃伅鍒楄〃 + * @returns {Promise} 杩斿洖浠撳簱鍒楄〃鏁版嵁 + */ +export function getWarehouseList() { + return request({ + url: "/warehouse/tree", + method: "get", + }); +} + +/** + * 鏂板浠撳簱 + * @description 鍒涘缓鏂扮殑浠撳簱璁板綍 + * @param {Object} data 浠撳簱淇℃伅瀵硅薄锛屽寘鍚粨搴撳悕绉扮瓑瀛楁 + * @returns {Promise} 杩斿洖鏂板缁撴灉 + */ +export function addWarehouse(data) { + return request({ + url: "/warehouse/add", + method: "post", + data, + }); +} + +/** + * 鏇存柊浠撳簱淇℃伅 + * @description 淇敼鐜版湁浠撳簱鐨勫熀鏈俊鎭� + * @param {Object} data 浠撳簱淇℃伅瀵硅薄锛屽繀椤诲寘鍚粨搴揑D + * @returns {Promise} 杩斿洖鏇存柊缁撴灉 + */ +export function updateWarehouse(data) { + return request({ + url: "/warehouse/update", + method: "put", + data, + }); +} + +/** + * 鍒犻櫎浠撳簱 + * @description 鏍规嵁浠撳簱ID鍒犻櫎鎸囧畾鐨勪粨搴撹褰� + * @param {string|number} id 浠撳簱ID + * @returns {Promise} 杩斿洖鍒犻櫎缁撴灉 + */ +export function deleteWarehouse(data) { + return request({ + url: `/warehouse/delete/`, + method: "delete", + data, + }); +} + +/** + * 鑾峰彇璐ф灦鍒楄〃 + * @description 鏍规嵁浠撳簱ID鑾峰彇璇ヤ粨搴撲笅鐨勬墍鏈夎揣鏋朵俊鎭� + * @param {string|number} warehouseId 浠撳簱ID + * @returns {Promise} 杩斿洖璐ф灦鍒楄〃鏁版嵁 + */ +export function getShelfList(warehouseId) { + return request({ + url: `/shelf/list/${warehouseId}`, + method: "get", + }); +} + +/** + * 鏂板璐ф灦 + * @description 鍦ㄦ寚瀹氫粨搴撲笅鍒涘缓鏂扮殑璐ф灦璁板綍 + * @param {Object} data 璐ф灦淇℃伅瀵硅薄锛屽寘鍚揣鏋跺悕绉般�佸眰鏁般�佸垪鏁扮瓑瀛楁 + * @returns {Promise} 杩斿洖鏂板缁撴灉 + */ +export function addShelf(data) { + return request({ + url: "/warehouse/goodsShelves/add", + method: "post", + data, + }); +} + +/** + * 鏇存柊璐ф灦淇℃伅 + * @description 淇敼鐜版湁璐ф灦鐨勫熀鏈俊鎭� + * @param {Object} data 璐ф灦淇℃伅瀵硅薄锛屽繀椤诲寘鍚揣鏋禝D + * @returns {Promise} 杩斿洖鏇存柊缁撴灉 + */ +export function updateShelf(data) { + return request({ + url: "/warehouse/goodsShelves/update", + method: "put", + data, + }); +} + +/** + * 鍒犻櫎璐ф灦 + * @description 鏍规嵁璐ф灦ID鍒犻櫎鎸囧畾鐨勮揣鏋惰褰� + * @param {string|number} id 璐ф灦ID + * @returns {Promise} 杩斿洖鍒犻櫎缁撴灉 + */ +export function deleteShelf(id) { + return request({ + url: `/warehouse/goodsShelves/delete/${id}`, + method: "delete", + }); +} + +/** + * 鑾峰彇浠撳簱缁撴瀯 + * @description 鑾峰彇鎸囧畾浠撳簱鐨勫畬鏁寸粨鏋勪俊鎭紝鍖呮嫭璐ф灦銆佸眰鏁般�佸垪鏁扮瓑 + * @param {string|number} warehouseId 浠撳簱ID + * @returns {Promise} 杩斿洖浠撳簱鐨勫畬鏁寸粨鏋勬暟鎹� + */ +export function getWarehouseStructure(data) { + return request({ + url: `/warehouse/goodsShelvesRowcol/list`, + method: "get", + params: data, + }); +} diff --git a/src/api/fileManagement/borrow.js b/src/api/fileManagement/borrow.js new file mode 100644 index 0000000..1f4c72c --- /dev/null +++ b/src/api/fileManagement/borrow.js @@ -0,0 +1,47 @@ +import request from "@/utils/request"; + +// 鏂囨。鍊熼槄绠$悊鐩稿叧鎺ュ彛 + +// 鑾峰彇鏂囨。鍒楄〃锛堢敤浜庡�熼槄涔︾睄閫夋嫨锛� +export function getDocumentList() { + return request({ + url: "/documentation/list", + method: "get", + }); +} + +// 鍊熼槄鍒嗛〉鏌ヨ +export function getBorrowList(params) { + return request({ + url: "/documentationBorrowManagement/listPage", + method: "get", + params: params, + }); +} + +// 鏂板鍊熼槄 +export function addBorrow(data) { + return request({ + url: "/documentationBorrowManagement/add", + method: "post", + data: data, + }); +} + +// 鏇存柊鍊熼槄 +export function updateBorrow(data) { + return request({ + url: "/documentationBorrowManagement/update", + method: "put", + data: data, + }); +} + +// 鍒犻櫎鍊熼槄 +export function deleteBorrow(ids) { + return request({ + url: "/documentationBorrowManagement/delete", + method: "delete", + data: ids, + }); +} diff --git a/src/api/fileManagement/document.js b/src/api/fileManagement/document.js new file mode 100644 index 0000000..49b156e --- /dev/null +++ b/src/api/fileManagement/document.js @@ -0,0 +1,164 @@ +import request from "@/utils/request"; + +// 鑾峰彇鍒嗙被鏍� +export function getCategoryTree() { + return request({ + url: "/warehouse/documentClassification/getList", + method: "get", + }); +} + +// 鏂板鍒嗙被 +export function addCategory(data) { + return request({ + url: "/warehouse/documentClassification/add", + method: "post", + data: { + category: data.category, + parentId: data.parentId, + }, + }); +} + +// 淇敼鍒嗙被 +export function updateCategory(data) { + return request({ + url: "/warehouse/documentClassification/update", + method: "put", + data: { + id: data.id, + category: data.category, + }, + }); +} + +// 鍒犻櫎鍒嗙被 +export function deleteCategory(ids) { + return request({ + url: "/warehouse/documentClassification/delete", + method: "delete", + data: ids, + }); +} + +// 鑾峰彇鏂囨。鍒楄〃锛堝垎椤碉級 +export function getDocumentList(query) { + return request({ + url: "/documentation/listPage", + method: "get", + params: query, + }); +} + +// 鏂板鏂囨。 +export function addDocument(data) { + return request({ + url: "/documentation/add", + method: "post", + data: data, + }); +} + +// 淇敼鏂囨。 +export function updateDocument(data) { + return request({ + url: "/documentation/update", + method: "put", + data: data, + }); +} + +// 鍒犻櫎鏂囨。 +export function deleteDocument(ids) { + return request({ + url: "/documentation/delete", + method: "delete", + data: ids, + }); +} + +// 鑾峰彇鏂囨。璇︽儏 +export function getDocumentDetail(id) { + return request({ + url: "/document/" + id, + method: "get", + }); +} + +// 鎼滅储鏂囨。 +export function searchDocument(query) { + return request({ + url: "/document/search", + method: "get", + params: query, + }); +} + +// 鑾峰彇浠撳簱缁撴瀯 +export function getWarehouseStructure() { + return request({ + url: "/document/warehouse/structure", + method: "get", + }); +} + +// 闄勪欢绠$悊鐩稿叧鎺ュ彛 +// 娣诲姞闄勪欢 +export function addDocumentationFile(data) { + return request({ + url: "/documentation/documentationFile/add", + method: "post", + data: data, + }); +} + +// 鑾峰彇闄勪欢鍒楄〃 +export function getDocumentationFileList(params) { + return request({ + url: "/documentation/documentationFile/listPage", + method: "get", + params: params, + }); +} + +// 鍒犻櫎闄勪欢 +export function deleteDocumentationFile(ids) { + return request({ + url: "/documentation/documentationFile/del", + method: "delete", + data: ids, + }); +} + +// 鏂囨。鍊熼槄绠$悊鐩稿叧鎺ュ彛 +export function getBorrowList(params) { + return request({ + url: "/documentationBorrowManagement/listPage", + method: "get", + params: params, + }); +} + +export function addBorrow(data) { + return request({ + url: "/documentationBorrowManagement/add", + method: "post", + data: data, + }); +} + +export function updateBorrow(data) { + return request({ + url: "/documentationBorrowManagement/update", + method: "put", + data: data, + }); +} + +export function deleteBorrow(ids) { + return request({ + url: "/documentationBorrowManagement/delete", + method: "delete", + data: ids, + }); +} diff --git a/src/api/fileManagement/return.js b/src/api/fileManagement/return.js new file mode 100644 index 0000000..2e0c0e7 --- /dev/null +++ b/src/api/fileManagement/return.js @@ -0,0 +1,54 @@ +import request from "@/utils/request"; + +// 鍒嗛〉鏌ヨ褰掕繕璁板綍 +export function getReturnListPage(query) { + return request({ + url: "/documentationBorrowManagement/listPageReturn", + method: "get", + params: query, + }); +} + +// 褰掕繕鎿嶄綔 +export function returnDocument(data) { + return request({ + url: "/documentationBorrowManagement/revent", + method: "put", + data: data, + }); +} + +// 鍒犻櫎褰掕繕璁板綍 +export function deleteReturn(ids) { + return request({ + url: "/documentationBorrowManagement/reventDelete", + method: "delete", + data: ids, + }); +} + +// 鏇存柊鍊熼槄璁板綍 +export function updateBorrow(data) { + return request({ + url: "/documentationBorrowManagement/update", + method: "put", + data: data, + }); +} + +// 褰掕繕鏇存柊 +export function reventUpdate(data) { + return request({ + url: "/documentationBorrowManagement/reventUpdate", + method: "put", + data: data, + }); +} + +// 鑾峰彇鏂囨。鍒楄〃 +export function getDocumentList() { + return request({ + url: "/documentationBorrowManagement/list", + method: "get", + }); +} diff --git a/src/views/fileManagement/bookshelf/detail.vue b/src/views/fileManagement/bookshelf/detail.vue new file mode 100644 index 0000000..5d7d3ac --- /dev/null +++ b/src/views/fileManagement/bookshelf/detail.vue @@ -0,0 +1,110 @@ +<template> + <div class="detail-container"> + <div class="header"> + <el-button @click="handleBack" type="primary" size="small">杩斿洖</el-button> + <h2>鍥句功璇︽儏</h2> + </div> + + <div class="content" v-loading="loading"> + <el-card v-if="current"> + <template #header> + <div class="card-header"> + <span>鍩烘湰淇℃伅</span> + </div> + </template> + + <el-descriptions :column="2" border> + <el-descriptions-item label="鍥句功缂栧彿">{{ current.docNumber }}</el-descriptions-item> + <el-descriptions-item label="鍥句功鍚嶇О">{{ current.docName }}</el-descriptions-item> + <el-descriptions-item label="鍏ュ簱鏃堕棿">{{ current.createTime }}</el-descriptions-item> + <!-- <el-descriptions-item label="褰撳墠浣嶇疆">{{ current.currentLocation }}</el-descriptions-item> --> + <el-descriptions-item label="鐘舵��">{{ current.docStatus }}</el-descriptions-item> + </el-descriptions> + + <!-- <div class="additional-info" v-if="current.description"> + <h4>鍥句功绠�浠�</h4> + <p>{{ current.description }}</p> + </div> --> + </el-card> + + <el-empty v-else description="鏆傛棤鏁版嵁" /> + </div> + </div> +</template> + +<script setup> +import { ref } from 'vue' + +// 瀹氫箟props +const props = defineProps({ + current: { + type: Object, + required: true + } +}) + +// 瀹氫箟emits +const emit = defineEmits(['hanldeBack']) + +// 鍝嶅簲寮忔暟鎹� +const loading = ref(false) +// const bookInfo = ref(null) + +// 鏂规硶 +const handleBack = () => { + emit('hanldeBack') +} + +</script> + +<style scoped> +.detail-container { + padding: 20px; + height: 100%; + background-color: #f5f5f5; +} + +.header { + display: flex; + align-items: center; + margin-bottom: 20px; + background-color: #fff; + padding: 15px 20px; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.header h2 { + margin: 0 0 0 20px; + color: #333; +} + +.content { + background-color: #fff; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.card-header { + font-weight: bold; + color: #333; +} + +.additional-info { + margin-top: 20px; + padding-top: 20px; + border-top: 1px solid #ebeef5; +} + +.additional-info h4 { + margin: 0 0 10px 0; + color: #333; + font-size: 16px; +} + +.additional-info p { + margin: 0; + color: #666; + line-height: 1.6; +} +</style> diff --git a/src/views/fileManagement/bookshelf/index.vue b/src/views/fileManagement/bookshelf/index.vue new file mode 100644 index 0000000..2689900 --- /dev/null +++ b/src/views/fileManagement/bookshelf/index.vue @@ -0,0 +1,688 @@ +<template> + <div class="sample"> + <div class="main-content" v-if="!isDetail"> + <div class="search"> + <div class="search_thing"> + <div class="search_label">浠撳簱鍚嶇О锛�</div> + <div class="search_input"> + <el-select v-model="entity.warehouseId" placeholder="閫夋嫨浠撳簱" size="small" @change="warehouseChange"> + <el-option v-for="item in warehouse" :key="item.id" :label="item.label" :value="item.id"> + </el-option> + </el-select> + </div> + </div> + <div class="search_thing"> + <div class="search_label">璐ф灦锛�</div> + <div class="search_input"> + <el-select v-model="entity.shelfId" placeholder="閫夋嫨璐ф灦" size="small" @change="handleShelf"> + <el-option v-for="item in shelf" :key="item.id" :label="item.label" :value="item.id"> + </el-option> + </el-select> + </div> + </div> + <!-- <div class="search_thing"> + <el-button size="small" @click="handleShelf(entity.shelfId,'')">閲嶇疆</el-button> + <el-button size="small" type="primary" @click="handleShelf(entity.shelfId)">鏌ヨ</el-button> + </div> --> + <div class="btns"> + <el-button size="small" style="color:#3A7BFA" @click="keepVisible=true">缁存姢</el-button> + <el-button size="small" style="color:#3A7BFA" @click="warehouseVisible=true,isEdit=false">娣诲姞浠撳簱</el-button> + <el-button size="small" style="color:#3A7BFA" @click="shelvesVisible=true,isEdit=false" + :disabled="entity.warehouseId==null">娣诲姞璐ф灦</el-button> + </div> + </div> + <div class="table" v-loading="tableLoading"> + <table class="tables" style="table-layout:fixed;" v-if="tableList.length>0"> + <tbody> + <tr v-for="(item,index) in tableList" :key="index"> + <td v-for="(m,i) in item" :key="i" class="content"> + <h4 v-if="m.row!=undefined">{{ m.row }} - {{ m.col }}</h4> + <ul> + <el-tooltip + effect="dark" + placement="top" + v-for="(n,j) in m.documentationDtoList" + :key="j"> + <template #content><span>{{ n.docName }}</span> + <span> [{{ n.docNumber }}]</span></template> + <li class="green" + @click="handelDetail(n)"> + <i></i> + <span>{{ n.docName }}</span> + <span> [{{ n.docNumber }}] <span :style="{ color: getStatusColor(n.docStatus) }">锛坽{ n.docStatus }}锛�</span></span> + </li> + </el-tooltip> + </ul> + </td> + </tr> + <tr> + <td v-for="(item,index) in rowList" :key="index" style="background: ghostwhite;height: 20px;">{{ item }} + </td> + </tr> + </tbody> + </table> + <span v-else style="color: rgb(144, 147, 153);display: inline-block;position: absolute;top: 60%;left: 50%;transform: translate(-50%,-50%);">鏆傛棤鏁版嵁</span> + </div> + </div> + <Detail v-else @hanldeBack="isDetail=false" :current="current" /> + + <!-- 搴撲綅缁存姢瀵硅瘽妗� --> + <el-dialog v-model="keepVisible" title="搴撲綅缁存姢" width="350px" :append-to-body="true"> + <el-tree :data="warehouse" ref="tree" node-key="id" + highlight-current v-if="keepVisible" + empty-text="鏆傛棤鏁版嵁"> + <template #default="{ node, data }"> + <div class="custom-tree-node" style="width: 100%;"> + <el-row style="width: 100%;display: flex;align-items: center;"> + <el-col :span="14"> + <span> + <el-icon v-if="node.level < 2" class="folder-icon"> + <FolderOpened /> + </el-icon> + <el-icon v-else class="file-icon"> + <Document /> + </el-icon> + {{ data.label }} + </span> + </el-col> + <el-col :span="10" v-if="node.level<3"> + <el-button type="link" size="small" :icon="Edit" @click.stop="handleEdit(data,node.level)"> + </el-button> + <el-button type="danger" size="small" :icon="Delete" @click.stop="handleDelete(data,node.level)"> + </el-button> + </el-col> + </el-row> + </div> + </template> + </el-tree> + <template #footer> + <span class="dialog-footer"> + <el-button @click="keepVisible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="keepVisible = false" >纭� 瀹�</el-button> + </span> + </template> + </el-dialog> + + <!-- 浠撳簱鏂板/淇敼瀵硅瘽妗� --> + <el-dialog v-model="warehouseVisible" :title="isEdit?'浠撳簱淇敼':'浠撳簱鏂板'" width="350px"> + <el-row> + <el-col class="search_thing" :span="24"> + <div class="search_label"><span class="required-span">* </span>浠撳簱鍚嶇О锛�</div> + <div class="search_input"> + <el-input v-model="name" size="small" @keyup.enter="confirmWarehouse"></el-input> + </div> + </el-col> + </el-row> + <template #footer> + <span class="dialog-footer"> + <el-button @click="warehouseVisible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="confirmWarehouse" :loading="upLoadWarehouse">纭� 瀹�</el-button> + </span> + </template> + </el-dialog> + + <!-- 璐ф灦鏂板/淇敼瀵硅瘽妗� --> + <el-dialog v-model="shelvesVisible" :title="isEdit?'璐ф灦淇敼':'璐ф灦鏂板'" width="350px"> + <el-row> + <el-col class="search_thing" :span="24"> + <div class="search_label"><span class="required-span">* </span>璐ф灦鍚嶇О锛�</div> + <div class="search_input"> + <el-input v-model="shelves.name" size="small"></el-input> + </div> + </el-col> + </el-row> + <el-row> + <el-col class="search_thing" :span="24"> + <div class="search_label"><span class="required-span">* </span>璐ф灦灞傛暟锛�</div> + <div class="search_input"> + <el-input v-model="shelves.row" size="small"></el-input> + </div> + </el-col> + </el-row> + <el-row> + <el-col class="search_thing" :span="24"> + <div class="search_label"><span class="required-span">* </span>璐ф灦鍒楁暟锛�</div> + <div class="search_input"> + <el-input v-model="shelves.col" size="small"></el-input> + </div> + </el-col> + </el-row> + <template #footer> + <span class="dialog-footer"> + <el-button @click="shelvesVisible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="confirmShelves" :loading="upLoadShelves">纭� 瀹�</el-button> + </span> + </template> + </el-dialog> + + + </div> +</template> + +<script setup> +import { ref, reactive, onMounted, watch } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' +import { Edit, Delete, FolderOpened, Document } from '@element-plus/icons-vue' +import { getWarehouseList, addWarehouse, updateWarehouse, deleteWarehouse, getWarehouseStructure, addShelf, updateShelf, deleteShelf } from '@/api/fileManagement/bookshelf' +import Detail from './detail.vue' + +// 鍝嶅簲寮忔暟鎹� +const entity = reactive({ + warehouseId: null, + shelfId: null +}) + +const warehouse = ref([]) +const shelf = ref([]) +const keepVisible = ref(false) +const warehouseVisible = ref(false) +const shelvesVisible = ref(false) +const upLoadWarehouse = ref(false) +const upLoadShelves = ref(false) +const tableList = ref([]) +const rowList = ref([]) +const value = ref('') +const name = ref('') +const shelves = reactive({}) +const isEdit = ref(false) +const isDetail = ref(false) +const currentEdit = ref(null) +const tableLoading = ref(false) +const current = ref({}) + +// 妯℃澘寮曠敤 +const organization = ref(null) + +// 鐩戝惉鍣� +watch(isEdit, (newVal) => { + if (!newVal) { + Object.keys(shelves).forEach(key => delete shelves[key]) + } +}) + +// 鏂规硶 + +const selectList = async () => { + // 杩欓噷闇�瑕佹浛鎹负瀹為檯鐨凙PI璋冪敤 + const res = await getWarehouseList() + warehouse.value = res.data + + if (warehouse.value.length == 0) { + entity.warehouseId = '' + entity.shelfId = '' + tableList.value = [] + } + + + + if (!entity.warehouseId && warehouse.value.length > 0) { + entity.warehouseId = warehouse.value[0].id + warehouseChange(entity.warehouseId) + if (shelf.value.length > 0) { + entity.shelfId = shelf.value[0].id + handleShelf(entity.shelfId) + } else { + tableList.value = [] + } + } else if (warehouse.value.length > 0) { + warehouseChange(entity.warehouseId) + if (shelf.value.length > 0) { + entity.shelfId = shelf.value[0].id + handleShelf(entity.shelfId) + } else { + tableList.value = [] + } + } +} + +const confirmWarehouse = () => { + if (!name.value) { + ElMessage.error('璇峰~鍐欎粨搴撳悕绉�') + return + } + upLoadWarehouse.value = true + + if (currentEdit.value && currentEdit.value.id) { + // 淇敼浠撳簱 + // 杩欓噷闇�瑕佹浛鎹负瀹為檯鐨凙PI璋冪敤 + updateWarehouse({ + id: currentEdit.value.id, + warehouseName: name.value + }).then(res => { + upLoadWarehouse.value = false + warehouseVisible.value = false + currentEdit.value = null + ElMessage.success('淇敼鎴愬姛') + selectList() + name.value = '' + warehouseChange(entity.warehouseId) + }) + + } else { + // 鏂板浠撳簱 + // 杩欓噷闇�瑕佹浛鎹负瀹為檯鐨凙PI璋冪敤 + addWarehouse({ + warehouseName: name.value + }).then(res => { + upLoadWarehouse.value = false + warehouseVisible.value = false + ElMessage.success('娣诲姞鎴愬姛') + selectList() + name.value = '' + warehouseChange(entity.warehouseId) + }) + } +} + +const confirmShelves = () => { + if (!shelves.name) { + ElMessage.error('璇峰~鍐欒揣鏋跺悕绉�') + return + } + if (!shelves.row) { + ElMessage.error('璇峰~鍐欒揣鏋跺眰鏁�') + return + } + if (!shelves.col) { + ElMessage.error('璇峰~鍐欒揣鏋跺垪鏁�') + return + } + upLoadShelves.value = true + + if (currentEdit.value && currentEdit.value.id) { + // 淇敼 + updateShelf({ + id: currentEdit.value.id, + name: shelves.name, + row: Number(shelves.row), + col: Number(shelves.col), + warehouseId: entity.warehouseId + }).then(res => { + upLoadShelves.value = false + shelvesVisible.value = false + ElMessage.success('淇敼鎴愬姛') + selectList() + currentEdit.value = {} + }).catch(err => { + upLoadShelves.value = false + shelvesVisible.value = false + ElMessage.error('淇敼澶辫触') + }) + + } else { + // 鏂板 + // 杩欓噷闇�瑕佹浛鎹负瀹為檯鐨凙PI璋冪敤 + addShelf({ + name: shelves.name, + row: Number(shelves.row), + col: Number(shelves.col), + warehouseId: entity.warehouseId + }).then(res => { + upLoadShelves.value = false + shelvesVisible.value = false + ElMessage.success('娣诲姞鎴愬姛') + selectList() + Object.keys(shelves).forEach(key => delete shelves[key]) + }).catch(err => { + upLoadShelves.value = false + shelvesVisible.value = false + ElMessage.error('娣诲姞澶辫触') + }) + } + warehouseChange(entity.warehouseId) +} + + + +const handleDelete = (row, level) => { + ElMessageBox.confirm('鏄惁鍒犻櫎褰撳墠鏁版嵁?', "璀﹀憡", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning" + }).then(() => { + if (level == 1) { + // 鍒犻櫎浠撳簱 + deleteWarehouse([row.id]).then(res => { + ElMessage.success('鍒犻櫎鎴愬姛') + selectList() + }) + } else { + // 鍒犻櫎璐ф灦 + deleteShelf({ + id: row.id + }).then(res => { + ElMessage.success('鍒犻櫎鎴愬姛') + selectList() + }) + } + warehouseChange(entity.warehouseId) + }).catch(() => {}) +} + +const handleEdit = (data, level) => { + isEdit.value = true + if (level == 1) { + warehouseVisible.value = true + currentEdit.value = data + name.value = data.label + } else { + shelvesVisible.value = true + currentEdit.value = data + Object.assign(shelves, { + name: data.label, + row: data.row, + col: data.col, + warehouseId: data.warehouseId + }) + } +} + +const handelDetail = (row) => { + current.value = row + isDetail.value = true +} + +// 鏍规嵁鏂囨。鐘舵�佽繑鍥炲搴旂殑棰滆壊 +const getStatusColor = (status) => { + if (status === '姝e父') { + return '#34BD66' // 缁胯壊 + } else if (status === '鍊熷嚭') { + return '#F56C6C' // 绾㈣壊 + } + return '#606266' // 榛樿棰滆壊 +} + +const warehouseChange = (val) => { +tableList.value = [] +let map = warehouse.value.find(a => { + return a && a.id === val ? a : null +}) +if (map && map.children) { + shelf.value = map.children + entity.shelfId = '' +} else { + shelf.value = [] +} +currentEdit.value = null +} + +const handleShelf = async(e) => { + if (e) { + tableLoading.value = true + let data = [] + const res = await getWarehouseStructure({warehouseGoodsShelvesId:e}) + if(res.code == 200){ + data = res.data.map(m=>{ + m.books = m.documentationDtoList|[] + return m + }) + }else{ + ElMessage.error(res.message) + } + setTimeout(() => { + tableLoading.value = false + let set = new Set() + tableList.value = [] + let arr = [] + + if (data && data.length > 0) { + data.forEach(m => { + if (m && m.row && m.col) { + set.add(m.col) + if (arr.length > 0) { + if (arr.find(n => n.row == m.row)) { + arr.push(m) + } else { + tableList.value.push(arr) + arr = [] + arr.push(m) + } + } else { + arr.push(m) + } + } + }) + + if (arr.length > 0) { + tableList.value.push(arr) + } + } + + rowList.value = [] + for (let i = 0; i < set.size; i++) { + rowList.value.push(`${i + 1} 鍒梎) + } + console.log(6666, tableList.value,rowList.value,data) + }, 1000) + } +} + + + +// 鐢熷懡鍛ㄦ湡 +onMounted(() => { + selectList() +}) +</script> + +<style scoped> + .main-content { + width: 100%; + height: 100%; + padding: 20px; + box-sizing: border-box; + } + + .title { + height: 20px; + line-height: 20px; + margin-bottom: 20px; + } + + .search { + background-color: #fff; + height: 80px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + margin-bottom: 20px; + } + + .search_thing { + display: flex; + align-items: center; + height: 50px; + margin-right: 20px; + } + + .search_label { + width: 90px; + font-size: 14px; + text-align: right; + color: #606266; + font-weight: 500; + margin-right: 10px; + } + + .search_input { + width: 200px; + } + + .table { + background-color: #fff; + width: 100%; + height: calc(100% - 100px); + padding: 20px; + overflow-y: auto; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + } + + .el-form-item { + margin-bottom: 16px; + } + + .btns { + display: flex; + align-items: center; + gap: 10px; + } + + .tables { + width: 100%; + height: 100%; + border-collapse: collapse; + border: 1px solid #e4e7ed; + } + + .tables th { + font-size: 14px; + border: 1px solid #e4e7ed; + background-color: #fafafa; + padding: 8px; + font-weight: 500; + } + + .tables td { + font-size: 12px; + text-align: center; + vertical-align: top; + border: 1px solid #e4e7ed; + padding: 8px; + box-sizing: border-box; + height: 120px; + background-color: #fff; + } + + .tables ul { + list-style-type: none; + } + + .tables ul li { + border-radius: 3px; + padding: 4px 10px; + box-sizing: border-box; + margin-bottom: 5px; + font-size: 12px; + display: flex; + align-items: center; + justify-content: start; + color: #333333; + cursor: pointer; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .tables h4 { + color: #999999; + font-size: 14px; + font-weight: 400; + padding: 6px 0; + } + + .tables i { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + margin-right: 6px; + } + + li:hover { + background: rgba(58, 123, 250, 0.18); + } + + li:hover i { + background: #3A7BFA; + } + + li:hover .num { + color: #3A7BFA; + } + + .green { + background: #E0F6EA; + } + + .green i { + background: #34BD66; + } + + .green .num { + color: #34BD66; + } + + .el-dialog { + position: relative; + } + + .shaoma { + display: flex; + align-items: center; + font-size: 14px; + color: #3A7BFA; + position: absolute; + top: 23px; + right: 54px; + cursor: pointer; + } + + .folder-icon { + color: #409eff; + font-size: 16px; + margin-right: 6px; + } + + .file-icon { + color: #67c23a; + font-size: 16px; + margin-right: 6px; + } + + .node_i { + color: orange; + font-size: 18px; + } + + .custom-tree-node .el-button { + opacity: 0; + } + + .custom-tree-node:hover .el-button { + opacity: 1; + } + + :deep(.el-loading-mask) { + z-index: 10; + } + + .required-span { + color: #f56c6c; + } + + .table-row { + border-bottom: 1px solid #e4e7ed; + } + + .table-row:last-child { + border-bottom: none; + } + + .column-header { + background-color: #fafafa !important; + font-weight: 500; + color: #606266; + } + + .content { + transition: background-color 0.2s ease; + } + + .content:hover { + background-color: #f5f7fa; + } +</style> diff --git a/src/views/fileManagement/borrow/index.vue b/src/views/fileManagement/borrow/index.vue new file mode 100644 index 0000000..9a9ee81 --- /dev/null +++ b/src/views/fileManagement/borrow/index.vue @@ -0,0 +1,584 @@ +<template> + <div class="app-container borrow-view"> + <!-- 鏌ヨ鍖哄煙 --> + <div class="search-container"> + <el-form :model="searchForm" :inline="true" class="search-form"> + <el-form-item label="鍊熼槄鐘舵�侊細"> + <el-select v-model="searchForm.borrowStatus" placeholder="璇烽�夋嫨鍊熼槄鐘舵��" clearable style="width: 150px"> + <el-option label="鍊熼槄" value="鍊熼槄" /> + <el-option label="褰掕繕" value="褰掕繕" /> + </el-select> + </el-form-item> + <el-form-item label="鍊熼槄浜猴細"> + <el-input + v-model="searchForm.borrower" + placeholder="璇疯緭鍏ュ�熼槄浜�" + clearable + style="width: 200px" + /> + </el-form-item> + <el-form-item label="鍊熼槄鏃ユ湡鑼冨洿锛�"> + <el-date-picker + v-model="searchForm.dateRange" + type="daterange" + range-separator="鑷�" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + style="width: 300px" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="handleSearch"> + <el-icon><Search /></el-icon> + 鏌ヨ + </el-button> + <el-button @click="handleReset"> + <el-icon><Refresh /></el-icon> + 閲嶇疆 + </el-button> + </el-form-item> + <el-form-item style="margin-left: auto;"> + <el-button type="primary" @click="openBorrowDia('add')"> + <el-icon><Plus /></el-icon> + 鏂板鍊熼槄 + </el-button> + <el-button + type="danger" + @click="handleBatchDelete" + :disabled="selectedRows.length === 0" + > + <el-icon><Delete /></el-icon> + 鎵归噺鍒犻櫎 ({{ selectedRows.length }}) + </el-button> + </el-form-item> + </el-form> + </div> + + <!-- 琛ㄦ牸鍖哄煙 --> + <div class="table-container"> + <PIMTable + :table-data="borrowList" + :column="tableColumns" + :is-selection="true" + :border="true" + :table-loading="tableLoading" + :page="{ + current: pagination.currentPage, + size: pagination.pageSize, + total: pagination.total, + layout: 'total, sizes, prev, pager, next, jumper' + }" + @selection-change="handleSelectionChange" + @pagination="handlePagination" + /> + </div> + + <!-- 鍊熼槄鏂板/缂栬緫瀵硅瘽妗� --> + <el-dialog + v-model="borrowDia" + :title="borrowOperationType === 'add' ? '鏂板鍊熼槄' : '缂栬緫鍊熼槄'" + width="800px" + @close="closeBorrowDia" + @keydown.enter.prevent + > + <el-form + :model="borrowForm" + label-width="140px" + :rules="borrowRules" + ref="borrowFormRef" + > + <el-row :gutter="20"> + + </el-row> + + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鍊熼槄浜猴細" prop="borrower"> + <el-input v-model="borrowForm.borrower" placeholder="璇疯緭鍏ュ�熼槄浜�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鍊熼槄涔︾睄锛�" prop="documentationId"> + <el-select v-model="borrowForm.documentationId" placeholder="璇烽�夋嫨鍊熼槄涔︾睄" style="width: 100%"> + <el-option + v-for="item in documentList" + :key="item.id" + :label="item.docName || item.name" + :value="item.id" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鍊熼槄鏃ユ湡锛�" prop="borrowDate"> + <el-date-picker + v-model="borrowForm.borrowDate" + type="date" + placeholder="閫夋嫨鍊熼槄鏃ユ湡" + style="width: 100%" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="搴斿綊杩樻棩鏈燂細" prop="dueReturnDate"> + <el-date-picker + v-model="borrowForm.dueReturnDate" + type="date" + placeholder="閫夋嫨搴斿綊杩樻棩鏈�" + style="width: 100%" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + /> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="24"> + <el-form-item label="鍊熼槄鐩殑锛�" prop="borrowPurpose"> + <el-input v-model="borrowForm.borrowPurpose" placeholder="璇疯緭鍏ュ�熼槄鐩殑" /> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="24"> + <el-form-item label="澶囨敞锛�" prop="remark"> + <el-input + v-model="borrowForm.remark" + type="textarea" + :rows="3" + placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�" + /> + </el-form-item> + </el-col> + </el-row> + </el-form> + + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitBorrowForm">纭</el-button> + <el-button @click="closeBorrowDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import { ref, reactive, onMounted, getCurrentInstance } from "vue"; +import { ElMessageBox, ElMessage } from "element-plus"; +import { Search, Refresh, Plus, Delete } from '@element-plus/icons-vue'; +import PIMTable from '@/components/PIMTable/PIMTable.vue'; +import { getBorrowList, addBorrow, updateBorrow, deleteBorrow, getDocumentList } from '@/api/fileManagement/borrow'; + +const { proxy } = getCurrentInstance(); + +// 鍝嶅簲寮忔暟鎹� +const borrowDia = ref(false); +const borrowOperationType = ref(""); +const tableLoading = ref(false); +const borrowList = ref([]); +const selectedRows = ref([]); +const documentList = ref([]); // 鏂囨。鍒楄〃锛岀敤浜庡�熼槄涔︾睄閫夋嫨 + +// 鍒嗛〉鐩稿叧 +const pagination = reactive({ + currentPage: 1, + pageSize: 10, + total: 0, +}); + +// 鏌ヨ琛ㄥ崟 +const searchForm = reactive({ + documentationId: "", + borrowStatus: "", + borrower: "", + returnerId: "", + dateRange: [] +}); + +// 鍊熼槄琛ㄥ崟 +const borrowForm = reactive({ + id: "", + documentationId: "", + borrower: "", + returnerId: "", + borrowPurpose: "", + borrowDate: "", + dueReturnDate: "", + returnDate: "", + borrowStatus: "", + remark: "" +}); + +// 琛ㄥ崟楠岃瘉瑙勫垯 +const borrowRules = reactive({ + documentationId: [{ required: true, message: "璇烽�夋嫨鍊熼槄涔︾睄", trigger: "change" }], + borrower: [{ required: true, message: "璇疯緭鍏ュ�熼槄浜�", trigger: "blur" }], + borrowPurpose: [{ required: true, message: "璇疯緭鍏ュ�熼槄鐩殑", trigger: "blur" }], + borrowDate: [{ required: true, message: "璇烽�夋嫨鍊熼槄鏃ユ湡", trigger: "change" }], + dueReturnDate: [{ required: true, message: "璇烽�夋嫨搴斿綊杩樻棩鏈�", trigger: "change" }], + borrowStatus: [{ required: true, message: "璇烽�夋嫨鍊熼槄鐘舵��", trigger: "change" }] +}); + +// 琛ㄦ牸鍒楅厤缃� +const tableColumns = ref([ + { + label: '鏂囨。鍚嶇О', + prop: 'documentationId', + width: '200', + formatData: (params) => { + if (!params) return '-'; + const doc = documentList.value.find(item => item.id === params); + return doc ? (doc.docName || doc.name) : params; + } + }, + { label: '鍊熼槄浜�', prop: 'borrower' }, + { label: '鍊熼槄鐩殑', prop: 'borrowPurpose' }, + { label: '鍊熼槄鏃ユ湡', prop: 'borrowDate' }, + { label: '搴斿綊杩樻棩鏈�', prop: 'dueReturnDate' }, + { + label: '鍊熼槄鐘舵��', + prop: 'borrowStatus', + width: '100', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + return params; + }, + formatType: (params) => { + if (params === '褰掕繕') return 'success'; + if (params === '鍊熼槄') return 'warning'; + return 'info'; + } + }, + { label: '澶囨敞', prop: 'remark', width: '150' }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + fixed: 'right', + width: '150', + operation: [ + { + name: "缂栬緫", + type: "text", + clickFun: (row) => { + openBorrowDia('edit', row) + }, + }, + { + name: "鍒犻櫎", + type: "text", + clickFun: (row) => { + handleDelete(row) + }, + }, + ], + } +]); + +// 鍒濆鍖栨暟鎹� +const initData = async () => { + await Promise.all([ + loadDocumentList(), + loadBorrowList() + ]); +}; + +// 鍔犺浇鏂囨。鍒楄〃 +const loadDocumentList = async () => { + try { + const res = await getDocumentList(); + if (res.code === 200) { + documentList.value = res.data || []; + } else { + ElMessage.error(res.msg || "鑾峰彇鏂囨。鍒楄〃澶辫触"); + documentList.value = []; + } + } catch (error) { + ElMessage.error("鑾峰彇鏂囨。鍒楄〃澶辫触锛岃閲嶈瘯"); + documentList.value = []; + } +}; + +// 鍔犺浇鍊熼槄鍒楄〃 +const loadBorrowList = async () => { + try { + tableLoading.value = true; + + // 鏋勫缓鏌ヨ鍙傛暟 + const query = { + page: pagination.currentPage, + size: pagination.pageSize, + documentationId: searchForm.documentationId || undefined, + borrowStatus: searchForm.borrowStatus || undefined, + borrower: searchForm.borrower || undefined, + returnerId: searchForm.returnerId || undefined, + entryDateStart: searchForm.dateRange && searchForm.dateRange.length > 0 ? searchForm.dateRange[0] : undefined, + entryDateEnd: searchForm.dateRange && searchForm.dateRange.length > 1 ? searchForm.dateRange[1] : undefined + }; + + // 绉婚櫎undefined鐨勫弬鏁� + Object.keys(query).forEach(key => { + if (query[key] === undefined) { + delete query[key]; + } + }); + + const res = await getBorrowList(query); + if (res.code === 200) { + borrowList.value = res.data.records || []; + pagination.total = res.data.total || 0; + } else { + ElMessage.error(res.msg || "鑾峰彇鍊熼槄鍒楄〃澶辫触"); + borrowList.value = []; + pagination.total = 0; + } + + // 閲嶇疆閫夋嫨鐘舵�� + selectedRows.value = []; + } catch (error) { + ElMessage.error("鑾峰彇鍊熼槄鍒楄〃澶辫触锛岃閲嶈瘯"); + borrowList.value = []; + pagination.total = 0; + } finally { + tableLoading.value = false; + } +}; + +// 鏌ヨ +const handleSearch = () => { + pagination.currentPage = 1; + loadBorrowList(); +}; + +// 閲嶇疆鏌ヨ +const handleReset = () => { + searchForm.documentationId = ""; + searchForm.borrowStatus = ""; + searchForm.borrower = ""; + searchForm.returnerId = ""; + searchForm.dateRange = []; + pagination.currentPage = 1; + loadBorrowList(); + ElMessage.success("鏌ヨ鏉′欢宸查噸缃�"); +}; + +// 鎵撳紑鍊熼槄寮规 +const openBorrowDia = (type, data) => { + borrowOperationType.value = type; + borrowDia.value = true; + + if (type === "edit") { + // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹� + Object.assign(borrowForm, data); + } else { + // 鏂板妯″紡锛屾竻绌鸿〃鍗� + Object.keys(borrowForm).forEach(key => { + borrowForm[key] = ""; + }); + // 璁剧疆榛樿鐘舵�� + borrowForm.borrowStatus = "鍊熼槄"; + // 璁剧疆褰撳墠鏃ユ湡涓哄�熼槄鏃ユ湡 + borrowForm.borrowDate = new Date().toISOString().split('T')[0]; + } +}; + +// 鍏抽棴鍊熼槄寮规 +const closeBorrowDia = () => { + proxy.$refs.borrowFormRef.resetFields(); + borrowDia.value = false; +}; + +// 鎻愪氦鍊熼槄琛ㄥ崟 +const submitBorrowForm = () => { + proxy.$refs.borrowFormRef.validate(async (valid) => { + if (valid) { + try { + if (borrowOperationType.value === "edit") { + // 缂栬緫妯″紡锛屾洿鏂扮幇鏈夋暟鎹� + const res = await updateBorrow({ + borrower:borrowForm.borrower, + id: borrowForm.id, + borrowPurpose: borrowForm.borrowPurpose, + borrowDate: borrowForm.borrowDate, + dueReturnDate: borrowForm.dueReturnDate, + returnDate: borrowForm.returnDate, + remark: borrowForm.remark + }); + + if (res.code === 200) { + ElMessage.success("缂栬緫鎴愬姛"); + await loadBorrowList(); + closeBorrowDia(); + } else { + ElMessage.error(res.msg || "缂栬緫澶辫触"); + } + } else { + // 鏂板妯″紡锛屾坊鍔犳柊鏁版嵁 + const res = await addBorrow({ + documentationId: borrowForm.documentationId, + borrower: borrowForm.borrower, + returnerId: borrowForm.returnerId, + borrowPurpose: borrowForm.borrowPurpose, + borrowDate: borrowForm.borrowDate, + dueReturnDate: borrowForm.dueReturnDate, + returnDate: borrowForm.returnDate, + borrowStatus: borrowForm.borrowStatus, + remark: borrowForm.remark + }); + + if (res.code === 200) { + ElMessage.success("鏂板鎴愬姛"); + await loadBorrowList(); + closeBorrowDia(); + } else { + ElMessage.error(res.msg || "鏂板澶辫触"); + } + } + } catch (error) { + ElMessage.error("鎿嶄綔澶辫触锛岃閲嶈瘯"); + } + } + }); +}; + +// 鍒犻櫎鍊熼槄璁板綍 +const handleDelete = (row) => { + ElMessageBox.confirm( + `纭畾瑕佸垹闄よ繖鏉″�熼槄璁板綍鍚楋紵`, + "鍒犻櫎鎻愮ず", + { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + } + ).then(async () => { + try { + const res = await deleteBorrow([row.id]); + if (res.code === 200) { + ElMessage.success("鍒犻櫎鎴愬姛"); + await loadBorrowList(); + } else { + ElMessage.error(res.msg || "鍒犻櫎澶辫触"); + } + } catch (error) { + ElMessage.error("鍒犻櫎澶辫触锛岃閲嶈瘯"); + } + }).catch(() => { + ElMessage.info("宸插彇娑堝垹闄�"); + }); +}; + +// 鎵归噺鍒犻櫎 +const handleBatchDelete = () => { + if (selectedRows.value.length === 0) { + ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑璁板綍"); + return; + } + + ElMessageBox.confirm( + `纭畾瑕佸垹闄ら�変腑鐨� ${selectedRows.value.length} 鏉″�熼槄璁板綍鍚楋紵`, + "鎵归噺鍒犻櫎鎻愮ず", + { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + } + ).then(async () => { + try { + const selectedIds = selectedRows.value.map(row => row.id); + const res = await deleteBorrow(selectedIds); + if (res.code === 200) { + ElMessage.success("鎵归噺鍒犻櫎鎴愬姛"); + await loadBorrowList(); + } else { + ElMessage.error(res.msg || "鎵归噺鍒犻櫎澶辫触"); + } + } catch (error) { + ElMessage.error("鎵归噺鍒犻櫎澶辫触锛岃閲嶈瘯"); + } + }).catch(() => { + ElMessage.info("宸插彇娑堝垹闄�"); + }); +}; + +// 閫夋嫨鍙樺寲浜嬩欢 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; + +// 澶勭悊鍒嗛〉鍙樺寲 +const handlePagination = (current, size) => { + pagination.currentPage = current; + pagination.pageSize = size; + loadBorrowList(); +}; + +// 鐢熷懡鍛ㄦ湡 +onMounted(() => { + initData(); +}); +</script> + +<style scoped> +.borrow-view { + padding: 20px; +} + +.search-container { + background: #ffffff; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.search-form { + margin: 0; +} + +.table-container { + background: #ffffff; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.empty-data { + text-align: center; + color: #909399; + padding: 40px; + font-size: 14px; +} + +.dialog-footer { + text-align: right; +} + +:deep(.el-form-item__label) { + font-weight: 500; + color: #303133; +} + +:deep(.el-input__wrapper) { + box-shadow: 0 0 0 1px #dcdfe6 inset; +} + +:deep(.el-input__wrapper:hover) { + box-shadow: 0 0 0 1px #c0c4cc inset; +} + +:deep(.el-input__wrapper.is-focus) { + box-shadow: 0 0 0 1px #409eff inset; +} +</style> diff --git a/src/views/fileManagement/document/attachmentManager.vue b/src/views/fileManagement/document/attachmentManager.vue new file mode 100644 index 0000000..a4e1d43 --- /dev/null +++ b/src/views/fileManagement/document/attachmentManager.vue @@ -0,0 +1,426 @@ +<template> + <el-dialog v-model="dialogVisible" title="闄勪欢绠$悊" width="60%" :before-close="handleClose"> + <div class="attachment-manager"> + <!-- 涓婁紶鍖哄煙 --> + <div class="upload-section"> + <el-upload + ref="uploadRef" + :action="uploadUrl" + :headers="uploadHeaders" + :before-upload="handleBeforeUpload" + :on-success="handleUploadSuccess" + :on-error="handleUploadError" + :on-remove="handleRemove" + :file-list="fileList" + multiple + :limit="10" + :show-file-list="false" + :data="{documentId: currentDocumentId}" + accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt,.xml,.jpg,.jpeg,.png,.gif,.bmp,.rar,.zip,.7z" + > + <el-button type="primary" :icon="Plus">涓婁紶闄勪欢</el-button> + <template #tip> + <div class="el-upload__tip"> + 鏀寔鏍煎紡锛歞oc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z + <br>鍗曚釜鏂囦欢澶у皬涓嶈秴杩�50MB + </div> + </template> + </el-upload> + </div> + + <!-- 闄勪欢鍒楄〃 --> + <div class="attachment-list"> + <el-table :data="fileList" border height="400px" v-loading="loading"> + <el-table-column label="搴忓彿" type="index" width="60" align="center" /> + <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="200" show-overflow-tooltip /> + <el-table-column label="鏂囦欢澶у皬" prop="size" width="100" align="center"> + <template #default="scope"> + {{ formatFileSize(scope.row.size) }} + </template> + </el-table-column> + <el-table-column label="涓婁紶鏃堕棿" prop="uploadTime" width="160" align="center"> + <template #default="scope"> + {{ formatDate(scope.row.uploadTime) }} + </template> + </el-table-column> + <el-table-column label="鐘舵��" prop="status" width="80" align="center"> + <template #default="scope"> + <el-tag :type="scope.row.status === 'success' ? 'success' : 'danger'" size="small"> + {{ scope.row.status === 'success' ? '鎴愬姛' : '澶辫触' }} + </el-tag> + </template> + </el-table-column> + <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center"> + <template #default="scope"> + <el-button link type="primary" size="small" @click="previewFile(scope.row)"> + 棰勮 + </el-button> + <el-button link type="primary" size="small" @click="downloadFile(scope.row)"> + 涓嬭浇 + </el-button> + <el-button link type="danger" size="small" @click="removeFile(scope.row)"> + 鍒犻櫎 + </el-button> + </template> + </el-table-column> + </el-table> + </div> + </div> + + <!-- 鏂囦欢棰勮缁勪欢 --> + <filePreview ref="filePreviewRef" /> + </el-dialog> +</template> + +<script setup> +import { ref, reactive, computed } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' +import { Plus } from '@element-plus/icons-vue' +import { getToken } from "@/utils/auth" +import { addDocumentationFile, getDocumentationFileList, deleteDocumentationFile } from '@/api/fileManagement/document' +import filePreview from '@/components/filePreview/index.vue' + +const props = defineProps({ + // documentId 閫氳繃 open 浜嬩欢浼犲叆锛屼笉闇�瑕佷綔涓� props +}) + +const emit = defineEmits(['update:attachments']) + +const dialogVisible = ref(false) +const loading = ref(false) +const fileList = ref([]) +const uploadRef = ref() +const filePreviewRef = ref() +const currentDocumentId = ref('') // 鍐呴儴绠$悊褰撳墠鏂囨。ID + +// 涓婁紶閰嶇疆 +const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/file/upload" +const uploadHeaders = computed(() => ({ + Authorization: "Bearer " + getToken() +})) + +// 鎵撳紑寮规 +const open = (attachments = [], documentId = '') => { + dialogVisible.value = true + currentDocumentId.value = documentId // 璁剧疆褰撳墠鏂囨。ID + // 濡傛灉鏈夋枃妗D锛屽垯鍔犺浇闄勪欢鍒楄〃 + if (documentId) { + loadAttachmentList(documentId) + } else { + fileList.value = attachments || [] + // total.value = fileList.value.length // Removed total.value + } + // currentPage.value = 1 // Removed currentPage.value +} + +// 鍔犺浇闄勪欢鍒楄〃 +const loadAttachmentList = async (documentId) => { + try { + loading.value = true + const params = { + page: 1, // Always load from page 1 + size: 1000, // Load all for now + documentationId: documentId + } + + const res = await getDocumentationFileList(params) + if (res.code === 200) { + const records = res.data + + // 杞崲鏁版嵁鏍煎紡 + fileList.value = records.map(item => ({ + id: item.id, + name: item.name, + size: item.fileSize, + url: item.url, + uploadTime: item.createTime || item.uploadTime, + status: 'success', + uid: item.id + })) + + // total.value = totalCount // Removed total.value + } else { + ElMessage.error(res.msg || '鑾峰彇闄勪欢鍒楄〃澶辫触') + fileList.value = [] + // total.value = 0 // Removed total.value + } + } catch (error) { + console.error('鑾峰彇闄勪欢鍒楄〃澶辫触:', error) + ElMessage.error('鑾峰彇闄勪欢鍒楄〃澶辫触') + fileList.value = [] + // total.value = 0 // Removed total.value + } finally { + loading.value = false + } +} + +// 鍏抽棴寮规 +const handleClose = () => { + dialogVisible.value = false + emit('update:attachments', fileList.value) +} + +// 鏂囦欢涓婁紶鍓嶆牎楠� +const handleBeforeUpload = (file) => { + // 妫�鏌ユ枃浠跺ぇ灏忥紙50MB锛� + const isLt50M = file.size / 1024 / 1024 < 50 + if (!isLt50M) { + ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃50MB!') + return false + } + + // 妫�鏌ユ枃浠剁被鍨� + const allowedTypes = [ + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/vnd.ms-powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/pdf', + 'text/plain', + 'text/xml', + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/bmp', + 'application/x-rar-compressed', + 'application/zip', + 'application/x-7z-compressed' + ] + + if (!allowedTypes.includes(file.type)) { + ElMessage.error('涓嶆敮鎸佺殑鏂囦欢绫诲瀷!') + return false + } + + return true +} + +// 鏂囦欢涓婁紶鎴愬姛 +const handleUploadSuccess = (response, file, fileList) => { + console.log('鏂囦欢涓婁紶鎴愬姛鍝嶅簲:', response); + console.log('鏂囦欢淇℃伅:', file); + + if (response.code === 200) { + // 鏋勫缓闄勪欢鏁版嵁 - 纭繚姝g‘鑾峰彇URL + const attachmentData = { + name: file.name, + url: response.data.url || response.data.path || response.data.tempPath || file.url, + fileSize: file.size, + documentationId: currentDocumentId.value + }; + + console.log('鏋勫缓鐨勯檮浠舵暟鎹�:', attachmentData); + + // 璋冪敤淇濆瓨闄勪欢鎺ュ彛 + saveAttachment(attachmentData, file, fileList); + } else { + ElMessage.error(response.msg || '鏂囦欢涓婁紶澶辫触') + } +} + +// 淇濆瓨闄勪欢淇℃伅 +const saveAttachment = async (attachmentData, file, fileList) => { + try { + console.log('寮�濮嬩繚瀛橀檮浠讹紝鏁版嵁:', attachmentData); + + // 纭繚URL瀛楁瀛樺湪涓旀湁鏁� + if (!attachmentData.url) { + console.error('闄勪欢URL涓虹┖锛屾棤娉曚繚瀛�'); + ElMessage.error('鏂囦欢URL鑾峰彇澶辫触锛屾棤娉曚繚瀛橀檮浠�'); + return; + } + + const res = await addDocumentationFile(attachmentData); + console.log('淇濆瓨闄勪欢鎺ュ彛鍝嶅簲:', res); + + if (res.code === 200) { + const newFile = { + id: res.data.id || Date.now(), + name: attachmentData.name, + size: attachmentData.fileSize, + url: attachmentData.url, + uploadTime: new Date().toISOString(), + status: 'success', + uid: file.uid + } + + console.log('鍒涘缓鐨勬柊鏂囦欢瀵硅薄:', newFile); + fileList.push(newFile) + ElMessage.success('鏂囦欢涓婁紶骞朵繚瀛樻垚鍔�') + + // 淇濆瓨鎴愬姛鍚庡埛鏂伴檮浠跺垪琛� + if (currentDocumentId.value) { + await loadAttachmentList(currentDocumentId.value); + } + } else { + ElMessage.error(res.msg || '淇濆瓨闄勪欢淇℃伅澶辫触') + // 淇濆瓨澶辫触鏃剁Щ闄ゆ枃浠� + const index = fileList.findIndex(item => item.uid === file.uid) + if (index > -1) { + fileList.splice(index, 1) + } + } + } catch (error) { + console.error('淇濆瓨闄勪欢澶辫触:', error) + ElMessage.error('淇濆瓨闄勪欢淇℃伅澶辫触') + // 淇濆瓨澶辫触鏃剁Щ闄ゆ枃浠� + const index = fileList.findIndex(item => item.uid === file.uid) + if (index > -1) { + fileList.splice(index, 1) + } + } +} + +// 鏂囦欢涓婁紶澶辫触 +const handleUploadError = (error, file, fileList) => { + console.error('鏂囦欢涓婁紶澶辫触:', error); + console.error('澶辫触鐨勬枃浠�:', file); + console.error('褰撳墠鏂囦欢鍒楄〃:', fileList); + + ElMessage.error('鏂囦欢涓婁紶澶辫触锛岃妫�鏌ョ綉缁滆繛鎺ユ垨鏂囦欢鏍煎紡') +} + +// 绉婚櫎鏂囦欢 +const handleRemove = (file, fileList) => { + const index = fileList.findIndex(item => item.uid === file.uid) + if (index > -1) { + fileList.splice(index, 1) + // total.value = fileList.length // Removed total.value + } +} + +// 鍒犻櫎鏂囦欢 +const removeFile = (file) => { + ElMessageBox.confirm(`纭畾瑕佸垹闄ゆ枃浠� "${file.name}" 鍚楋紵`, '鍒犻櫎纭', { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning' + }).then(async () => { + try { + // 璋冪敤鍒犻櫎鎺ュ彛 + const res = await deleteDocumentationFile([file.id]); + if (res.code === 200) { + // 浠庢湰鍦板垪琛ㄤ腑绉婚櫎 + const index = fileList.value.findIndex(item => item.id === file.id); + if (index > -1) { + fileList.value.splice(index, 1); + } + ElMessage.success('鍒犻櫎鎴愬姛'); + + // 濡傛灉鏈夋枃妗D锛屽埛鏂伴檮浠跺垪琛� + if (currentDocumentId.value) { + await loadAttachmentList(currentDocumentId.value); + } + } else { + ElMessage.error(res.msg || '鍒犻櫎澶辫触'); + } + } catch (error) { + console.error('鍒犻櫎闄勪欢澶辫触:', error); + ElMessage.error('鍒犻櫎闄勪欢澶辫触'); + } + }).catch(() => { + // 鍙栨秷鍒犻櫎 + }) +} + +// 棰勮鏂囦欢 +const previewFile = (file) => { + if (file.url) { + filePreviewRef.value.open(file.url) + } else { + ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曢瑙�') + } +} + +// 涓嬭浇鏂囦欢 +const downloadFile = (file) => { + if (file.url) { + // 鍒涘缓涓嬭浇閾炬帴 + const link = document.createElement('a') + link.href = file.url + link.download = file.name + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + ElMessage.success('寮�濮嬩笅杞芥枃浠�') + } else { + ElMessage.warning('鏂囦欢鍦板潃鏃犳晥锛屾棤娉曚笅杞�') + } +} + +// 鏍煎紡鍖栨枃浠跺ぇ灏� +const formatFileSize = (bytes) => { + if (bytes === 0) return '0 B' + const k = 1024 + const sizes = ['B', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] +} + +// 鏍煎紡鍖栨棩鏈� +const formatDate = (dateString) => { + if (!dateString) return '' + const date = new Date(dateString) + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }) +} + +// 娴嬭瘯鏂囦欢涓婁紶 +const testUpload = () => { + console.log('褰撳墠鏂囨。ID:', currentDocumentId.value); + console.log('涓婁紶URL:', uploadUrl); + console.log('涓婁紶Headers:', uploadHeaders.value); +} + +// 鏆撮湶鏂规硶 +defineExpose({ + open, + loadAttachmentList, + testUpload +}) +</script> + +<style scoped> +.attachment-manager { + padding: 20px; +} + +.upload-section { + margin-bottom: 20px; + padding: 20px; + background-color: #f8f9fa; + border-radius: 8px; + border: 2px dashed #d9d9d9; +} + +.upload-section:hover { + border-color: #409eff; +} + +.attachment-list { + margin-bottom: 20px; +} + +.el-upload__tip { + margin-top: 10px; + color: #666; + font-size: 12px; + line-height: 1.5; +} + +:deep(.el-upload) { + width: 100%; +} + +:deep(.el-upload-dragger) { + width: 100%; + height: 120px; +} +</style> diff --git a/src/views/fileManagement/document/index.vue b/src/views/fileManagement/document/index.vue new file mode 100644 index 0000000..7a8b27e --- /dev/null +++ b/src/views/fileManagement/document/index.vue @@ -0,0 +1,1262 @@ +<template> + <div class="app-container document-view"> + <div class="left"> + <div> + <el-input + v-model="search" + style="width: 210px" + placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�" + @change="searchFilter" + @clear="searchFilter" + clearable + prefix-icon="Search" + /> + <el-button + type="primary" + @click="openCategoryDia('addOne')" + style="margin-left: 10px" + >鏂板鍒嗙被</el-button + > + </div> + <div ref="containerRef"> + <el-tree + ref="tree" + v-loading="treeLoad" + :data="categoryList" + @node-click="handleNodeClick" + :expand-on-click-node="false" + default-expand-all + :default-expanded-keys="expandedKeys" + :draggable="true" + :filter-node-method="filterNode" + :props="{ children: 'children', label: 'category' }" + highlight-current + node-key="id" + style=" + height: calc(100vh - 190px); + overflow-y: scroll; + scrollbar-width: none; + margin-top: 10px; + " + > + <template #default="{ node, data }"> + <div class="custom-tree-node"> + <span class="tree-node-content"> + <el-icon class="orange-icon"> + <component :is="data.children && data.children.length > 0 + ? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" /> + </el-icon> + {{ data.category }} + </span> + <div> + <el-button + type="primary" + link + @click="openCategoryDia('edit', data)" + > + 缂栬緫 + </el-button> + <el-button + type="primary" + link + @click="openCategoryDia('addSub', data)" + v-if="node.level < 2" + > + 娣诲姞瀛愬垎绫� + </el-button> + <el-button + v-if="!node.childNodes.length" + style="margin-left: 4px" + type="danger" + link + @click="removeCategory(node, data)" + > + 鍒犻櫎 + </el-button> + </div> + </div> + </template> + </el-tree> + </div> + </div> + <div class="right"> + <div style="margin-bottom: 10px" v-if="isShowButton"> + <el-button type="primary" @click="openDocumentDia('add')"> + 鏂板鏂囨。 + </el-button> + <el-button + type="danger" + @click="handleDelete" + style="margin-left: 10px" + plain + :disabled="selectedRows.length === 0" + > + 鍒犻櫎 ({{ selectedRows.length }}) + </el-button> + </div> + <div class="table-container"> + + <!-- PIMTable 缁勪欢 --> + <PIMTable + :table-data="documentList" + :column="tableColumns" + :is-selection="true" + :border="true" + :table-loading="tableLoading" + :page="{ + current: pagination.currentPage, + size: pagination.pageSize, + total: pagination.total, + layout: 'total, sizes, prev, pager, next, jumper' + }" + @selection-change="handleSelectionChange" + @pagination="handlePagination" + /> + </div> + </div> + + <!-- 鍒嗙被鏂板/淇敼瀵硅瘽妗� --> + <el-dialog v-model="categoryDia" title="鍒嗙被" width="400px" @keydown.enter.prevent> + <el-form + :model="categoryForm" + label-width="140px" + label-position="top" + :rules="categoryRules" + ref="categoryFormRef" + > + <el-row :gutter="30"> + <el-col :span="24" v-if="categoryOperationType === 'addSub'"> + <el-form-item label="鐖跺垎绫伙細" prop="parentName"> + <el-input + v-model="categoryForm.parentName" + placeholder="鐖跺垎绫诲悕绉�" + disabled + /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="鍒嗙被鍚嶇О锛�" prop="category"> + <el-input + v-model="categoryForm.category" + placeholder="璇疯緭鍏ュ垎绫诲悕绉�" + clearable + @keydown.enter.prevent + /> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitCategoryForm">纭</el-button> + <el-button @click="closeCategoryDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + + <!-- 鏂囨。鏂板/淇敼瀵硅瘽妗� --> + <el-dialog + v-model="documentDia" + :title="documentOperationType === 'add' ? '鏂板鏂囨。' : '缂栬緫鏂囨。'" + width="600px" + @close="closeDocumentDia" + @keydown.enter.prevent + > + <el-form + :model="documentForm" + label-width="140px" + label-position="top" + :rules="documentRules" + ref="documentFormRef" + > + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。鍚嶇О锛�" prop="docName"> + <el-input v-model="documentForm.docName" placeholder="璇疯緭鍏�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="骞村害锛�" prop="year"> + <el-date-picker + v-model="documentForm.year" + type="year" + value-format="YYYY" + format="YYYY" + placeholder="閫夋嫨骞村害" + style="width: 100%" + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。缂栧彿锛�" prop="docNumber"> + <el-input v-model="documentForm.docNumber" placeholder="璇疯緭鍏�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="璐d换浜猴細" prop="responsiblePerson"> + <el-input v-model="documentForm.responsiblePerson" placeholder="璇疯緭鍏�" /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。鍒嗙被锛�" prop="documentClassificationId"> + <el-select v-model="documentForm.documentClassificationId" placeholder="璇烽�夋嫨鏂囨。鍒嗙被" style="width: 100%"> + <el-option + v-for="item in categoryList" + :key="item.id" + :label="item.category" + :value="item.id" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鏂囨。鏀剧疆浣嶇疆锛�" prop="warehouseGoodsShelvesRowcolId"> + <el-tree-select + v-model="documentForm.warehouseGoodsShelvesRowcolId" + :data="locationTree" + placeholder="璇烽�夋嫨鏂囦欢鏀剧疆浣嶇疆" + clearable + check-strictly + :render-after-expand="false" + :props="{ children: 'children', label: 'label', value: 'value' }" + style="width: 100%" + @change="handleLocationChange" + /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。鏃ユ湡锛�" prop="docData"> + <el-date-picker + v-model="documentForm.docData" + type="date" + value-format="YYYY-MM-DD" + format="YYYY-MM-DD" + placeholder="閫夋嫨鏃ユ湡" + style="width: 100%" + /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="淇濈鏈熼檺锛�" prop="retentionPeriod"> + <el-select v-model="documentForm.retentionPeriod" placeholder="璇烽�夋嫨"> + <el-option + v-for="item in retention_period" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="淇濆瘑绾у埆锛�" prop="securityLevel"> + <el-select v-model="documentForm.securityLevel" placeholder="璇烽�夋嫨"> + <el-option + v-for="item in confidentiality_level" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鍒嗘暟锛�" prop="copyCount"> + <el-input v-model="documentForm.copyCount" placeholder="璇疯緭鍏�" /> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="椤垫暟锛�" prop="pageCount"> + <el-input v-model="documentForm.pageCount" placeholder="璇疯緭鍏�" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鏂囨。绫诲埆锛�" prop="docCategory"> + <el-select v-model="documentForm.docCategory" placeholder="璇烽�夋嫨"> + <el-option + v-for="item in document_type" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。绉嶇被锛�" prop="docType"> + <el-select v-model="documentForm.docType" placeholder="璇烽�夋嫨"> + <el-option + v-for="item in document_categories" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="绱ф�ョ▼搴︼細" prop="urgencyLevel"> + <el-select v-model="documentForm.urgencyLevel" placeholder="璇烽�夋嫨"> + <el-option + v-for="item in document_urgency" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。鐘舵�侊細" prop="docStatus"> + <el-select v-model="documentForm.docStatus" placeholder="璇烽�夋嫨"> + <el-option + v-for="item in document_status" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="24"> + <el-form-item label="澶囨敞锛�" prop="remark"> + <el-input + v-model="documentForm.remark" + type="textarea" + :rows="3" + placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�" + /> + </el-form-item> + </el-col> + </el-row> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitDocumentForm">纭</el-button> + <el-button @click="closeDocumentDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + <AttachmentManager ref="attachmentManagerRef" /> + </div> + </template> + +<script setup> +import { ref, reactive, onMounted, getCurrentInstance, toRefs, watch } from "vue"; +import { ElMessageBox, ElMessage } from "element-plus"; +import { ArrowRight, Folder, FolderOpened, Tickets, Document } from '@element-plus/icons-vue'; +import PIMTable from '@/components/PIMTable/PIMTable.vue'; +import { getToken } from "@/utils/auth"; +import { getCategoryTree, addCategory, updateCategory, deleteCategory, getDocumentList, addDocument, updateDocument, deleteDocument, getDocumentDetail, searchDocument, getWarehouseStructure } from '@/api/fileManagement/document' +import { getWarehouseList } from '@/api/fileManagement/bookshelf' +import AttachmentManager from './attachmentManager.vue' +import { useDict } from '@/utils/dict' + +const { proxy } = getCurrentInstance(); +const tree = ref(null); +const containerRef = ref(null); + +// 浣跨敤瀛楀吀鏁版嵁 +const { confidentiality_level, document_urgency, document_status, document_type, document_categories, retention_period } = useDict('confidentiality_level', 'document_urgency', 'document_status', 'document_type', 'document_categories', 'retention_period') + +// 鐩戝惉瀛楀吀鏁版嵁鍙樺寲 +watch([confidentiality_level, document_urgency, document_status, document_type, document_categories, retention_period], () => { + // 瀛楀吀鏁版嵁宸叉洿鏂� +}, { immediate: true, deep: true }); + +const categoryDia = ref(false); +const documentDia = ref(false); +const categoryOperationType = ref(""); +const documentOperationType = ref(""); +const search = ref(""); +const currentId = ref(""); +const currentParentId = ref(""); +const treeLoad = ref(false); +const categoryList = ref([]); +const expandedKeys = ref([]); +const documentList = ref([]); +const isShowButton = ref(false); +const selectedRows = ref([]); +const selectAll = ref(false); +const isIndeterminate = ref(false); +const tableLoading = ref(false); +const attachmentManagerRef = ref(null); + +// 鏂囦欢涓婁紶閰嶇疆 +const upload = reactive({ + url: import.meta.env.VITE_APP_BASE_API + "/file/upload", + headers: { Authorization: "Bearer " + getToken() }, +}); + +// 浣嶇疆鏍戞暟鎹� +const locationTree = ref([]); + +// 琛ㄦ牸鍒楅厤缃� +const tableColumns = ref([ + { label: '鏂囨。鍚嶇О', prop: 'docName', width: '200' }, + { label: '鏂囨。缂栧彿', prop: 'docNumber', width: '120' }, + { label: '骞村害', prop: 'year', width: '80' }, + { label: '璐d换浜�', prop: 'responsiblePerson', width: '100' }, + { + label: '鏂囨。鏀剧疆浣嶇疆', + prop: 'warehouseGoodsShelvesRowcolId', + width: '150', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + return getLocationName(params); + } + }, + { label: '鏂囨。鏃ユ湡', prop: 'docData', width: '120' }, + { + label: '淇濈鏈熼檺', + prop: 'retentionPeriod', + width: '100', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + if (!retention_period.value || retention_period.value.length === 0) { + return params; + } + const item = retention_period.value.find(item => item.value == params); + return item ? item.label : params; + }, + formatType: (params) => { + if (params === null || params === undefined || params === '') return 'info'; + if (!retention_period.value || retention_period.value.length === 0) { + return 'info'; + } + const item = retention_period.value.find(item => item.value == params); + const validTypes = ['success', 'warning', 'danger', 'info']; + return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info'; + } + }, + { + label: '淇濆瘑绾у埆', + prop: 'securityLevel', + width: '80', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + if (!confidentiality_level.value || confidentiality_level.value.length === 0) { + return params; + } + const item = confidentiality_level.value.find(item => item.value == params); + return item ? item.label : params; + }, + formatType: (params) => { + if (params === null || params === undefined || params === '') return 'info'; + if (!confidentiality_level.value || confidentiality_level.value.length === 0) { + return 'info'; + } + const item = confidentiality_level.value.find(item => item.value == params); + const validTypes = ['success', 'warning', 'danger', 'info']; + return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info'; + } + }, + { label: '鍒嗘暟', prop: 'copyCount', width: '80' }, + { label: '椤垫暟', prop: 'pageCount', width: '80' }, + { + label: '鏂囨。绫诲埆', + prop: 'docCategory', + width: '100', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + if (!document_type.value || document_type.value.length === 0) { + return params; + } + const item = document_type.value.find(item => item.value == params); + return item ? item.label : params; + }, + formatType: (params) => { + if (params === null || params === undefined || params === '') return 'info'; + if (!document_type.value || document_type.value.length === 0) { + return 'info'; + } + const item = document_type.value.find(item => item.value == params); + const validTypes = ['success', 'warning', 'danger', 'info']; + return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info'; + } + }, + { + label: '鏂囨。绉嶇被', + prop: 'docType', + width: '100', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + if (!document_categories.value || document_categories.value.length === 0) { + return params; + } + const item = document_categories.value.find(item => item.value == params); + return item ? item.label : params; + }, + formatType: (params) => { + if (params === null || params === undefined || params === '') return 'info'; + if (!document_categories.value || document_categories.value.length === 0) { + return 'info'; + } + const item = document_categories.value.find(item => item.value == params); + const validTypes = ['success', 'warning', 'danger', 'info']; + return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info'; + } + }, + { + label: '绱ф�ョ▼搴�', + prop: 'urgencyLevel', + width: '100', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + if (!document_urgency.value || document_urgency.value.length === 0) { + return params; + } + const item = document_urgency.value.find(item => item.value == params); + return item ? item.label : params; + }, + formatType: (params) => { + if (params === null || params === undefined || params === '') return 'info'; + if (!document_urgency.value || document_urgency.value.length === 0) { + return 'info'; + } + const item = document_urgency.value.find(item => item.value == params); + const validTypes = ['success', 'warning', 'danger', 'info']; + return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info'; + } + }, + { + label: '鏂囨。鐘舵��', + prop: 'docStatus', + width: '100', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + if (!document_status.value || document_status.value.length === 0) { + return params; + } + const item = document_status.value.find(item => item.value == params); + return item ? item.label : params; + }, + formatType: (params) => { + if (params === null || params === undefined || params === '') return 'info'; + if (!document_status.value || document_status.value.length === 0) { + return 'info'; + } + const item = document_status.value.find(item => item.value == params); + const validTypes = ['success', 'warning', 'danger', 'info']; + return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info'; + } + }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + fixed: 'right', + width: '150', + operation: [ + { + name: "缂栬緫", + type: "text", + clickFun: (row) => { + openDocumentDia('edit', row) + }, + }, + { + name: "闄勪欢", + type: "text", + clickFun: (row) => { + openAttachment(row) + }, + }, + ], + } +]); + +// 鍒嗙被琛ㄥ崟 +const categoryForm = reactive({ + category: "", + parentId: "", + parentName: "", +}); + +const categoryRules = reactive({ + category: [{ required: true, message: "璇疯緭鍏ュ垎绫诲悕绉�", trigger: "blur" }], +}); + +// 鏂囨。琛ㄥ崟 +const documentForm = reactive({ + id: "", + documentClassificationId: "", + docName: "", + docNumber: "", + year: "", + responsiblePerson: "", + warehouseGoodsShelvesRowcolId: "", + docData: "", + retentionPeriod: "", + securityLevel: "", + copyCount: "", + pageCount: "", + docCategory: "", + docType: "", + urgencyLevel: "", + docStatus: "", + remark: "", + attachments: [], // 鏂板闄勪欢鏁扮粍 +}); + +const documentRules = reactive({ + docName: [{ required: true, message: "璇疯緭鍏ユ枃妗e悕绉�", trigger: "blur" }], + docNumber: [{ required: true, message: "璇疯緭鍏ユ枃妗g紪鍙�", trigger: "blur" }], + year: [{ required: true, message: "璇烽�夋嫨骞村害", trigger: "change" }], + documentClassificationId: [{ required: true, message: "璇烽�夋嫨鏂囨。鍒嗙被", trigger: "change" }], + warehouseGoodsShelvesRowcolId: [{ required: true, message: "璇烽�夋嫨鏂囨。鏀剧疆浣嶇疆", trigger: "change" }], +}); + +// 鍒嗛〉鐩稿叧 +const pagination = reactive({ + currentPage: 1, + pageSize: 10, + total: 0, +}); + +// 鍒濆鍖栧垎绫绘爲鏁版嵁 +const initCategoryTree = async() => { + try { + treeLoad.value = true; + const res = await getCategoryTree(); + if (res.code === 200) { + categoryList.value = res.data || []; + + // 璁剧疆灞曞紑鐨勮妭鐐� + expandedKeys.value = []; + categoryList.value.forEach((item) => { + if (item.id) { + expandedKeys.value.push(item.id); + } + }); + } else { + ElMessage.error(res.msg || "鑾峰彇鍒嗙被鏍戝け璐�"); + } + } catch (error) { + ElMessage.error("鑾峰彇鍒嗙被鏍戝け璐ワ紝璇烽噸璇�"); + } finally { + treeLoad.value = false; + } +}; + +// 鍒濆鍖栦粨搴撲綅缃暟鎹� +const initLocationTree = async() => { + try { + const res = await getWarehouseList(); + if (res.code === 200) { + // 杞崲鏁版嵁鏍煎紡锛岄�傞厤el-tree-select缁勪欢 + locationTree.value = transformWarehouseData(res.data || []); + } else { + ElMessage.error(res.msg || "鑾峰彇浠撳簱浣嶇疆澶辫触"); + } + } catch (error) { + ElMessage.error("鑾峰彇浠撳簱浣嶇疆澶辫触锛岃閲嶈瘯"); + } +}; + +// 杞崲浠撳簱鏁版嵁鏍煎紡 +const transformWarehouseData = (data) => { + return data.map(item => ({ + id: item.id, + label: item.name || item.warehouseName || item.label, + value: item.id, + children: item.children ? transformWarehouseData(item.children) : [] + })); +}; + +// 鏍规嵁ID鑾峰彇浣嶇疆鍚嶇О +const getLocationName = (locationId) => { + if (!locationId || !locationTree.value || locationTree.value.length === 0) { + return locationId || '-'; + } + + const findLocation = (tree, id) => { + for (let item of tree) { + if (item.value === locationId || item.id === locationId) { + return item.label; + } + if (item.children && item.children.length > 0) { + const result = findLocation(item.children, id); + if (result) return result; + } + } + return null; + }; + + const locationName = findLocation(locationTree.value, locationId); + return locationName || locationId; +}; + +// 杩囨护鍒嗙被鏍� +const searchFilter = () => { + if (proxy.$refs.tree) { + proxy.$refs.tree.filter(search.value); + } +}; + +// 鎵撳紑鍒嗙被寮规 +const openCategoryDia = (type, data) => { + categoryOperationType.value = type; + categoryDia.value = true; + categoryForm.category = ""; + categoryForm.parentId =""; + categoryForm.parentName = ""; + + if (type === "edit") { + categoryForm.category = data.category; + // 淇濆瓨褰撳墠缂栬緫鐨勫垎绫籌D + currentId.value = data.id; + } else if (type === "addSub") { + categoryForm.parentId = data.id; + categoryForm.parentName = data.category; + } +}; + +// 鎵撳紑鏂囨。寮规 +const openDocumentDia = (type, data) => { + documentOperationType.value = type; + documentDia.value = true; + + if (type === "edit") { + // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹� + Object.assign(documentForm, data); + documentForm.retentionPeriod = String(documentForm.retentionPeriod) + documentForm.securityLevel = String(documentForm.securityLevel) + documentForm.docCategory = String(documentForm.docCategory) + documentForm.docType = String(documentForm.docType) + documentForm.urgencyLevel = String(documentForm.urgencyLevel) + documentForm.docStatus = String(documentForm.docStatus) + + // 鍔犺浇闄勪欢淇℃伅 + if (data.attachments) { + documentForm.attachments = [...data.attachments]; + } else { + documentForm.attachments = []; + } + } else { + // 鏂板妯″紡锛屾竻绌鸿〃鍗� + Object.keys(documentForm).forEach(key => { + documentForm[key] = ""; + }); + documentForm.attachments = []; // 鏂板妯″紡涓嬩篃娓呯┖闄勪欢 + // 璁剧疆榛樿鍊� - 浣跨敤瀛楀吀鏁版嵁鐨勭涓�涓�夐」浣滀负榛樿鍊� + if (document_status.value && document_status.value.length > 0) { + documentForm.docStatus = document_status.value[0].value; + } + if (document_urgency.value && document_urgency.value.length > 0) { + documentForm.urgencyLevel = document_urgency.value[0].value; + } + } +}; + +// 鎻愪氦鍒嗙被琛ㄥ崟 +const submitCategoryForm = () => { + proxy.$refs.categoryFormRef.validate(async (valid) => { + if (valid) { + try { + if (categoryOperationType.value === "addSub") { + // 娣诲姞瀛愬垎绫� + const res = await addCategory({ + category: categoryForm.category, + parentId: categoryForm.parentId + }); + if (res.code === 200) { + ElMessage.success("娣诲姞瀛愬垎绫绘垚鍔�"); + // 閲嶆柊鍔犺浇鍒嗙被鏍� + await initCategoryTree(); + } else { + ElMessage.error(res.msg || "娣诲姞瀛愬垎绫诲け璐�"); + } + } else if (categoryOperationType.value === "edit") { + // 缂栬緫鍒嗙被 + const res = await updateCategory({ + id: currentId.value, + category: categoryForm.category + }); + if (res.code === 200) { + ElMessage.success("缂栬緫鍒嗙被鎴愬姛"); + // 閲嶆柊鍔犺浇鍒嗙被鏍� + await initCategoryTree(); + } else { + ElMessage.error(res.msg || "缂栬緫鍒嗙被澶辫触"); + } + } else { + // 鏂板椤剁骇鍒嗙被 + const res = await addCategory({ + category: categoryForm.category, + parentId: null + }); + if (res.code === 200) { + ElMessage.success("鏂板鍒嗙被鎴愬姛"); + // 閲嶆柊鍔犺浇鍒嗙被鏍� + await initCategoryTree(); + } else { + ElMessage.error(res.msg || "鏂板鍒嗙被澶辫触"); + } + } + + closeCategoryDia(); + } catch (error) { + ElMessage.error("鎿嶄綔澶辫触锛岃閲嶈瘯"); + } + } + }); +}; + +// 鍏抽棴鍒嗙被寮规 +const closeCategoryDia = () => { + proxy.$refs.categoryFormRef.resetFields(); + categoryForm.parentId = ""; + categoryForm.parentName = ""; + categoryDia.value = false; +}; + +// 鍒犻櫎鍒嗙被 +const removeCategory = (node, data) => { + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(async () => { + try { + const res = await deleteCategory([data.id]); + if (res.code === 200) { + ElMessage.success("鍒犻櫎鎴愬姛"); + // 閲嶆柊鍔犺浇鍒嗙被鏍� + await initCategoryTree(); + } else { + ElMessage.error(res.msg || "鍒犻櫎澶辫触"); + } + } catch (error) { + ElMessage.error("鍒犻櫎澶辫触锛岃閲嶈瘯"); + } + }) + .catch(() => { + ElMessage("宸插彇娑�"); + }); +}; + +// 閫夋嫨鍒嗙被 +const handleNodeClick = (val, node, el) => { + // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐� + isShowButton.value = !(val.children && val.children.length > 0); + // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫 + currentId.value = val.id; + currentParentId.value = val.parentId; + + // 娓呯┖閫夋嫨鐘舵�� + selectedRows.value = []; + selectAll.value = false; + isIndeterminate.value = false; + + // 閲嶇疆鍒嗛〉 + pagination.currentPage = 1; + pagination.total = 0; + + // 鍔犺浇鏂囨。鍒楄〃 + if (isShowButton.value) { + loadDocumentList(); + } else { + // 濡傛灉涓嶆槸鍙跺瓙鑺傜偣锛屾竻绌烘枃妗e垪琛� + documentList.value = []; + } +}; + +// 鎻愪氦鏂囨。琛ㄥ崟 +const submitDocumentForm = () => { + proxy.$refs.documentFormRef.validate(async (valid) => { + if (valid) { + try { + // 鏋勫缓鎻愪氦鏁版嵁 + const submitData = { + ...documentForm, + // 璁剧疆褰撳墠閫変腑鐨勫垎绫籌D + documentClassificationId: currentId.value || documentForm.documentClassificationId, + // 娣诲姞闄勪欢淇℃伅 + // attachments: documentForm.attachments + }; + + if (documentOperationType.value === "edit") { + // 缂栬緫妯″紡锛屾洿鏂扮幇鏈夋暟鎹� + const res = await updateDocument(submitData); + if (res.code === 200) { + ElMessage.success("缂栬緫鎴愬姛"); + // 閲嶆柊鍔犺浇鏂囨。鍒楄〃 + await loadDocumentList(); + // 鍒锋柊闄勪欢鍒楄〃 + if (attachmentManagerRef.value && documentForm.id) { + attachmentManagerRef.value.loadAttachmentList(documentForm.id); + } + } else { + ElMessage.error(res.msg || "缂栬緫澶辫触"); + } + } else { + // 鏂板妯″紡锛屾坊鍔犳柊鏁版嵁 + const res = await addDocument(submitData); + if (res.code === 200) { + ElMessage.success("鏂板鎴愬姛"); + // 閲嶆柊鍔犺浇鏂囨。鍒楄〃 + await loadDocumentList(); + // 鍒锋柊闄勪欢鍒楄〃 + if (attachmentManagerRef.value && res.data && res.data.id) { + attachmentManagerRef.value.loadAttachmentList(res.data.id); + } + } else { + ElMessage.error(res.msg || "鏂板澶辫触"); + } + } + closeDocumentDia(); + } catch (error) { + ElMessage.error("鎿嶄綔澶辫触锛岃閲嶈瘯"); + } + } + }); +}; + +// 鍏抽棴鏂囨。寮规 +const closeDocumentDia = () => { + proxy.$refs.documentFormRef.resetFields(); + documentDia.value = false; + // 娓呯┖琛ㄥ崟鏁版嵁 + Object.keys(documentForm).forEach(key => { + documentForm[key] = ""; + }); + documentForm.attachments = []; // 鍏抽棴寮规鏃朵篃娓呯┖闄勪欢 +}; + +// 澶勭悊浣嶇疆閫夋嫨鍙樺寲 +const handleLocationChange = (value) => { + if (value) { + // 妫�鏌ラ�夋嫨鐨勬槸鍚︿负鍙跺瓙鑺傜偣 + const isLeafNode = checkIfLeafNode(locationTree.value, value); + if (!isLeafNode) { + ElMessage.warning("璇烽�夋嫨鏈�搴曞眰鐨勪綅缃紙濡傦細鏌滃眰锛�"); + documentForm.warehouseGoodsShelvesRowcolId = ""; + return; + } + } +}; + +// 妫�鏌ユ槸鍚︿负鍙跺瓙鑺傜偣 +const checkIfLeafNode = (tree, value) => { + for (let item of tree) { + if (item.value === value || item.id === value) { + // 濡傛灉娌℃湁瀛愯妭鐐癸紝鍒欎负鍙跺瓙鑺傜偣 + return !item.children || item.children.length === 0; + } + if (item.children && item.children.length > 0) { + const result = checkIfLeafNode(item.children, value); + if (result !== null) { + return result; + } + } + } + return null; +}; + +// 鍒犻櫎鏂囨。 +const handleDelete = () => { + if (selectedRows.value.length > 0) { + ElMessageBox.confirm(`纭畾瑕佸垹闄ら�変腑鐨� ${selectedRows.value.length} 鏉¤褰曞悧锛焋, "鍒犻櫎鎻愮ず", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(async () => { + try { + const selectedIds = selectedRows.value.map(row => row.id); + const res = await deleteDocument(selectedIds); + if (res.code === 200) { + ElMessage.success("鍒犻櫎鎴愬姛"); + // 閲嶆柊鍔犺浇鏂囨。鍒楄〃 + await loadDocumentList(); + } else { + ElMessage.error(res.msg || "鍒犻櫎澶辫触"); + } + } catch (error) { + ElMessage.error("鍒犻櫎澶辫触锛岃閲嶈瘯"); + } + }) + .catch(() => { + ElMessage("宸插彇娑�"); + }); + } else { + ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁"); + } +}; + +// PIMTable 閫夋嫨鍙樺寲浜嬩欢 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; + + // 鏇存柊鍏ㄩ�夌姸鎬� + const selectedCount = selection.length; + const totalCount = documentList.value.length; + + if (selectedCount === 0) { + selectAll.value = false; + isIndeterminate.value = false; + } else if (selectedCount === totalCount) { + selectAll.value = true; + isIndeterminate.value = false; + } else { + selectAll.value = false; + isIndeterminate.value = true; + } +}; + +// 鍔犺浇鏂囨。鍒楄〃 +const loadDocumentList = async () => { + try { + tableLoading.value = true; + + // 鏋勫缓鏌ヨ鍙傛暟 + const query = { + page: pagination.currentPage, + size: pagination.pageSize, + documentClassificationId:currentId.value + }; + + const res = await getDocumentList(query); + if (res.code === 200) { + documentList.value = res.data.records || []; + pagination.total = res.data.total || 0; + } else { + ElMessage.error(res.msg || "鑾峰彇鏂囨。鍒楄〃澶辫触"); + documentList.value = []; + pagination.total = 0; + } + + // 閲嶇疆閫夋嫨鐘舵�� + selectedRows.value = []; + selectAll.value = false; + isIndeterminate.value = false; + } catch (error) { + ElMessage.error("鑾峰彇鏂囨。鍒楄〃澶辫触锛岃閲嶈瘯"); + documentList.value = []; + pagination.total = 0; + } finally { + tableLoading.value = false; + } +}; + +// 澶勭悊鍒嗛〉鍙樺寲 +const handlePagination = (current, size) => { + pagination.currentPage = current; + pagination.pageSize = size; + loadDocumentList(); +}; + +// 璋冪敤tree杩囨护鏂规硶 +const filterNode = (value, data, node) => { + if (!value) { + return true; + } + let val = value.toLowerCase(); + return chooseNode(val, data, node); +}; + +// 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� +const chooseNode = (value, data, node) => { + if (data.category && data.category.toLowerCase().indexOf(value) !== -1) { + return true; + } + const level = node.level; + if (level === 1) { + return false; + } + let parentData = node.parent; + let index = 0; + while (index < level - 1) { + if (parentData.data.category && parentData.data.category.toLowerCase().indexOf(value) !== -1) { + return true; + } + parentData = parentData.parent; + index++; + } + return false; +}; + +// 鎵撳紑闄勪欢 +const openAttachment = (row) => { + attachmentManagerRef.value.open([], row.id); +}; + +onMounted(() => { + initCategoryTree(); + initLocationTree(); + + // 涓嶅湪鍒濆鍖栨椂鍔犺浇鏂囨。鍒楄〃锛岀瓑寰呯敤鎴烽�夋嫨鍒嗙被鍚庡啀鍔犺浇 +}); +</script> + +<style scoped> +.document-view { + display: flex; + height: 100%; +} + +.left { + width: 380px; + padding: 16px; + background: #ffffff; + border-right: 1px solid #e4e7ed; +} + +.right { + width: calc(100% - 380px); + padding: 16px; + background: #ffffff; +} + +.custom-tree-node { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 14px; + padding-right: 8px; +} + +.tree-node-content { + display: flex; + align-items: center; + height: 100%; +} + +.orange-icon { + color: orange; + font-size: 18px; + margin-right: 8px; +} + +.table-container { + background: #ffffff; + border-radius: 8px; + overflow: hidden; + position: relative; +} + +.add-row { + display: flex; + align-items: center; + gap: 8px; + background-color: #f5f7fa; + cursor: pointer; + transition: background-color 0.2s ease; + padding: 12px 16px; + margin-bottom: 16px; + border-radius: 6px; + border: 1px dashed #d9d9d9; +} + +.add-row:hover { + background-color: #e4e7ed; + border-color: #c0c4cc; +} + +.add-icon { + color: #909399; + font-size: 16px; +} + +.add-row span { + color: #606266; + font-size: 14px; +} + +.empty-data { + text-align: center; + color: #909399; + padding: 40px; + font-size: 14px; +} + +.dialog-footer { + text-align: right; +} + +.operation-column { + position: absolute; + right: 0; + top: 0; + width: 120px; + background: #ffffff; + border-left: 1px solid #e4e7ed; + z-index: 1; + box-shadow: -2px 0 4px rgba(0, 0, 0, 0.1); +} + +.operation-header { + height: 40px; + line-height: 40px; + text-align: center; + background: #fafafa; + border-bottom: 1px solid #e4e7ed; + font-weight: 500; + color: #606266; +} + +.operation-cell { + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-bottom: 1px solid #e4e7ed; +} + +.operation-cell:last-child { + border-bottom: none; +} + +.attachment-section { + width: 100%; +} + +.attachment-list { + margin-bottom: 10px; +} + +.attachment-item { + display: flex; + align-items: center; + padding: 8px 12px; + background-color: #f5f7fa; + border-radius: 4px; + margin-bottom: 8px; +} + +.file-icon { + margin-right: 8px; + color: #409eff; +} + +.file-name { + flex: 1; + color: #606266; + font-size: 14px; +} +</style> diff --git a/src/views/fileManagement/return/index.vue b/src/views/fileManagement/return/index.vue new file mode 100644 index 0000000..80724ea --- /dev/null +++ b/src/views/fileManagement/return/index.vue @@ -0,0 +1,599 @@ +<template> + <div class="app-container return-view"> + <!-- 鏌ヨ鍖哄煙 --> + <div class="search-container"> + <el-form :model="searchForm" :inline="true" class="search-form"> + <!-- <el-form-item label="鍊熼槄鐘舵�侊細"> + <el-select v-model="searchForm.borrowStatus" placeholder="璇烽�夋嫨鍊熼槄鐘舵��" clearable style="width: 150px"> + <el-option label="鍊熼槄" value="鍊熼槄" /> + <el-option label="褰掕繕" value="褰掕繕" /> + </el-select> + </el-form-item> --> + <el-form-item label="鍊熼槄浜猴細"> + <el-input + v-model="searchForm.borrower" + placeholder="璇疯緭鍏ュ�熼槄浜�" + clearable + style="width: 200px" + /> + </el-form-item> + <el-form-item label="褰掕繕浜猴細"> + <el-input + v-model="searchForm.returner" + placeholder="璇疯緭鍏ュ綊杩樹汉" + clearable + style="width: 200px" + /> + </el-form-item> + <el-form-item label="褰掕繕鏃ユ湡鑼冨洿锛�"> + <el-date-picker + v-model="searchForm.dateRange" + type="daterange" + range-separator="鑷�" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + style="width: 300px" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="handleSearch"> + <el-icon><Search /></el-icon> + 鏌ヨ + </el-button> + <el-button @click="handleReset"> + <el-icon><Refresh /></el-icon> + 閲嶇疆 + </el-button> + </el-form-item> + <el-form-item style="margin-left: auto;"> + <el-button type="primary" @click="openReturnDia('add')"> + <el-icon><Plus /></el-icon> + 鏂板褰掕繕 + </el-button> + <el-button + type="danger" + @click="handleBatchDelete" + :disabled="selectedRows.length === 0" + > + <el-icon><Delete /></el-icon> + 鎵归噺鍒犻櫎 ({{ selectedRows.length }}) + </el-button> + </el-form-item> + </el-form> + </div> + + <!-- 琛ㄦ牸鍖哄煙 --> + <div class="table-container"> + <PIMTable + :table-data="returnList" + :column="tableColumns" + :is-selection="true" + :border="true" + :table-loading="tableLoading" + :page="{ + current: pagination.currentPage, + size: pagination.pageSize, + total: pagination.total, + layout: 'total, sizes, prev, pager, next, jumper' + }" + @selection-change="handleSelectionChange" + @pagination="handlePagination" + /> + + <div v-if="returnList.length === 0" class="empty-data"> + 鏆傛棤鏁版嵁 + </div> + </div> + + <!-- 褰掕繕鏂板/缂栬緫瀵硅瘽妗� --> + <el-dialog + v-model="returnDia" + :title="returnOperationType === 'add' ? '鏂板褰掕繕' : '缂栬緫褰掕繕'" + width="800px" + @close="closeReturnDia" + @keydown.enter.prevent + > + <el-form + :model="returnForm" + label-width="140px" + :rules="returnRules" + ref="returnFormRef" + > + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏂囨。锛�" prop="borrowId"> + <el-select v-model="returnForm.borrowId" placeholder="璇烽�夋嫨鏂囨。" style="width: 100%" @change="handleDocumentChange"> + <el-option + v-for="item in documentList" + :key="item.id" + :label="item.docName || item.name" + :value="item.id" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鍊熼槄浜猴細" prop="borrower"> + <el-input v-model="returnForm.borrower" placeholder="鍊熼槄浜哄皢鏍规嵁鏂囨。閫夋嫨鑷姩甯﹀嚭" disabled /> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="褰掕繕浜猴細" prop="returner"> + <el-input v-model="returnForm.returner" placeholder="璇疯緭鍏ュ綊杩樹汉" /> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="褰掕繕鏃ユ湡锛�" prop="returnDate"> + <el-date-picker + v-model="returnForm.returnDate" + type="date" + placeholder="閫夋嫨褰掕繕鏃ユ湡" + style="width: 100%" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + /> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="24"> + <el-form-item label="搴斿綊杩樻棩鏈燂細" prop="dueReturnDate"> + <el-date-picker + v-model="returnForm.dueReturnDate" + type="date" + placeholder="搴斿綊杩樻棩鏈熷皢鏍规嵁鏂囨。閫夋嫨鑷姩甯﹀嚭" + style="width: 100%" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + disabled + /> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="24"> + <el-form-item label="澶囨敞璇存槑锛�" prop="remark"> + <el-input + v-model="returnForm.remark" + type="textarea" + :rows="3" + placeholder="璇疯緭鍏ュ娉ㄨ鏄�" + /> + </el-form-item> + </el-col> + </el-row> + </el-form> + + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitReturnForm">纭</el-button> + <el-button @click="closeReturnDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import { ref, reactive, onMounted, getCurrentInstance } from "vue"; +import { ElMessageBox, ElMessage } from "element-plus"; +import { Search, Refresh, Plus, Delete } from '@element-plus/icons-vue'; +import PIMTable from '@/components/PIMTable/PIMTable.vue'; +import { getReturnListPage, returnDocument, deleteReturn, getDocumentList, updateBorrow, reventUpdate } from '@/api/fileManagement/return'; + +const { proxy } = getCurrentInstance(); + +// 鍝嶅簲寮忔暟鎹� +const returnDia = ref(false); +const returnOperationType = ref(""); +const tableLoading = ref(false); +const returnList = ref([]); +const selectedRows = ref([]); +const documentList = ref([]); // 鏂囨。鍒楄〃 + +// 鍒嗛〉鐩稿叧 +const pagination = reactive({ + currentPage: 1, + pageSize: 10, + total: 0, +}); + +// 鏌ヨ琛ㄥ崟 +const searchForm = reactive({ + borrowStatus: "", + borrower: "", + returner: "", + dateRange: [] +}); + +// 褰掕繕琛ㄥ崟 +const returnForm = reactive({ + id: "", + borrowId: "", + borrower: "", + returner: "", + borrowStatus: "", + returnDate: "", + dueReturnDate: "", + remark: "" +}); + +// 琛ㄥ崟楠岃瘉瑙勫垯 +const returnRules = reactive({ + borrowId: [{ required: true, message: "璇烽�夋嫨鏂囨。", trigger: "change" }], + returner: [{ required: true, message: "璇疯緭鍏ュ綊杩樹汉", trigger: "blur" }], + returnDate: [{ required: true, message: "璇烽�夋嫨褰掕繕鏃ユ湡", trigger: "change" }] +}); + +// 琛ㄦ牸鍒楅厤缃� +const tableColumns = ref([ + { + label: '鏂囨。鍚嶇О', + prop: 'docName', + width: '200', + }, + { label: '鍊熼槄浜�', prop: 'borrower' }, + { label: '褰掕繕浜�', prop: 'returner' }, + { + label: '鍊熼槄鐘舵��', + prop: 'borrowStatus', + dataType: 'tag', + formatData: (params) => { + if (params === null || params === undefined || params === '') return '-'; + return params; + }, + formatType: (params) => { + if (params === '褰掕繕') return 'success'; + if (params === '鍊熼槄') return 'warning'; + return 'info'; + } + }, + { label: '褰掕繕鏃ユ湡', prop: 'returnDate' }, + { label: '搴斿綊杩樻棩鏈�', prop: 'dueReturnDate' }, + { label: '澶囨敞', prop: 'remark', width: '150' }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + fixed: 'right', + width: '150', + operation: [ + { + name: "缂栬緫", + type: "text", + clickFun: (row) => { + openReturnDia('edit', row) + }, + }, + { + name: "鍒犻櫎", + type: "text", + clickFun: (row) => { + handleDelete(row) + }, + }, + ], + } +]); + +// 鍒濆鍖栨暟鎹� +const initData = async () => { + await Promise.all([ + loadDocumentList(), + loadReturnList() + ]); +}; + +// 鍔犺浇鏂囨。鍒楄〃 +const loadDocumentList = async () => { + try { + const res = await getDocumentList(); + if (res.code === 200) { + documentList.value = res.data || []; + } else { + ElMessage.error(res.msg || "鑾峰彇鏂囨。鍒楄〃澶辫触"); + documentList.value = []; + } + } catch (error) { + ElMessage.error("鑾峰彇鏂囨。鍒楄〃澶辫触锛岃閲嶈瘯"); + documentList.value = []; + } +}; + +// 鍔犺浇褰掕繕鍒楄〃 +const loadReturnList = async () => { + try { + tableLoading.value = true; + + // 鏋勫缓鏌ヨ鍙傛暟 + const query = { + page: pagination.currentPage, + size: pagination.pageSize, + borrowStatus: searchForm.borrowStatus || undefined, + borrower: searchForm.borrower || undefined, + returner: searchForm.returner || undefined, + entryDateStart: searchForm.dateRange && searchForm.dateRange.length > 0 ? searchForm.dateRange[0] : undefined, + entryDateEnd: searchForm.dateRange && searchForm.dateRange.length > 1 ? searchForm.dateRange[1] : undefined + }; + + // 绉婚櫎undefined鐨勫弬鏁� + Object.keys(query).forEach(key => { + if (query[key] === undefined) { + delete query[key]; + } + }); + + const res = await getReturnListPage(query); + if (res.code === 200) { + returnList.value = res.data.records || []; + pagination.total = res.data.total || 0; + } else { + ElMessage.error(res.msg || "鑾峰彇褰掕繕鍒楄〃澶辫触"); + returnList.value = []; + pagination.total = 0; + } + + // 閲嶇疆閫夋嫨鐘舵�� + selectedRows.value = []; + } catch (error) { + ElMessage.error("鑾峰彇褰掕繕鍒楄〃澶辫触锛岃閲嶈瘯"); + returnList.value = []; + pagination.total = 0; + } finally { + tableLoading.value = false; + } +}; + +// 鏌ヨ +const handleSearch = () => { + pagination.currentPage = 1; + loadReturnList(); +}; + +// 閲嶇疆鏌ヨ +const handleReset = () => { + searchForm.borrowStatus = ""; + searchForm.borrower = ""; + searchForm.returner = ""; + searchForm.dateRange = []; + pagination.currentPage = 1; + loadReturnList(); + ElMessage.success("鏌ヨ鏉′欢宸查噸缃�"); +}; + +// 鎵撳紑褰掕繕寮规 +const openReturnDia = (type, data) => { + returnOperationType.value = type; + returnDia.value = true; + + if (type === "edit") { + // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹� + Object.assign(returnForm, data); + // 缂栬緫妯″紡涓嬶紝鏂囨。閫夋嫨鍚庤嚜鍔ㄥ~鍏呭�熼槄浜哄拰搴斿綊杩樻棩鏈� + if (returnForm.borrowId) { + handleDocumentChange(returnForm.borrowId); + } + } else { + // 鏂板妯″紡锛屾竻绌鸿〃鍗� + Object.keys(returnForm).forEach(key => { + returnForm[key] = ""; + }); + // 璁剧疆榛樿鐘舵�� + returnForm.borrowStatus = "褰掕繕"; + // 璁剧疆褰撳墠鏃ユ湡涓哄綊杩樻棩鏈� + returnForm.returnDate = new Date().toISOString().split('T')[0]; + } +}; + +// 鍏抽棴褰掕繕寮规 +const closeReturnDia = () => { + proxy.$refs.returnFormRef.resetFields(); + returnDia.value = false; +}; + +// 鎻愪氦褰掕繕琛ㄥ崟 +const submitReturnForm = () => { + proxy.$refs.returnFormRef.validate(async (valid) => { + if (valid) { + try { + if (returnOperationType.value === "edit") { + // 缂栬緫妯″紡锛岃皟鐢ㄥ綊杩樻洿鏂版帴鍙� + const res = await reventUpdate({ + id: returnForm.id, + documentationId: returnForm.documentationId, + borrower: returnForm.borrower, + returner: returnForm.returner, + borrowStatus: returnForm.borrowStatus, + returnDate: returnForm.returnDate, + dueReturnDate: returnForm.dueReturnDate, + remark: returnForm.remark + }); + + if (res.code === 200) { + ElMessage.success("缂栬緫鎴愬姛"); + await loadReturnList(); + closeReturnDia(); + } else { + ElMessage.error(res.msg || "缂栬緫澶辫触"); + } + } else { + // 鏂板妯″紡锛岃皟鐢ㄥ綊杩樻帴鍙� + const res = await returnDocument({ + borrowId: returnForm.borrowId, + borrower: returnForm.borrower, + returner: returnForm.returner, + borrowStatus: returnForm.borrowStatus, + returnDate: returnForm.returnDate, + dueReturnDate: returnForm.dueReturnDate, + remark: returnForm.remark + }); + + if (res.code === 200) { + ElMessage.success("鏂板鎴愬姛"); + await loadReturnList(); + closeReturnDia(); + } else { + ElMessage.error(res.msg || "鏂板澶辫触"); + } + } + } catch (error) { + ElMessage.error("鎿嶄綔澶辫触锛岃閲嶈瘯"); + } + } + }); +}; + +// 鍒犻櫎褰掕繕璁板綍 +const handleDelete = (row) => { + ElMessageBox.confirm( + `纭畾瑕佸垹闄よ繖鏉″綊杩樿褰曞悧锛焋, + "鍒犻櫎鎻愮ず", + { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + } + ).then(async () => { + try { + const res = await deleteReturn([row.id]); + if (res.code === 200) { + ElMessage.success("鍒犻櫎鎴愬姛"); + await loadReturnList(); + } else { + ElMessage.error(res.msg || "鍒犻櫎澶辫触"); + } + } catch (error) { + ElMessage.error("鍒犻櫎澶辫触锛岃閲嶈瘯"); + } + }).catch(() => { + ElMessage.info("宸插彇娑堝垹闄�"); + }); +}; + +// 鎵归噺鍒犻櫎 +const handleBatchDelete = () => { + if (selectedRows.value.length === 0) { + ElMessage.warning("璇烽�夋嫨瑕佸垹闄ょ殑璁板綍"); + return; + } + + ElMessageBox.confirm( + `纭畾瑕佸垹闄ら�変腑鐨� ${selectedRows.value.length} 鏉″綊杩樿褰曞悧锛焋, + "鎵归噺鍒犻櫎鎻愮ず", + { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + } + ).then(async () => { + try { + const selectedIds = selectedRows.value.map(row => row.id); + const res = await deleteReturn(selectedIds); + if (res.code === 200) { + ElMessage.success("鎵归噺鍒犻櫎鎴愬姛"); + await loadReturnList(); + } else { + ElMessage.error(res.msg || "鎵归噺鍒犻櫎澶辫触"); + } + } catch (error) { + ElMessage.error("鎵归噺鍒犻櫎澶辫触锛岃閲嶈瘯"); + } + }).catch(() => { + ElMessage.info("宸插彇娑堝垹闄�"); + }); +}; + +// 閫夋嫨鍙樺寲浜嬩欢 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; + +// 澶勭悊鍒嗛〉鍙樺寲 +const handlePagination = (current, size) => { + pagination.currentPage = current; + pagination.pageSize = size; + loadReturnList(); +}; + +// 澶勭悊鏂囨。閫夋嫨鍙樺寲 +const handleDocumentChange = (documentId) => { + if (documentId) { + // 鏍规嵁閫夋嫨鐨勬枃妗D锛屼粠鏂囨。鍒楄〃涓煡鎵惧搴旂殑鏂囨。淇℃伅 + const selectedDoc = documentList.value.find(doc => doc.id === documentId); + if (selectedDoc) { + // 鑷姩濉厖鍊熼槄浜哄拰搴斿綊杩樻棩鏈� + returnForm.borrower = selectedDoc.borrower || selectedDoc.borrowerName || ''; + returnForm.dueReturnDate = selectedDoc.dueReturnDate || selectedDoc.expectedReturnDate || ''; + } + } else { + // 娓呯┖鐩稿叧瀛楁 + returnForm.borrower = ''; + returnForm.dueReturnDate = ''; + } +}; + +// 鐢熷懡鍛ㄦ湡 +onMounted(() => { + initData(); +}); +</script> + +<style scoped> +.return-view { + padding: 20px; +} + +.search-container { + background: #ffffff; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.search-form { + margin: 0; +} + +.table-container { + background: #ffffff; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.empty-data { + text-align: center; + color: #909399; + padding: 40px; + font-size: 14px; +} + +.dialog-footer { + text-align: right; +} + +:deep(.el-form-item__label) { + font-weight: 500; + color: #303133; +} + +:deep(.el-input__wrapper) { + box-shadow: 0 0 0 1px #dcdfe6 inset; +} + +:deep(.el-input__wrapper:hover) { + box-shadow: 0 0 0 1px #c0c4cc inset; +} + +:deep(.el-input__wrapper.is-focus) { + box-shadow: 0 0 0 1px #409eff inset; +} +</style> diff --git a/vite.config.js b/vite.config.js index b96c1d4..6d04eb7 100644 --- a/vite.config.js +++ b/vite.config.js @@ -8,8 +8,8 @@ const { VITE_APP_ENV } = env; const baseUrl = VITE_APP_ENV == "development" - ? "http://114.132.189.42:8092" // 寮�鍙戠幆澧冨悗绔帴鍙� - : "http://114.132.189.42:8092"; // 鐢熶骇鐜鍚庣鎺ュ彛 + ? "http://192.168.1.138:7788" // 寮�鍙戠幆澧冨悗绔帴鍙� + : "http://114.132.189.42:7003"; // 鐢熶骇鐜鍚庣鎺ュ彛 return { // 閮ㄧ讲鐢熶骇鐜鍜屽紑鍙戠幆澧冧笅鐨刄RL銆� @@ -45,7 +45,7 @@ }, // vite 鐩稿叧閰嶇疆 server: { - port: 80, + port: 8001, host: true, open: true, proxy: { -- Gitblit v1.9.3