فهرست منبع

入库审核开发联调

zhangsq 8 ماه پیش
والد
کامیت
4091ea66be
5فایلهای تغییر یافته به همراه595 افزوده شده و 23 حذف شده
  1. 18 0
      src/api/InventoryReview.js
  2. 14 0
      src/api/resourceOverview.js
  3. 185 0
      src/utils/newRequest.js
  4. 281 0
      src/views/InventoryReview/index.vue
  5. 97 23
      src/views/resourceOverview/index.vue

+ 18 - 0
src/api/InventoryReview.js

@@ -0,0 +1,18 @@
+import { baseRequest } from '@/utils/newRequest'
+
+const request = (url, ...arg) => baseRequest(`/api/webapp/examine/` + url, ...arg)
+
+export default {
+	// 获取审核列表
+	queryList(data) {
+		return request('queryList', data, 'get')
+	},
+	// 通过
+	reject(data) {
+		return request('reject', data, 'post')
+	},
+	// 驳回
+	reject(data) {
+		return request('pass', data, 'post')
+	},
+}

+ 14 - 0
src/api/resourceOverview.js

@@ -0,0 +1,14 @@
+import { baseRequest } from '@/utils/newRequest'
+
+const request = (url, ...arg) => baseRequest(`/api/webapp/` + url, ...arg)
+
+export default {
+	// 获取资源概括列表
+	getList(data) {
+		return request('operationLog/getList', data, 'get')
+	},
+	// 获取资源概览存储空间接口
+	queryList(data={}) {
+		return request('fileStastic/queryList', data, 'get')
+	}
+}

+ 185 - 0
src/utils/newRequest.js

