当前位置: 首页 > article >正文

ELectron 中 BrowserView 如何进行实时定位和尺寸调整

背景

BrowserView 是继 Webview 后推出来的高性能多视图管理工具,与 Webview 最大的区别是,Webview 是一个 DOM 节点,依附于主渲染进程的附属进程,Webview 节点的崩溃会导致主渲染进程的连锁反应,会引起软件的崩溃。

而 BrowserView 可以理解为比主渲染窗口更简洁的窗口,砍掉了一些窗口没必要的功能,只保留渲染视窗,且这个视窗是独立于主渲染进程的,但其所处层次和相对位置,取决于其追加进来的主渲染窗口。

代码分支

electron-demo: electron 22 初始代码开发和讲解 - Gitee.com

思想启发

什么叫代理?代理何时会用?在这个场景中就出现了一种必然概念,被代理者并不能直接参与当前操作中的环节,但是它又时时刻刻参与到操作中来,此时,采用代理的方式,将必要参数实时传递给被代理者,就达到了无缝增强的概念,例如,一个谋士非常厉害,但因犯了罪不能当官,若想继续实现自己才能,则需要一个当官的能实时与他实时通信,保持执行策略的通畅,当官的和谋士就是必不可缺的一对组合。

  1. 被代理者无法直接融入到操作环境中来,但是被代理者的表现方式又与操作环境密不可分

BrowserView 与 Webview 的差异比较

BrowserView相比WebView有几个显著优势:

性能更好 BrowserView使用独立的渲染进程,不需要像WebView那样在主窗口中嵌入,避免了额外的渲染开销。WebView本质上是一个DOM元素,会增加主窗口的渲染负担。

更灵活的布局控制 BrowserView可以通过setBounds()方法精确控制位置和大小,支持动态调整。而WebView受限于CSS布局,在复杂场景下布局控制较为受限。

更好的进程隔离 BrowserView运行在完全独立的渲染进程中,崩溃时不会影响主窗口。WebView虽然也有进程隔离,但与主窗口的耦合度更高。

资源消耗更低 BrowserView不需要额外的DOM操作和CSS渲染,内存占用通常更小。特别是在需要多个Web视图的场景下,差异更明显。

更现代的API设计 BrowserView的API更加简洁和现代化,避免了WebView中的一些历史包袱和兼容性问题。

更好的安全性 由于完全独立的进程和更严格的沙箱机制,BrowserView在安全性方面表现更好。

最麻烦的 BrowserView 定位问题

BrowserView 有很多优势,但是作为独立的渲染单位,定位和宽高完全适配就成了很大的问题,但是从源码 Franz 中我们得到了启发,那就是代理机制,让一个 div 占据这个区域,采用响应式布局时,监听该 div 的相对主窗口的定位和宽高变化,然后再将这个参数,实时发送给 BrowserView,BrowserView 就可以实现实时定位了

实现效果录屏

实现代码

main/index.js 代码 和 renderer/App.vue 代码

最核心的就是 ResizeObserver 对象,可以实时监听中间的 div 的偏移和宽高,父容器偏移那个是 0,0,不应该有那块逻辑,AI 生成的,我也懒得删,没有影响

主渲染使用Vue脚手架开发的,所以给的Vue脚手架下的组件代码App.vue

