Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer

This commit is contained in:
guochunsi
2026-02-02 18:33:02 +08:00
19 changed files with 214 additions and 27 deletions

View File

@@ -1,11 +0,0 @@
# port 端口号
VITE_PORT=8888
#浏览器自动打开
VITE_OPEN=true
# 本地环境
ENV=development
# ADMIN 服务地址
VITE_ADMIN_PROXY_PATH = http://scj-v3.zhxy.link/api

1
.gitignore vendored
View File

@@ -32,3 +32,4 @@ pnpm-lock.yaml
# cursor review
final_review_gate.py
.cursor/
.env.development

Binary file not shown.

Binary file not shown.

View File

@@ -16,7 +16,8 @@ import SignInput from "./sign/index.vue";
// vite glob导入
const modules: Record<string, () => Promise<unknown>> = import.meta.glob(
['../../views/jsonflow/*/*.vue', '../../views/order/*/*.vue', '../../views/purchase/*/*.vue']
['../../views/jsonflow/*/*.vue', '../../views/order/*/*.vue',
'../../views/purchase/*/*.vue','../../views/finance/purchasingrequisition/add.vue']
)
/**

View File

@@ -29,6 +29,7 @@ declare module 'vue-router' {
isIframe?: boolean;
roles?: string[];
icon?: string;
activeMenu?: string; // 指定菜单高亮的路径
}
}
@@ -98,6 +99,14 @@ export const staticRoutes: Array<RouteRecordRaw> = [
isAuth: true,
},
},
{
path: '/finance/purchasingrequisition/add',
name: 'purchasingrequisition.add',
component: () => import('/@/views/finance/purchasingrequisition/add.vue'),
meta: {
isAuth: false, // 不需要认证,纯页面展示
},
},
...staticRoutesFlow
];

View File

@@ -422,6 +422,16 @@
</el-form-item>
</template>
<!-- 特殊情况紧急 -->
<template v-if="isUrgentSpecial">
<el-form-item label="会议纪要" prop="meetingMinutesUrgent" class="mb20">
<upload-file
v-model="dataForm.meetingMinutesUrgent"
:limit="5"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
<!-- 特殊情况单一来源 -->
<template v-if="dataForm.isSpecial === '2'">
<el-form-item
@@ -632,7 +642,7 @@
</template>
<script setup lang="ts" name="PurchasingRequisitionAdd">
import { reactive, ref, onMounted, computed, watch } from 'vue'
import { reactive, ref, onMounted, computed, watch, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { addObj, tempStore } from '/@/api/finance/purchasingrequisition';
import { getTree } from '/@/api/finance/purchasingcategory';
@@ -682,6 +692,7 @@ const dataForm = reactive({
purchaseRequirement: '',
meetingMinutes: '',
feasibilityReport: '',
meetingMinutesUrgent: '',
meetingMinutesSingle: '',
meetingMinutesImport: '',
singleSourceProof: '',
@@ -735,6 +746,13 @@ const isDeptPurchase = computed(() => {
return false;
});
// 判断是否为紧急情况ID: 6509b59e24c1c6568f4277e544f3e55e
const isUrgentSpecial = computed(() => {
const urgentItem = isSpecialList.value.find(item => item.id === '6509b59e24c1c6568f4277e544f3e55e');
if (!urgentItem) return false;
return dataForm.isSpecial === urgentItem.value;
});
// 第二步标题
const stepTwoTitle = computed(() => {
return isDeptPurchase.value ? '部门自行采购' : '学校统一采购';
@@ -885,7 +903,7 @@ const downloadTemplate = async (type: string) => {
'direct_select': { fileName: '服务商城项目需求模板(直选).doc', displayName: '服务商城项目需求模板(直选).doc' },
'public_select': { fileName: '服务商城项目需求模板(公开比选).doc', displayName: '服务商城项目需求模板(公开比选).doc' },
'invite_select': { fileName: '服务商城项目需求模板(邀请比选).doc', displayName: '服务商城项目需求模板(邀请比选).doc' },
'purchase_requirement': { fileName: '表1采购需求填报模板.xlsx', displayName: '采购需求填报模板.xlsx' },
'purchase_requirement': { fileName: '表1采购需求填报模板.doc', displayName: '采购需求填报模板.doc' },
'import_application': { fileName: '附件1进口产品申请及专家论证意见表.doc', displayName: '进口产品申请及专家论证意见表.doc' },
'single_source': { fileName: '表7单一来源论专家证附件.docx', displayName: '单一来源论专家证附件.docx' },
'feasibility_report': { fileName: '表6项目可行性论证报告模板.doc', displayName: '项目可行性论证报告模板.doc' },
@@ -987,7 +1005,14 @@ const prevStep = () => {
// 取消
const handleCancel = () => {
router.back();
// 如果是在 iframe 中,向父窗口发送关闭消息
if (window.parent !== window) {
window.parent.postMessage({
type: 'purchasingrequisition:close'
}, '*');
} else {
router.back();
}
};
// 获取品目树形数据
@@ -1207,8 +1232,8 @@ const handleSubmit = async () => {
'businessNegotiationTable', 'marketPurchaseMinutes', 'onlineMallMaterials',
'serviceDirectSelect', 'servicePublicSelect', 'purchaseRequirementTemplate',
'serviceInviteSelect', 'servicePublicSelectAuto', 'purchaseRequirement',
'meetingMinutes', 'feasibilityReport', 'meetingMinutesSingle',
'meetingMinutesImport', 'singleSourceProof', 'importApplication',
'meetingMinutes', 'feasibilityReport', 'meetingMinutesUrgent',
'meetingMinutesSingle', 'meetingMinutesImport', 'singleSourceProof', 'importApplication',
'governmentPurchaseIntent', 'servicePublicSelectSchool'
];
@@ -1220,7 +1245,15 @@ const handleSubmit = async () => {
await addObj(submitData);
useMessage().success('提交成功');
router.push('/finance/purchasingrequisition');
// 如果是在 iframe 中,向父窗口发送消息
if (window.parent !== window) {
window.parent.postMessage({
type: 'purchasingrequisition:submitSuccess'
}, '*');
} else {
router.push('/finance/purchasingrequisition');
}
} catch (err: any) {
useMessage().error(err.msg || '提交失败');
} finally {
@@ -1248,8 +1281,8 @@ const handleTempStore = async () => {
'businessNegotiationTable', 'marketPurchaseMinutes', 'onlineMallMaterials',
'serviceDirectSelect', 'servicePublicSelect', 'purchaseRequirementTemplate',
'serviceInviteSelect', 'servicePublicSelectAuto', 'purchaseRequirement',
'meetingMinutes', 'feasibilityReport', 'meetingMinutesSingle',
'meetingMinutesImport', 'singleSourceProof', 'importApplication',
'meetingMinutes', 'feasibilityReport', 'meetingMinutesUrgent',
'meetingMinutesSingle', 'meetingMinutesImport', 'singleSourceProof', 'importApplication',
'governmentPurchaseIntent', 'servicePublicSelectSchool'
];
@@ -1261,7 +1294,15 @@ const handleTempStore = async () => {
await tempStore(submitData);
useMessage().success('暂存成功');
router.push('/finance/purchasingrequisition');
// 如果是在 iframe 中,向父窗口发送消息
if (window.parent !== window) {
window.parent.postMessage({
type: 'purchasingrequisition:submitSuccess'
}, '*');
} else {
router.push('/finance/purchasingrequisition');
}
} catch (err: any) {
useMessage().error(err.msg || '暂存失败');
} finally {
@@ -1292,6 +1333,31 @@ watch(() => categoryTreeData.value, () => {
// 初始化
onMounted(async () => {
// 检测是否在 iframe 中,如果是,则修改相关元素的 overflow 样式以支持滚动
if (window.parent !== window) {
nextTick(() => {
// 修改 html 和 body
document.documentElement.style.overflow = 'auto';
document.documentElement.style.height = 'auto';
document.body.style.overflow = 'auto';
document.body.style.height = 'auto';
// 修改 html 和 body添加类名
document.documentElement.classList.add('iframe-mode');
document.body.classList.add('iframe-mode');
// 修改 #app
const app = document.getElementById('app');
if (app) {
app.style.overflow = 'auto';
app.style.height = 'auto';
app.style.minHeight = '100%';
// 添加一个类名标记,方便样式控制
app.classList.add('iframe-mode');
}
});
}
await Promise.all([
getCategoryTreeData(),
getFundSourceDict(),
@@ -1341,3 +1407,26 @@ onMounted(async () => {
}
</style>
<style>
/* 当页面在 iframe 中时,允许滚动 */
html.iframe-mode,
body.iframe-mode {
overflow: auto !important;
height: 100% !important;
min-height: 100% !important;
}
#app.iframe-mode {
overflow: auto !important;
height: auto !important;
min-height: 100% !important;
}
/* 在 iframe 模式下,修改页面容器样式 */
.iframe-mode .modern-page-container {
min-height: auto !important;
height: auto !important;
overflow: visible !important;
}
</style>

