From ba01c8bd58bea9acbb98c2097765b939a81b21cd Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期五, 29 八月 2025 17:52:41 +0800 Subject: [PATCH] Merge branch 'refs/heads/dev_huangjin' into dev --- src/views/index.vue | 6 src/components/DynamicTable/index.vue | 402 +++ src/api/lavorissce/ledger.js | 55 src/views/lavorissue/ledger/filesDia.vue | 202 + src/views/lavorissue/statistics/index.vue | 285 ++ src/views/fileManagement/document/attachmentManager.vue | 426 +++ src/views/fileManagement/bookshelf/detail.vue | 110 src/api/fileManagement/borrow.js | 47 src/views/fileManagement/return/index.vue | 595 ++++ src/utils/util.js | 33 src/views/fileManagement/borrow/index.vue | 582 ++++ src/views/fileManagement/statistics/index.vue | 539 ++++ src/views/equipmentManagement/ledger/index.vue | 4 src/views/lavorissue/ledger/index.vue | 300 ++ src/api/fileManagement/bookshelf.js | 128 + src/views/chatHome/chatHomeIndex/MobileChat.vue | 6 src/views/fileManagement/document/index.vue | 1262 ++++++++++ src/permission.js | 95 src/views/example/DynamicTableExample.vue | 354 ++ src/views/fileManagement/bookshelf/index.vue | 688 +++++ src/views/inventoryManagement/index.vue | 309 ++ src/api/fileManagement/document.js | 189 + src/api/fileManagement/statistics.js | 75 src/views/equipmentManagement/deviceInfo/index.vue | 190 + src/views/lavorissue/ledger/Form.vue | 154 + src/api/equipmentManagement/deviceInfo.js | 10 src/views/example/SimpleExample.vue | 135 + src/api/fileManagement/return.js | 54 src/views/lavorissue/ledger/Modal.vue | 70 29 files changed, 7,251 insertions(+), 54 deletions(-) diff --git a/src/api/equipmentManagement/deviceInfo.js b/src/api/equipmentManagement/deviceInfo.js new file mode 100644 index 0000000..d71b713 --- /dev/null +++ b/src/api/equipmentManagement/deviceInfo.js @@ -0,0 +1,10 @@ +import request from "@/utils/request"; + +// 鑾峰彇璁惧鍩烘湰淇℃伅 +export function getDeviceInfo(params) { + return request({ + url: "/device/ledger/scanDevice", + method: "get", + params, + }); +} 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..f3d5f4f --- /dev/null +++ b/src/api/fileManagement/document.js @@ -0,0 +1,189 @@ +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, + }); +} + +// 缁熻鐩稿叧鎺ュ彛 +// 鑾峰彇鎬讳綋缁熻鏁版嵁 +export function getDocumentationOverview() { + return request({ + url: "/documentation/overview", + method: "get", + }); +} + +// 鑾峰彇鍒嗙被缁熻鏁版嵁 +export function getDocumentationCategoryStats() { + return request({ + url: "/documentation/category", + method: "get", + }); +} + +// 鑾峰彇鐘舵�佺粺璁℃暟鎹� +export function getDocumentationStatusStats() { + return request({ + url: "/documentation/status", + method: "get", + }); +} 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/api/fileManagement/statistics.js b/src/api/fileManagement/statistics.js new file mode 100644 index 0000000..d77375c --- /dev/null +++ b/src/api/fileManagement/statistics.js @@ -0,0 +1,75 @@ +import request from "@/utils/request"; + +// 鑾峰彇妗f鎬讳綋缁熻 +export function getDocumentStatistics() { + return request({ + url: "/fileManagement/statistics/overview", + method: "get", + }); +} + +// 鑾峰彇妗f鍒嗙被缁熻 +export function getCategoryStatistics() { + return request({ + url: "/fileManagement/statistics/category", + method: "get", + }); +} + +// 鑾峰彇妗f鐘舵�佺粺璁� +export function getStatusStatistics() { + return request({ + url: "/fileManagement/statistics/status", + method: "get", + }); +} + +// 鑾峰彇妗f鍊熼槄缁熻 +export function getBorrowStatistics() { + return request({ + url: "/fileManagement/statistics/borrow", + method: "get", + }); +} + +// 鑾峰彇妗f骞村害缁熻 +export function getYearStatistics() { + return request({ + url: "/fileManagement/statistics/year", + method: "get", + }); +} + +// 鑾峰彇妗f浣嶇疆缁熻 +export function getLocationStatistics() { + return request({ + url: "/fileManagement/statistics/location", + method: "get", + }); +} + +// 鑾峰彇妗f瓒嬪娍缁熻 +export function getTrendStatistics(params) { + return request({ + url: "/fileManagement/statistics/trend", + method: "get", + params: params, + }); +} + +// 鑾峰彇妗f鍊熼槄鎺掕 +export function getBorrowRanking() { + return request({ + url: "/fileManagement/statistics/borrowRanking", + method: "get", + }); +} + +// 鑾峰彇妗f鍒嗙被璇︽儏缁熻 +export function getCategoryDetailStatistics(categoryId) { + return request({ + url: `/fileManagement/statistics/categoryDetail/${categoryId}`, + method: "get", + }); +} + diff --git a/src/api/lavorissce/ledger.js b/src/api/lavorissce/ledger.js new file mode 100644 index 0000000..f4f710c --- /dev/null +++ b/src/api/lavorissce/ledger.js @@ -0,0 +1,55 @@ +import request from '@/utils/request' + +// 鍒嗛〉鏌ヨ +export function listPage(query) { + return request({ + url: '/lavorIssue/listPage', + method: 'get', + params: query + }) +} + +// 鍒嗛〉鏌ヨ +export function statistics(params) { + return request({ + url: '/lavorIssue/statistics', + method: 'get', + params + }) +} + +export function statisticsList(params) { + return request({ + url: '/lavorIssue/statisticsList', + method: 'get', + params + }) +} + +// 娣诲姞 +export function add(data) { + return request({ + url: '/lavorIssue/add', + method: 'post', + data + }) +} + +// 淇敼 +export function update(data) { + return request({ + url: '/lavorIssue/update', + method: 'post', + data + }) +} + +// 鍒犻櫎 +export function deleteLedger(data) { + return request({ + url: '/lavorIssue/delete', + method: 'delete', + data + }) +} + diff --git a/src/components/DynamicTable/index.vue b/src/components/DynamicTable/index.vue new file mode 100644 index 0000000..9da9a3c --- /dev/null +++ b/src/components/DynamicTable/index.vue @@ -0,0 +1,402 @@ +<template> + <div class="dynamic-table-container"> + <el-table + ref="tableRef" + v-loading="loading" + :data="tableData" + :border="border" + :height="height" + :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" + style="width: 100%" + @selection-change="handleSelectionChange" + @row-click="handleRowClick" + > + <!-- 閫夋嫨鍒� --> + <el-table-column + v-if="showSelection" + align="center" + type="selection" + width="55" + /> + + <!-- 搴忓彿鍒� --> + <el-table-column + v-if="showIndex" + align="center" + label="搴忓彿" + type="index" + width="60" + /> + + <!-- 鍥哄畾鍒楋細閮ㄩ棬 --> + <el-table-column + label="閮ㄩ棬" + prop="department" + width="120" + show-overflow-tooltip + align="center" + /> + + <!-- 鍥哄畾鍒楋細濮撳悕 --> + <el-table-column + label="濮撳悕" + prop="name" + width="100" + show-overflow-tooltip + align="center" + /> + + <!-- 鍥哄畾鍒楋細宸ュ彿 --> + <el-table-column + label="宸ュ彿" + prop="employeeId" + width="100" + show-overflow-tooltip + align="center" + /> + + <!-- 鍔ㄦ�佸垪锛氭牴鎹瓧鍏告覆鏌� --> + <el-table-column + v-for="(dictItem, index) in dynamicColumns" + :key="dictItem.value" + :label="dictItem.label" + :prop="dictItem.value" + :width="dictItem.width || 120" + show-overflow-tooltip + align="center" + > + <template #default="scope"> + <!-- 鏍规嵁瀛楀吀绫诲瀷娓叉煋涓嶅悓鐨勬樉绀烘柟寮� --> + <template v-if="dictItem.renderType === 'tag'"> + <el-tag + :type="getTagType(scope.row[dictItem.value])" + size="small" + > + {{ getDictValueLabel(dictItem.dictType, scope.row[dictItem.value]) }} + </el-tag> + </template> + <template v-else-if="dictItem.renderType === 'select'"> + <el-select + v-model="scope.row[dictItem.value]" + placeholder="璇烽�夋嫨" + size="small" + @change="handleSelectChange(scope.row, dictItem.value, $event)" + > + <el-option + v-for="option in dictItem.options" + :key="option.value" + :label="option.label" + :value="option.value" + /> + </el-select> + </template> + <template v-else-if="dictItem.renderType === 'input'"> + <el-input + v-model="scope.row[dictItem.value]" + size="small" + placeholder="璇疯緭鍏�" + @blur="handleInputChange(scope.row, dictItem.value, $event)" + /> + </template> + <template v-else> + <span>{{ getDictValueLabel(dictItem.dictType, scope.row[dictItem.value]) }}</span> + </template> + </template> + </el-table-column> + + <!-- 鎿嶄綔鍒� --> + <el-table-column + v-if="showActions" + label="鎿嶄綔" + width="150" + align="center" + fixed="right" + > + <template #default="scope"> + <el-button + type="primary" + link + size="small" + @click="handleEdit(scope.row, scope.$index)" + > + 缂栬緫 + </el-button> + <el-button + type="danger" + link + size="small" + @click="handleDelete(scope.row, scope.$index)" + > + 鍒犻櫎 + </el-button> + </template> + </el-table-column> + </el-table> + + <!-- 鍒嗛〉缁勪欢 --> + <div v-if="showPagination" class="pagination-container"> + <el-pagination + v-model:current-page="pagination.current" + v-model:page-size="pagination.size" + :page-sizes="[10, 20, 50, 100]" + :total="pagination.total" + layout="total, sizes, prev, pager, next, jumper" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> + </div> +</template> + +<script setup> +import { ref, computed, onMounted, watch } from 'vue' +import { useDict } from '@/utils/dict' + +// 瀹氫箟缁勪欢灞炴�� +const props = defineProps({ + // 琛ㄦ牸鏁版嵁 + data: { + type: Array, + default: () => [] + }, + // 瀛楀吀绫诲瀷鏁扮粍锛岀敤浜庡姩鎬佺敓鎴愬垪 + dictTypes: { + type: Array, + default: () => [] + }, + // 鏄惁鏄剧ず閫夋嫨鍒� + showSelection: { + type: Boolean, + default: false + }, + // 鏄惁鏄剧ず搴忓彿鍒� + showIndex: { + type: Boolean, + default: true + }, + // 鏄惁鏄剧ず鎿嶄綔鍒� + showActions: { + type: Boolean, + default: false + }, + // 鏄惁鏄剧ず鍒嗛〉 + showPagination: { + type: Boolean, + default: false + }, + // 琛ㄦ牸楂樺害 + height: { + type: [String, Number], + default: 'auto' + }, + // 鏄惁鏄剧ず杈规 + border: { + type: Boolean, + default: true + }, + // 鍔犺浇鐘舵�� + loading: { + type: Boolean, + default: false + }, + // 鍒嗛〉閰嶇疆 + pagination: { + type: Object, + default: () => ({ + current: 1, + size: 10, + total: 0 + }) + } +}) + +// 瀹氫箟浜嬩欢 +const emit = defineEmits([ + 'selection-change', + 'row-click', + 'edit', + 'delete', + 'select-change', + 'input-change', + 'size-change', + 'current-change' +]) + +// 鍝嶅簲寮忔暟鎹� +const tableRef = ref(null) +const tableData = ref([]) + +// 鑾峰彇瀛楀吀鏁版嵁 +const dictData = ref({}) + +// 鍔ㄦ�佸垪閰嶇疆 +const dynamicColumns = computed(() => { + const columns = [] + + props.dictTypes.forEach(dictType => { + const dictItems = dictData.value[dictType] || [] + // 涓烘瘡涓瓧鍏哥被鍨嬪垱寤轰竴涓垪锛岃�屼笉鏄负姣忎釜瀛楀吀椤瑰垱寤哄垪 + if (dictItems.length > 0) { + columns.push({ + label: getDictLabel(dictType), // 鑾峰彇瀛楀吀绫诲瀷鐨勬樉绀哄悕绉� + value: dictType, // 浣跨敤瀛楀吀绫诲瀷浣滀负瀛楁鍚� + width: 120, + renderType: 'tag', // 榛樿浣跨敤鏍囩鏄剧ず + options: dictItems, // 鎻愪緵閫夐」 + dictType: dictType + }) + } + }) + + return columns +}) + +// 鑾峰彇瀛楀吀绫诲瀷鐨勬樉绀哄悕绉� +const getDictLabel = (dictType) => { + const labelMap = { + 'sys_normal_disable': '鐘舵��', + 'sys_user_level': '绾у埆', + 'sys_user_position': '鑱屼綅', + 'sys_yes_no': '鏄惁', + 'sys_user_sex': '鎬у埆', + 'sys_lavor_issue': '鍔冲姟闂' // 娣诲姞鍔冲姟闂瀛楀吀 + } + return labelMap[dictType] || dictType +} + +// 鑾峰彇瀛楀吀鏁版嵁 +const loadDictData = async () => { + try { + const dictPromises = props.dictTypes.map(async (dictType) => { + const { getDicts } = await import('@/api/system/dict/data') + const response = await getDicts(dictType) + return { + type: dictType, + data: response.data.map(item => ({ + label: item.dictLabel, + value: item.dictValue, + elTagType: item.listClass, + elTagClass: item.cssClass + })) + } + }) + + const results = await Promise.all(dictPromises) + results.forEach(result => { + dictData.value[result.type] = result.data + }) + } catch (error) { + console.error('鍔犺浇瀛楀吀鏁版嵁澶辫触:', error) + // 濡傛灉瀛楀吀鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ゆ暟鎹� + props.dictTypes.forEach(dictType => { + if (!dictData.value[dictType]) { + dictData.value[dictType] = [] + } + }) + } +} + +// 鑾峰彇鏍囩绫诲瀷 +const getTagType = (value) => { + // 鏍规嵁鍊艰繑鍥炰笉鍚岀殑鏍囩绫诲瀷 + if (value === '1' || value === 'true' || value === '鏄�') return 'success' + if (value === '0' || value === 'false' || value === '鍚�') return 'danger' + if (value === '2' || value === 'warning') return 'warning' + return 'info' +} + +// 鑾峰彇瀛楀吀鍊肩殑鏍囩 +const getDictValueLabel = (dictType, value) => { + if (!value) return '-' + const dictItems = dictData.value[dictType] || [] + const item = dictItems.find(item => item.value === value) + return item ? item.label : value +} + +// 浜嬩欢澶勭悊鍑芥暟 +const handleSelectionChange = (selection) => { + emit('selection-change', selection) +} + +const handleRowClick = (row, column, event) => { + emit('row-click', row, column, event) +} + +const handleEdit = (row, index) => { + emit('edit', row, index) +} + +const handleDelete = (row, index) => { + emit('delete', row, index) +} + +const handleSelectChange = (row, prop, value) => { + emit('select-change', row, prop, value) +} + +const handleInputChange = (row, prop, event) => { + emit('input-change', row, prop, event.target.value) +} + +const handleSizeChange = (size) => { + emit('size-change', size) +} + +const handleCurrentChange = (current) => { + emit('current-change', current) +} + +// 鐩戝惉鏁版嵁鍙樺寲 +watch(() => props.data, (newData) => { + tableData.value = newData +}, { immediate: true }) + +// 鐩戝惉瀛楀吀绫诲瀷鍙樺寲 +watch(() => props.dictTypes, () => { + loadDictData() +}, { immediate: true }) + +// 缁勪欢鎸傝浇鏃跺姞杞藉瓧鍏告暟鎹� +onMounted(() => { + loadDictData() +}) + +// 鏆撮湶鏂规硶缁欑埗缁勪欢 +defineExpose({ + tableRef, + getSelection: () => tableRef.value?.getSelectionRows() || [], + clearSelection: () => tableRef.value?.clearSelection(), + toggleRowSelection: (row, selected) => tableRef.value?.toggleRowSelection(row, selected), + setCurrentRow: (row) => tableRef.value?.setCurrentRow(row) +}) +</script> + +<style scoped> +.dynamic-table-container { + width: 100%; +} + +.pagination-container { + margin-top: 20px; + display: flex; + justify-content: flex-end; +} + +:deep(.el-table .el-table__header-wrapper th) { + background-color: #F0F1F5 !important; + color: #333333; + font-weight: 600; +} + +:deep(.el-table .el-table__body-wrapper td) { + padding: 8px 0; +} + +:deep(.el-select) { + width: 100%; +} + +:deep(.el-input) { + width: 100%; +} +</style> diff --git a/src/permission.js b/src/permission.js index a7d9f87..5b2566b 100644 --- a/src/permission.js +++ b/src/permission.js @@ -1,69 +1,76 @@ -import router from './router' -import { ElMessage } from 'element-plus' -import NProgress from 'nprogress' -import 'nprogress/nprogress.css' -import { getToken } from '@/utils/auth' -import { isHttp, isPathMatch } from '@/utils/validate' -import { isRelogin } from '@/utils/request' -import useUserStore from '@/store/modules/user' -import useSettingsStore from '@/store/modules/settings' -import usePermissionStore from '@/store/modules/permission' +import router from "./router"; +import { ElMessage } from "element-plus"; +import NProgress from "nprogress"; +import "nprogress/nprogress.css"; +import { getToken } from "@/utils/auth"; +import { isHttp, isPathMatch } from "@/utils/validate"; +import { isRelogin } from "@/utils/request"; +import useUserStore from "@/store/modules/user"; +import useSettingsStore from "@/store/modules/settings"; +import usePermissionStore from "@/store/modules/permission"; -NProgress.configure({ showSpinner: false }) +NProgress.configure({ showSpinner: false }); -const whiteList = ['/login', '/register'] +const whiteList = ["/login", "/register", "/device-info"]; const isWhiteList = (path) => { - return whiteList.some(pattern => isPathMatch(pattern, path)) -} + return whiteList.some((pattern) => isPathMatch(pattern, path)); +}; router.beforeEach((to, from, next) => { - NProgress.start() + NProgress.start(); if (getToken()) { - to.meta.title && useSettingsStore().setTitle(to.meta.title) + to.meta.title && useSettingsStore().setTitle(to.meta.title); /* has token*/ - if (to.path === '/login') { - next({ path: '/' }) - NProgress.done() + if (to.path === "/login") { + next({ path: "/" }); + NProgress.done(); } else if (isWhiteList(to.path)) { - next() + next(); } else { if (useUserStore().roles.length === 0) { - isRelogin.show = true + isRelogin.show = true; // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁宸叉媺鍙栧畬user_info淇℃伅 - useUserStore().getInfo().then(() => { - isRelogin.show = false - usePermissionStore().generateRoutes().then(accessRoutes => { - // 鏍规嵁roles鏉冮檺鐢熸垚鍙闂殑璺敱琛� - accessRoutes.forEach(route => { - if (!isHttp(route.path)) { - router.addRoute(route) // 鍔ㄦ�佹坊鍔犲彲璁块棶璺敱琛� - } - }) - next({ ...to, replace: true }) // hack鏂规硶 纭繚addRoutes宸插畬鎴� + useUserStore() + .getInfo() + .then(() => { + isRelogin.show = false; + usePermissionStore() + .generateRoutes() + .then((accessRoutes) => { + // 鏍规嵁roles鏉冮檺鐢熸垚鍙闂殑璺敱琛� + accessRoutes.forEach((route) => { + if (!isHttp(route.path)) { + router.addRoute(route); // 鍔ㄦ�佹坊鍔犲彲璁块棶璺敱琛� + } + }); + next({ ...to, replace: true }); // hack鏂规硶 纭繚addRoutes宸插畬鎴� + }); }) - }).catch(err => { - useUserStore().logOut().then(() => { - ElMessage.error(err) - next({ path: '/' }) - }) - }) + .catch((err) => { + useUserStore() + .logOut() + .then(() => { + ElMessage.error(err); + next({ path: "/" }); + }); + }); } else { - next() + next(); } } } else { // 娌℃湁token if (isWhiteList(to.path)) { // 鍦ㄥ厤鐧诲綍鐧藉悕鍗曪紝鐩存帴杩涘叆 - next() + next(); } else { - next(`/login?redirect=${to.fullPath}`) // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤� - NProgress.done() + next(`/login?redirect=${to.fullPath}`); // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤� + NProgress.done(); } } -}) +}); router.afterEach(() => { - NProgress.done() -}) + NProgress.done(); +}); diff --git a/src/utils/util.js b/src/utils/util.js index 78846dc..be08cc1 100644 --- a/src/utils/util.js +++ b/src/utils/util.js @@ -1,4 +1,6 @@ //闃叉姈 +import dayjs from "dayjs"; + export function debounce(fn) { console.log(1) let t = null //鍙細鎵ц涓�娆� @@ -86,7 +88,34 @@ 'aplication/zip': 'zpi', } } - + export const deepCopySameProperties = (source, target) =>{ + for (const key in source) { + if (target.hasOwnProperty(key)) { + if (typeof source[key] === 'object' && source[key] !== null && + typeof target[key] === 'object' && target[key] !== null) { + // 閫掑綊澶勭悊瀵硅薄 + deepCopySameProperties(source[key], target[key]); + } else { + // 鍩烘湰绫诲瀷鐩存帴璧嬪�� + target[key] = source[key]; + } + } + } + return target; +} export function filterArr(arr) { return arr.filter(item => item.flag !== false); - } \ No newline at end of file + } + + export function getCurrentMonth () { + let month = dayjs().month() + 1 + if (month <= 3) { + return '1'; + } else if (month <= 6) { + return '2'; + } else if (month <= 9) { + return '3'; + } else if (month <= 12) { + return '4'; + } +} \ No newline at end of file diff --git a/src/views/chatHome/chatHomeIndex/MobileChat.vue b/src/views/chatHome/chatHomeIndex/MobileChat.vue index 5b06e76..adeb5e7 100644 --- a/src/views/chatHome/chatHomeIndex/MobileChat.vue +++ b/src/views/chatHome/chatHomeIndex/MobileChat.vue @@ -14,7 +14,7 @@ <span class="flash_cursor"></span> </template> <template v-else> - <pre>{{ item.msg }}</pre> + <pre style="font-family: none;">{{ item.msg }}</pre> </template> </div> <div class="chat-img" v-if="item.chatType == 1"> @@ -140,7 +140,7 @@ } chatList.value.push(chatMsg) let chatGPT = { - headImg: headPortrait, + headImg: chatGPTHeadImg, name: '灏忔櫤', time: new Date().toLocaleTimeString(), msg: "", @@ -185,7 +185,7 @@ uid: '1002' }) chatList.value.push({ - headImg: chatGPTHeadImg, + headImg: headPortrait, name: '鍗ч緳', time: new Date().toLocaleTimeString(), msg: route.query.keyWord, diff --git a/src/views/equipmentManagement/deviceInfo/index.vue b/src/views/equipmentManagement/deviceInfo/index.vue new file mode 100644 index 0000000..de162cc --- /dev/null +++ b/src/views/equipmentManagement/deviceInfo/index.vue @@ -0,0 +1,190 @@ +<template> + <div class="device-info-container"> + <div class="page-header"> + <h1>璁惧淇℃伅</h1> + <div class="device-status" :class="deviceStatusClass"> + {{ deviceStatusText }} + </div> + </div> + + <div class="info-card"> + <div class="card-header">鍩烘湰淇℃伅</div> + <div class="card-content"> + <div class="info-row"> + <span class="label">璁惧鍚嶇О锛�</span> + <span class="value">{{ deviceInfo.deviceName }}</span> + </div> + <div class="info-row"> + <span class="label">瑙勬牸鍨嬪彿锛�</span> + <span class="value">{{ deviceInfo.deviceModel }}</span> + </div> + <div class="info-row"> + <span class="label">鐢熶骇鍘傚锛�</span> + <span class="value">{{ deviceInfo.supplierName }}</span> + </div> + <div class="info-row"> + <span class="label">鍗曚綅锛�</span> + <span class="value">{{ deviceInfo.unit }}</span> + </div> + </div> + </div> + + <div class="info-card"> + <div class="card-header">缁存姢淇℃伅</div> + <div class="card-content"> + <div class="maintenance-info"> + <div class="maintenance-item"> + <span class="label">鏈�鍚庣淮鎶わ細</span> + <span class="value">{{ deviceInfo.updateTime }}</span> + </div> + <div class="maintenance-item"> + <span class="label">涓嬫缁存姢锛�</span> + <span class="value">{{ deviceInfo.createTime }}</span> + </div> + <div class="maintenance-item"> + <span class="label">缁存姢鐘舵�侊細</span> + <span class="value status-normal">{{ deviceInfo.statusText }}</span> + </div> + </div> + </div> + </div> + </div> +</template> + +<script setup> +import { ref, reactive, onMounted, computed } from 'vue' +import { useRoute } from 'vue-router' +import { ElMessage } from 'element-plus' +import { + getDeviceInfo, +} from '@/api/equipmentManagement/deviceInfo' + +const route = useRoute() + +const deviceInfo = reactive({ + deviceName: '', + deviceModel: '', + supplierName: '', + unit: '', + statusText:'姝e父', + updateTime:'', + createTime:'' +}) + +const deviceStatusClass = computed(() => { + return 'status-normal' +}) + +const deviceStatusText = computed(() => { + return '姝e父' +}) + +const fetchDeviceInfo = async (deviceId) => { + try { + // 鑾峰彇璁惧淇℃伅 + const deviceResponse = await getDeviceInfo({id:deviceId}) + if (deviceResponse.code === 200) { + Object.assign(deviceInfo, deviceResponse.data) + } + + + } catch (error) { + + ElMessage.warning('浣跨敤妯℃嫙鏁版嵁锛屽疄闄匒PI璋冪敤澶辫触') + } +} + +onMounted(() => { + const deviceId = route.query.deviceId || route.params.deviceId || '' + fetchDeviceInfo(deviceId) +}) +</script> + +<style scoped> +.device-info-container { + min-height: 100vh; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + padding: 20px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +.page-header { + background: rgba(255, 255, 255, 0.95); + border-radius: 16px; + padding: 20px; + margin-bottom: 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.page-header h1 { + margin: 0; + color: #2c3e50; + font-size: 24px; +} + +.device-status { + padding: 8px 16px; + border-radius: 20px; + font-size: 14px; + color: white; + background: #52c41a; +} + +.info-card { + background: rgba(255, 255, 255, 0.95); + border-radius: 16px; + margin-bottom: 20px; + overflow: hidden; +} + +.card-header { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 16px 20px; + font-weight: 500; +} + +.card-content { + padding: 20px; +} + +.info-row, .maintenance-item { + display: flex; + margin-bottom: 12px; + align-items: center; +} + +.label { + width: 100px; + color: #666; + font-size: 14px; +} + +.value { + flex: 1; + color: #2c3e50; + font-weight: 500; +} + +.status-normal { + color: #52c41a; +} + + + +@media (max-width: 768px) { + .device-info-container { + padding: 16px; + } + + .page-header h1 { + font-size: 20px; + } + + .label { + width: 80px; + } +} +</style> diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue index 428a6d9..28e5ec8 100644 --- a/src/views/equipmentManagement/ledger/index.vue +++ b/src/views/equipmentManagement/ledger/index.vue @@ -275,8 +275,8 @@ }; const showQRCode = async (row) => { - // 浣犲彲浠ヨ嚜瀹氫箟浜岀淮鐮佸唴瀹癸紝姣斿 row.id 鎴� row.deviceName - const qrContent = JSON.stringify(row); // 鎴� `${row.id}` + // 鐩存帴浣跨敤URL锛屼笉瑕佺敤JSON.stringify鍖呰 + const qrContent = proxy.javaApi + '/device-info?deviceId=' + row.id; qrCodeUrl.value = await QRCode.toDataURL(qrContent); qrRowData.value = row; qrDialogVisible.value = true; diff --git a/src/views/example/DynamicTableExample.vue b/src/views/example/DynamicTableExample.vue new file mode 100644 index 0000000..038cd43 --- /dev/null +++ b/src/views/example/DynamicTableExample.vue @@ -0,0 +1,354 @@ +<template> + <div class="app-container"> + <div class="search-form"> + <el-form :inline="true" :model="searchForm"> + <el-form-item label="閮ㄩ棬"> + <el-input + v-model="searchForm.department" + placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" + clearable + style="width: 200px" + /> + </el-form-item> + <el-form-item label="濮撳悕"> + <el-input + v-model="searchForm.name" + placeholder="璇疯緭鍏ュ鍚�" + clearable + style="width: 200px" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="handleSearch">鎼滅储</el-button> + <el-button @click="handleReset">閲嶇疆</el-button> + <el-button type="success" @click="handleAdd">鏂板</el-button> + </el-form-item> + </el-form> + </div> + + <div class="table-container"> + <DynamicTable + ref="dynamicTableRef" + :data="tableData" + :dict-types="dictTypes" + :loading="loading" + :show-selection="true" + :show-actions="true" + :show-pagination="true" + :pagination="pagination" + height="calc(100vh - 280px)" + @selection-change="handleSelectionChange" + @edit="handleEdit" + @delete="handleDelete" + @select-change="handleSelectChange" + @input-change="handleInputChange" + @size-change="handleSizeChange" + @current-change="handleCurrentChange" + /> + </div> + + <!-- 鏂板/缂栬緫瀵硅瘽妗� --> + <el-dialog + v-model="dialogVisible" + :title="dialogTitle" + width="600px" + append-to-body + > + <el-form + ref="formRef" + :model="form" + :rules="rules" + label-width="100px" + > + <el-form-item label="閮ㄩ棬" prop="department"> + <el-input v-model="form.department" placeholder="璇疯緭鍏ラ儴闂�" /> + </el-form-item> + <el-form-item label="濮撳悕" prop="name"> + <el-input v-model="form.name" placeholder="璇疯緭鍏ュ鍚�" /> + </el-form-item> + <el-form-item label="宸ュ彿" prop="employeeId"> + <el-input v-model="form.employeeId" placeholder="璇疯緭鍏ュ伐鍙�" /> + </el-form-item> + + <!-- 鍔ㄦ�佽〃鍗曢」锛氭牴鎹瓧鍏哥敓鎴� --> + <el-form-item + v-for="dictItem in dynamicFormItems" + :key="dictItem.value" + :label="dictItem.label" + :prop="dictItem.value" + > + <el-select + v-model="form[dictItem.value]" + placeholder="璇烽�夋嫨" + style="width: 100%" + > + <el-option + v-for="option in dictItem.options" + :key="option.value" + :label="option.label" + :value="option.value" + /> + </el-select> + </el-form-item> + </el-form> + + <template #footer> + <div class="dialog-footer"> + <el-button @click="dialogVisible = false">鍙栨秷</el-button> + <el-button type="primary" @click="handleSubmit">纭畾</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup> +import { ref, reactive, computed, onMounted } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' +import DynamicTable from '@/components/DynamicTable/index.vue' + +// 鍝嶅簲寮忔暟鎹� +const loading = ref(false) +const dialogVisible = ref(false) +const dialogTitle = ref('') +const editIndex = ref(-1) +const selectedRows = ref([]) + +// 鎼滅储琛ㄥ崟 +const searchForm = reactive({ + department: '', + name: '' +}) + +// 琛ㄦ牸鏁版嵁 +const tableData = ref([ + { + id: 1, + department: '鎶�鏈儴', + name: '寮犱笁', + employeeId: 'EMP001', + status: '1', + level: '2', + position: '1' + }, + { + id: 2, + department: '浜轰簨閮�', + name: '鏉庡洓', + employeeId: 'EMP002', + status: '0', + level: '1', + position: '2' + }, + { + id: 3, + department: '璐㈠姟閮�', + name: '鐜嬩簲', + employeeId: 'EMP003', + status: '1', + level: '3', + position: '1' + } +]) + +// 瀛楀吀绫诲瀷閰嶇疆 +const dictTypes = ref([ + 'sys_normal_disable', // 鐘舵�佸瓧鍏� + 'sys_user_level', // 绾у埆瀛楀吀 + 'sys_user_position' // 鑱屼綅瀛楀吀 +]) + +// 鍒嗛〉閰嶇疆 +const pagination = reactive({ + current: 1, + size: 10, + total: 0 +}) + +// 琛ㄥ崟鏁版嵁 +const form = reactive({ + department: '', + name: '', + employeeId: '', + status: '', + level: '', + position: '' +}) + +// 琛ㄥ崟楠岃瘉瑙勫垯 +const rules = { + department: [ + { required: true, message: '璇疯緭鍏ラ儴闂�', trigger: 'blur' } + ], + name: [ + { required: true, message: '璇疯緭鍏ュ鍚�', trigger: 'blur' } + ], + employeeId: [ + { required: true, message: '璇疯緭鍏ュ伐鍙�', trigger: 'blur' } + ] +} + +// 鍔ㄦ�佽〃鍗曢」 +const dynamicFormItems = computed(() => { + // 杩欓噷鍙互鏍规嵁瀛楀吀鏁版嵁鍔ㄦ�佺敓鎴愯〃鍗曢」 + return [ + { + label: '鐘舵��', + value: 'status', + options: [ + { label: '鍚敤', value: '1' }, + { label: '绂佺敤', value: '0' } + ] + }, + { + label: '绾у埆', + value: 'level', + options: [ + { label: '鍒濈骇', value: '1' }, + { label: '涓骇', value: '2' }, + { label: '楂樼骇', value: '3' } + ] + }, + { + label: '鑱屼綅', + value: 'position', + options: [ + { label: '鍛樺伐', value: '1' }, + { label: '涓荤', value: '2' }, + { label: '缁忕悊', value: '3' } + ] + } + ] +}) + +// 缁勪欢寮曠敤 +const dynamicTableRef = ref(null) +const formRef = ref(null) + +// 浜嬩欢澶勭悊鍑芥暟 +const handleSearch = () => { + // 瀹炵幇鎼滅储閫昏緫 + console.log('鎼滅储鏉′欢:', searchForm) + ElMessage.success('鎼滅储鍔熻兘寰呭疄鐜�') +} + +const handleReset = () => { + searchForm.department = '' + searchForm.name = '' +} + +const handleAdd = () => { + dialogTitle.value = '鏂板鍛樺伐' + editIndex.value = -1 + resetForm() + dialogVisible.value = true +} + +const handleEdit = (row, index) => { + dialogTitle.value = '缂栬緫鍛樺伐' + editIndex.value = index + Object.assign(form, row) + dialogVisible.value = true +} + +const handleDelete = async (row, index) => { + try { + await ElMessageBox.confirm('纭畾瑕佸垹闄よ繖鏉¤褰曞悧锛�', '鎻愮ず', { + type: 'warning' + }) + + tableData.value.splice(index, 1) + ElMessage.success('鍒犻櫎鎴愬姛') + } catch (error) { + // 鐢ㄦ埛鍙栨秷鍒犻櫎 + } +} + +const handleSelectionChange = (selection) => { + selectedRows.value = selection +} + +const handleSelectChange = (row, prop, value) => { + console.log('閫夋嫨鍙樺寲:', row, prop, value) + // 鍙互鍦ㄨ繖閲屽鐞嗘暟鎹洿鏂伴�昏緫 +} + +const handleInputChange = (row, prop, value) => { + console.log('杈撳叆鍙樺寲:', row, prop, value) + // 鍙互鍦ㄨ繖閲屽鐞嗘暟鎹洿鏂伴�昏緫 +} + +const handleSizeChange = (size) => { + pagination.size = size + // 閲嶆柊鍔犺浇鏁版嵁 +} + +const handleCurrentChange = (current) => { + pagination.current = current + // 閲嶆柊鍔犺浇鏁版嵁 +} + +const handleSubmit = async () => { + try { + await formRef.value.validate() + + if (editIndex.value === -1) { + // 鏂板 + const newRow = { + id: Date.now(), + ...form + } + tableData.value.push(newRow) + ElMessage.success('鏂板鎴愬姛') + } else { + // 缂栬緫 + Object.assign(tableData.value[editIndex.value], form) + ElMessage.success('缂栬緫鎴愬姛') + } + + dialogVisible.value = false + } catch (error) { + console.error('琛ㄥ崟楠岃瘉澶辫触:', error) + } +} + +const resetForm = () => { + Object.assign(form, { + department: '', + name: '', + employeeId: '', + status: '', + level: '', + position: '' + }) + formRef.value?.resetFields() +} + +// 缁勪欢鎸傝浇鏃跺垵濮嬪寲鏁版嵁 +onMounted(() => { + pagination.total = tableData.value.length +}) +</script> + +<style scoped> +.app-container { + padding: 20px; +} + +.search-form { + margin-bottom: 20px; + padding: 20px; + background-color: #f5f5f5; + border-radius: 4px; +} + +.table-container { + background-color: #fff; + border-radius: 4px; + padding: 20px; +} + +.dialog-footer { + text-align: right; +} +</style> diff --git a/src/views/example/SimpleExample.vue b/src/views/example/SimpleExample.vue new file mode 100644 index 0000000..fb528eb --- /dev/null +++ b/src/views/example/SimpleExample.vue @@ -0,0 +1,135 @@ +<template> + <div class="app-container"> + <!-- 绠�鍗曠殑鎼滅储鍖哄煙 --> + <el-card class="search-card"> + <el-form :inline="true"> + <el-form-item label="閮ㄩ棬"> + <el-input v-model="searchForm.department" placeholder="璇疯緭鍏ラ儴闂�" clearable /> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="handleSearch">鎼滅储</el-button> + <el-button @click="handleReset">閲嶇疆</el-button> + </el-form-item> + </el-form> + </el-card> + + <!-- 鍔ㄦ�佽〃鏍� --> + <el-card class="table-card"> + <template #header> + <div class="card-header"> + <span>鍛樺伐淇℃伅琛�</span> + <el-button type="primary" size="small" @click="handleAdd">鏂板鍛樺伐</el-button> + </div> + </template> + + <DynamicTable + :data="tableData" + :dict-types="dictTypes" + :loading="loading" + :show-selection="true" + :show-actions="true" + height="400px" + @selection-change="handleSelectionChange" + @edit="handleEdit" + @delete="handleDelete" + /> + </el-card> + </div> +</template> + +<script setup> +import { ref, reactive } from 'vue' +import { ElMessage } from 'element-plus' +import DynamicTable from '@/components/DynamicTable/index.vue' + +// 鎼滅储琛ㄥ崟 +const searchForm = reactive({ + department: '' +}) + +// 琛ㄦ牸鏁版嵁 +const tableData = ref([ + { + id: 1, + department: '鎶�鏈儴', + name: '寮犱笁', + employeeId: 'EMP001', + sys_normal_disable: '1', // 鐘舵�� + sys_user_level: '2', // 绾у埆 + sys_user_position: '1' // 鑱屼綅 + }, + { + id: 2, + department: '浜轰簨閮�', + name: '鏉庡洓', + employeeId: 'EMP002', + sys_normal_disable: '0', // 鐘舵�� + sys_user_level: '1', // 绾у埆 + sys_user_position: '2' // 鑱屼綅 + } +]) + +// 瀛楀吀绫诲瀷 +const dictTypes = ref([ + 'sys_normal_disable', // 鐘舵�侊細鍚敤/绂佺敤 + 'sys_user_level', // 绾у埆锛氬垵绾�/涓骇/楂樼骇 + 'sys_user_position' // 鑱屼綅锛氬憳宸�/涓荤/缁忕悊 +]) + +// 鍔犺浇鐘舵�� +const loading = ref(false) + +// 浜嬩欢澶勭悊 +const handleSearch = () => { + loading.value = true + // 妯℃嫙鎼滅储 + setTimeout(() => { + loading.value = false + ElMessage.success('鎼滅储瀹屾垚') + }, 1000) +} + +const handleReset = () => { + searchForm.department = '' +} + +const handleAdd = () => { + ElMessage.info('鏂板鍔熻兘寰呭疄鐜�') +} + +const handleSelectionChange = (selection) => { + console.log('閫変腑鐨勮:', selection) +} + +const handleEdit = (row, index) => { + ElMessage.info(`缂栬緫绗�${index + 1}琛屾暟鎹甡) +} + +const handleDelete = (row, index) => { + ElMessage.warning(`鍒犻櫎绗�${index + 1}琛屾暟鎹甡) +} +</script> + +<style scoped> +.app-container { + padding: 20px; +} + +.search-card { + margin-bottom: 20px; +} + +.table-card { + margin-bottom: 20px; +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +:deep(.el-form-item) { + margin-bottom: 0; +} +</style> 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..6875571 --- /dev/null +++ b/src/views/fileManagement/borrow/index.vue @@ -0,0 +1,582 @@ +<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: 'docName', + width: '200', + }, + { 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 = async (type, data) => { + // 鍏堝埛鏂版枃妗e垪琛� + await loadDocumentList(); + + 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..7cf352c --- /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 = true; + // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫 + 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..a95c8af --- /dev/null +++ b/src/views/fileManagement/return/index.vue @@ -0,0 +1,595 @@ +<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> + + <!-- 褰掕繕鏂板/缂栬緫瀵硅瘽妗� --> + <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/src/views/fileManagement/statistics/index.vue b/src/views/fileManagement/statistics/index.vue new file mode 100644 index 0000000..42b81e4 --- /dev/null +++ b/src/views/fileManagement/statistics/index.vue @@ -0,0 +1,539 @@ +<template> + <div class="app-container statistics-container"> + + <!-- 鎬讳綋缁熻鍗$墖 --> + <el-row :gutter="20" class="statistics-cards"> + <el-col :span="6" v-for="(item, index) in overviewData" :key="index"> + <el-card class="statistics-card" :class="item.type"> + <div class="card-content"> + <div class="card-icon"> + <el-icon :size="32"> + <component :is="item.icon" /> + </el-icon> + </div> + <div class="card-info"> + <div class="card-number"> + <el-skeleton-item v-if="loading" variant="text" style="width: 60px; height: 32px;" /> + <span v-else>{{ item.value }}</span> + </div> + <div class="card-label">{{ item.label }}</div> + </div> + </div> + </el-card> + </el-col> + </el-row> + + <!-- 鍥捐〃鍖哄煙 --> + <el-row :gutter="20" class="charts-section"> + <el-col :span="12"> + <el-card class="chart-card"> + <template #header> + <div class="card-header"> + <span>妗f鍒嗙被缁熻</span> + </div> + </template> + <div class="chart-container"> + <div ref="categoryChartRef" class="chart"></div> + </div> + </el-card> + </el-col> + + <el-col :span="12"> + <el-card class="chart-card"> + <template #header> + <div class="card-header"> + <span>妗f鐘舵�佺粺璁�</span> + </div> + </template> + <div class="chart-container"> + <div ref="statusChartRef" class="chart"></div> + </div> + </el-card> + </el-col> + </el-row> + </div> +</template> + +<script setup> +import { ref, onMounted, nextTick, onUnmounted } from "vue"; +import { ElMessage } from "element-plus"; +import { Refresh } from "@element-plus/icons-vue"; +import * as echarts from "echarts"; +import { + getDocumentationOverview, + getDocumentationCategoryStats, + getDocumentationStatusStats +} from "@/api/fileManagement/document"; +import { + Document, + Folder, + Tickets, + Calendar +} from "@element-plus/icons-vue"; + +// 鍝嶅簲寮忔暟鎹� +const overviewData = ref([ + { + label: "鎬绘。妗堟暟", + value: 0, + icon: "Document", + type: "primary", + }, + { + label: "鍒嗙被鏁伴噺", + value: 0, + icon: "Folder", + type: "success", + }, + { + label: "鍊熷嚭妗f", + value: 0, + icon: "Tickets", + type: "warning", + }, + { + label: "鏈湀鏂板", + value: 0, + icon: "Calendar", + type: "info", + }, +]); + +const categoryChartRef = ref(null); +const statusChartRef = ref(null); + +// 鍥捐〃瀹炰緥 +let categoryChart = null; +let statusChart = null; + +// 鍔犺浇鐘舵�� +const loading = ref(false); +const autoRefreshInterval = ref(null); + +// 鑷姩鍒锋柊寮�鍏� +const autoRefreshEnabled = ref(true); + +// 鑷姩鍒锋柊闂撮殧锛�5鍒嗛挓锛� +const AUTO_REFRESH_INTERVAL = 5 * 60 * 1000; + +// 鍚姩鑷姩鍒锋柊 +const startAutoRefresh = () => { + if (autoRefreshInterval.value) { + clearInterval(autoRefreshInterval.value); + } + if (autoRefreshEnabled.value) { + autoRefreshInterval.value = setInterval(() => { + refreshData(); + }, AUTO_REFRESH_INTERVAL); + } +}; + +// 鍋滄鑷姩鍒锋柊 +const stopAutoRefresh = () => { + if (autoRefreshInterval.value) { + clearInterval(autoRefreshInterval.value); + autoRefreshInterval.value = null; + } +}; + +// 鍒囨崲鑷姩鍒锋柊鐘舵�� +const toggleAutoRefresh = (value) => { + if (value) { + startAutoRefresh(); + } else { + stopAutoRefresh(); + } +}; + +// 鍔犺浇鎬讳綋缁熻鏁版嵁 +const loadOverviewData = async () => { + try { + const response = await getDocumentationOverview(); + if (response.code === 200) { + const data = response.data; + overviewData.value[0].value = data.totalDocsCount || 0; + overviewData.value[1].value = data.categoryNumCount || 0; + overviewData.value[2].value = data.borrowedDocsCount || 0; + overviewData.value[3].value = data.monthlyAddedDocsCount || 0; + } + } catch (error) { + console.error('鍔犺浇鎬讳綋缁熻鏁版嵁澶辫触:', error); + ElMessage.error('鍔犺浇鎬讳綋缁熻鏁版嵁澶辫触'); + } +}; + +// 鍔犺浇鍒嗙被缁熻鏁版嵁 +const loadCategoryData = async () => { + try { + const response = await getDocumentationCategoryStats(); + if (response.code === 200) { + renderCategoryChart(response.data); + } + } catch (error) { + console.error('鍔犺浇鍒嗙被缁熻鏁版嵁澶辫触:', error); + ElMessage.error('鍔犺浇鍒嗙被缁熻鏁版嵁澶辫触'); + } +}; + +// 鍔犺浇鐘舵�佺粺璁℃暟鎹� +const loadStatusData = async () => { + try { + const response = await getDocumentationStatusStats(); + if (response.code === 200) { + renderStatusChart(response.data); + } + } catch (error) { + console.error('鍔犺浇鐘舵�佺粺璁℃暟鎹け璐�:', error); + ElMessage.error('鍔犺浇鐘舵�佺粺璁℃暟鎹け璐�'); + } +}; + +// 鍒锋柊鏁版嵁 +const refreshData = async () => { + loading.value = true; + try { + await Promise.all([ + loadOverviewData(), + loadCategoryData(), + loadStatusData() + ]); + ElMessage.success('鏁版嵁鍒锋柊鎴愬姛'); + } catch (error) { + console.error('鍒锋柊鏁版嵁澶辫触:', error); + ElMessage.error('鍒锋柊鏁版嵁澶辫触'); + } finally { + loading.value = false; + } +}; + +// 鍒濆鍖栧浘琛� +const initCharts = () => { + // 寤惰繜鍒濆鍖栵紝纭繚DOM鍏冪礌宸茬粡娓叉煋 + setTimeout(() => { + if (categoryChartRef.value) { + categoryChart = echarts.init(categoryChartRef.value); + } + + if (statusChartRef.value) { + statusChart = echarts.init(statusChartRef.value); + } + + // 鍒濆鍖栧畬鎴愬悗鍔犺浇鏁版嵁 + loadCategoryData(); + loadStatusData(); + }, 300); +}; + +// 娓叉煋鍒嗙被缁熻鍥捐〃 +const renderCategoryChart = (data) => { + if (!categoryChart) return; + let newData = data.map(item => { + return { + name: item.category, + value: item.count + } + }) + + const option = { + title: { + text: "妗f鍒嗙被鍒嗗竷", + left: "center", + textStyle: { + fontSize: 16, + fontWeight: "normal", + }, + }, + tooltip: { + trigger: "item", + formatter: "{a} <br/>{b}: {c} ({d}%)", + }, + legend: { + orient: "vertical", + left: "left", + top: "middle", + }, + series: [ + { + name: "妗f鏁伴噺", + type: "pie", + radius: ["40%", "70%"], + center: ["60%", "50%"], + data: newData || [ + { name: "鎶�鏈枃妗�", value: 450 }, + { name: "绠$悊鏂囨。", value: 320 }, + { name: "璐㈠姟鏂囨。", value: 280 }, + { name: "浜轰簨鏂囨。", value: 200 }, + ], + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: "rgba(0, 0, 0, 0.5)", + }, + }, + }, + ], + }; + + try { + categoryChart.setOption(option); + } catch (error) { + console.error('鍒嗙被鍥捐〃娓叉煋澶辫触:', error); + } +}; + +// 娓叉煋鐘舵�佺粺璁″浘琛� +const renderStatusChart = (data) => { + if (!statusChart) return; + let newData = data.map(item => { + return { + name: item.docStatus, + value: item.count + } + }) + const option = { + title: { + text: "妗f鐘舵�佸垎甯�", + left: "center", + textStyle: { + fontSize: 16, + fontWeight: "normal", + }, + }, + tooltip: { + trigger: "item", + formatter: "{a} <br/>{b}: {c} ({d}%)", + }, + legend: { + orient: "vertical", + left: "left", + top: "middle", + }, + series: [ + { + name: "妗f鏁伴噺", + type: "pie", + radius: ["40%", "70%"], + center: ["60%", "50%"], + roseType: false, + data: newData || [ + { name: "姝e父", value: 1150, itemStyle: { color: "#67C23A" } }, + { name: "鍊熷嚭", value: 89, itemStyle: { color: "#E6A23C" } }, + { name: "涓㈠け", value: 8, itemStyle: { color: "#F56C6C" } }, + { name: "鎹熷潖", value: 4, itemStyle: { color: "#909399" } }, + ], + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: "rgba(0, 0, 0, 0.5)", + }, + }, + }, + ], + }; + + try { + statusChart.setOption(option); + } catch (error) { + console.error('鐘舵�佸浘琛ㄦ覆鏌撳け璐�:', error); + } +}; + +onMounted(() => { + loadOverviewData(); + initCharts(); + startAutoRefresh(); +}); + +// 缁勪欢鍗歌浇鏃舵竻鐞嗗畾鏃跺櫒 +onUnmounted(() => { + stopAutoRefresh(); +}); +</script> + +<style scoped> +.statistics-container { + padding: 20px; + background-color: #f5f7fa; + min-height: 100vh; +} + +.page-header { + text-align: center; + margin-bottom: 30px; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 12px; + color: white; +} + +.page-header h2 { + color: white; + margin-bottom: 10px; + font-size: 28px; + font-weight: 600; +} + +.page-header p { + color: rgba(255, 255, 255, 0.9); + font-size: 14px; + margin: 0 0 15px 0; +} + +.header-controls { + display: flex; + justify-content: center; + align-items: center; + margin-top: 10px; + gap: 20px; +} + +.refresh-btn { + margin-left: 20px; +} + +.statistics-cards { + margin-bottom: 30px; +} + +.statistics-card { + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + border: none; + overflow: hidden; +} + +.statistics-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); +} + +.statistics-card.primary { + border-left: 4px solid #409EFF; + background: linear-gradient(135deg, #409EFF 0%, #36a3f7 100%); +} + +.statistics-card.success { + border-left: 4px solid #67C23A; + background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%); +} + +.statistics-card.warning { + border-left: 4px solid #E6A23C; + background: linear-gradient(135deg, #E6A23C 0%, #ebb563 100%); +} + +.statistics-card.info { + border-left: 4px solid #909399; + background: linear-gradient(135deg, #909399 0%, #a6a9ad 100%); +} + +.card-content { + display: flex; + align-items: center; + padding: 20px; +} + +.card-icon { + margin-right: 20px; + color: white; +} + +.card-info { + flex: 1; +} + +.card-number { + font-size: 32px; + font-weight: 600; + color: white; + margin-bottom: 5px; +} + +.card-label { + font-size: 14px; + color: rgba(255, 255, 255, 0.9); +} + +.charts-section { + margin-bottom: 30px; +} + +.chart-card { + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border: none; +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; + font-weight: 600; + color: #303133; + padding: 15px 20px; + border-bottom: 1px solid #ebeef5; +} + +.chart-container { + height: 400px; + padding: 20px; +} + +.chart { + width: 100%; + height: 100%; +} + +/* 鍝嶅簲寮忚璁� */ +@media (max-width: 768px) { + .statistics-container { + padding: 10px; + } + + .page-header { + padding: 15px; + } + + .page-header h2 { + font-size: 24px; + } + + .header-controls { + flex-direction: column; + gap: 15px; + } + + .refresh-btn { + margin-left: 0; + } + + .statistics-cards .el-col { + margin-bottom: 15px; + } + + .charts-section .el-col { + margin-bottom: 20px; + } + + .chart-container { + height: 300px; + } +} + +@media (max-width: 480px) { + .page-header h2 { + font-size: 20px; + } + + .card-number { + font-size: 24px; + } + + .chart-container { + height: 250px; + } +} +</style> diff --git a/src/views/index.vue b/src/views/index.vue index 5c23f5e..0e7fdcf 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -68,11 +68,11 @@ <li v-for="item in todoList" :key="item.id"> <div style="display: flex;flex-direction: column;justify-content: space-between;width: 100%;gap: 20px"> <div style="display: flex;justify-content: space-between;align-items: center;"> - <div class="todo-title">娴佺▼缂栧彿锛歿{item.approveId}}</div> - <div class="todo-division">鐢宠閮ㄩ棬锛歿{item.approveDeptName}}</div> + <div class="todo-title">寰呭姙缂栧彿锛歿{item.approveId}}</div> + <div class="todo-division">閮ㄩ棬锛歿{item.approveDeptName}}</div> <div class="todo-time">{{item.approveTime}}</div> </div> - <div class="todo-division">瀹℃壒浜嬬敱锛歿{item.approveReason}}</div> + <div class="todo-division">寰呭姙浜嬬敱锛歿{item.approveReason}}</div> </div> </li> </ul> diff --git a/src/views/inventoryManagement/index.vue b/src/views/inventoryManagement/index.vue new file mode 100644 index 0000000..5e97bcb --- /dev/null +++ b/src/views/inventoryManagement/index.vue @@ -0,0 +1,309 @@ +<template> + <div class="app-container"> + <div class="search_form"> + <div> + <span class="search_title">鍙戞斁瀛e害锛�</span> + <el-select + style="width: 200px;" + @change="handleQuery" + v-model="searchForm.season" + placeholder="璇烽�夋嫨" + :clearable="false" + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in jidu" :key="item.value" /> + </el-select> + <span class="search_title ml10">鍛樺伐鍚嶇О锛�</span> + <el-input + v-model="searchForm.staffName" + style="width: 240px" + placeholder="璇疯緭鍏�" + @change="handleQuery" + clearable + prefix-icon="Search" + /> + <el-button type="primary" @click="handleQuery" style="margin-left: 10px" + >鎼滅储</el-button + > + </div> + <div> + <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button> + <el-button @click="handleOut" icon="download">瀵煎嚭</el-button> + <el-button + type="danger" + icon="Delete" + :disabled="multipleList.length <= 0" + @click="deleteRow(multipleList.map((item) => item.id))" + > + 鎵归噺鍒犻櫎 + </el-button> + </div> + </div> + <div class="table_list"> + <el-table + ref="tableRef" + v-loading="tableLoading" + :data="tableData" + border + height="calc(100vh - 21em)" + :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" + style="width: 100%" + @selection-change="handleSelectionChange" + > + <!-- 閫夋嫨鍒� --> + <el-table-column + align="center" + type="selection" + width="55" + fixed="left" + /> + + <!-- 搴忓彿鍒� --> + <el-table-column + align="center" + label="搴忓彿" + type="index" + width="60" + fixed="left" + /> + + <!-- 鍥哄畾鍒楋細濮撳悕 --> + <el-table-column + label="濮撳悕" + prop="staffName" + width="100" + show-overflow-tooltip + align="center" + fixed="left" + /> + + <!-- 鍥哄畾鍒楋細宸ュ彿 --> + <el-table-column + label="宸ュ彿" + prop="staffNo" + width="100" + show-overflow-tooltip + align="center" + fixed="left" + /> + + <!-- 鍔ㄦ�佸垪锛氭牴鎹瓧鍏告覆鏌� --> + <el-table-column + v-for="(dictItem, index) in sys_lavor_issue" + :key="dictItem.value" + :label="dictItem.label" + :prop="dictItem.value" + show-overflow-tooltip + > + </el-table-column> + + <!-- 鎿嶄綔鍒� --> + <el-table-column + label="鎿嶄綔" + width="150" + align="center" + fixed="right" + > + <template #default="scope"> + <el-button + type="primary" + link + size="small" + @click="edit(scope.row)" + > + 缂栬緫 + </el-button> + <el-button + type="danger" + link + size="small" + :disabled="!!scope.row.adoptedDate" + @click="adopted(scope.row)" + > + 棰嗙敤 + </el-button> + </template> + </el-table-column> + </el-table> + <pagination :total="total" layout="total, sizes, prev, pager, next, jumper" + :page="page.current" :limit="page.size" @pagination="paginationChange" /> + </div> + <Modal ref="modalRef" @success="handleQuery"></Modal> + <files-dia ref="filesDia"></files-dia> + </div> +</template> + +<script setup> +import { ref, onMounted, reactive, toRefs, nextTick, getCurrentInstance } from 'vue' +import dayjs from "dayjs"; +import Modal from "./Modal.vue"; +import FilesDia from "./filesDia.vue"; +import Pagination from "@/components/Pagination/index.vue"; +import {lavorIssueListPage, deleteLedger, update} from "@/api/lavorissce/ledger.js"; +import {ElMessageBox, ElMessage} from "element-plus"; +const { proxy } = getCurrentInstance(); +import { getCurrentMonth } from "@/utils/util" + +const page = ref({ + current: 1, + size: 100, +}) +const total = ref(0) +// 鍝嶅簲寮忔暟鎹� +const tableRef = ref(null) +const tableData = ref([]) +const tableLoading = ref(false) +const { sys_lavor_issue } = proxy.useDict("sys_lavor_issue") +const data = reactive({ + searchForm: { + season: "", + staffName: "", + }, +}); +const { searchForm } = toRefs(data); + +const modalRef = ref(); +const filesDia = ref(); +const multipleList = ref([]); +const jidu = ref([ + { + value: '1', + label: '绗竴瀛e害' + }, + { + value: '2', + label: '绗簩瀛e害' + }, + { + value: '3', + label: '绗笁瀛e害' + }, + { + value: '4', + label: '绗洓瀛e害' + } +]) + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + page.value.current = 1; + getList(); +}; +// 鑾峰彇瀛楀吀鏁版嵁 +const getList = async () => { + tableLoading.value = true; + const params = { ...searchForm.value, ...page.value }; + lavorIssueListPage(params).then(res => { + tableLoading.value = false; + tableData.value = res.data.records; + total.value = res.data.total; + }).catch(err => { + tableLoading.value = false; + }) +} +const add = () => { + modalRef.value.openModal(); +}; +const edit = (row) => { + modalRef.value.loadForm(row); +}; +const deleteRow = (id) => { + ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ暟鎹�, 鏄惁缁х画?", "鎻愮ず", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(async () => { + const { code } = await deleteLedger(id); + if (code == 200) { + ElMessage({ + type: "success", + message: "鍒犻櫎鎴愬姛", + }); + await getList(); + } + }); +}; +const handleOut = () => { + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + proxy.download(`/lavorIssue/exportCopy`, {season: searchForm.value.season}, "鍔充繚鍙拌处.xlsx"); + }) + .catch(() => { + ElMessage.info("宸插彇娑�"); + }); +}; +const adopted = (row) => { + ElMessageBox.confirm("鏄惁纭棰嗙敤?", "鎻愮ず", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(async () => { + const params = { + id: row.id, + adoptedDate: dayjs().format("YYYY-MM-DD") + } + const { code } = await update(params); + if (code == 200) { + ElMessage({ + type: "success", + message: "棰嗙敤鎴愬姛", + }); + await getList(); + } + }) +} +// 鎵撳紑闄勪欢寮规 +const openFilesFormDia = (row) => { + nextTick(() => { + filesDia.value?.openDialog( row,'鏀跺叆') + }) +}; +// 浜嬩欢澶勭悊鍑芥暟 +const handleSelectionChange = (selection) => { + multipleList.value = selection; +} + +const paginationChange = (pagination) => { + page.value.current = pagination.page; + page.value.size = pagination.limit; + getList(); +} + +// 缁勪欢鎸傝浇鏃跺姞杞藉瓧鍏告暟鎹� +onMounted(() => { + handleQuery() +}) +</script> + +<style scoped> +.dynamic-table-container { + width: 100%; +} + +.pagination-container { + margin-top: 20px; + display: flex; + justify-content: flex-end; +} + +:deep(.el-table .el-table__header-wrapper th) { + background-color: #F0F1F5 !important; + color: #333333; + font-weight: 600; +} + +:deep(.el-table .el-table__body-wrapper td) { + padding: 8px 0; +} + +:deep(.el-select) { + width: 100%; +} + +:deep(.el-input) { + width: 100%; +} +</style> diff --git a/src/views/lavorissue/ledger/Form.vue b/src/views/lavorissue/ledger/Form.vue new file mode 100644 index 0000000..7031957 --- /dev/null +++ b/src/views/lavorissue/ledger/Form.vue @@ -0,0 +1,154 @@ +<template> + <el-form :model="form" label-width="100px" :rules="formRules" ref="formRef"> + <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptId"> + <el-select + v-model="form.deptId" + placeholder="璇烽�夋嫨" + clearable + disabled + > + <el-option :label="item.deptName" :value="item.deptId" v-for="(item,index) in productOptions" :key="deptId" /> + </el-select> + </el-form-item> + <el-form-item label="鍛樺伐鍚嶇О" prop="staffId"> + <el-select + v-model="form.staffId" + placeholder="璇烽�夋嫨" + clearable + > + <el-option :label="item.staffName" :value="item.id" v-for="(item,index) in personList" :key="id" /> + </el-select> + </el-form-item> + <el-form-item label="鍔充繚绫诲瀷" prop="dictType"> + <el-select + v-model="form.dictType" + placeholder="璇烽�夋嫨" + clearable + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in sys_lavor_issue_type" :key="value" /> + </el-select> + </el-form-item> + <el-form-item label="鍔充繚闃插叿" prop="dictId"> + <el-select + v-model="form.dictId" + placeholder="璇烽�夋嫨" + clearable + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in sys_lavor_issue" :key="value" /> + </el-select> + </el-form-item> + <el-form-item label="鍙戞斁鏁伴噺" prop="num"> + <el-input-number :step="1" :min="0" style="width: 100%" + v-model="form.num" + placeholder="璇疯緭鍏�" + /> + </el-form-item> + <el-form-item label="杩涘巶鏃ユ湡" prop="factoryDate"> + <el-date-picker + style="width: 100%" + v-model="form.factoryDate" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + type="date" + placeholder="璇烽�夋嫨鏃ユ湡" + clearable + /> + </el-form-item> + <el-form-item label="鍙戞斁鏃ユ湡" prop="issueDate"> + <el-date-picker + style="width: 100%" + v-model="form.issueDate" + format="YYYY-MM-DD" + value-format="YYYY-MM-DD" + type="date" + placeholder="璇烽�夋嫨鏃ユ湡" + clearable + /> + </el-form-item> + + </el-form> +</template> + +<script setup> +import useFormData from "@/hooks/useFormData"; +import {ref,onMounted} from "vue"; +import useUserStore from "@/store/modules/user"; +import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js"; +import {deepCopySameProperties} from '@/utils/util' +const userStore = useUserStore(); +import { + getDept +} from "@/api/collaborativeApproval/approvalProcess.js"; +const { proxy } = getCurrentInstance(); + + +defineOptions({ + name: "鏂板鏀跺叆", +}); +const { sys_lavor_issue } = proxy.useDict("sys_lavor_issue") +const { sys_lavor_issue_type } = proxy.useDict("sys_lavor_issue_type") +const formRef = ref(null); +const productOptions = ref([]); +const personList = ref([]); +const formRules = { + deptId: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }], + dictType: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + staffId: [{ required: true, trigger: "blur", message: "璇疯緭鍏�" }], + dictId: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + num: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + adoptedDate: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + factoryDate: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], + issueDate: [{ required: true, trigger: "change", message: "璇烽�夋嫨" }], +} + +const { form, resetForm } = useFormData({ + deptId: undefined, // + dictType: undefined, + staffId: undefined, // + dictId: undefined, // + num: undefined, // + adoptedDate: undefined, + factoryDate: undefined, + issueDate: undefined, +}); +const getPersonList = () => { + getStaffOnJob().then(res => { + personList.value = res.data + }) +}; +const loadForm = (data) => { + deepCopySameProperties(data, form) +}; + +const getProductOptions = () => { + getDept().then((res) => { + productOptions.value = res.data; + }); +} +// 娓呴櫎琛ㄥ崟鏍¢獙鐘舵�� +const clearValidate = () => { + formRef.value?.clearValidate(); +}; + +// 閲嶇疆琛ㄥ崟鏁版嵁鍜屾牎楠岀姸鎬� +const resetFormAndValidate = () => { + resetForm(); + clearValidate(); + form.deptId = userStore.currentDeptId + getProductOptions(); + getPersonList(); +}; +onMounted(() => { + form.deptId = userStore.currentDeptId + getProductOptions(); + getPersonList(); +}) +defineExpose({ + form, + resetForm, + clearValidate, + loadForm, + resetFormAndValidate, + formRef, +}); +</script> diff --git a/src/views/lavorissue/ledger/Modal.vue b/src/views/lavorissue/ledger/Modal.vue new file mode 100644 index 0000000..5d63236 --- /dev/null +++ b/src/views/lavorissue/ledger/Modal.vue @@ -0,0 +1,70 @@ +<template> + <el-dialog :title="modalOptions.title" v-model="visible" @close="close" width="30%"> + <Form ref="formRef"></Form> + <template #footer> + <el-button type="primary" @click="sendForm" :loading="loading"> + {{ modalOptions.confirmText }} + </el-button> + <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button> + </template> + </el-dialog> +</template> + +<script setup> +import { useModal } from "@/hooks/useModal"; +import { add, update } from "@/api/lavorissce/ledger"; +import Form from "./Form.vue"; +import { ElMessage } from "element-plus"; +const { proxy } = getCurrentInstance() + +defineOptions({ + name: "鏀跺叆鏂板缂栬緫", +}); + +const emits = defineEmits(["success"]); + +const formRef = ref(); +const { + id, + visible, + loading, + openModal, + modalOptions, + handleConfirm, + closeModal, +} = useModal({ title: "鍔充繚鍙拌处" }); + +const sendForm = () => { + proxy.$refs.formRef.$refs.formRef.validate(async valid => { + if (valid) { + const {code} = id.value + ? await update({id: id.value, ...formRef.value.form}) + : await add(formRef.value.form); + if (code == 200) { + emits("success"); + ElMessage({message: "鎿嶄綔鎴愬姛", type: "success"}); + close(); + } else { + loading.value = false; + } + } + }) +}; + +const close = () => { + formRef.value.resetFormAndValidate(); + closeModal(); +}; + +const loadForm = async (row) => { + openModal(row.id); + await nextTick(); + formRef.value.loadForm(row); + +}; + +defineExpose({ + openModal, + loadForm, +}); +</script> diff --git a/src/views/lavorissue/ledger/filesDia.vue b/src/views/lavorissue/ledger/filesDia.vue new file mode 100644 index 0000000..f752496 --- /dev/null +++ b/src/views/lavorissue/ledger/filesDia.vue @@ -0,0 +1,202 @@ +<template> + <div> + <el-dialog + v-model="dialogFormVisible" + title="涓婁紶闄勪欢" + width="50%" + @close="closeDia" + > + <div style="margin-bottom: 10px;text-align: right"> + <el-upload + v-model:file-list="fileList" + class="upload-demo" + :action="uploadUrl" + :on-success="handleUploadSuccess" + :on-error="handleUploadError" + name="file" + :show-file-list="false" + :headers="headers" + style="display: inline;margin-right: 10px" + > + <el-button type="primary">涓婁紶闄勪欢</el-button> + </el-upload> + <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button> + </div> + <PIMTable + rowKey="id" + :column="tableColumn" + :tableData="tableData" + :tableLoading="tableLoading" + :isSelection="true" + @selection-change="handleSelectionChange" + height="500" + > + </PIMTable> + <pagination + style="margin: 10px 0" + v-show="total > 0" + @pagination="paginationSearch" + :total="total" + :page="page.current" + :limit="page.size" + /> + <template #footer> + <div class="dialog-footer"> + <el-button @click="closeDia">鍙栨秷</el-button> + </div> + </template> + </el-dialog> + <filePreview ref="filePreviewRef" /> + </div> +</template> + +<script setup> +import {ref} from "vue"; +import {ElMessageBox} from "element-plus"; +import {getToken} from "@/utils/auth.js"; +import filePreview from '@/components/filePreview/index.vue' +import { + fileAdd, + fileDel, + fileListPage +} from "@/api/financialManagement/revenueManagement.js"; +import Pagination from "@/components/PIMTable/Pagination.vue"; +const { proxy } = getCurrentInstance() +const emit = defineEmits(['close']) + +const dialogFormVisible = ref(false); +const currentId = ref('') +const selectedRows = ref([]); +const filePreviewRef = ref() +const tableColumn = ref([ + { + label: "鏂囦欢鍚嶇О", + prop: "name", + }, + { + dataType: "action", + label: "鎿嶄綔", + align: "center", + operation: [ + { + name: "涓嬭浇", + type: "text", + clickFun: (row) => { + downLoadFile(row); + }, + }, + { + name: "棰勮", + type: "text", + clickFun: (row) => { + lookFile(row); + }, + } + ], + }, +]); +const page = reactive({ + current: 1, + size: 100, +}); +const total = ref(0); +const tableData = ref([]); +const fileList = ref([]); +const tableLoading = ref(false); +const accountType = ref('') +const headers = ref({ + Authorization: "Bearer " + getToken(), +}); +const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃 + +// 鎵撳紑寮规 +const openDialog = (row,type) => { + accountType.value = type; + dialogFormVisible.value = true; + currentId.value = row.id; + getList() +} +const paginationSearch = (obj) => { + page.current = obj.page; + page.size = obj.limit; + getList(); +}; +const getList = () => { + fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => { + tableData.value = res.data.records; + total.value = res.data.total; + }) +} +// 琛ㄦ牸閫夋嫨鏁版嵁 +const handleSelectionChange = (selection) => { + selectedRows.value = selection; +}; + +// 鍏抽棴寮规 +const closeDia = () => { + dialogFormVisible.value = false; + emit('close') +}; +// 涓婁紶鎴愬姛澶勭悊 +function handleUploadSuccess(res, file) { + // 濡傛灉涓婁紶鎴愬姛 + if (res.code == 200) { + const fileRow = {} + fileRow.name = res.data.originalName + fileRow.url = res.data.tempPath + uploadFile(fileRow) + } else { + proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触"); + } +} +function uploadFile(file) { + file.accountId = currentId.value; + file.accountType = accountType.value; + fileAdd(file).then(res => { + proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛"); + getList() + }) +} +// 涓婁紶澶辫触澶勭悊 +function handleUploadError() { + proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触"); +} +// 涓嬭浇闄勪欢 +const downLoadFile = (row) => { + proxy.$download.name(row.url); +} +// 鍒犻櫎 +const handleDelete = () => { + let ids = []; + if (selectedRows.value.length > 0) { + ids = selectedRows.value.map((item) => item.id); + } else { + proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁"); + return; + } + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(() => { + fileDel(ids).then((res) => { + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); + getList(); + }); + }).catch(() => { + proxy.$modal.msg("宸插彇娑�"); + }); +}; +// 棰勮闄勪欢 +const lookFile = (row) => { + filePreviewRef.value.open(row.url) +} + +defineExpose({ + openDialog, +}); +</script> + +<style scoped> + +</style> \ No newline at end of file diff --git a/src/views/lavorissue/ledger/index.vue b/src/views/lavorissue/ledger/index.vue new file mode 100644 index 0000000..6b7e5a8 --- /dev/null +++ b/src/views/lavorissue/ledger/index.vue @@ -0,0 +1,300 @@ +<template> + <div class="app-container"> + <el-form :model="filters" :inline="true"> + <el-form-item label="鍙戞斁瀛e害:" prop="season"> + <el-select + style="width: 200px;" + @change="handleQuery" + v-model="filters.season" + placeholder="璇烽�夋嫨" + :clearable="false" + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in jidu" :key="value" /> + </el-select> + </el-form-item> + <el-form-item label="鍛樺伐鍚嶇О:"> + <el-input + v-model="filters.staffName" + style="width: 240px" + placeholder="璇疯緭鍏�" + @change="handleQuery" + clearable + prefix-icon="Search" + /> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="getTableData">鎼滅储</el-button> + <el-button @click="resetFilters">閲嶇疆</el-button> + </el-form-item> + </el-form> + <div class="table_list"> + <div class="actions"> + <div></div> + <div> + <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button> +<!-- <el-button @click="handleOut" icon="download">瀵煎嚭</el-button>--> + <el-button + type="danger" + icon="Delete" + :disabled="multipleList.length <= 0" + @click="deleteRow(multipleList.map((item) => item.id))" + > + 鎵归噺鍒犻櫎 + </el-button> + </div> + </div> + <PIMTable + rowKey="id" + isSelection + :column="columns" + :tableData="dataList" + :page="{ + current: pagination.currentPage, + size: pagination.pageSize, + total: pagination.total, + }" + @selection-change="handleSelectionChange" + @pagination="changePage" + > + <template #operation="{ row }"> + <el-button type="primary" text @click="edit(row)" icon="editPen"> + 缂栬緫 + </el-button> + <el-button type="primary" :disabled="row.adoptedDate ? true : false" text @click="adopted(row)"> + 棰嗙敤 + </el-button> + </template> + </PIMTable> + </div> + <Modal ref="modalRef" @success="getTableData"></Modal> + <files-dia ref="filesDia"></files-dia> + </div> +</template> + +<script setup> +import { usePaginationApi } from "@/hooks/usePaginationApi"; +import { listPage,deleteLedger,update } from "@/api/lavorissce/ledger"; +import { onMounted, getCurrentInstance } from "vue"; +import Modal from "./Modal.vue"; +import { ElMessageBox, ElMessage } from "element-plus"; +import dayjs from "dayjs"; +import FilesDia from "./filesDia.vue"; +import { getCurrentMonth } from "@/utils/util" + +// 琛ㄦ牸澶氶�夋閫変腑椤� +const multipleList = ref([]); +const { proxy } = getCurrentInstance(); +const modalRef = ref(); +const { payment_methods } = proxy.useDict("payment_methods"); +const { income_types } = proxy.useDict("income_types"); +const filesDia = ref() + +const { + filters, + columns, + dataList, + pagination, + getTableData, + resetFilters, + onCurrentChange, +} = usePaginationApi( + listPage, + { + staffName: '', + season: getCurrentMonth(), + }, + [ + { + label: "鍔充繚鍗曞彿", + align: "center", + prop: "orderNo", + }, + { + label: "鍛樺伐鍚嶇О", + align: "center", + prop: "staffName", + }, + { + label: "鍛樺伐缂栧彿", + align: "center", + prop: "staffNo" + }, + + { + label: "鍔充繚绫诲瀷", + align: "center", + prop: "dictTypeName", + + }, + { + label: "鍔充繚闃插叿", + align: "center", + prop: "dictName", + + }, + { + label: "鍙戞斁鏁伴噺", + align: "center", + prop: "num", + + }, + { + label: "杩涘巶鏃ユ湡", + align: "center", + prop: "factoryDate", + + }, + { + label: "鍙戞斁鏃ユ湡", + align: "center", + prop: "issueDate", + + }, + { + label: "棰嗙敤鏃ユ湡", + align: "center", + prop: "adoptedDate", + + }, + { + fixed: "right", + label: "鎿嶄綔", + dataType: "slot", + slot: "operation", + align: "center", + width: "200px", + }, + ] +); + +const jidu = ref([ + { + value: '1', + label: '绗竴瀛e害' + }, + { + value: '2', + label: '绗簩瀛e害' + }, + { + value: '3', + label: '绗笁瀛e害' + }, + { + value: '4', + label: '绗洓瀛e害' + } +]) + +// 澶氶�夊悗鍋氫粈涔� +const handleSelectionChange = (selectionList) => { + multipleList.value = selectionList; +}; + +const adopted = (row) => { + ElMessageBox.confirm("鏄惁纭棰嗙敤?", "鎻愮ず", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(async () => { + const params = { + id: row.id, + adoptedDate: dayjs().format("YYYY-MM-DD") + } + const { code } = await update(params); + if (code == 200) { + ElMessage({ + type: "success", + message: "棰嗙敤鎴愬姛", + }); + getTableData(); + } + }) +} + +const add = () => { + modalRef.value.openModal(); +}; +const edit = (row) => { + modalRef.value.loadForm(row); +}; + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + getTableData(); +}; +const changePage = ({ page, limit }) => { + pagination.currentPage = page; + pagination.pageSize = limit; + onCurrentChange(page); +}; +const deleteRow = (id) => { + ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ暟鎹�, 鏄惁缁х画?", "鎻愮ず", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + }).then(async () => { + const { code } = await deleteLedger(id); + if (code == 200) { + ElMessage({ + type: "success", + message: "鍒犻櫎鎴愬姛", + }); + getTableData(); + } + }); +}; + +const changeDaterange = (value) => { + if (value) { + filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); + filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); + } else { + filters.entryDateStart = undefined; + filters.entryDateEnd = undefined; + } + getTableData(); +}; + +const handleOut = () => { + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + proxy.download(`/lavorIssue/exportCopy`, {season: filters.season}, "鍔充繚鍙拌处.xlsx"); + }) + .catch(() => { + proxy.$modal.msg("宸插彇娑�"); + }); +}; +// 鎵撳紑闄勪欢寮规 +const openFilesFormDia = (row) => { + nextTick(() => { + filesDia.value?.openDialog( row,'鏀跺叆') + }) +}; + +onMounted(() => { + filters.entryDate = [ + dayjs().format("YYYY-MM-DD"), + dayjs().add(1, "day").format("YYYY-MM-DD"), + ] + filters.entryDateStart = dayjs().format("YYYY-MM-DD") + filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD") + getTableData(); +}); +</script> + +<style lang="scss" scoped> +.table_list { + margin-top: unset; +} +.actions { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} +</style> + diff --git a/src/views/lavorissue/statistics/index.vue b/src/views/lavorissue/statistics/index.vue new file mode 100644 index 0000000..2c34f67 --- /dev/null +++ b/src/views/lavorissue/statistics/index.vue @@ -0,0 +1,285 @@ +<template> + <div class="app-container"> + <div class="search_form"> + <div> + <span class="search_title">鍙戞斁瀛e害锛�</span> + <el-select + style="width: 200px;" + @change="handleQuery" + v-model="searchForm.season" + placeholder="璇烽�夋嫨" + @clear="clearSeason" + clearable + :disabled="searchForm.issueDate ? true : false" + + > + <el-option :label="item.label" :value="item.value" v-for="(item,index) in jidu" :key="item.value" /> + </el-select> + <span class="search_title ml10">鍙戞斁鏈堜唤锛�</span> + <el-date-picker + style="width: 200px;" + :disabled="searchForm.season ? true : false" + v-model="searchForm.issueDate" + @change="handleQuery" + @clear="clearIssueDaten" + type="month" + value-format="YYYY-MM-DD" + format="YYYY-MM" + placeholder="璇烽�夋嫨鏈堜唤" + clearable + /> + <el-button type="primary" @click="handleQuery" style="margin-left: 10px" + >鎼滅储</el-button + > + <el-button type="primary" @click="resetHandleQuery" style="margin-left: 10px" + >閲嶇疆</el-button + > + </div> + <div> + <el-button @click="handleOut" icon="download">瀵煎嚭</el-button> + </div> + </div> + <div class="table_list"> + <div class="actions"> + <div class="head" @click="handleQuery(1)">宸查鍙栧姵淇濇暟閲�:{{statisticsObj.ylqNum}}</div> + <div class="head" @click="handleQuery(2)">鏈鍙栧姵淇濇暟閲�: {{ statisticsObj.wlqNum }}</div> + <div class="head" @click="handleQuery(3)">瓒呮椂宸查鍙栧姵淇濇暟閲�: {{statisticsObj.csylqNum}}</div> + <div class="head" @click="handleQuery(4)">瓒呮椂鏈鍙栧姵淇濇暟閲�: {{statisticsObj.cswlqNum}}</div> + </div> + <el-table + ref="tableRef" + v-loading="tableLoading" + :data="tableData" + border + height="calc(100vh - 21em)" + :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" + style="width: 100%" + @selection-change="handleSelectionChange" + > + <!-- 閫夋嫨鍒� --> + <el-table-column + align="center" + type="selection" + width="55" + fixed="left" + /> + + <!-- 搴忓彿鍒� --> + <el-table-column + align="center" + label="搴忓彿" + type="index" + width="60" + fixed="left" + /> + + <!-- 鍥哄畾鍒楋細濮撳悕 --> + <el-table-column + label="濮撳悕" + prop="staffName" + width="100" + show-overflow-tooltip + align="center" + fixed="left" + /> + + <!-- 鍥哄畾鍒楋細宸ュ彿 --> + <el-table-column + label="宸ュ彿" + prop="staffNo" + width="100" + show-overflow-tooltip + align="center" + fixed="left" + /> + + <!-- 鍔ㄦ�佸垪锛氭牴鎹瓧鍏告覆鏌� --> + <el-table-column + v-for="(dictItem, index) in sys_lavor_issue" + :key="dictItem.value" + :label="dictItem.label" + :prop="dictItem.value" + show-overflow-tooltip + > + </el-table-column> + </el-table> + </div> + </div> +</template> + +<script setup> +import { ref, onMounted, reactive, toRefs, nextTick, getCurrentInstance } from 'vue' +import dayjs from "dayjs"; +import {statisticsList, statistics} from "@/api/lavorissce/ledger.js"; +import {ElMessageBox, ElMessage} from "element-plus"; +const { proxy } = getCurrentInstance(); +import { getCurrentMonth } from "@/utils/util" + +const page = ref({ + current: 1, + size: 100, +}) +const total = ref(0) +// 鍝嶅簲寮忔暟鎹� +const tableRef = ref(null) +const tableData = ref([]) +const tableLoading = ref(false) +const { sys_lavor_issue } = proxy.useDict("sys_lavor_issue") +const data = reactive({ + searchForm: { + season: getCurrentMonth(), + issueDate: "", + status: 0 + }, +}); +const { searchForm } = toRefs(data); + +const modalRef = ref(); +const filesDia = ref(); +const multipleList = ref([]); +const jidu = ref([ + { + value: '1', + label: '绗竴瀛e害' + }, + { + value: '2', + label: '绗簩瀛e害' + }, + { + value: '3', + label: '绗笁瀛e害' + }, + { + value: '4', + label: '绗洓瀛e害' + } +]) +const clearSeason = () => { + console.log("req") + searchForm.value.season = "" + searchForm.value.issueDate = dayjs().format("YYYY-MM-DD"); +} + +const clearIssueDaten = () => { + searchForm.value.issueDate = "" + searchForm.value.season = getCurrentMonth() +} +const statisticsObj = ref({ + ylqNum: 0, // 宸查鍙栨暟閲� + wlqNum: 0, // 鏈鍙栨暟閲� + csylqNum: 0, // 瓒呮椂宸查鍙栨暟閲� + cswlqNum: 0 // 瓒呮椂鏈鍙栨暟閲� +}) +const resetHandleQuery = () => { + searchForm.value.issueDate = ""; + searchForm.value.season = getCurrentMonth(); + handleQuery(0) +}; + +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = (status) => { + switch (status){ + case 1: + searchForm.value.status = 1 + break; + case 2: + searchForm.value.status = 2 + break; + case 3: + searchForm.value.status = 3 + break; + case 4: + searchForm.value.status = 4 + break; + default: + searchForm.value.status = 0 + } + getList(); + getStatistics(); +}; + +const getStatistics = () => { + statistics(searchForm.value).then(res => { + statisticsObj.value.cswlqNum = res.data.cswlqNum + statisticsObj.value.csylqNum = res.data.csylqNum + statisticsObj.value.ylqNum = res.data.ylqNum + statisticsObj.value.wlqNum = res.data.wlqNum + }) +} +// 鑾峰彇瀛楀吀鏁版嵁 +const getList = async () => { + tableLoading.value = true; + const params = { ...searchForm.value}; + statisticsList(params).then(res => { + tableLoading.value = false; + tableData.value = res.data; + }).catch(err => { + tableLoading.value = false; + }) +} +const handleOut = () => { + ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", { + confirmButtonText: "纭", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + proxy.download(`/lavorIssue/exportCopy`, {season: searchForm.value.season,issueDate: searchForm.value.issueDate}, "鍔充繚鍙拌处.xlsx"); + }) + .catch(() => { + ElMessage.info("宸插彇娑�"); + }); +}; + +// 浜嬩欢澶勭悊鍑芥暟 +const handleSelectionChange = (selection) => { + multipleList.value = selection; +} + +// 缁勪欢鎸傝浇鏃跺姞杞藉瓧鍏告暟鎹� +onMounted(() => { + handleQuery() +}) +</script> + +<style scoped> +.dynamic-table-container { + width: 100%; +} + +.pagination-container { + margin-top: 20px; + display: flex; + justify-content: flex-end; +} + +:deep(.el-table .el-table__header-wrapper th) { + background-color: #F0F1F5 !important; + color: #333333; + font-weight: 600; +} + +:deep(.el-table .el-table__body-wrapper td) { + padding: 8px 0; +} + +:deep(.el-select) { + width: 100%; +} + +:deep(.el-input) { + width: 100%; +} +.actions { + display: flex; + justify-content: space-around; + align-items: center; + margin-bottom: 30px; +} +.head{ + cursor: pointer; + font-size: 18px; + font-weight: 600; +} +</style> -- Gitblit v1.9.3