// main 主进程代码const { app, BrowserWindow, BrowserView, ipcMain } = require('electron');
const path = require('path');let mainWindow;
let browserView;const winURL = process.env.NODE_ENV === 'development'? `http://localhost:9080`: `file://${__dirname}/index.html`function createWindow() {mainWindow = new BrowserWindow({width: 1000,height: 600,webPreferences: {preload: path.join(__dirname, 'renderer.js'),nodeIntegration: true,contextIsolation: false, // 简化示例,禁用上下文隔离},});mainWindow.loadURL(winURL);browserView = new BrowserView();mainWindow.setBrowserView(browserView);browserView.setBounds({ x: 200, y: 0, width: 600, height: 600 }); // 初始值browserView.webContents.loadURL('https://web.whatsapp.com');ipcMain.on('update-center-div', (event, { x, y, width, height }) => {console.log(`Received center div bounds: x=${x}, y=${y}, width=${width}, height=${height}`);browserView.setBounds({x: Math.round(x),y: Math.round(y),width: Math.round(width),height: Math.round(height),});});mainWindow.on('closed', () => {mainWindow = null;browserView = null;});
}app.on('ready', createWindow);app.on('window-all-closed', () => {if (process.platform !== 'darwin') {app.quit();}
});app.on('activate', () => {if (mainWindow === null) {createWindow();}
});

App.vue 全部代码

// renderer 进程的 App.vue 代码
<template><div class="container"><div id="left" class="panel" ref="leftPanel" :style="{ width: leftWidthStyle }"><div class="content">左侧内容</div></div><div class="resize-handle" @mousedown="startDragging('left', $event)"></div><div id="center" class="panel" ref="center"></div><div class="resize-handle" @mousedown="startDragging('right', $event)"></div><div id="right" class="panel" ref="rightPanel" :style="{ width: rightWidthStyle }"><div class="content">右侧内容</div></div><div id="info" v-if="width > 0 || height > 0">中心div相对于父容器偏移: ({{ offsetX.toFixed(2) }}, {{ offsetY.toFixed(2) }}) px<br>宽度: {{ width.toFixed(2) }} px<br>高度: {{ height.toFixed(2) }} px</div></div>
</template><script>
// Assuming ipcRenderer is correctly set up (e.g., via preload script or nodeIntegration:true)
const { ipcRenderer } = require('electron'); // Or window.electronAPI if using contextBridgeexport default {name: 'App',data() {return {// Use null or a specific initial value like 'auto' if you prefer,// then handle it in computed. Here, 0 will mean 'auto' initially.actualLeftWidth: 0, // Store the numeric width in pixelsactualRightWidth: 0, // Store the numeric width in pixelsisDragging: null,    // 'left' or 'right'startX: 0,           // Mouse X position at drag startstartPanelDragWidth: 0, // Width of the panel being dragged at drag startoffsetX: 0,offsetY: 0,width: 0,height: 0,resizeObserver: null,};},computed: {leftWidthStyle() {// If actualLeftWidth is 0, treat it as 'auto' to let content define it initially.// Otherwise, use the pixel value.return this.actualLeftWidth > 0 ? `${this.actualLeftWidth}px` : 'auto';},rightWidthStyle() {return this.actualRightWidth > 0 ? `${this.actualRightWidth}px` : 'auto';}},methods: {initializePanelWidths() {// $nextTick ensures the DOM is updated and refs are available.this.$nextTick(() => {if (this.$refs.leftPanel) {// If 'auto' (actualLeftWidth is 0), set actualLeftWidth to its rendered content width.if (this.actualLeftWidth === 0) {this.actualLeftWidth = this.$refs.leftPanel.clientWidth;}} else {console.warn("Ref 'leftPanel' not found during initialization.");}if (this.$refs.rightPanel) {if (this.actualRightWidth === 0) {this.actualRightWidth = this.$refs.rightPanel.clientWidth;}} else {console.warn("Ref 'rightPanel' not found during initialization.");}// Update center div info after initial widths are potentially setthis.updateCenterDivInfo();});},startDragging(side, event) {if (!event) return;this.isDragging = side;this.startX = event.clientX;let targetPanelRef;if (side === 'left') {targetPanelRef = this.$refs.leftPanel;if (!targetPanelRef) {console.error("Left panel ref not found for dragging.");return;}// Get the current rendered width as the starting point for draggingthis.startPanelDragWidth = targetPanelRef.clientWidth;// If the panel's width was 'auto' (actualLeftWidth is 0),// set actualLeftWidth to its current clientWidth so drag calculations have a numeric base.if (this.actualLeftWidth === 0) this.actualLeftWidth = this.startPanelDragWidth;} else if (side === 'right') {targetPanelRef = this.$refs.rightPanel;if (!targetPanelRef) {console.error("Right panel ref not found for dragging.");return;}this.startPanelDragWidth = targetPanelRef.clientWidth;if (this.actualRightWidth === 0) this.actualRightWidth = this.startPanelDragWidth;}document.addEventListener('mousemove', this.onMouseMove);document.addEventListener('mouseup', this.stopDragging);document.body.style.userSelect = 'none'; // Prevent text selection globally},onMouseMove(event) {if (!this.isDragging) return;event.preventDefault();const deltaX = event.clientX - this.startX;let newWidth;if (this.isDragging === 'left') {newWidth = this.startPanelDragWidth + deltaX;this.actualLeftWidth = Math.max(50, Math.min(newWidth, 400)); // Apply constraints} else if (this.isDragging === 'right') {newWidth = this.startPanelDragWidth - deltaX; // Subtract delta for right panelthis.actualRightWidth = Math.max(50, Math.min(newWidth, 400));}// The ResizeObserver on #center will trigger updateCenterDivInfo},stopDragging() {if (!this.isDragging) return;this.isDragging = null;document.removeEventListener('mousemove', this.onMouseMove);document.removeEventListener('mouseup', this.stopDragging);document.body.style.userSelect = ''; // Re-enable text selection},updateCenterDivInfo() {const centerEl = this.$refs.center;if (centerEl && centerEl.parentElement) {const rect = centerEl.getBoundingClientRect();const parentRect = centerEl.parentElement.getBoundingClientRect();this.offsetX = rect.left - parentRect.left;this.offsetY = rect.top - parentRect.top;this.width = rect.width;this.height = rect.height; // This will be 100% of parent due to CSS if parent has heightif (ipcRenderer && typeof ipcRenderer.send === 'function') {ipcRenderer.send('update-center-div', {x: this.offsetX,y: this.offsetY,width: this.width,height: this.height,});}}},},mounted() {this.initializePanelWidths(); // This will use $nextTickif (this.$refs.center) {this.resizeObserver = new ResizeObserver(() => {this.updateCenterDivInfo();});this.resizeObserver.observe(this.$refs.center);} else {// Fallback if center ref isn't immediately available (less likely for direct refs)this.$nextTick(() => {if (this.$refs.center) {this.resizeObserver = new ResizeObserver(() => {this.updateCenterDivInfo();});this.resizeObserver.observe(this.$refs.center);this.updateCenterDivInfo(); // Call once if observer set up late} else {console.error("Center panel ref ('center') not found on mount for ResizeObserver.");}});}window.addEventListener('resize', this.updateCenterDivInfo);},beforeDestroy() {window.removeEventListener('resize', this.updateCenterDivInfo);if (this.resizeObserver) {if (this.$refs.center) { // Check if ref still exists before unobservingthis.resizeObserver.unobserve(this.$refs.center);}this.resizeObserver.disconnect();}// Clean up global listeners if component is destroyed mid-dragdocument.removeEventListener('mousemove', this.onMouseMove);document.removeEventListener('mouseup', this.stopDragging);document.body.style.userSelect = '';},
};
</script><style>
/* For 100% height to work all the way up */
html, body, #app { /* Assuming #app is your Vue mount point */height: 100%;margin: 0;padding: 0;overflow: hidden; /* Often good for the root to prevent unexpected scrollbars */
}.container {display: flex;width: 100%;height: 100%; /* Will fill its parent (e.g., #app) */position: relative; /* For #info positioning */background-color: #f0f0f0; /* Light grey background for the container itself */
}.panel {height: 100%; /* Panels will fill the .container's height */overflow: auto; /* Add scrollbars if content overflows */box-sizing: border-box; /* Includes padding and border in the element's total width and height */
}#left {background: #ffdddd; /* Lighter Red *//* width: auto; initially, will be set by content or actualLeftWidth */
}#center {flex: 1; /* Takes up remaining space */background: #e0e0e0; /* Light grey for center, instead of transparent */min-width: 50px; /* Prevent center from collapsing too much */display: flex; /* If you want to align content within the center panel */flex-direction: column;/* border-left: 1px solid #ccc;border-right: 1px solid #ccc; */
}#right {background: #ddffdd; /* Lighter Green *//* width: auto; initially, will be set by content or actualRightWidth */
}.resize-handle {width: 6px; /* Slightly wider for easier grabbing */background: #b0b0b0; /* Darker grey for handle */cursor: ew-resize;flex-shrink: 0; /* Prevent handles from shrinking if container space is tight */user-select: none; /* Prevent text selection on the handle itself */z-index: 10;display: flex; /* To center an icon or visual cue if you add one */align-items: center;justify-content: center;
}
/* Optional: add a visual indicator to the handle */
/* .resize-handle::before {content: '⋮';color: #fff;font-size: 12px;line-height: 0;
} */#info {position: absolute;top: 10px;left: 10px;background: rgba(255, 255, 255, 0.95);padding: 8px 12px;font-size: 13px;border-radius: 3px;z-index: 1000;border: 1px solid #d0d0d0;box-shadow: 0 1px 3px rgba(0,0,0,0.1);font-family: monospace;
}.content {padding: 15px; /* More padding */white-space: nowrap; /* This makes clientWidth reflect this content */min-height: 50px; /* Example */color: #333;
}
</style>

