基于react18.x+vite4+arco-design自研中后台管理系统解决方案ReactAdmin

react-vite-admin 基于vite4搭建react18.x后台管理项目。使用了react18 hooks+arco.design+zustand+bizcharts等技术实现权限管理模板框架。支持暗黑/亮色主题、i18n国际化、动态权限鉴定、3种布局模板、tab路由标签栏等功能。

React18Admin管理系统是首创自研的轻量级中后台框架,构建运行速度快,支持dark/light主题模式。

技术栈

  • 编辑器:Vscode
  • 框架技术:react18+vite4+react-router+zustand+axios
  • 组件库:arco-design (字节前端react组件库)
  • 路由管理:react-router-dom^6.16.0
  • 状态管理:zustand^4.4.1
  • 模拟数据:mockjs^1.1.0
  • 模拟请求:axios^1.5.1
  • 图表库:bizcharts^4.1.22
  • 编辑器组件:@wangeditor/editor-for-react^1.0.6
  • markdown编辑器:@uiw/react-md-editor^3.23.6
  • 请求进度插件:nprogress^0.2.0

react-admin采用字节出品的react桌面端组件库arco.design

特性

  1. 基于vite4.x构建react18后台,轻/快/小
  2. 使用最新前端技术栈react18、zustand、bizcharts、react-router、axios
  3. 搭配清新react组件库arco.design
  4. 支持中英文/繁体国际化语言
  5. 支持动态路由权限验证
  6. 支持动态tabs标签栏控制
  7. 内置多种模板布局样式

项目结构目录

采用标准化的react目录结构,整个项目使用react18 function语法编码开发。

构建预览图

wangeditor-react图文编辑器使用的是wangeditor的react版本,支持dark/light主题。

react-md-editor基于react的markdown编辑器,支持dark/light主题。

react18-scrollbar项目中使用的虚拟滚动条基于react18自定义组件实现功能。

// 引入滚动条组件
import RScroll from '@/components/rscroll' <RScroll autohide maxHeight={100}>
包裹需要滚动的内容块。。。
</RScroll>

React18-Admin布局模板

如上图:支持分栏+垂直+水平3种通用布局模板。也可以定制化模板样式。

/**
* 主布局模板
* @author Hs Q:282310962
*/ import { useMemo } from 'react'
import { appStore } from '@/store/app' import Columns from './template/columns'
import Vertical from './template/vertical'
import Transverse from './template/transverse' function Layout() {
const { config: { skin, layout } } = appStore() // 布局模板
const LayoutComponent = useMemo(() => {
switch(layout) {
case 'columns':
return Columns
case 'vertical':
return Vertical
case 'transverse':
return Transverse
default:
return Columns
}
}, [layout]) return (
<div className="radmin__container">
<LayoutComponent />
</div>
)
} export default Layout

主模板Main.jsx动态Permission鉴权验证。

import './index.scss'
import { Outlet } from 'react-router-dom'
import RScroll from '@/components/rscroll'
import Permission from '@/components/Permission'
import Forbidden from '@/views/error/forbidden'
import { useRoute } from '@/hooks/useRoutes' export default function Main() {
const route = useRoute() return (
<>
<RScroll>
<div className="ra__layout-main__wrapper">
{/* 鉴权组件 */}
<Permission
roles={route?.meta?.roles}
content={<Forbidden />}
>
<Outlet />
</Permission>
</div>
</RScroll>
</>
)
}

react-router路由配置

/**
* @title react-router-dom v6路由配置管理
* @author andy
*/ import { useRoutes, Navigate } from 'react-router-dom' import Error from '@views/error/404' // 批量导入modules路由
const modules = import.meta.glob('./modules/*.jsx', { eager: true })
const patchRoutes = Object.keys(modules).map(key => modules[key].default).flat() // useRoutes集中式路由配置
export const routes = [
{
path: '/',
element: <Navigate to="/home" replace={true} />,
meta: {
isWhite: true // 路由白名单
}
},
...patchRoutes,
// 404模块 path="*"不能省略
{
path: '*',
element: <Error />,
meta: {
isWhite: true
}
}
] const Router = () => useRoutes(routes) export default Router

lazyload.jsx配置

