Kaynağa Gözat

reactor:370 班长审批后派发工单到部门支持发送短信;369 恢复单个催办按钮;365 创建知识界面,热点类型设置为必选项;361 新增【延期审批统计】;354 部门进行省件甄别时,如果上传了附件,建议在中心领导环节推送省审批处增加一个附件是否推送勾选项,由我们自行决定附件是否推送省上;

zhangchong 1 ay önce
ebeveyn
işleme
68dcfd15a7

+ 21 - 0
src/api/business/discern.ts

@@ -74,6 +74,16 @@ export const screenDetail = (id: string) => {
 		method: 'get',
 	});
 };
+/**
+ * @description 获取甄别所有的文件
+ * @param {string} id
+ */
+export const screenFiles = (id: string) => {
+	return request({
+		url: `/api/v1/Order/screen/all_file/${id}`,
+		method: 'get',
+	});
+}
 /**
  * @description 甄别详情-- 部门满意度明细
  * @param {string} id
@@ -135,4 +145,15 @@ export const discernUpdateTime = (data: object) => {
 		method: 'put',
 		data,
 	});
+}
+/**
+ * @description 甄别审批通过
+ * @param {object} data
+ */
+export const discernApprove = (data: object) => {
+	return request({
+		url: `/api/v1/Order/screen/next`,
+		method: 'post',
+		data,
+	});
 }

+ 25 - 0
src/api/statistics/center.ts

@@ -880,4 +880,29 @@ export const centerOnlineRoute = (params: object) => {
     method: 'get',
     params,
   });
+}
+/**
+ * @description 延期审批统计
+ * @param {object} params
+ */
+export const statisticsCenterDelayAudit = (params: object) => {
+  return request({
+    url: `/api/v1/StatisticalReport/order_delay_statisical_list`,
+    method: 'get',
+    params,
+  });
+}
+/**
+ * @description 延期审批统计导出
+ * @param {object} data
+ */
+export const statisticsCenterDelayAuditExport = (data: object) => {
+  return request({
+    url: `/api/v1/StatisticalReport/order_delay_statisical_list_export`,
+    method: 'post',
+    data,
+    responseType: 'blob',
+  }, {
+    reduce_data_format: false
+  });
 }

+ 106 - 6
src/views/business/discern/components/Discern-audit.vue

@@ -129,6 +129,20 @@
 							<annex-list name="甄别附件" classify="甄别上传" v-model:format="handleFiles" />
 						</el-form-item>
 					</el-col>
+					<el-col>
+						<el-form-item
+							label="省甄别附件"
+							:rules="[{ required: false, message: '请上传省甄别附件', trigger: 'change' }]"
+							v-if="isPickFile && state.ruleForm.isPass"
+						>
+							<vxe-grid v-bind="gridOptions" class="w100" ref="gridRef" @checkbox-all="selectAllChangeEvent" @checkbox-change="selectChangeEvent">
+								<template #action="{ row }">
+									<el-button type="primary" link @click="onDownload(row)">下载</el-button>
+									<el-button type="primary" link @click="onPreview(row)">预览</el-button>
+								</template>
+							</vxe-grid>
+						</el-form-item>
+					</el-col>
 				</el-row>
 			</el-form>
 		</div>
@@ -147,15 +161,16 @@
 <script setup lang="ts" name="discernDetail">
 import { computed, defineAsyncComponent, reactive, ref } from 'vue';
 import { formatDate } from '@/utils/formatTime';
-import { discernApproveParams, screenDetail } from '@/api/business/discern';
+import { discernApprove, discernApproveParams, screenDetail, screenFiles } from '@/api/business/discern';
 import { transformFile } from '@/utils/tools';
-import { ElMessage, FormInstance } from 'element-plus';
-import { workflowNext, workflowReject, workflowTraces } from '@/api/system/workflow';
+import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
+import { workflowReject, workflowTraces } from '@/api/system/workflow';
 import { commonEnum } from '@/utils/constants';
 import { useAppConfig } from '@/stores/appConfig';
 import { storeToRefs } from 'pinia';
 import other from '@/utils/other';
 import { useThemeConfig } from '@/stores/themeConfig';
+import { fileDownloadByUrl } from '@/api/public/file';
 
 const AnnexList = defineAsyncComponent(() => import('@/components/AnnexList/index.vue')); // 附件列表
 const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
@@ -214,9 +229,11 @@ state.tabPaneList = filteredTabPaneList.value;
  * @description 打开弹窗
  * */
 const orderId = ref('');