相关文章:

ELectron 中 BrowserView 如何进行实时定位和尺寸调整

背景 BrowserView 是继 Webview 后推出来的高性能多视图管理工具&#xff0c;与 Webview 最大的区别是&#xff0c;Webview 是一个 DOM 节点&#xff0c;依附于主渲染进程的附属进程&#xff0c;Webview 节点的崩溃会导致主渲染进程的连锁反应&#xff0c;会引起软件的崩溃。 …...

深兰科技董事长陈海波率队考察南京,加速AI大模型区域落地应用

近日&#xff0c;深兰科技创始人、董事长陈海波受邀率队赴南京市&#xff0c;先后考察了南京高新技术产业开发区与鼓楼区&#xff0c;就推进深兰AI医诊大模型在南京的落地应用&#xff0c;与当地政府及相关部门进行了深入交流与合作探讨。 此次考察聚焦于深兰科技自主研发的AI医…...

《深度关系-从建立关系到彼此信任》

陈海贤老师推荐的书&#xff0c;花了几个小时&#xff0c;感觉现在的人与人之间特别缺乏这种深度的关系&#xff0c;但是与一个人建立深度的关系并没有那么简单&#xff0c;反正至今为止&#xff0c;自己好像没有与任何一个人建立了这种深度的关系&#xff0c;那种双方高度同频…...