/**
* 延迟加载提示
*/ import { Suspense } from 'react'
import { Spin } from '@arco-design/web-react'
import NprogressLoading from './nprogress' // 加载提示
const SpinLoading = () => {
return (
<Spin
tip='loading...'
style={{
width: '100%'
}}
/>
)
} // 延迟加载
const lazyload = LazyComponent => {
// React 16.6 新增了<Suspense>组件,懒加载的模式需要我们给他加上一层 Loading的提示加载组件
// return <Suspense fallback={<SpinLoading />}><LazyComponent /></Suspense>
return <Suspense fallback={<NprogressLoading />}><LazyComponent /></Suspense>
} export default lazyload

NProgress.jsx配置

/**
* 加载进度条NProgress
*/ import { Component } from 'react'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css' export default class NprogressLoading extends Component {
constructor(props) {
super(props)
NProgress.set(.4)
NProgress.start()
} componentDidMount() {
NProgress.done()
} render() {
return <div />
}
}

主路由main.jsx配置

/**
* 主路由配置
* @author Hs
*/ import { lazy } from 'react'
import {
IconHome, IconDashboard, IconLink, IconCommand, IconUserGroup, IconLock,
IconMenu, IconSafe, IconBug, IconHighlight, IconUnorderedList, IconStop
} from '@arco-design/web-react/icon'
import Layout from '@/layouts'
import Blank from '@/layouts/blank'
import lazyload from '../lazyload' export default [
/*首页模块*/
{
path: '/home',
key: '/home', // 用于Menu组件跳转路由地址
element: <Layout />,
meta: {
// icon: 've-icon-home', // 菜单图标
icon: <IconHome />,
name: 'layout__main-menu__home', // i18n国际化标题
title: '主页',
isAuth: true, // 需要鉴权
isHidden: false, // 是否隐藏菜单
isAffix: true // 固定tabview标签栏(不可关闭)
},
children: [
{
key: '/home',
index: true,
element: lazyload(lazy(() => import('@views/home'))),
meta: {
// icon: 've-icon-home',
icon: <IconHome />,
name: 'layout__main-menu__home-index',
title: '首页',
isAuth: true
}
},
// 工作台
{
path: 'dashboard',
key: '/home/dashboard',
element: lazyload(lazy(() => import('@views/home/dashboard'))),
meta: {
// icon: 've-icon-computer',
icon: <IconDashboard />,
name: 'layout__main-menu__home-workplace',
title: '工作台',
isAuth: true
}
},
// 外部链接
{
path: 'https://react.dev/',
key: 'https://react.dev/',
meta: {
// icon: 've-icon-clip',
icon: <IconLink />,
name: 'layout__main-menu__home-apidocs',
title: 'react.js官方文档',
rootRoute: '/home'
}
}
]
}, /*组件模块*/
{
...
}, /*用户管理模块*/
{
...
}, /*权限模块*/
{
...
}, /*错误模块*/
{
...
}
]

备注:路由菜单参数配置说明。

/**
* @description 路由参数说明
* @param path ==> 路由地址标识
* @param key ==> 用于Menu组件跳转路由地址
* @param redirect ==> 重定向地址
* @param element ==> 视图页面路径
* 菜单信息(meta)
* @param meta.icon ==> 菜单图标
* @param meta.title ==> 菜单标题
* @param meta.name ==> i18n国际化标题
* @param meta.roles ==> 页面权限 ['admin', 'dev', 'test']
* @param meta.isAuth ==> 是否需要验证
* @param meta.isHidden ==> 是否隐藏页面
* @param meta.isAffix ==> 是否固定标签(tabs标签栏不能关闭)
* */

react自定义路由菜单Menu

基于arco.design组件库提供的Menu组件封装三种不同的路由菜单。

  

<RouteMenu />

<RouteMenu rootRouteEnable />

<RouteMenu rootRouteEnable mode="horizontal" />

RouteMenu路由菜单模板