View File

@@ -213,13 +213,33 @@
</el-card>
</div>
<!-- 新增页面 iframe 对话框 -->
<el-dialog
v-model="showAddIframe"
title="新增采购申请"
width="1000px"
:close-on-click-modal="false"
:close-on-press-escape="true"
destroy-on-close
class="iframe-dialog"
@close="closeAddIframe">
<div class="iframe-dialog-content">
<iframe
ref="addIframeRef"
:src="addIframeSrc"
frameborder="0"
class="add-iframe"
@load="onIframeLoad" />
</div>
</el-dialog>
<!-- 编辑新增表单对话框 -->
<FormDialog ref="formDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="PurchasingRequisition">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { ref, reactive, defineAsyncComponent, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj } from "/@/api/finance/purchasingrequisition";
@@ -235,6 +255,9 @@ const tableRef = ref()
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const showAddIframe = ref(false)
const addIframeRef = ref<HTMLIFrameElement>()
const addIframeSrc = ref('')
/**
* 定义响应式表格数据
@@ -265,12 +288,42 @@ const handleReset = () => {
};
/**
* 新增采购申请 - 跳转到新页面
* 新增采购申请 - 在 iframe 中展示
*/
const handleAdd = () => {
router.push({
path: '/finance/purchasingrequisition/add'
});
// 构建 iframe 的 src使用当前页面的 hash 路由
const baseUrl = window.location.origin + window.location.pathname
addIframeSrc.value = `${baseUrl}#/finance/purchasingrequisition/add`
showAddIframe.value = true
// 监听来自 iframe 的消息
window.addEventListener('message', handleIframeMessage)
};
/**
* 关闭新增 iframe
*/
const closeAddIframe = () => {
showAddIframe.value = false
// 移除消息监听器
window.removeEventListener('message', handleIframeMessage)
};
/**
* 处理 iframe 发送的消息
*/
const handleIframeMessage = (event: MessageEvent) => {
// 验证消息来源(可选,根据实际需求)
// if (event.origin !== window.location.origin) return
if (event.data && event.data.type === 'purchasingrequisition:submitSuccess') {
// 提交成功,关闭 iframe 并刷新列表
closeAddIframe()
getDataList()
useMessage().success('提交成功')
} else if (event.data && event.data.type === 'purchasingrequisition:close') {
// 关闭 iframe
closeAddIframe()
}
};
/**
@@ -296,9 +349,54 @@ const handleDelete = async (row: any) => {
useMessage().error(err.msg || '删除失败');
}
};
// 组件卸载时清理事件监听器
onUnmounted(() => {
window.removeEventListener('message', handleIframeMessage)
});
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
.iframe-dialog-content {
width: 100%;
height: 70vh;
min-height: 500px;
max-height: calc(100vh - 200px);
position: relative;
overflow: hidden;
.add-iframe {
width: 100%;
height: 100%;
min-height: 500px;
border: none;
display: block;
}
}
:deep(.iframe-dialog) {
.el-dialog {
display: flex;
flex-direction: column;
max-height: 90vh;
margin-top: 5vh !important;
}
.el-dialog__header {
flex-shrink: 0;
padding: 20px 20px 10px;
}
.el-dialog__body {
padding: 20px;
overflow-y: auto;
overflow-x: hidden;
flex: 1;
min-height: 0;
max-height: calc(100vh - 200px);
}
}
</style>