@@ -0,0 +1,185 @@
+/**
+ *  Copyright [2022] [https://www.xiaonuo.vip]
+ *	Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *	1.请不要删除和修改根目录下的LICENSE文件。
+ *	2.请不要删除和修改Snowy源码头部的版权声明。
+ *	3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ *	4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ *	5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ *	6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+// 统一的请求发送
+import axios from 'axios'
+import qs from 'qs'
+import { Modal, message, notification } from 'ant-design-vue'
+import sysConfig from '@/config/index'
+import tool from '@/utils/tool'
+
+// 以下这些code需要重新登录
+const reloadCodes = [401, 1011007, 1011008]
+const errorCodeMap = {
+	400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
+	401: '用户没有权限(令牌、用户名、密码错误)。',
+	403: '用户得到授权,但是访问是被禁止的。',
+	404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
+	406: '请求的格式不可得。',
+	410: '请求的资源被永久删除,且不会再得到的。',
+	422: '当创建一个对象时,发生一个验证错误。',
+	500: '服务器发生错误,请检查服务器。',
+	502: '网关错误。',
+	503: '服务不可用,服务器暂时过载或维护。',
+	504: '网关超时。'
+}
+// 定义一个重新登录弹出窗的变量
+const loginBack = ref(false)
+// 创建 axios 实例
+const service = axios.create({
+	baseURL: '/api', // api base_url
+	timeout: sysConfig.TIMEOUT // 请求超时时间
+})
+
+// HTTP request 拦截器
+service.interceptors.request.use(
+	(config) => {
+		const token = tool.data.get('TOKEN')
+		if (token) {
+			config.headers[sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + token
+		}
+		if (!sysConfig.REQUEST_CACHE && config.method === 'get') {
+			config.params = config.params || {}
+			config.params._ = new Date().getTime()
+		}
+		Object.assign(config.headers, sysConfig.HEADERS)
+		return config
+	},
+	(error) => {
+		return Promise.reject(error)
+	}
+)
+
+// 保持重新登录Modal的唯一性
+const error = () => {
+	loginBack.value = true
+	Modal.error({
+		title: '提示:',
+		okText: '重新登录',
+		content: '登录已失效, 请重新登录',
+		onOk: () => {
+			loginBack.value = false
+			tool.data.remove('TOKEN')
+			tool.data.remove('USER_INFO')
+			tool.data.remove('MENU')
+			tool.data.remove('PERMISSIONS')
+			window.location.reload()
+		}
+	})
+}
+
+// HTTP response 拦截器
+service.interceptors.response.use(
+	(response) => {
+		// 配置了blob,不处理直接返回文件流
+		if (response.config.responseType === 'blob') {
+			if (response.status === 200) {
+				return response
+			} else {
+				message.warning('文件下载失败或此文件不存在')
+				return
+			}
+		}
+		const data = response.data
+		const code = data.code
+		if (reloadCodes.includes(code)) {
+			if (!loginBack.value) {
+				error()
+			}
+			return
+		}
+		if (code !== 200) {
+			const customErrorMessage = response.config.customErrorMessage
+			message.error(customErrorMessage || data.msg)
+			return Promise.reject(data)
+			// 自定义错误提示,覆盖后端返回的message
+			// 使用示例:
+			// export function customerList (data) {
+			//   return request('list', data, 'get', {
+			//     customErrorMessage: '自定义错误消息提示'
+			//   });
+			// }
+		} else {
+			// 统一成功提示
+			const responseUrl = response.config.url
+			const apiNameArray = [
+				'add',
+				'edit',
+				'delete',
+				'update',
+				'grant',
+				'reset',
+				'stop',
+				'pass',
+				'disable',
+				'enable',
+				'revoke',
+				'suspend',
+				'active',
+				'turn',
+				'adjust',
+				'reject',
+				'saveDraft'
+			]
+			apiNameArray.forEach((apiName) => {
+				if (responseUrl.includes(apiName)) {
+					message.success(data.msg)
+				}
+			})
+		}
+		return Promise.resolve(data)
+	},
+	(error) => {
+		if (error) {
+			const status = 503
+			const description = errorCodeMap[status]
+			notification.error({
+				message: '请求错误',
+				description
+			})
+			return Promise.reject(status)
+		}
+	}
+)
+
+// 适配器, 用于适配不同的请求方式
+export const baseRequest = (url, value = {}, method = 'post', options = {}) => {
+	url = sysConfig.API_URL + url
+	if (method === 'post') {
+		return service.post(url, value, options)
+	} else if (method === 'get') {
+		return service.get(url, { params: value, ...options })
+	} else if (method === 'formdata') {
+		// form-data表单提交的方式
+		return service.post(url, qs.stringify(value), {
+			headers: {
+				'Content-Type': 'multipart/form-data'
+			},
+			...options
+		})
+	} else {
+		// 其他请求方式,例如:put、delete
+		return service({
+			method: method,
+			url: url,
+			data: value,
+			...options
+		})
+	}
+}
+
+// 模块内的请求, 会自动加上模块的前缀
+export const moduleRequest =
+	(moduleUrl) =>
+	(url, ...arg) => {
+		return baseRequest(moduleUrl + url, ...arg)
+	}
+
+export default service

+ 281 - 0
src/views/InventoryReview/index.vue

@@ -0,0 +1,281 @@
+<template>
+	<a-card>
+		<a-tabs v-model:activeKey="activeKey">
+			<a-tab-pane key="pending" tab="待审核"></a-tab-pane>
+			<a-tab-pane key="approved" tab="已审核"></a-tab-pane>
+		</a-tabs>
+		<a-row :gutter="16" style="margin-bottom: 16px">
+			<a-col :span="2">
+				<a-select v-model:value="formState.examineStatus" style="width: 100px" placeholder="请选择状态">
+					<a-select-option value="pending">待审核</a-select-option>
+					<a-select-option value="approved">已审核</a-select-option>
+				</a-select>
+			</a-col>
+			<a-col :span="4">
+				<a-select v-model:value="formState.orgName" style="width: 200px" placeholder="请选择组织结构">
+					<a-select-option value="0">待审核</a-select-option>
+					<a-select-option value="1">已审核</a-select-option>
+				</a-select>
+			</a-col>
+			<a-col :span="6">
+				<a-input v-model:value="formState.fileName" placeholder="请输入关键词搜索" />
+			</a-col>
+			<a-col :span="6">
+				<a-button type="primary" @click="handleSearch">搜索</a-button>
+				<a-button style="margin-left: 8px" @click="handleBatchApprove">批量审核</a-button>
+				<a-button style="margin-left: 8px" @click="handleExportDetails">导出详情</a-button>
+			</a-col>
+		</a-row>
+		<a-table
+			:row-selection="rowSelection"
+			:columns="columns"
+			:data-source="dataSource"
+			:pagination="false"
+			:loading="loading"
+		>
+			<template #bodyCell="{ column, record }">
+				<template v-if="column.key === 'resourceName'">
+					<div class="resource-item">
+						<img :src="record.thumbnail" alt="Thumbnail" class="thumbnail" />
+						<span>{{ record.resourceName }}</span>
+					</div>
+				</template>
+				<template v-else-if="column.key === 'size'">
+					{{ formatSize(record.size) }}
+				</template>
+				<template v-else-if="column.key === 'applicant'">
+					{{ record.applicant }} ({{ record.applicantAccount }})
+				</template>
+				<template v-else-if="column.key === 'applyTime'">
+					{{ formatDate(record.applyTime) }}
+				</template>
+				<template v-else-if="column.key === 'aiAudit'">
+					<span :style="{ color: record.aiAudit === '审核失败' ? 'red' : 'green' }">{{ record.aiAudit }}</span>
+				</template>
+				<template v-else-if="column.key === 'operation'">
+					<a-button type="link" @click="handleAudit(record, 1)">同意</a-button>
+					<a-button type="link" style="color: red" @click="handleAudit(record, 0)">驳回</a-button>
+				</template>
+			</template>
+		</a-table>
+		<div class="dis-flex-end margin-top">
+			<CustomPagination
+				:total="pagination.total"
+				:current="pagination.current"
+				:pageSize="pagination.pageSize"
+				:showQuickJumper="true"
+				:showSizeChanger="true"
+				:showTotal="(total) => `共 ${total} 条数据`"
+				@change="handlePageChange"
+				@showSizeChange="handlePageSizeChange"
+			/>
+		</div>
+	</a-card>
+</template>
+
+  <script setup>
+import { ref, onMounted } from 'vue'
+import InventoryReviewApi from '@/api/InventoryReview.js'
+import CustomPagination from '@/components/customPagination.vue'
+// 状态管理
+const activeKey = ref('pending')
+const loading = ref(false)
+const formState = ref({
+	fileName: null,
+	examineStatus: null,
+	orgName: null
+})
+// 表格数据
+const columns = [
+	{
+		title: '资源名称',
+		key: 'fileName',
+		dataIndex: 'fileName',
+		width: 200,
+		align: 'center'
+	},
+	{
+		title: '大小',
+		key: 'fileSizeNum',
+		dataIndex: 'fileSizeNum',
+		width: 100,
+		align: 'center'
+	},
+	{
+		title: '申请人',
+		key: 'createUser',
+		dataIndex: 'createUser',
+		width: 200,
+		align: 'center'
+	},
+	{
+		title: '组织结构',
+		key: 'orgName',
+		dataIndex: 'orgName',
+		width: 150,
+		align: 'center'
+	},
+	{
+		title: '申请时间',
+		key: 'createTime',
+		dataIndex: 'createTime',
+		width: 150,
+		align: 'center'
+	},
+	{
+		title: '智能审核',
+		key: 'examineReason',
+		dataIndex: 'examineReason',
+		width: 100,
+		align: 'center'
+	},
+	{
+		title: '待审核人',
+		key: 'auditor',
+		dataIndex: 'auditor',
+		width: 150,
+		align: 'center'
+	},
+	{
+		title: '操作',
+		key: 'operation',
+		width: 100,
+		align: 'center'
+	}
+]
+
+const dataSource = ref([])
+
+const pagination = reactive({
+	pageSize: 10,
+	pageNum: 1,
+	total: 0
+})
+
+// 行选择配置
+const rowSelection = ref({
+	selectedRowKeys: [],
+	onChange: (selectedRowKeys) => {
+		rowSelection.value.selectedRowKeys = selectedRowKeys
+	}
+})
+// 翻页
+const handlePageChange = (page) => {
+	pagination.pageNum = page
+	getListData()
+}
+// 每页条数
+const handlePageSizeChange = (current, size) => {
+	pagination.pageNum = 1
+	pagination.pageSize = size
+	getListData()
+}
+const getListData = () => {
+	let params = {
+		pageNum: pagination.pageNum,
+		pageSize: pagination.pageSize,
+		orgName: formState.orgName,
+		examineStatus: formState.examineStatus,
+		fileName: formState.fileName
+	}
+	InventoryReviewApi.queryList(params)
+		.then((res) => {
+			console.log(res, '审核列表')
+			dataSource.value = res.data
+			pagination.total = res.total
+		})
+		.catch((err) => {
+			console.log(err)
+		})
+}
+
+// 方法
+const handleSearch = () => {
+	// 处理搜索逻辑
+}
+
+const handleBatchApprove = () => {
+	// 处理批量审核逻辑
+	console.log('批量审核:', rowSelection.value.selectedRowKeys)
+}
+
+const handleExportDetails = () => {
+	// 处理导出详情逻辑
+}
+
+const handleAudit = (record, state) => {
+	// 处理单个审核逻辑
+	console.log('审核记录:', record)
+	if (state == 0) {
+		let params = {
+			targetId: record.id,
+			examineReason: 'asdasd'
+		}
+		InventoryReviewApi.reject(params)
+			.then((res) => {
+				console.log(res, '驳回')
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	} else {
+		let params = {
+			targetId: record.id,
+			examineReason: 'asdasd'
+		}
+		InventoryReviewApi.reject(params)
+			.then((res) => {
+				console.log(res, '通过')
+			})
+			.catch((err) => {
+				console.log(err)
+			})
+	}
+}
+
+const formatSize = (size) => {
+	if (size < 1024) return `${size}B`
+	else if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)}KB`
+	else if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(2)}MB`
+	else return `${(size / (1024 * 1024 * 1024)).toFixed(2)}GB`
+}
+
+const formatDate = (dateStr) => {
+	return new Date(dateStr).toLocaleString()
+}
+onMounted(() => {
+	getListData()
+})
+</script>
+
+  <style scoped>
+.resource-item {
+	display: flex;
+	align-items: center;
+}
+
+.thumbnail {
+	width: 40px;
+	height: 40px;
+	margin-right: 8px;
+}
+.dis-flex-end {
+	display: flex;
+	justify-content: flex-end;
+}
+.margin-top {
+	margin-top: 15px;
+}
+
+@media (max-width: 768px) {
+	.resource-item {
+		flex-direction: column;
+		align-items: flex-start;
+	}
+
+	.thumbnail {
+		margin-right: 0;
+		margin-bottom: 8px;
+	}
+}
+</style>

+ 97 - 23
src/views/resourceOverview/index.vue

@@ -5,7 +5,6 @@
 			<a-col :span="12">
 				<a-select v-model:value="selectedOption" style="width: 100px" placeholder="请选择">
 					<a-select-option value="all">全部</a-select-option>
-					<!-- 其他选项... -->
 				</a-select>
 				<a-input-search
 					v-model:value="searchKeyword"
@@ -20,9 +19,10 @@
 			</a-col>
 		</a-row>
 
-		<!-- 左侧内容 -->
-		<a-row type="flex">
-			<a-col :span="14" class="left-content">
+		<!-- 主要内容区 -->
+		<a-row :gutter="[16, 16]">
+			<!-- 左侧内容 -->
+			<a-col :xs="24" :lg="14" class="left-content">
 				<!-- 存储空间 -->
 				<div class="storage-space">
 					<div id="storage-chart" style="width: 200px; height: 250px"></div>
@@ -64,12 +64,12 @@
 			</a-col>
 
 			<!-- 右侧内容 -->
-			<a-col :span="10" class="right-content">
+			<a-col :xs="24" :lg="10" class="right-content">
 				<h3>浏览历史</h3>
 				<ul class="history-list">
 					<li v-for="(history, index) in historyList" :key="index">
-						<span class="history-time">{{ history.date }}</span>
-						<span class="history-file">{{ history.file }}</span>
+						<span class="history-time">{{ history.time }}</span>
+						<span class="history-file">{{ history.fileName }}</span>
 					</li>
 				</ul>
 			</a-col>
@@ -80,13 +80,14 @@
   <script setup>
 import * as echarts from 'echarts'
 import { ref, onMounted } from 'vue'
+import resourceOverviewApi from '@/api/resourceOverview.js'
 // import { ARow, ACol, ASelect, ASelectOption, AInputSearch, AButton } from 'ant-design-vue'
 
 // 响应式数据
 const totalCapacity = ref(20)
-const usedCapacity = ref(2)
-const availableCapacity = ref(18)
-const resourceTotal = ref(36)
+const usedCapacity = ref(2) //已使用内容
+const availableCapacity = ref(18) //可用内存
+const resourceTotal = ref(36) //资源总量
 const resourceStats = ref([
 	{ width: '44%', color: '#5470c6' },
 	{ width: '6%', color: '#91cc75' },
@@ -100,16 +101,17 @@ const resourceLabels = ref([
 	{ text: '文档:9', color: '#fac858' },
 	{ text: '其他:9', color: '#c4ccd3' }
 ])
-const historyList = ref([
-	{ date: '2024-09-24 16:12:24', file: '现代教育技术应用-6.mp4' },
-	{ date: '2024-09-24 13:38:52', file: '怎样写出较高水平的学术文章.mp4' },
-	{ date: '2024-09-23 16:44:14', file: 'etl.yml' },
-	{ date: '2024-09-23 13:53:59', file: '学术论文书写套路.mp4' },
-	{ date: '2024-09-23 13:53:55', file: '怎样找到值得研究的问题.mp4' },
-	{ date: '2024-09-21 14:56:16', file: '第一讲 当代艺术概况.pptx' },
-	{ date: '2024-09-21 14:08:57', file: '论文汇总.xlsx' },
-	{ date: '2024-09-20 12:03:35', file: '现代教育技术应用-6-字幕.srt' }
-])
+// const historyList = ref([
+// 	{ date: '2024-09-24 16:12:24', file: '现代教育技术应用-6.mp4' },
+// 	{ date: '2024-09-24 13:38:52', file: '怎样写出较高水平的学术文章.mp4' },
+// 	{ date: '2024-09-23 16:44:14', file: 'etl.yml' },
+// 	{ date: '2024-09-23 13:53:59', file: '学术论文书写套路.mp4' },
+// 	{ date: '2024-09-23 13:53:55', file: '怎样找到值得研究的问题.mp4' },
+// 	{ date: '2024-09-21 14:56:16', file: '第一讲 当代艺术概况.pptx' },
+// 	{ date: '2024-09-21 14:08:57', file: '论文汇总.xlsx' },
+// 	{ date: '2024-09-20 12:03:35', file: '现代教育技术应用-6-字幕.srt' }
+// ])
+const historyList = ref([])
 
 // 搜索相关数据
 const selectedOption = ref('all')
@@ -119,6 +121,34 @@ const searchKeyword = ref('')
 const applyExpansion = () => {
 	console.log('申请扩容')
 }
+// 方法
+const getLists = () => {
+	resourceOverviewApi
+		.getList({ type: 'upload' })
+		.then((res) => {
+			console.log(res, '资源概括数据')
+			historyList.value = res.data
+		})
+		.catch((err) => {
+			console.log(err)
+		})
+}
+//存储空间数据
+const getQueryList = () => {
+	resourceOverviewApi
+		.queryList()
+		.then((res) => {
+			console.log(res, '存储空间数据')
+			resourceTotal.value = res.totalSize
+			availableCapacity.value = res.remainStorageSize
+			usedCapacity.value = Number(res.totalFileSize)
+			totalCapacity.value = res.totalStorageSize
+			initChart()
+		})
+		.catch((err) => {
+			console.log(err)
+		})
+}
 
 const handleSearch = () => {
 	console.log('搜索:', selectedOption.value, searchKeyword.value)
@@ -127,7 +157,8 @@ const handleSearch = () => {
 
 // 生命周期钩子
 onMounted(() => {
-	initChart()
+	getQueryList()
+	getLists()
 })
 
 // 图表初始化
@@ -170,8 +201,7 @@ const initChart = () => {
 	myChart.setOption(option)
 }
 </script>
-
-  <style scoped>
+<style scoped>
 .dashboard-container {
 	display: flex;
 	flex-direction: column;
@@ -258,4 +288,48 @@ const initChart = () => {
 .marginright5 {
 	margin-right: 5px;
 }
+
+/* 响应式调整 */
+@media (max-width: 768px) {
+	.left-content,
+	.right-content {
+		padding: 20px;
+	}
+
+	.storage-space {
+		flex-direction: column;
+		align-items: flex-start;
+	}
+
+	#storage-chart {
+		width: 150px;
+		height: 150px;
+		margin-bottom: 10px;
+	}
+
+	.storage-info p {
+		margin: 5px 0;
+	}
+
+	.resource-stats {
+		height: 15px;
+	}
+
+	.resource-labels > span {
+		margin-right: 10px;
+	}
+
+	.history-list li {
+		padding-left: 15px;
+		margin-bottom: 10px;
+	}
+
+	.history-time {
+		font-size: 12px;
+	}
+
+	.history-file {
+		font-size: 13px;
+	}
+}
 </style>