/**
* 路由菜单模板
*/ import './index.scss'
import { useState, useMemo, useEffect } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import { Menu } from '@arco-design/web-react'
import Icon from '@components/Icon'
import RouteSubMenu from './submenu'
import { routes } from '@/routers'
import { getCurrentRootRoute, findParentRoute } from '@/hooks/useRoutes'
import Locales from '@/locales' export default function RouteMenu(props) {
const {
// 菜单类型(垂直vertical 水平菜单horizontal 弹出pop)
mode = 'vertical',
// 菜单风格('light' | 'dark')
theme = 'light',
// 是否开启一级路由菜单
rootRouteEnable = false, style = {}
} = props const navigate = useNavigate()
const { pathname } = useLocation()
const t = Locales()
const [openKeys, setOpenKeys] = useState([])
const rootRoute = getCurrentRootRoute()
const filterRoutes = routes.filter(item => !item?.meta?.isWhite) const menuRoutes = useMemo(() => {
if(rootRouteEnable) {
return filterRoutes
}
// 过滤一级菜单
return filterRoutes.find(item => item.path == rootRoute && item.children)?.children
}, [pathname]) useEffect(() => {
setOpenKeys(getKeys(pathname))
}, [pathname]) // 获取选中菜单路由keys数组
const getKeys = (key) => {
return findParentRoute(menuRoutes, key)?.map(item => item?.key)
} const handleNavigate = (key) => {
const reg = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/
if(reg.test(key)) {
window.open(key)
}else {
navigate(key)
}
} return (
<Menu
className="ra__menus"
mode={mode}
theme={theme}
selectedKeys={[pathname]}
openKeys={openKeys}
levelIndent={28}
style={{ ...style }}
onClickMenuItem={handleNavigate}
onClickSubMenu={(_, openKeys) => {
setOpenKeys(openKeys)
}}
>
{ menuRoutes.map(item => {
if(item?.children) {
return RouteSubMenu(item, t)
}
return (
!item?.meta?.isHidden &&
<Menu.Item className="ra__menuItem" key={item.redirect || item.key}>
{ item?.meta?.icon && <Icon name={item.meta.icon} size={18} style={{marginRight: 10}} /> }
{ item?.meta?.name && <span>{t[item.meta.name]}</span> }
</Menu.Item>
)
})}
</Menu>
)
}

react18状态管理zustand

Zustand新一代react状态管理工具,内置多种插件,支持persist本地存储服务。

/**
* react18状态管理库Zustand4,中间件persist本地持久化存储
*/
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import { generate, getRgbStr } from '@arco-design/color' export const appStore = create(
persist(
(set, get) => ({
// 语言(中文zh-CN 英文en 繁体字zh-TW)
lang: 'zh-CN',
// 角色类型 roles: ['admin'] / roles: ['admin', 'dev'] / roles: ['dev', test']
roles: ["dev"],
// 配置信息
config: {
// 布局(分栏columns 纵向vertical 横向transverse)
layout: 'columns',
// 模式(亮色light - 暗黑dark)
mode: 'light',
// 主题色
theme: '#3491FA',
// 是否折叠菜单
collapsed: false,
// 开启面包屑导航
breadcrumb: true,
// 开启标签栏
tabsview: true,
tabRoutes: [],
// 显示搜索
showSearch: true,
// 显示全屏
showFullscreen: true,
// 显示语言
showLang: true,
// 显示公告
showNotice: true,
// 显示底部
showFooter: false
}, // 更新配置
updateConfig: (key, value) => set({
config: { ...get().config, [key]: value }
}),
// 设置角色
setRoles: (roles) => set({roles}),
// 设置多语言
setLang: (lang) => set({lang}),
// 设置主题模式
setMode: (mode) => {
if(mode == 'dark') {
// 设置为暗黑主题
document.body.setAttribute('arco-theme', 'dark')
}else {
// 恢复亮色主题
document.body.removeAttribute('arco-theme')
}
get().updateConfig('mode', mode)
},
// 设置主题样式
setTheme: (theme) => {
const colors = generate(theme, { list: true })
colors.map((item, index) => {
const rgbStr = getRgbStr(item)
document.body.style.setProperty(`--arcoblue-${index + 1}`, rgbStr)
})
get().updateConfig('theme', theme)
}
}),
{
name: 'appState',
// name: 'app-store', // name of the item in the storage (must be unique)
// storage: createJSONStorage(() => sessionStorage), // by default, 'localStorage'
}
)
)

react18国际化配置i18n

/**
* 国际化配置
* @author YXY
*/ import { appStore } from '@/store/app' // 引入语言配置
import enUS from './en-US'
import zhCN from './zh-CN'
import zhTW from './zh-TW' export const locales = {
'en': enUS,
'zh-CN': zhCN,
'zh-TW': zhTW
} export default (locale) => {
const appState = appStore()
const lang = appState.lang || 'zh-CN' return (locale || locales)[lang] || {}
}

