Javascript内置变量:top、parent、window.opener、iframe

本文深入探讨了Web开发中iframe、parent、window.opener等概念,详细解释了它们在页面嵌套与表单提交过程中的作用。通过实例演示,帮助开发者掌握如何在不刷新页面的情况下利用form表单进行数据提交,以及理解不同窗口对象之间的层级关系。
1.改变iframe值,controller打开的jsp将显示在iframe内,而不会打开一新窗口
2.parent指包含该iframe的父页面,而window.opener指用WINDOW.OPEN等方式创建的新窗口对应的原窗口。
3.form表单提交,其target指向隐藏iframe。target有多个属性,用于设置窗口打开位置,默认为指定iframe.当需要提交表单,同时不刷新当前页面的情况下,使用较多。
4.top:指分割窗口最高层次的浏览器窗口。如果计划从分割窗口的最高层次开始执行命令,就可以用top变量。
5.Window对象、Parent对象、Frame对象、Document对象和Form对象的阶层关系
Windwo对象→Parent对象→Frame对象→Document对象→Form对象,如下:
parent.frame1.document.forms[0].elements[0].value;
这是我父界面的内容<template> <div class="container"> <div class="preview-content" v-if="fileLink"> <iframe :src="fileLink" frameborder="0" class="preview-content-iframe" allow="fullscreen"></iframe> </div> <!--<div class="error-message" v-else> 该流程已评审或审批或链接加载失败,请检查网络或数据。 </div>--> </div> </template> <script> import { ref, onMounted } from 'vue' import { useRoute } from 'vue-router' import axios from 'axios' import { getUserInfo } from '@/api/workflow/fileApprovalFlow' export default { setup() { //这段代码我写的非常差劲,如果后人看到了希望你仔细研究优化一下 const route = useRoute() const fileLink = ref(null) const fetchPreviewLink = async () => { try { const tableId = route.query.TableId if (!tableId) { console.warn('缺少参数 TableId') return } debugger // 获取当前用户工号 const userInfo = await getUserInfo() const sWorkNum1 = userInfo.userName console.log(userInfo) const flowtask = await axios.get('/api/Flow_Task/getFlowInsTask1', { params: { TableId: tableId, id: userInfo.userName } }) if (flowtask.data.flow_Task != null) { debugger // 获取文件信息 const fileRes = await axios.get('/api/Flow_Task/getFile', { params: { TableId: tableId } }) const fileData = fileRes.data debugger if (!fileData) { debugger console.warn('未找到文件信息') return } console.log(fileData.result.reviewInfo) debugger if (fileData.result.reviewInfo == null) { debugger // 获取taskid const Task = await axios.get('/api/Flow_Task/getFlowInsTask', { params: { TableId: tableId } }) const task = Task.data const taskid = task.flow_Task.id const taskflowid = task.flow_Task.flowInstanceID const url = `/#/File_Approval_Flow/${taskflowid}/${taskid}?flowID=${'fileAbolishFlow'}` console.log('即将打开的路径:', url) // 打印路径 debugger fileLink.value = url return } const flowInstanceID = fileData.result.reviewInfo.id const count = fileData.result.count console.log(flowInstanceID) console.log(count) debugger // 获取流程ID const flowIdRes = await axios.get('/api/Flow_Task/getFlowID', { params: { flowInstanceID } }) const sFlowID = flowIdRes.data.flowID console.log(sFlowID) debugger if (count == 1) { if (sFlowID === 'fileBatchFlow') { const encodedReviewId = encodeURIComponent(flowInstanceID) const targetUrl = `/#/Drawings_Online_Approval_A?reviewId=${encodedReviewId}` fileLink.value = targetUrl //window.open(targetUrl, '_blank') } else if (sFlowID === 'fileReviewFlow') { debugger // 获取当前用户工号 const workNumRes = await axios.post('/api/Review_Info/getCurrentUserWorkNum') const sWorkNum = userInfo.userName // 构造跳转 URL const sFileCode = encodeURIComponent(fileData.result.reviewInfo.fileCode) const sReviewVersion = encodeURIComponent(fileData.result.reviewInfo.fileVersion) const targetUrl = `http://10.9.38.83:5773/#/document-review?sFileCode=${sFileCode}&sWorkNum=${encodeURIComponent( sWorkNum )}&sReviewVersion=${sReviewVersion}` console.log(targetUrl) debugger fileLink.value = targetUrl //window.open(targetUrl, '_blank') } else if (sFlowID === 'fileAbolishFlow') { // 获取taskid const Task = await axios.get('/api/Flow_Task/getFlowInsTask', { params: { TableId: tableId } }) const task = Task.data const taskid = task.flow_Task.id const taskflowid = task.flow_Task.flowInstanceID const url = `/#/File_Approval_Flow/${taskflowid}/${taskid}?flowID=${'fileAbolishFlow'}` console.log('即将打开的路径:', url) // 打印路径 debugger fileLink.value = url } } else { // 获取taskid const Task = await axios.get('/api/Flow_Task/getFlowInsTask', { params: { TableId: tableId } }) const task = Task.data const taskid = task.flow_Task.id const taskflowid = task.flow_Task.flowInstanceID const url = `/#/File_Approval_Flow/${taskflowid}/${taskid}?flowID=${'fileApprovalFlow'}` console.log('即将打开的路径:', url) // 打印路径 debugger fileLink.value = url //const childWindow = window.open(url, '_blank') //window.childWindow = childWindow // 挂到全局变量供子窗口访问 } } else { // 获取是否存在历史数据 const flowHistory = await axios.get('/api/Flow_Task/getFlowTaskHistory', { params: { TableId: tableId, id: sWorkNum1 } }) if (flowHistory.data.flowTaskHistory != null) { console.log(flowHistory) debugger if (flowHistory?.data?.flowTaskHistory?.node === '评委') { // 获取文件信息 debugger const fileRes = await axios.get('/api/Flow_Task/getFile', { params: { TableId: tableId } }) const fileData = fileRes.data debugger if (!fileData) { console.warn('未找到文件信息') return } const flowInstanceID = fileData.result.reviewInfo.id const count = fileData.result.count console.log(flowInstanceID) console.log(count) debugger // 获取流程ID const flowIdRes = await axios.get('/api/Flow_Task/getFlowID', { params: { flowInstanceID } }) const sFlowID = flowIdRes.data.flowID console.log(sFlowID) debugger if (sFlowID === 'fileBatchFlow') { const encodedReviewId = encodeURIComponent(flowInstanceID) const targetUrl = `/#/Drawings_Online_Approval_A?reviewId=${encodedReviewId}` fileLink.value = targetUrl //window.open(targetUrl, '_blank') } else if (sFlowID === 'fileReviewFlow') { debugger // 获取当前用户工号 const workNumRes = await axios.post('/api/Review_Info/getCurrentUserWorkNum') const sWorkNum = userInfo.userName // 构造跳转 URL const sFileCode = encodeURIComponent(fileData.result.reviewInfo.fileCode) const sReviewVersion = encodeURIComponent(fileData.result.reviewInfo.fileVersion) const targetUrl = `http://10.9.38.83:5773/#/document-review?sFileCode=${sFileCode}&sWorkNum=${encodeURIComponent( sWorkNum )}&sReviewVersion=${sReviewVersion}` fileLink.value = targetUrl //window.open(targetUrl, '_blank') } } else { const fallbackResponse1 = await axios.get('/api/Flow_Task/getFlowIns', { params: { TableId: route.query.TableId } }) debugger console.log(fallbackResponse1.data) const taskflowid2 = fallbackResponse1.data.flow_Instance.id console.log(taskflowid2) debugger const url2 = `/#/File_Approval_Flow/${taskflowid2}?flowID=${'fileApprovalFlow'}` console.log('即将打开的路径:', url2) // 打印路径 debugger fileLink.value = url2 } } else { debugger // 获取文件信息 const fileRes = await axios.get('/api/Flow_Task/getFile', { params: { TableId: tableId } }) const fileData = fileRes.data debugger if (!fileData) { console.warn('未找到文件信息') return } const flowInstanceID = fileData.result.reviewInfo.id const count = fileData.result.count console.log(flowInstanceID) console.log(count) debugger // 获取流程ID const flowIdRes = await axios.get('/api/Flow_Task/getFlowID', { params: { flowInstanceID } }) const sFlowID = flowIdRes.data.flowID console.log(sFlowID) debugger if (count == 1) { if (sFlowID === 'fileBatchFlow') { const encodedReviewId = encodeURIComponent(flowInstanceID) const targetUrl = `/#/Drawings_Online_Approval_A?reviewId=${encodedReviewId}` fileLink.value = targetUrl //window.open(targetUrl, '_blank') } else if (sFlowID === 'fileReviewFlow') { debugger // 获取当前用户工号 const workNumRes = await axios.post('/api/Review_Info/getCurrentUserWorkNum') const sWorkNum = userInfo.userName // 构造跳转 URL const sFileCode = encodeURIComponent(fileData.result.reviewInfo.fileCode) const sReviewVersion = encodeURIComponent(fileData.result.reviewInfo.fileVersion) const targetUrl = `http://10.9.38.83:5773/#/document-review?sFileCode=${sFileCode}&sWorkNum=${encodeURIComponent( sWorkNum )}&sReviewVersion=${sReviewVersion}` console.log(targetUrl) debugger fileLink.value = targetUrl //window.open(targetUrl, '_blank') } else if (sFlowID === 'fileAbolishFlow') { // 获取taskid const Task = await axios.get('/api/Flow_Task/getFlowInsTask', { params: { TableId: tableId } }) const task = Task.data const taskid = task.flow_Task.id const taskflowid = task.flow_Task.flowInstanceID const url = `/#/File_Approval_Flow/${taskflowid}/${taskid}?flowID=${'fileAbolishFlow'}` console.log('即将打开的路径:', url) // 打印路径 debugger fileLink.value = url } } else { // 获取taskid const Task = await axios.get('/api/Flow_Task/getFlowInsTask', { params: { TableId: tableId } }) const task = Task.data const taskid = task.flow_Task.id const taskflowid = task.flow_Task.flowInstanceID const url = `/#/File_Approval_Flow/${taskflowid}/${taskid}?flowID=${'fileApprovalFlow'}` console.log('即将打开的路径:', url) // 打印路径 debugger fileLink.value = url //const childWindow = window.open(url, '_blank') //window.childWindow = childWindow // 挂到全局变量供子窗口访问 } } } } catch (error) { console.log(error) debugger if (error) { debugger // 当返回 404 时调用备用接口 try { debugger const fallbackResponse = await axios.get('/api/Flow_Task/getFlowIns', { params: { TableId: route.query.TableId } }) debugger console.log(fallbackResponse.data) const taskflowid1 = fallbackResponse.data.flow_Instance.id console.log(taskflowid1) debugger const url1 = `/#/File_Approval_Flow/${taskflowid1}?flowID=${'fileApprovalFlow'}` console.log('即将打开的路径:', url1) // 打印路径 debugger fileLink.value = url1 } catch (error) { console.error('备用接口调用失败:') } } else { console.error('请求异常:', error) } } } onMounted(() => { fetchPreviewLink() // 监听来自新窗口的消息 window.addEventListener('message', (event) => { console.log(event.data) debugger if (event.data === 'closeIframe') { setTimeout(() => { //fileLink.value = null window.close() }, 0) // 2000 毫秒 = 2 秒 //window.childWindow.close() debugger } }) }) return { fileLink } } } </script> <style lang="less" scoped> .container { display: flex; width: 100%; height: 100%; flex-direction: row; flex-wrap: wrap; border-right: 1px solid rgb(182, 197, 240); border-bottom: 1px solid rgb(182, 197, 240); .error-message { font-size: 24px; color: #d90000; text-align: center; padding: 20px; width: 100%; height: 100%; } .preview-content { width: 100%; height: 100%; margin-top: 5px; border: 1px solid #dfeafc; .preview-content-iframe { width: 100%; height: 100%; } } } </style> ,这是我子界面的内容<template> <div class="approve-btns-container" v-if="isStartFlow === true || (isStartFlow === false && taskId !== '')" > <div class="comment-container" v-if="isFlowEnd === false"> <span>处理意见</span> <div class="comment-textarea-container"> <el-input :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" placeholder="请输入处理意见!" :resize="'none'" v-model="approveMsg" :maxlength="1000" show-word-limit /> </div> </div> <div class="approve-btns"> <!-- 审批流程使用 --> <!-- 发起 --> <el-button v-if="isStartFlow && !isReview" type="primary" :disabled="approveLoading" @click="start" > 发起 </el-button> <!-- 提交 --> <el-button v-if="!isStartFlow && !isFlowEnd && !isReview && !isLastNode" type="primary" :disabled="approveLoading||isDebouncing" @click="getSubmitNode" > 提交 </el-button> <!-- 结案提交按钮 --> <el-button v-if="!isStartFlow && !isFlowEnd && !isReview && isLastNode" type="primary" :disabled="approveLoading||isDebouncing" @click="getSubmitNode" > 结案 </el-button> <!-- 退回 --> <el-button v-if="!isStartFlow && !isFlowEnd && !isFirstNode && !isReview" type="primary" :disabled="approveLoading" @click="back" >退回</el-button > <!-- 驳回 --> <el-button type="primary" :disabled="approveLoading" v-if="!isStartFlow && !isFlowEnd && !isFirstNode && !isReview" @click="returnToLaun" > 驳回 </el-button> <!-- 转交 --> <el-button v-if="!isStartFlow && !isFlowEnd && !isFirstNode && !isReview" type="primary" :disabled="approveLoading" @click="openTransfer" > 转交 </el-button> <!-- 终止 --> <el-button v-if="!isFlowEnd && isFirstNode && !isReview" type="primary" :disabled="approveLoading" @click="stop" > {{ flowEntityData.flowID === 'fileAbolishFlow' ? '终止' : '退回评审' }} </el-button> <!-- 评审使用按钮 --> <!-- 评审通过 --> <el-button v-if="!isStartFlow && !isFlowEnd && !isFirstNode && isReview" type="primary" :disabled="approveLoading" @click="getPassNode" > 通过 </el-button> <!-- 评审不通过 --> <el-button v-if="!isStartFlow && !isFlowEnd && !isFirstNode && isReview" type="primary" :disabled="approveLoading" @click="getNotPassNode" > 不通过 </el-button> </div> <!-- 转交组织树 --> <organizeTreeDialog ref="transferOrganizeTree" v-model="transferUser" @submit="transfer" :title="'选择转交人'" :okText="'提交'" :isMultiChoice="false" ></organizeTreeDialog> <!-- 提交可能用到的组织树 --> <!-- <organizeTreeDialog ref="submitOrganizeTree"></organizeTreeDialog> --> </div> </template> <script setup> import { ref, defineProps, watch, onMounted,onBeforeUnmount } from 'vue' import organizeTreeDialog from './organize_tree_dialog.vue' import { ElMessage, componentSizeMap } from 'element-plus' import { submitApi, getSuperior, getManager, finishApi, backApi, rejectApi, stopApi, trasnferApi, signApi, passApi, notPassApi, passFinishApi, notPassFinishApi } from '@/api/workflow/fileApprovalFlow' const emits = defineEmits() // 防抖状态 const isDebouncing = ref(false) const props = defineProps({ isStartFlow: { type: Boolean }, isFlowEnd: { type: Boolean }, taskId: { type: String }, taskInfo: { type: Object }, nodeList: { type: Object }, flowEntity: Object }) const isFirstNode = ref(false) // 是否第一个节点 const isLastNode = ref(false) // 是否最后一个节点 const isReview = ref(false) // 是否时评审节点 const approveLoading = ref(false) // 是否加载中 const approveMsg = ref('') // 处理意见 const transferOrganizeTree = ref() const submitOrganizeTree = ref() const transferUser = ref([]) let curTask = {} const flowEntityData = ref({}) /** * 发起流程 */ function start() { emits('createFlow', approveMsg.value) } /** * 第一次审批,拟稿人发起流程后使用 */ function firstSubmit(task) { curTask = task getSubmitNode() } /** * 流程结案 */ function finish() { const finishData = { id: curTask.id, comment: approveMsg.value } finishApi(finishData).then((res) => { emits('otherFinish') }) setTimeout(() => { closePage() }, 500) } /** * 评审通过结案 */ function passFinish() { const passFinishData = { id: curTask.id, comment: approveMsg.value } passFinishApi(passFinishData).then((res) => { closePage() }) } /** * 评审不通过结案 */ function notPassFinish() { const notPassFinishData = { id: curTask.id, comment: approveMsg.value } notPassFinishApi(notPassFinishData).then((res) => { closePage() }) } /** * 流程提交,获取下一个节点审批人 */ async function getSubmitNode() { // 防止重复点击 if (isDebouncing.value || approveLoading.value) { return } isDebouncing.value = true debugger try{ const curNode = curTask.node const curAssigin = curTask.assignee console.log(props.nodeList) console.log(curNode) const nodeindex = props.nodeList.indexOf(curNode) if (nodeindex === -1) { alert('未查询到当前节点,进行xx操作') } // 最后一个节点,结案 if (nodeindex + 1 === props.nodeList.length) { finish() setTimeout(() => { window.location.reload() }, 1000) debugger setTimeout(() => { if (window.opener && !window.opener.closed) { window.opener.postMessage('closeIframe', '*') // 推荐指定域名 } else if (window.parent && window.parent !== window) { window.parent.postMessage('closeIframe', '*') debugger } else { console.warn('无法发送消息:window.opener 不存在或已关闭') } }, 1000) debugger return } // 拿到下一个节点 const nextNode = props.nodeList[nodeindex + 1] emits('setAssignUser', nextNode) setTimeout(() => { if (window.opener && !window.opener.closed) { window.opener.postMessage('closeIframe', '*') // 推荐指定域名 } else if (window.parent && window.parent !== window) { window.parent.postMessage('closeIframe', '*') debugger } else { console.warn('无法发送消息:window.opener 不存在或已关闭') } }, 1000) debugger }finally{ // 5秒后解除防抖 setTimeout(() => { isDebouncing.value = false }, 5000) } } /** * 评审通过,获取下一个节点审批人 */ async function getPassNode() { const curNode = curTask.node const curAssigin = curTask.assignee const nodeindex = props.nodeList.indexOf(curNode) if (nodeindex === -1) { alert('未查询到当前节点,进行xx操作') } // 最后一个节点,结案 if (nodeindex + 1 === props.nodeList.length) { passFinish() return } // 拿到下一个节点 const nextNode = props.nodeList[nodeindex + 1] emits('setPassUser', nextNode) if (window.opener && !window.opener.closed) { window.opener.postMessage('closeIframe', '*') } } /** * 评审不通过,获取下一个节点审批人 */ async function getNotPassNode() { const curNode = curTask.node const curAssigin = curTask.assignee const nodeindex = props.nodeList.indexOf(curNode) if (nodeindex === -1) { alert('未查询到当前节点,进行xx操作') } // 最后一个节点,结案 if (nodeindex + 1 === props.nodeList.length) { notPassFinish() return } // 拿到下一个节点 const nextNode = props.nodeList[nodeindex + 1] emits('setNotPassUser', nextNode) setTimeout(() => { window.location.reload() }, 1000) } /** * 提交 */ async function submit(Assignee, AssigneeName, node) { const submitData = { ID: curTask.id, Assignee: Assignee, AssigneeName: AssigneeName, Comment: approveMsg.value, Node: node } const submitRes = await submitApi(submitData) closePage() } /** * 评审通过 */ async function pass(Assignee, AssigneeName, node) { const submitData = { ID: curTask.id, Assignee: Assignee, AssigneeName: AssigneeName, Comment: approveMsg.value, Node: node } const submitRes = await passApi(submitData) closePage() } /** * 评审不通过 */ async function notPass(Assignee, AssigneeName, node) { const submitData = { ID: curTask.id, Assignee: Assignee, AssigneeName: AssigneeName, Comment: approveMsg.value, Node: node } const submitRes = await notPassApi(submitData) closePage() } /** * 发起会签 */ async function sign(Assignee, AssigneeName, node) { const submitData = { id: curTask.id, Assignee, AssigneeName, Comment: approveMsg.value, Node: node } const submitRes = await signApi(submitData) closePage() } /** * 退回流程 * 需要检查处理意见是否已输入,如果未输入,则显示警告消息 */ function back() { if (approveMsg.value.length == 0) { ElMessage.warning('请输入处理意见!') return } const backData = { id: curTask.id, comment: approveMsg.value } backApi(backData).then((res) => { setTimeout(() => { if (window.opener && !window.opener.closed) { window.opener.postMessage('closeIframe', '*') // 推荐指定域名 } else if (window.parent && window.parent !== window) { window.parent.postMessage('closeIframe', '*') debugger } else { console.warn('无法发送消息:window.opener 不存在或已关闭') } }, 0) closePage() }) } /** * 驳回到拟稿人 * 需要检查处理意见是否已输入,如果未输入,则显示警告消息 */ function returnToLaun() { if (approveMsg.value.length == 0) { ElMessage.warning('请输入处理意见!') return } const rejectData = { id: curTask.id, comment: approveMsg.value } rejectApi(rejectData).then((res) => { setTimeout(() => { if (window.opener && !window.opener.closed) { window.opener.postMessage('closeIframe', '*') // 推荐指定域名 } else if (window.parent && window.parent !== window) { window.parent.postMessage('closeIframe', '*') debugger } else { console.warn('无法发送消息:window.opener 不存在或已关闭') } }, 0) closePage() }) } /** * 打开转交流程组织树选择框 * 需要检查处理意见是否已输入,如果未输入,则显示警告消息 */ function openTransfer() { if (approveMsg.value.length == 0) { ElMessage.warning('请输入处理意见!') return } transferOrganizeTree.value.open() } /** * 转交流程 * @param {*} userStrList */ function transfer(userStrList) { if (userStrList.length === 0) { ElMessage.warning('请选择转交人') return } const userData = userStrList[0]['text'].split('-') const transferData = { id: curTask.id, assignee: userData[1], assigneeName: userData[0], comment: approveMsg.value } debugger if (flowEntityData.value.creatorID == userData[1]) { ElMessage.warning('不能转交给拟稿人审批!') return } trasnferApi(transferData).then(() => { setTimeout(() => { if (window.opener && !window.opener.closed) { window.opener.postMessage('closeIframe', '*') // 推荐指定域名 } else if (window.parent && window.parent !== window) { window.parent.postMessage('closeIframe', '*') debugger } else { console.warn('无法发送消息:window.opener 不存在或已关闭') } }, 0) closePage() }) } /** * 终止流程 * 需要检查处理意见是否已输入,如果未输入,则显示警告消息 */ function stop() { if (approveMsg.value.length == 0) { ElMessage.warning('请输入处理意见!') return } const stopData = { id: curTask.id, comment: approveMsg.value } stopApi(stopData).then((res) => { }) setTimeout(() => { closePage() }, 500) } /** * 检测是否是第一个节点 */ function testFirstNodeOrReview() { const curNode = curTask.node if (curNode === '评委') { isReview.value = true } else { isReview.value = false } if (curNode === props.nodeList[0]) { isFirstNode.value = true } else { isFirstNode.value = false } } //检测是否最后一个节点 function testIsLastNode() { const curNode = curTask.node const nodeindex = props.nodeList.indexOf(curNode) if (nodeindex + 1 === props.nodeList.length) { isLastNode.value = true } } function closePage() { localStorage.setItem('refresh', Math.random()) window.close() } onMounted(() => { if (props.taskInfo && props.taskInfo !== '') { curTask = Object.assign({}, props.taskInfo) testFirstNodeOrReview() } }) watch( () => props.taskInfo, (val) => { if (val && val !== '') { curTask = Object.assign({}, val) testFirstNodeOrReview() } } ) watch( () => props.nodeList, (val) => { if (val && val !== '') { testIsLastNode() } } ) watch( () => props.flowEntity, (val) => { flowEntityData.value = val ?? {} } ) defineExpose({ firstSubmit, sign, submit, pass, notPass }) </script> <style lang="less" scoped> .approve-btns-container { position: fixed; left: 50%; bottom: 0; width: 1400px; height: auto; padding-bottom: 5px; background-color: rgb(255, 255, 255); transform: translateX(-50%); z-index: 1000; .comment-container { display: flex; height: 56px; border: 1px solid #409eff; line-height: 52px; span { width: 8%; background-color: rgb(223, 234, 252); text-align: center; } .comment-textarea-container { box-sizing: border-box; width: 92%; height: 52px; padding: 1px; } } .approve-btns { display: flex; justify-content: center; width: 100%; margin-top: 8px; .el-button { background-color: rgb(9, 96, 189); color: rgb(255, 255, 255); border: 1px solid rgb(9, 96, 189); } } } </style> 我现在想让子界面如果不是通过父界面打开的话就跳过postmessage那些代码,我该如何修改
11-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值