+const discernId = ref(''); // 甄别ID
 const openDialog = async (row: any) => {
 	state.dialogVisible = true;
 	state.loading = true;
+	discernId.value = row.id;
 	try {
 		const { result } = await screenDetail(row.id);
 		state.infoForm = result ?? {};
@@ -225,7 +242,7 @@ const openDialog = async (row: any) => {
 		state.order = result.order ?? {};
 		orderId.value = result.orderId;
 		await getWorkflow(result.workflowId);
-		await selectNextStepOptions(result.workflowId);
+		await selectNextStepOptions(result.workflowId, row.id);
 		state.loading = false;
 	} catch (e) {
 		console.log(e);
@@ -274,15 +291,85 @@ const getWorkflow = async (workflowId: string) => {
 		console.log(e);
 	}
 };
+const gridOptions = reactive<any>({
+	border: true,
+	maxHeight: 500,
+	rowConfig: {
+		isHover: true,
+	},
+	columns: [
+		{ type: 'checkbox', width: 50 },
+		{ field: 'fileName', title: '文件名' },
+		{ field: 'orgName', title: '上传部门' },
+		{ field: 'userName', title: '上传人' },
+		{ title: '操作', width: 120, fixed: 'right', align: 'center', slots: { default: 'action' } },
+	],
+	data: [],
+});
+const gridRef = ref<RefType>();
+const provinceFiles = ref<EmptyArrayType>([]); // 省甄别附件
+const selectAllChangeEvent = ({ checked }) => {
+	if (gridRef.value) {
+		const records = gridRef.value.getCheckboxRecords();
+		provinceFiles.value = records;
+		console.log(checked ? '所有勾选事件' : '所有取消事件', records);
+	}
+};
+
+const selectChangeEvent = ({ checked }) => {
+	if (gridRef.value) {
+		const records = gridRef.value.getCheckboxRecords();
+		provinceFiles.value = records;
+		console.log(checked ? '勾选事件' : '取消事件', records);
+	}
+};
+// 下载附件
+const onDownload = (row: any) => {
+	// 确定是否下载
+	ElMessageBox.confirm(`确定要下载附件 ${row.fileName} 吗?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+		draggable: true,
+		cancelButtonClass: 'default-button',
+	})
+		.then(() => {
+			fileDownloadByUrl({
+				Source: 'hotline',
+				Id: row.additions,
+			}).then((res: any) => {
+				let blob: Blob = new Blob([res.data], { type: res.data.type }); // 创建blob 设置blob文件类型 data 设置为后端返回的文件(例如mp3,jpeg) type:这里设置后端返回的类型 为 mp3
+				let down: HTMLAnchorElement = document.createElement('a'); // 创建A标签
+				let href: string = window.URL.createObjectURL(blob); // 创建下载的链接
+				down.href = href; // 下载地址
+				down.download = row.fileName; // 下载文件名
+				document.body.appendChild(down);
+				down.click(); // 模拟点击A标签
+				document.body.removeChild(down); // 下载完成移除元素
+				window.URL.revokeObjectURL(href); // 释放blob对象
+			});
+		})
+		.catch(() => {});
+};
+// 预览附件
+const onPreview = (row: any) => {
+	const url = import.meta.env.VITE_API_UPLOAD_URL + row.path;
+	window.open(url, '_blank');
+};
 const nextHandlersRequired = ref<Boolean>(false); // 办理对象是否必填
 const canStartCountersign = ref<Boolean>(false); // 是否可以会签
 const selectNext = ref<any>({});
+const isPickFile = ref<Boolean>(false); // 是否要展示选择的附件列表选择
 // 查询流程办理节点
-const selectNextStepOptions = async (workflowId: string) => {
+const selectNextStepOptions = async (workflowId: string, discernId: string) => {
 	try {
 		const { result } = await discernApproveParams(workflowId); //获取甄别审批流程参数
 		state.nextStepOptions = result.steps; //办理对象选择内容
 		canStartCountersign.value = result.canStartCountersign ?? false; // 是否可以发起会签
+		isPickFile.value = result.isPickFile ?? false;
+		if (result.isPickFile) {
+			getAllFiles(discernId);
+		}
 		if (state.nextStepOptions.length === 1) {
 			// 下一节点是否只有一个 默认选中第一个
 			setTimeout(() => {
@@ -301,6 +388,16 @@ const selectNextStepOptions = async (workflowId: string) => {
 		console.log(e);
 	}
 };
+// 获取甄别的文件
+const getAllFiles = (id: string) => {
+	screenFiles(id)
+		.then((res: any) => {
+			gridOptions.data = res.result ?? [];
+		})
+		.catch((err) => {
+			console.log(err, '2222');
+		});
+};
 const selectNextStep = (val: any) => {
 	ruleFormRef.value?.resetFields('nextHandlers');
 	const next = state.nextStepOptions.find((item: any) => item.key === val);
@@ -359,6 +456,9 @@ const close = () => {
 	ruleFormRef.value?.resetFields();
 	ruleFormRef.value?.resetFields();
 	state.activeName = '0';
+	provinceFiles.value = [];
+	discernId.value = '';
+	orderId.value = '';
 };
 // 提交
 const afterSubmit = (emitType?: 'updateList', showMessage?: boolean, message?: string) => {
@@ -378,7 +478,7 @@ const onSubmit = (formEl: FormInstance | undefined) => {
 		let submitObj = other.deepClone(state.ruleForm);
 		if (state.ruleForm.isPass) {
 			// 审批通过 下一步
-			workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
+			discernApprove({ ...submitObj, reviewResult: 1, files: handleFiles.value, provinceFiles: provinceFiles.value, screenId: discernId.value })
 				.then(() => {
 					afterSubmit('updateList', true);
 				})

+ 1 - 0
src/views/home/components/Home-date.vue

@@ -74,6 +74,7 @@ const isOldHotlineUrl = computed(() => {
 });
 // 地址跳转
 const linkUrl = () => {
+	console.log('热线赋智',`https://rxfz.scopenai.com:18027/TokenLogin?id=${userInfos.value.id}`);
 	window.open(`https://rxfz.scopenai.com:18027/TokenLogin?id=${userInfos.value.id}`);
 };
 onMounted(() => {

+ 12 - 4
src/views/knowledge/index/edit.vue

@@ -147,7 +147,11 @@
 								</el-form-item>
 							</template>
 							<template #default>
-								<el-form-item label="热点分类" prop="hotspotId" :rules="[{ required: false, message: '请选择热点分类', trigger: 'change' }]">
+								<el-form-item
+									label="热点分类"
+									prop="hotspotId"
+									:rules="[{ required: hotspotRequired, message: '请选择热点分类', trigger: 'change' }]"
+								>
 									<hot-spot-select v-model="state.ruleForm.hotspotId" v-model:externalArr="state.hotspotExternal" @change="chooseHotSpot" clearable />
 								</el-form-item>
 							</template>
@@ -426,7 +430,7 @@
 </template>
 
 <script setup lang="ts" name="knowledgeEdit">
-import { defineAsyncComponent, nextTick, onMounted, reactive, ref } from 'vue';
+import { computed, defineAsyncComponent, nextTick, onMounted, reactive, ref } from 'vue';
 import type { FormInstance } from 'element-plus';
 import { ElMessage } from 'element-plus';
 import mittBus from '@/utils/mitt';
@@ -502,6 +506,10 @@ const chooseHotSpot = (val: any, node: any, externalArr: any) => {
 		state.ruleForm.hotspotExternal = null;
 	}
 };
+// 热点分类是否必填 泸州必填
+const hotspotRequired = computed(() => {
+	return ['LuZhou'].includes(themeConfig.value.appScope);
+});
 const validatePassTitle = (rule: any, value: any, callback: any) => {
 	if (value === '' || value === null) {
 		callback(new Error('请填写知识标题'));
@@ -547,7 +555,7 @@ const isRepeat = (type: string) => {
 			const ids = res.result.map((item: any) => item.id);
 			keyWordsNameArr.value = removeDuplicate([...keyWordsNameArr.value, ...nameArr]);
 			state.ruleForm.keywordsName = keyWordsNameArr.value.join(',');
-      if(!state.ruleForm.keywords) state.ruleForm.keywords = [];
+			if (!state.ruleForm.keywords) state.ruleForm.keywords = [];
 			state.ruleForm.keywords = removeDuplicate([...state.ruleForm.keywords, ...ids]);
 		}
 	});
@@ -586,7 +594,7 @@ const selectKeyword = (val: any) => {
 		const nameArr = val.name;
 		keyWordsNameArr.value = removeDuplicate([...keyWordsNameArr.value, ...nameArr]);
 		state.ruleForm.keywordsName = keyWordsNameArr.value.join(',');
-    if(!state.ruleForm.keywords) state.ruleForm.keywords = [];
+		if (!state.ruleForm.keywords) state.ruleForm.keywords = [];
 		state.ruleForm.keywords = removeDuplicate([...state.ruleForm.keywords, ...val.ids]);
 	} else {
 		keyWordsNameArr.value = [];

+ 130 - 0
src/views/statistics/center/delayAudit.vue

@@ -0,0 +1,130 @@
+<template>
+	<div class="statistics-center-delay-audit-container layout-padding">
+		<div class="layout-padding-auto layout-padding-view pd20">
+			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
+				<el-form-item prop="crTime">
+					<statistical-time v-model="state.queryParams.crTime" @change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading" />
+				</el-form-item>
+				<el-form-item label="审批人" prop="UserName">
+					<el-input v-model="state.queryParams.UserName" placeholder="审批人" clearable @keyup.enter="handleQuery" class="keyword-input" />
+				</el-form-item>
+				<el-form-item label="审批环节" prop="NodeCode">
+					<el-input v-model="state.queryParams.NodeCode" placeholder="审批环节" clearable @keyup.enter="handleQuery" class="keyword-input" />
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+					<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+						<SvgIcon name="ele-Refresh" class="mr5" />重置
+					</el-button>
+				</el-form-item>
+			</el-form>
+			<vxe-toolbar
+				ref="toolbarRef"
+				:loading="state.loading"
+				custom
+				:refresh="{
+					queryMethod: handleQuery,
+				}"
+				:tools="[{ toolRender: { name: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }]"
+			>
+			</vxe-toolbar>
+			<div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
+				<vxe-table
+					border
+					:loading="state.loading"
+					:data="state.tableData"
+					:column-config="{ resizable: true }"
+					:row-config="{ isCurrent: true, isHover: true, height: 30, useKey: true }"
+					ref="tableRef"
+					height="auto"
+					auto-resize
+					:scrollY="{ enabled: true, gt: 100 }"
+					show-overflow
+					id="statisticsCenterDelayAudit"
+					:custom-config="{ storage: true }"
+					:params="{ exportMethod: statisticsCenterDelayAuditExport, exportParams: requestParams }"
+					showHeaderOverflow
+				>
+					<vxe-column field="userName" title="审批人"></vxe-column>
+					<vxe-column field="nodeName" title="审批环节名称"> </vxe-column>
+					<vxe-column field="totalNum" title="审批数量"></vxe-column>
+					<vxe-column field="passNum" title="同意数量"></vxe-column>
+					<vxe-column field="noPassNum" title="不同意数量"></vxe-column>
+				</vxe-table>
+			</div>
+			<pagination
+				@pagination="queryList"
+				:total="state.total"
+				v-model:current-page="state.queryParams.PageIndex"
+				v-model:page-size="state.queryParams.PageSize"
+				:disabled="state.loading"
+			/>
+		</div>
+	</div>
+</template>
+<script setup lang="tsx" name="statisticsCenterDelayAudit">
+import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { FormInstance } from 'element-plus';
+import { defaultDate } from '@/utils/constants';
+import Other from '@/utils/other';
+import { statisticsCenterDelayAudit, statisticsCenterDelayAuditExport } from '@/api/statistics/center';
+
+const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
+const pagination = defineAsyncComponent(() => import('@/components/ProTable/components/Pagination.vue')); // 分页
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive<any>({
+	queryParams: {
+		// 查询条件
+		PageIndex: 1,
+		PageSize: 20,
+		// 查询条件
+		UserName: null, // 审批人
+		NodeCode: null, // 审批环节
+		crTime: defaultDate, // 时间默认今天开始到今天结束
+		StartTime: null,
+		EndTime: null,
+	},
+	tableData: [],
+	loading: false, // 加载
+	total: 0, // 总数
+});
+/** 搜索按钮操作 */
+const handleQuery = () => {
+	state.queryParams.PageIndex = 1;
+	queryList();
+};
+/** 获取列表 */
+const requestParams = ref<EmptyObjectType>({});
+const queryList = () => {
+	state.loading = true;
+	requestParams.value = Other.deepClone(state.queryParams);
+	requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
+	requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+	Reflect.deleteProperty(requestParams.value, 'crTime');
+	statisticsCenterDelayAudit(requestParams.value)
+		.then((res: any) => {
+			state.tableData = res.result.items ?? [];
+			state.total = res.result?.total ?? 0;
+			state.loading = false;
+		})
+		.catch(() => {
+			state.loading = false;
+		});
+};
+/** 重置按钮操作 */
+const statisticalTimeRef = ref<RefType>();
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	statisticalTimeRef.value.reset();
+	queryList();
+};
+const toolbarRef = ref<RefType>();
+const tableRef = ref<RefType>();
+onMounted(() => {
+	queryList();
+	if (tableRef.value && toolbarRef.value) {
+		tableRef.value.connect(toolbarRef.value);
+	}
+});
+</script>