App.jsx引入arco.design组件库语言包。

/**
* 入口模板
* @author Hs
*/ import { useEffect, useMemo } from 'react'
import { HashRouter } from 'react-router-dom'
// 通过 ConfigProvider 组件实现国际化
import { ConfigProvider } from '@arco-design/web-react'
// 引入语言包
import enUS from '@arco-design/web-react/es/locale/en-US'
import zhCN from '@arco-design/web-react/es/locale/zh-CN'
import zhTW from '@arco-design/web-react/es/locale/zh-TW' import { AuthRouter } from '@/hooks/useRoutes'
import { appStore } from '@/store/app' // 引入路由配置
import Router from './routers' function App() {
const { lang, config: { mode, theme }, setMode, setTheme } = appStore() const locale = useMemo(() => {
switch(lang) {
case 'en':
return enUS
case 'zh-CN':
return zhCN
case 'zh-TW':
return zhTW
default:
return zhCN
}
}, [lang]) useEffect(() => {
setMode(mode)
setTheme(theme)
}, []) return (
<ConfigProvider locale={locale}>
<HashRouter>
<AuthRouter>
<Router />
</AuthRouter>
</HashRouter>
</ConfigProvider>
)
} export default App

Lang.jsx配置

import { Dropdown, Menu, Button } from '@arco-design/web-react'
import Icon from '@components/Icon'
import { appStore } from '@/store/app' export default function Lang() {
const { lang, setLang } = appStore() const handleLang = val => {
setLang(val)
} return (
<Dropdown
position="bottom"
droplist={
<Menu className="radmin__dropdownLang" defaultSelectedKeys={[lang]} onClickMenuItem={handleLang}>
<Menu.Item key='zh-CN'>简体中文 <span>zh-CN</span></Menu.Item>
<Menu.Item key="zh-TW">繁体字 <span>zh-TW</span></Menu.Item>
<Menu.Item key="en">英文 <span>en</span></Menu.Item>
</Menu>
}
>
<Button
shape="circle"
size="small"
icon={<Icon name="ve-icon-lang" />}
/>
</Dropdown>
)
}

Tabs.jsx动态路由栏

项目中动态路由栏tabs采用arco.design组件库Tabs组件自定义实现功能。