IT选型指南:电信行业需要怎样的服务器?

从第一条电报发出的 那一刻起 电信技术便踏上了飞速发展的征程 百余年间 将世界编织成一个紧密相连的整体 而在今年 我们迎来了第25届世界电信日 同时也是国际电联成立的第160周年 本届世界电信日的主题为:“弥合性别数字鸿沟,为所有人创造机遇”,但在新兴技术浪潮汹涌…...

【ConvLSTM第二期】模拟视频帧的时序建模(Python代码实现)

目录 1 准备工作&#xff1a;python库包安装1.1 安装必要库 案例说明&#xff1a;模拟视频帧的时序建模ConvLSTM概述损失函数说明&#xff08;python全代码&#xff09; 参考 ConvLSTM的原理说明可参见另一博客-【ConvLSTM第一期】ConvLSTM原理。 1 准备工作&#xff1a;pytho…...

[VMM]分享一个用SystemC编写的页表管理程序

分享一个用SystemC编写的页表管理程序 摘要:分享一个用SystemC编写的页表管理的程序,这个程序将模拟页表(PDE和PTE)的创建、虚拟地址(VA)到物理地址(PA)的转换,以及对内存的读写操作。 为了简化实现,我们做出以下假设: 页表是两级结构:PDE (Page Directory…...

将docker数据目录迁移到 home目录下

将 Docker 数据目录从默认位置&#xff08;通常是 /var/lib/docker&#xff09;迁移到 /home 目录下&#xff0c;可以通过几个步骤来完成。以下是详细的迁移步骤&#xff1a; 步骤 1&#xff1a;停止 Docker 服务 在进行任何操作之前&#xff0c;确保先停止 Docker 服务以避免…...

【论文解读】DETR: 用Transformer实现真正的End2End目标检测

1st authors: About me - Nicolas Carion‪Francisco Massa‬ - ‪Google Scholar‬ paper: [2005.12872] End-to-End Object Detection with Transformers ECCV 2020 code: facebookresearch/detr: End-to-End Object Detection with Transformers 1. 背景 目标检测&#…...

Pytest 是什么

Pytest 是 Python 生态中最流行的 测试框架&#xff0c;用于编写、运行和组织单元测试、功能测试甚至复杂的集成测试。它以简洁的语法、强大的插件系统和高度可扩展性著称&#xff0c;广泛应用于 Python 项目的自动化测试中。以下是其核心特性和使用详解&#xff1a; Pytest 的…...

ElasticSearch简介及常用操作指南

一. ElasticSearch简介 ElasticSearch 是一个基于 Lucene 构建的开源、分布式、RESTful 风格的搜索和分析引擎。 1. 核心功能 强大的搜索能力 它能够提供全文检索功能。例如&#xff0c;在海量的文档数据中&#xff0c;可以快速准确地查找到包含特定关键词的文档。这在处理诸如…...

缓存常见问题:缓存穿透、缓存雪崩以及缓存击穿

缓存常见问题 一、缓存穿透 (Cache Penetration) 是什么 缓存穿透是指客户端持续请求一个缓存和数据库中都根本不存在的数据。这导致每次请求都会先查缓存&#xff08;未命中&#xff09;&#xff0c;然后穿透到数据库查询&#xff08;也未命中&#xff09;。如果这类请求量…...

纤维组织效应偏斜如何影响您的高速设计

随着比特率继续飙升&#xff0c;光纤编织效应 &#xff08;FWE&#xff09; 偏移&#xff0c;也称为玻璃编织偏移 &#xff08;GWS&#xff09;&#xff0c;正变得越来越成为一个问题。今天的 56GB/s 是高速路由器中最先进的&#xff0c;而 112 GB/s 指日可待。而用于个人计算机…...

【深度学习】sglang 的部署参数详解

SGLang 的部署参数详解 SGLang(Structured Generation Language)是一个高性能的大语言模型推理框架,专为结构化生成和多模态应用设计。本文将全面介绍SGLang的部署参数,帮助你充分发挥其性能潜力。 🚀 SGLang 项目概览 SGLang是由UC Berkeley开发的新一代LLM推理引擎,…...

SDL2常用函数:SDL_RendererSDL_CreateRendererSDL_RenderCopySDL_RenderPresent

SDL 渲染器系统详解 SDL_Renderer 概述 SDL_Renderer 是 SDL 2.0 引入的核心渲染抽象&#xff0c;它提供了一种高效的、硬件加速的 2D 渲染方式&#xff0c;比传统的表面(Surface)操作更加高效和灵活。 主要函数 1. SDL_CreateRenderer - 创建渲染器 SDL_Renderer* SDL_Cr…...

[git]忽略.gitignore文件

git rm --cached .gitignore 是一个 Git 命令,主要用于 从版本控制中移除已追踪的 .gitignore 文件,但保留该文件在本地工作目录中。以下是详细解析: 一、命令拆解与核心作用 语法解析 git rm:Git 的删除命令,用于从版本库(Repository)中移除文件。--cached:关键参数…...

FEMFAT许可的有效期限

在工程仿真领域&#xff0c;FEMFAT作为一款领先的疲劳分析软件&#xff0c;为用户提供了强大的功能和卓越的性能。然而&#xff0c;为了确保软件的合法使用和持续合规&#xff0c;了解FEMFAT许可的有效期限至关重要。本文将为您详细解读FEMFAT许可的有效期限&#xff0c;帮助您…...

Rust使用Cargo构建项目

文章目录 你好&#xff0c;Cargo&#xff01;验证Cargo安装使用Cargo创建项目新建项目配置文件解析默认代码结构 Cargo工作流常用命令速查表详细使用说明1. 编译项目2. 运行程序3.快速检查4. 发布版本构建 Cargo的设计哲学约定优于配置工程化优势 开发建议1. 新项目初始化​2. …...

Python训练营打卡Day39

DAY 39 图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 作业&#xff1a;今日代码较少&#xff0c;理解内容…...

UE5蓝图中播放背景音乐和使用代码播放声音

UE5蓝图中播放背景音乐 1.创建背景音乐Cube 2.勾选looping 循环播放背景音乐 3.在关卡蓝图中 Event BeginPlay-PlaySound2D Sound选择自己创建的Bgm_Cube 蓝图播放声音方法二&#xff1a; 使用代码播放声音方法一 .h文件中 头文件引用 #include "Kismet/GameplayS…...

AI 赋能数据可视化:漏斗图制作的创新攻略

在数据可视化的广阔天地里&#xff0c;漏斗图以其独特的形状和强大的功能&#xff0c;成为展示流程转化、分析数据变化的得力助手。传统绘制漏斗图的方式往往需要耗费大量时间和精力&#xff0c;对使用者的绘图技能和软件操作熟练度要求颇高。但随着技术的蓬勃发展&#xff0c;…...

用 Python 模拟下雨效果

用 Python 模拟下雨效果 雨天别有一番浪漫情怀&#xff1a;淅淅沥沥的雨滴、湿润的空气、朦胧的光影……在屏幕上也能感受下雨的美妙。本文将带你用一份简单的 Python 脚本&#xff0c;手把手实现「下雨效果」动画。文章深入浅出&#xff0c;零基础也能快速上手&#xff0c;完…...

C#对象集合去重的一种方式

前言 现在AI越来越强大了&#xff0c;有很多问题其实不需要在去各个网站上查了&#xff0c;直接问AI就好了&#xff0c;但是呢&#xff0c;AI给的代码可能能用&#xff0c;也可能需要调整&#xff0c;但是自己肯定是要会的&#xff0c;所以还是总结一下吧。 问题 如果有一个…...

【LangChain】

以下是关于 LangChain框架 各核心组件的详细解析&#xff0c;结合其功能定位、技术实现和实际应用场景&#xff1a; 一、LangChain Libraries&#xff08;核心库&#xff09; 功能定位 跨语言支持&#xff1a;提供Python/JS双版本API&#xff0c;统一不同语言的LLM开发生态 …...

Java 面试实录:从Spring到微服务的技术探讨

在一个明亮的会议室里&#xff0c;严肃的面试官与搞笑的程序员谢飞机正进行一场关于Java技术栈的面试。场景设定在一家知名互联网大厂&#xff0c;他们的对话充满了技术性与娱乐性。 第一轮&#xff1a;Spring框架与数据库 面试官&#xff1a;“谢飞机&#xff0c;能解释一下…...

在ROS2(humble)+Gazebo+rqt下,实时显示仿真无人机的相机图像

文章目录 前言一、版本检查检查ROS2版本 二、步骤1.下载对应版本的PX4(1)检查PX4版本(2)修改文件名(3)下载正确的PX4版本 2.下载对应版本的Gazebo(1)检查Gazebo版本(2)卸载不正确的Gazebo版本(3)下载正确的Gazebo版本 3.安装bridge包4.启动 总结 前言 在ROS2的环境下&#xff…...

github双重认证怎么做

引言 好久没登陆github了&#xff0c; 今天登陆github后&#xff0c;提醒进行2FA认证。 查看了github通知&#xff0c;自 2023 年 3 月起&#xff0c;GitHub 要求所有在 GitHub.com 上贡献代码的用户启用一种或多种形式的双重身份验证 (2FA)。 假如你也遇到这个问题&#xf…...

数据的类型——认识你的数据

第02篇&#xff1a;数据的类型——认识你的数据 写在前面&#xff1a;嗨&#xff0c;大家好&#xff01;我是蓝皮怪。在上一篇文章中&#xff0c;我们聊了统计学的基本概念&#xff0c;今天我们来深入了解一个非常重要的话题——数据的类型。你可能会想&#xff1a;"数据就…...

DeepSeek与AI提示语设计的全面指南

文章目录 什么是提示语设计&#xff1f;为什么提示语设计如此重要&#xff1f;DeepSeek提示语设计的基本原则1. 明确性是王道2. 结构化你的需求3. 提供上下文4. 指定输出格式5. 使用示例6. 设定角色 进阶技巧&#xff1a;让你的提示语更上一层楼1. 链式思考2. 反向提问3. 限定条…...

Kafka KRaft + SSL + SASL/PLAIN 部署文档

本文档介绍如何在 Windows 环境下部署 Kafka 4.x&#xff0c;使用 KRaft 模式、SSL 加密和 SASL/PLAIN 认证。stevensu1/kafka_2.13-4.0.0 1. 环境准备 JDK 17 或更高版本Kafka 4.x 版本&#xff08;本文档基于 kafka_2.13-4.0.0&#xff09; 2. 目录结构 D:\kafka_2.13-4.…...

Codeforces Round 1027 (Div. 3)

A. Square Year 题目大意&#xff1a;拆分完全平方数。 【解题】&#xff1a;如果是完全平方数输出0 平方根就行&#xff0c;不是就输出-1。 code: #include <iostream> #include <string> #include <cmath> using namespace std; typedef long long LL…...