<Tabs
activeTab={pathname}
editable
showAddButton={false}
onDeleteTab={key => delTabs(key)}
>
{ tabRoutes.map(item => (
<Tabs.TabPane
closable={!item?.meta?.isAffix}
key={item?.redirect || item?.key}
title={
<Dropdown
trigger='contextMenu'
position='bl'
droplist={
<Menu className="ra__dropdownContext" onClickMenuItem={(key, e) => handleClickMenuItem(key, e, item)}>
<Menu.Item key="close" disabled={item?.meta?.isAffix}><Icon name="ve-icon-close" />{t['tabview__contextmenu-close']}</Menu.Item>
<Menu.Item key="closeLeft" disabled={isFirstTab()}><Icon name="ve-icon-prev" />{t['tabview__contextmenu-closeleft']}</Menu.Item>
<Menu.Item key="closeRight" disabled={isLastTab()}><Icon name="ve-icon-next" />{t['tabview__contextmenu-closeright']}</Menu.Item>
<Menu.Item key="closeOther"><Icon name="ve-icon-reset" />{t['tabview__contextmenu-closeother']}</Menu.Item>
<Menu.Item key="closeAll"><Icon name="ve-icon-close-circle-o" />{t['tabview__contextmenu-closeall']}</Menu.Item>
</Menu>
}
onVisibleChange={visible=>handleOpenContextMenu(visible, item)}
>
<span className="ra__tabsview-title" onClick={() => navigate(item?.redirect || item?.key)}>
<TabIcon path={item?.key} />
{ t[item?.meta?.name] }
</span>
</Dropdown>
}
/>
))}
</Tabs>
export default function TabsView() {
const { pathname } = useLocation()
const navigate = useNavigate()
const [selectedTab, setSelectedTab] = useState()
const { config: { tabRoutes }, updateConfig } = appStore()
const route = useRoute()
const t = Locales() useEffect(() => {
addTabs()
}, [pathname]) // 添加
const addTabs = () => {
const tabIndex = tabRoutes.findIndex(item => item?.key === pathname)
let newTabs = tabRoutes
if(tabIndex == -1) {
newTabs.push(route)
}
newTabs.map(item => {
item.isActive = false
if(item?.key === pathname) {
item.isActive = true
}
})
updateConfig('tabRoutes', newTabs)
} // 删除
const delTabs = (path) => {
const tabIndex = tabRoutes.findIndex(item => item?.key === path)
let newTabs = tabRoutes
if(tabIndex > -1) {
newTabs.splice(tabIndex, 1)
updateConfig('tabRoutes', newTabs)
updateTabs(newTabs)
}
} // 删除左侧标签
const delLeftTabs = (path) => {
const tabIndex = tabRoutes.findIndex(item => item?.key === path)
let newTabs = tabRoutes
if(tabIndex > -1) {
newTabs = newTabs.filter((item, i) => item?.meta?.isAffix || i >= tabIndex)
updateConfig('tabRoutes', newTabs)
updateTabs(newTabs)
}
} // 删除右侧标签
const delRightTabs = (path) => {
const tabIndex = tabRoutes.findIndex(item => item?.key === path)
let newTabs = tabRoutes
if(tabIndex > -1) {
newTabs = newTabs.filter((item, i) => item?.meta?.isAffix || i <= tabIndex)
updateConfig('tabRoutes', newTabs)
updateTabs(newTabs)
}
} // 删除其它
const delOtherTabs = (path) => {
let newTabs = tabRoutes.filter(item => item?.meta?.isAffix || item?.key === path)
updateConfig('tabRoutes', newTabs)
updateTabs(newTabs)
} // 删除所有
const delAllTabs = () => {
let newTabs = tabRoutes.filter(item => item?.meta?.isAffix)
updateConfig('tabRoutes', newTabs)
updateTabs(newTabs)
} // 更新跳转选项卡
const updateTabs = (tabs) => {
const nextTab = tabs[tabs.length + 1] || tabs[tabs.length - 1]
if(!nextTab) return
navigate(nextTab?.redirect || nextTab?.key)
} // 是否第一个标签
const isFirstTab = () => {
return selectedTab?.key === tabRoutes[0]?.key || selectedTab?.key === '/home'
}
// 是否最后一个标签
const isLastTab = () => {
return selectedTab?.key === tabRoutes[tabRoutes.length - 1]?.key
} // 打开右键菜单
const handleOpenContextMenu = (visible, item) => {
if(visible) {
setSelectedTab(item)
}
} // 点击右键菜单项
const handleClickMenuItem = (key, e, item) => {
e.stopPropagation()
const path = item?.key
switch(key) {
case 'close':
delTabs(path)
break
case 'closeLeft':
delLeftTabs(path)
break
case 'closeRight':
delRightTabs(path)
break
case 'closeOther':
delOtherTabs(path)
break
case 'closeAll':
delAllTabs()
break
}
} return (
...
)
}

OK,以上就是react18+arco开发后台系统的一些分享,希望对大家有所帮助~

最后附上两个最新项目实例

https://www.cnblogs.com/xiaoyan2017/p/17630296.html

https://www.cnblogs.com/xiaoyan2017/p/17468074.html

React-Admin后台管理模板|react18+arco+zustand后台解决方案的更多相关文章

  1. 28个漂亮的React.js后台管理模板

    React管理模板 为您的React Web应用程序开发一个管理区域可能非常耗时.它与设计所有前端页面一样重要. 这是2020年设计出色的顶级React.js后台管理模板的列表. 这些模板确实有价值, ...

  2. 推荐10个bootstrap及其他框架的后台管理模板

    相对于网站前台炫目多彩的设计,网址后台的设计模板貌似有点少,在这里推荐10个可以免费下载的~ 1.自适应超酷整站 win8风格的Bootstrap响应式网站后台管理模板-Apricot 2.boots ...

  3. Bootstrap的响应式后台管理模板推荐

    1.Admin LTE 该模版开源免费.已用到项目中,客户评价说UI很好看... AdminLTE - 是一个完全响应式管理模板.基于Bootstrap3的框架.高度可定制的,易于使用.支持很多的屏幕 ...

  4. 最值得拥有的免费Bootstrap后台管理模板

    在PHP开发项目中,后台管理因为面向群体相对比较固定,大部分以实现业务逻辑和功能.使用Bootstrap后台模板可以让后端开发很轻松的就展现给客户一个响应式的后台,节约前端开发的时间.下面PHP程序员 ...

  5. 推荐几款基于Bootstrap的响应式后台管理模板

    1.Admin LTE 该模版开源免费. AdminLTE - 是一个完全响应式管理模板.基于Bootstrap3的框架.高度可定制的,易于使用.支持很多的屏幕分辨率适合从小型移动设备到大型台式机. ...

  6. Bootstrap后台管理模板调研

    Bootstrap后台管理模板调研 SB Admin 2(推荐) SB Admin 2是一款开源的基于Bootstrap搭建的后台管理模板,简约,易用.没有复杂的组件和花炫的设计,很质朴,但较为美观. ...

  7. [转]最值得拥有的免费Bootstrap后台管理模板

    在PHP开发项目中,后台管理因为面向群体相对比较固定,大部分以实现业务逻辑和功能.使用Bootstrap后台模板可以让后端开发很轻松的就展现给客户一个响应式的后台,节约前端开发的时间.下面PHP程序员 ...

  8. 使用前端后台管理模板库admin-lte(转)

    使用前端后台管理模板库admin-lte 使用前端后台管理模板库admin-lte 安装 搭建环境 安装 安装admin-lte,可以通过以下几种办法安装,下图是GitHub中admin-lte的主页 ...

  9. 基于 Blazui 的 Blazor 后台管理模板 BlazAdmin 正式尝鲜

    简介 BlazAdmin 是一个基于Blazui的后台管理模板,无JS,无TS,非 Silverlight,非 WebForm,一个标签即可使用. 我将在下一篇文章讨论 Blazor 服务器端渲染与客 ...

  10. Vue + ElementUI 后台管理模板推荐

    最近学习和项目都用到了Vue和ElementUI,自己不是专业前端,搞这些UI上的东西还是有些难度,这里推荐两个Vue + ElementUI后台管理模板 vue-element-admin vue- ...

随机推荐

  1. ThinkPHP6.0 链式SQL语句

    ThinkPHP6.0 链式SQL语句 查询单个数据 $user = Db::query('select * from `user`'); $user=Db::table('user')->wh ...

  2. Containerd组件 —— containerd-shim-runc-v2作用

    1.概述 通过<浅析开源容器标准--OCI>.<浅析容器运行时>和<浅析Kubernetes CRI>这三篇博文我们了解了容器标准OCI.容器运行时以及Kubern ...

  3. Asp-Net-Core学习笔记:gRPC快速入门

    前言 此前,我在做跨语言调用时,用的是 Facebook 的 Thrift,挺轻量的,还不错. Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务.它被当作一个远程过程调用 ...

  4. Cookie相关基础

    1) 创建Cookie对象, public Cookie(java.lang.String name, java.lang.String value) 参数1:表示cookie名称 参数2:表示coo ...

  5. Jenkins主从架构的实现

    一.概要 提到K8S环境下的CI/CD,可以使用的工具有很多,比如Jenkins.Gitlab CI.新兴的drone等,考虑到大多公司在VM环境下都采用 Jenkins 集群来搭建符合需求的 CI/ ...

  6. MAUI Blazor 显示本地图片的新思路

    前言 好久没写文章了,水一篇 关于MAUI Blazor 显示本地图片这个问题,有大佬发过了. 就是 token 大佬的那篇 Blazor Hybrid (Blazor混合开发)更好的读取本地图片 主 ...

  7. MAUI Blazor Android 输入框软键盘遮挡问题2.0

    前言 关于MAUI Blazor Android 输入框软键盘遮挡问题,之前的文章已经有了答案,MAUI Blazor Android 输入框软键盘遮挡问题 但是这个方案一直存在一点小的瑕疵 在小窗模 ...

  8. 图形视图体系结构——Graphics View

    Graphics View框架结构的特点.主要包含元素及坐标系统. 1.特点 Graphics View框架结构的主要特点如下. (1) Graphics View框架结构中,系统可以利用Qt绘图系统 ...

  9. sudo提权操作

    sudo提权操作 sudo sudo是linux系统管理指令,是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,如halt,reboot,su等等.这样不仅减少了root用户的登录 ...

  10. 创建python虚拟环境并打包python文件

    前言 当需要为一个离线环境部署python应用时,离线环境可能缺少各种python环境,有docker的话可以用docker,没有docker可以用pyinstaller打包成二进制文件.pyinst ...