前端初学者的Ant Design Pro V6总结(下)

前端初学者的Ant Design Pro V6总结(下)

文章目录

  • 前端初学者的Ant Design Pro V6总结(下)
    • @umi 请求相关
      • 一个能用的请求配置
      • Service层 TS 类型规范
      • Service层 函数定义
      • @umi 请求代理 Proxy
    • @umi/max 简易数据流
      • useModel 没有类型提示?
      • useModel 书写规范
    • ProForm 复杂表单
      • 当外部数据发生变化,ProForm不更新?
      • ProForm onFinish中请求错误,提交按钮一直Loading
    • EditorTable 可编辑表格
      • 提交按钮一直Loading?
      • columns 自定义表单、自定义渲染
      • form / formRef 的 setFieldValue / getFieldsValue 无效?
    • Upload / ProUploader 文件上传
      • ImgCrop 实现图片裁切
      • ImgCrop 组件注意事项
    • StepsForm 分布表单
      • 如何在 StepsForm 中 更新子表单?
      • 如何手动控制 步骤 前进、后退?
    • 微前端 Qiankun
      • 子应用配置(@umi)
      • 父应用配置(@umi/max)

@umi 请求相关

一个能用的请求配置

Antd Pro的默认的请求配置太复杂了,我写了个简单的,能用,有需要可以做进一步拓展。

import { message } from 'antd';
import { history } from '@umijs/max';
import type { RequestOptions } from '@@/plugin-request/request';
import { RequestConfig } from '@@/plugin-request/request';
import { LOGIN_URL } from '@/common/constant';export const httpCodeDispose = async (code: string | number) => {if (code.toString().startsWith('4')) {message.error({ content: `请求错误` });if (code === 401) {message.error({ content: `登录已过期,请重新登录` });history.replace({ pathname: LOGIN_URL });}if (code === 403) {message.error({ content: `登录已过期,请重新登录` });localStorage.removeItem('UserInfo');history.replace({ pathname: LOGIN_URL });}}// 500状态码if (code.toString().startsWith('5')) {message.error({ content: `服务器错误,请稍后再试` });}
};// 运行时配置
export const errorConfig: RequestConfig = {// 统一的请求设定timeout: 20000,headers: { 'X-Requested-With': 'XMLHttpRequest' },// 错误处理: umi@3 的错误处理方案。errorConfig: {/*** 错误接收及处理,主要返回状态码非200,Axios错误的情况* @param error 错误类型* @param opts 请求参数,请求方法*/errorHandler: async (error: any, opts: any) => {if (opts?.skipErrorHandler) throw error;// 我们的 errorThrower 抛出的错误。if (error.response) {// Axios 的错误// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围if ((error.message as string).includes('timeout')) {message.error('请求错误,请检查网络');}await httpCodeDispose(error.response.status);} else if (error.request) {// 请求已经成功发起,但没有收到响应// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,// 而在node.js中是 http.ClientRequest 的实例// message.error('无服务器相应,请重试');} else {// 发送请求时出了点问题message.error('请求错误,请重试');}},},// 请求拦截器requestInterceptors: [(config: RequestOptions) => {// 拦截请求配置,进行个性化处理。const userInfo = JSON.parse(localStorage.getItem('UserInfo') ?? '{}');const token = userInfo.token ?? '';const headers = {...config.headers,'Content-Type': 'application/json',Whiteverse: token,// Authorization: {//   key: 'Whiteverse',//   value: `Bearer ${token}`// },};return { ...config, headers };},],/*** 响应拦截器,主要处理服务器返回200,但是实际请求异常的问题*/responseInterceptors: [(response: any) => response,(error: any) => {const code = error.data.code;if (!code.toString().startsWith('2')) {httpCodeDispose(code);return Promise.reject(error);}return error;},],
};

Service层 TS 类型规范

目前团队采用 [name].d.ts 的方式定义公用类型

- src > - types > service.d.tsenv.d.tsmodule.d.ts

服务层命名 nameplace 要求全部大写

type SortOrder = 'descend' | 'ascend' | null;/*** 通用API*/
declare namespace API {type Response<T> = {message: string;code: number;data: T;};type QuerySort<T = any> = Record<string | keyof T, SortOrder>;
}declare namespace COMMON {interface Select {value: string;label: string;}
}/*** 分页相关*/
declare namespace PAGINATE {type Data<T> = { total: number; data: T };type Query = { current?: number; pageSize?: number };
}/*** 用户服务相关*/
declare namespace USER {/*** 用户*/interface User {id: string;/*** 头像*/avatar: string;/*** 昵称*/nickname: string;}/*** 用户基本信息*/type UserInfo = Omit<User, 'roleIds' | 'updatedAt'>;type UsersQuery = PAGINATE.Query & {sort?: API.QuerySort;nickname?: string;mobile?: string;roleId?: string;};/*** 创建用户*/type Create = Omit<User, 'id'>;/*** 登录信息*/interface Login {Mobile: string;VerificationCode: string;}/*** 管理员登录参数*/interface ALoginParam {Mobile: string;VerificationCode: string;}/*** 验证码*/interface Captcha {base64: string;id: string;}
}

Service层 函数定义

  1. 为了与普通的函数做区别,方法名全部大写
  2. 使用 PREFIX_URL 请求前缀,方便后期维护

src -> services -> activity -> index.ts

export async function GetActivityList(body: ACTIVITY.ActivitiesQuery,options?: { [key: string]: any },
) {return request<API.Response<PAGINATE.Data<ACTIVITY.Activity[]>>>(`${PREFIX_URL}/activity/list`, {method: 'POST',data: body,...(options || {}),});
}

@umi 请求代理 Proxy

在开发阶段,如果后端服务的端口经常发生变化,可以使用umi 请求代理 替换原有的请求前缀,转发请求。

/*** @name 代理的配置* @see 在生产环境 代理是无法生效的,所以这里没有生产环境的配置* -------------------------------* The agent cannot take effect in the production environment* so there is no configuration of the production environment* For details, please see* https://pro.ant.design/docs/deploy** @doc https://umijs.org/docs/guides/proxy*/
export default {// 如果需要自定义本地开发服务器  请取消注释按需调整dev: {'/api-mock/': {// 要代理的地址target: 'http://127.0.0.1:4523/m1/3280694-0-default',// 配置了这个可以从 http 代理到 https// 依赖 origin 的功能可能需要这个,比如 cookiechangeOrigin: true,pathRewrite: { '^/api-mock': '' },},'/api-sys/': {// 要代理的地址target: 'http://192.168.50.131:8021',// 配置了这个可以从 http 代理到 https// 依赖 origin 的功能可能需要这个,比如 cookiechangeOrigin: true,pathRewrite: { '^/api-sys': '' },},'/api-user/': {// 要代理的地址target: 'http://192.168.50.131:8020',// 配置了这个可以从 http 代理到 https// 依赖 origin 的功能可能需要这个,比如 cookiechangeOrigin: true,pathRewrite: { '^/api-user': '' },},},/*** @name 详细的代理配置* @doc https://github.com/chimurai/http-proxy-middleware*/test: {// localhost:8000/api/** -> https://preview.pro.ant.design/api/**'/api/': {target: 'https://proapi.azurewebsites.net',changeOrigin: true,pathRewrite: { '^': '' },},},pre: {'/api/': {target: 'your pre url',changeOrigin: true,pathRewrite: { '^': '' },},},
};

@umi/max 简易数据流

useModel 没有类型提示?

还原 tsconfig.json 为默认配置

{"extends": "./src/.umi/tsconfig.json"
}

useModel 书写规范

定义Model仓库时,推荐使用匿名默认导出语法

export default () => {}

如果为页面绑定Model,注意页面的层级不要过深,页面组件的名称尽量短

  • 文件名定义
- pages- Activity- components- ActivityList.tsx- models- ActivityModels.ts
  • 使用Model
const { getActivityData } = useModel('Activity.ActivityModels', (models) => ({getActivityData: models.getActivityData,
}));

带有分页查询的 Model

带有loading,query,分页

可使用Ahooks 的 useRequest 或 自定封装 useRequest

注意Ahooks的 usePagination函数 对Service层的参数有要求

  • service 的第一个参数为 { current: number, pageSize: number }
  • service 返回的数据结构为 { total: number, list: Item[] }
  • 具体看Ahooks文档,不推荐使用或二封分页Hook.
import { useEffect, useState } from 'react';
import { useSetState } from 'ahooks';
import to from 'await-to-js';
import { GetActivityList } from '@/services/activity';export default () => {const initialParam = { current: 1, pageSize: 20 };const [query, queryChange] = useSetState<ACTIVITY.ActivitiesQuery>(initialParam);const [loading, setLoading] = useState<boolean>(false);const [error, setError] = useState<Error | null>();const [activityData, setActivityData] = useState<ACTIVITY.Activity[]>();const [total, setTotal] = useState<number>(0);const getActivityData = async (_param: ACTIVITY.ActivitiesQuery) => {// 请求前if (loading) await Promise.reject();// 请求中setLoading(true);const [err, res] = await to(GetActivityList(_param));setLoading(false);// 请求结束if (!err && res.code === 200) {setActivityData(res.data.data);setTotal(res.data.total);return res.data;} else {setError(err);return await Promise.reject();}};useEffect(() => {if (!activityData) getActivityData(query);}, []);return {// 状态loading,setLoading,error,setError,query,queryChange,total,setTotal,activityData,setActivityData,// 方法getActivityData,};
};

ProForm 复杂表单

当外部数据发生变化,ProForm不更新?

解决方案一:

// 监测外部值的变化,更新表单内的数据
useEffect(() => formRef.current && formRef.current.setFieldsValue(selectedNode), [selectedNode]);

解决方案二:

<ProForm<SysRole.Role>request={async (params) => {formRef.current?.resetFields();const res = await GetRole({id: params.id});return res.data}}
>
// ...	
</ProForm>

ProForm onFinish中请求错误,提交按钮一直Loading

onFinish 方法需要返回一个Promise.resolve(boolean),reject时,会一直loading

一个综合案例

const handleAddActivity = async (fields: ACTIVITY.Create) => {const hide = message.loading('正在创建活动');try {const response = await CreateActivity({ ...fields });hide();message.success('活动创建成功!');return response;} catch (error) {hide();message.error('添加失败,请重试!');return Promise.reject(false);}
};<StepsForm.StepForm<ACTIVITY.Create>title={"创建活动"}stepProps={{description: "请输入活动信息",}}onFinish={async (formData: ACTIVITY.Create & { ActivityTime?: string[] }) => {try {const requestBody = { ...formData };requestBody.StartTime = formData.ActivityTime![0];requestBody.EndTime = formData.ActivityTime![1]!;delete requestBody["ActivityTime"];const response = await handleAddActivity(requestBody);const ActivityId = response.data;uploadFormsRef.current?.setFieldValue("ActivityId", ActivityId);return Promise.resolve(true);} catch (e) {return Promise.resolve(true);}}}
/>

更加优雅的办法是给onFinish 提交的数据添加一个convertValues

const convertValues = useMemo((values: FormColumn) => {return { ...values };
}, []);

注意:

ProForm中的transform和convertValue属性,仅能操作本字段内容,这个特性在某种情况下会出现一些问题

例如:

<ProFormDateTimeRangePickername="ActivityTime"label="投放时间"width={'lg'}rules={[{required: true, message: '请选择活动投放时间!'}]}dataFormat={FORMAT_DATE_TIME_CN}
/>

时间范围组件返回的数据格式是

ActivityTime: string[] // 如果不给dataFormat,就是 Dayjs[]

如果后端接口的数据格式是

{startTime: string, endTime: string}

这个时候如果使用convertValue无法解决业务问题,需要在onFinish或onSubmit中进行数据转化。

EditorTable 可编辑表格

提交按钮一直Loading?

如果onSave时网络请求错误或者发生异常,返回Promise.reject,onSave就不会生效。

if (!activityIdField) {const errorContent = '请先创建活动';message.error(errorContent);return Promise.reject(errorContent);
}return handleSaveRow(record);

columns 自定义表单、自定义渲染

 const columns: ProColumns<DataSourceType>[] = [{title: '模型文件',dataIndex: '_File',width: 150,render: (_, entity) => {return (<Buttontype={'link'}onClick={() => {downloadFile(entity._File!.originFileObj!);}}>{entity._File?.name}</Button>);},formItemProps: {valuePropName: 'file',trigger: 'fileChange',rules: [{ required: true, message: '此项是必填项.' }],},renderFormItem: () => <ModelUploadButton />,}   
]

formItemProps 它本质就是<Form.Item>,基本照着Form.Item那边去配置就行。

form / formRef 的 setFieldValue / getFieldsValue 无效?

原因一:

由于EditorTable的 Form实际上是新增的一行,是动态的,formRef 更新不及时可能导致formRef.current 为 undefined。

原因二:

普通的form组件内部的数据模型形如这样:

{"homePath": "/","status": true,"sort": 1
}

但是editorForm在编辑时内部的数据模型是这样的:

{"229121": {"ModelLoadName": "11","ModelShowName": "222","ModelNo": "333","MobileOS": "android","_Position": [{"position": [123.42932734052755,41.79745486673118]}],}
}

它在外面包了一层,因此设置列的时候需要这么写

renderFormItem: (schema, config, form, action) => {const fieldsValue = form.getFieldsValue()const key = Object.keys(fieldsValue)[0];const fields = fieldsValue[key];const fieldName = schema.dataIndex! as keyof typeof fields // you want setting fieldfields[fieldName] = 'you want setting value';formRef?.current?.setFieldValue(key, fields);return <Component />
},

Upload / ProUploader 文件上传

ImgCrop 实现图片裁切

实现功能:

  • 文件格式限制
  • 文件上传尺寸限制
  • 文件缩放大小限制

工具函数

function getImageFileAsync(file: File): Promise<{width: number;height: number;aspectRatio: number;image: HTMLImageElement;
}> {return new Promise((resolve, reject) => {const reader = new FileReader();const img = new Image();reader.onload = () => {img.src = reader.result as string;};img.onload = () => {const width = img.width;const height = img.height;const aspectRatio = width / height;resolve({width,height,aspectRatio,image: img,});};img.onerror = () => {reject(new Error('图片加载失败'));};reader.onerror = () => {reject(new Error('文件读取错误'));};// 读取文件内容reader.readAsDataURL(file);});
}

组件

import { FC, ReactNode, useRef, useState } from 'react';
import { message, Modal, Upload, UploadFile, UploadProps } from 'antd';
import ImgCrop, { ImgCropProps } from 'antd-img-crop';
import { RcFile } from 'antd/es/upload';
import { getBase64, getImageFileAsync } from '@/utils/common';const fileTypes = ['image/jpg', 'image/jpeg', 'image/png'];interface PictureUploadProps {// 上传最大数量maxCount?: number;// 文件更新filesChange?: (files: UploadFile[]) => void;// 图片最小大小,宽,高minImageSize?: number[];// 图片裁切组件配置imgCropProps?: Omit<ImgCropProps, 'children'>;// 上传提示内容文本children?: ReactNode | ReactNode[];
}const PictureUpload: FC<PictureUploadProps> = ({maxCount,filesChange,minImageSize,imgCropProps,children,
}) => {const [previewOpen, setPreviewOpen] = useState(false);const [previewImage, setPreviewImage] = useState('');const [previewTitle, setPreviewTitle] = useState('');const [fileList, setFileList] = useState<UploadFile[]>([]);const [maxZoom, setMaxZoom] = useState(2);const isCropRef = useRef<boolean>(false);const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {setFileList(newFileList);if (filesChange) filesChange(fileList);};const handleCancel = () => setPreviewOpen(false);const handlePreview = async (file: UploadFile) => {if (!file.url && !file.preview) {file.preview = await getBase64(file.originFileObj as RcFile);}setPreviewImage(file.url || (file.preview as string));setPreviewOpen(true);setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1));};return (<><ImgCropquality={1}zoomSlider={true}minZoom={1}maxZoom={maxZoom}aspect={minImageSize && minImageSize[0] / minImageSize[1]}beforeCrop={async (file) => {isCropRef.current = false;// 判断文件类型const typeMatch = fileTypes.some((type) => type === file.type);if (!typeMatch) {await message.error('图片格式仅支持' +fileTypes.reduce((prev, cur, index, array) => prev + cur + (index === array.length - 1 ? '' : ','),'',),);return false;}// 判断图片大小限制if (minImageSize) {const { width: imageWidth, height: imageHeight } = await getImageFileAsync(file);if (imageWidth < minImageSize[0]) {await message.error(`当前图片宽度为${imageWidth}像素,请上传不小于${minImageSize[0]}像素的图片.`,);return false;}if (imageHeight < minImageSize[1]) {await message.error(`当前图片高度为${imageHeight}像素,请上传不小于${minImageSize[1]}像素的图片.`,);return false;}// 计算最大缩放比例const widthMaxZoom = Number((imageWidth / minImageSize[0]).toFixed(1));const heightMaxZoom = Number((imageHeight / minImageSize[1]).toFixed(1));setMaxZoom(Math.min(widthMaxZoom, heightMaxZoom));}isCropRef.current = true;return true;}}{...imgCropProps}><Uploadaction="/"listType="picture-card"fileList={fileList}onPreview={handlePreview}onChange={(files) => {handleChange(files);console.log(files);}}maxCount={maxCount}accept={'.jpg, .jpeg, .png'}beforeUpload={async (file) => {if (!isCropRef.current) return Upload.LIST_IGNORE;return file;}}>{maxCount ? fileList.length < maxCount && children : children}</Upload></ImgCrop><Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel}><img alt="example" style={{ width: '100%' }} src={previewImage} /></Modal></>);
};export default PictureUpload;

ImgCrop 组件注意事项

  • 拦截裁切事件

    • ImgCrop 组件 的 beforeCrop 返回 false 后不再弹出模态框,但是文件会继续走 Upload 的 beforeUpload 流程,如果想要拦截上传事件,需要在beforeUpload 中返回 Upload.LIST_IGNORE
    • 判断是否拦截的状态变量需要用 useRef ,useState测试无效。
  • Upload组件 配合 ImgCrop组件时,一定要在 beforeUpload 中返回 事件回调中的 file,否则裁切无效。

  • 如果不想做像素压缩,设置quality={1}

StepsForm 分布表单

如何在 StepsForm 中 更新子表单?

通过StepsForm的 formMapRef 属性,它可以拿到子StepForm的全部ref。

const stepFormMapRef = useRef<Array<MutableRefObject<ProFormInstance>>>([]);
return <StepsForm formMapRef={stepFormMapRef} />

打印 ref.current

[{"current": {// getFieldError: f(name)}},{"current": {// getFieldError: f(name)}},{"current": {// getFieldError: f(name)}}
]

如何手动控制 步骤 前进、后退?

灵活使用 current、onCurrentChange、submitter属性

const [currentStep, setCurrentStep] = useState<number>(0);return (<StepsForm current={currentStep}onCurrentChange={setCurrentStep}submitter={{render: (props) => {switch (props.step) {case 0: {return (<Button type="primary" onClick={() => props.onSubmit?.()}>下一步</Button>);}case 1: {return (<Button type="primary" onClick={() => props.onSubmit?.()}>下一步</Button>);}case 2: {return (<Buttontype="primary"onClick={() => {setCurrentStep(0);onCancel();}}>完成</Button>);}}},}}stepsProps={{ direction: 'horizontal', style: { padding: '0 50px' } }}>{ // StepForm }</StepsForm>
)

微前端 Qiankun

文档:https://umijs.org/docs/max/micro-frontend

子应用配置(@umi)

一、使用umi创建React App

二、配置umi

这里有一些WASM的配置,不想要可以去掉

import { defineConfig } from 'umi';export default defineConfig({title: 'xxxxxx',routes: [{path: '/',component: 'index',},{ path: '/scene-obj', component: 'OBJScene' },{ path: '/*', redirect: '/' },],npmClient: 'pnpm',proxy: {'/api': {target: 'http://jsonplaceholder.typicode.com/',changeOrigin: true,pathRewrite: { '^/api': '' },},},plugins: ['@umijs/plugins/dist/model','@umijs/plugins/dist/qiankun','@umijs/plugins/dist/request',],model: {},qiankun: {slave: {},},request: {dataField: 'data',},mfsu: {mfName: 'umiR3f', // 默认的会冲突,所以需要随便取个名字避免冲突},chainWebpack(config) {config.set('experiments', {...config.get('experiments'),asyncWebAssembly: true,});const REG = /\.wasm$/;config.module.rule('asset').exclude.add(REG).end();config.module.rule('wasm').test(REG).exclude.add(/node_modules/).end().type('webassembly/async').end();},
});

三、跨域配置

import type { IApi } from 'umi';export default (api: IApi) => {// 中间件支持 corsapi.addMiddlewares(() => {return function cors(req, res, next) {res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Headers', '*');next();};});api.onBeforeMiddleware(({ app }) => {app.request.headers['access-control-allow-origin'] = '*';app.request.headers['access-control-allow-headers'] = '*';app.request.headers['access-control-allow-credentials'] = '*';app.request.originalUrl = '*';});
};

四、修改app.ts,子应用配置生命周期钩子.

export const qiankun = {// 应用加载之前async bootstrap(props: any) {console.log('app1 bootstrap', props);},// 应用 render 之前触发async mount(props: any) {console.log('app1 mount', props);},// 应用卸载之后触发async unmount(props: any) {console.log('app1 unmount', props);},
};

父应用配置(@umi/max)

config.ts

export default defineConfig({qiankun: {master: {apps: [{name: 'r3f-viewer', // 子应用的名称entry: 'http://localhost:5174', // your microApp address},],},},
})

使用路由的方式引入子应用

export default [{name: 'slave',path: '/slave/*',microApp: 'slave',microAppProps: {autoSetLoading: true,autoCaptureError: true,className: 'MicroApp',wrapperClassName: 'MicroAppWrapper'},},
]

使用组件的方式引入子应用

index.tsx

import { PageContainer } from '@ant-design/pro-components';
import { memo } from 'react';
import { MicroAppWithMemoHistory } from '@umijs/max';
import './index.less';const Role = () => {return (<PageContainer><MicroAppWithMemoHistoryname="r3f-viewer"url="/umi-r3f-view"autoSetLoading={true}className={'microApp'}/></PageContainer>);
};export default memo(Role);

index.less

.microApp,
#root {min-height: 800px !important;height: 800px !important;max-height: 800px !important;width: 100% !important;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/197447.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下超轻量级Rust开发环境搭建:二、安装Helix Editor

创建一个用户命令的目录&#xff1a; $> mkdir ~/.bin 我习惯将用户自己的命令都放在这个.bin目录下。 $> echo "export PATH\$HOME/.bin:\$PATH" >> ~/.bashrc $> source ~/.bashrc 上面两个命令&#xff0c;将.bin目录加入到PATH环境变量上。 …

小程序1rpx边框不完美

问题展示 原因 rpx类似rem&#xff0c;渲染后实际转换成px之后可能存在小数&#xff0c;在不同的设备上多多少少会存在渲染的问题。而1rpx的问题就更加明显&#xff0c;因为不足1个物理像素的话&#xff0c;在IOS会进行四舍五入&#xff0c;而安卓好像统一向上取整&#xff0c…

人工智能|网络爬虫——用Python爬取电影数据并可视化分析

一、获取数据 1.技术工具 IDE编辑器&#xff1a;vscode 发送请求&#xff1a;requests 解析工具&#xff1a;xpath def Get_Detail(Details_Url):Detail_Url Base_Url Details_UrlOne_Detail requests.get(urlDetail_Url, headersHeaders)One_Detail_Html One_Detail.cont…

2023年甘肃省职业院校技能大赛(中职教师组)网络安全竞赛样题(一)

2023年甘肃省职业院校技能大赛&#xff08;中职教师组&#xff09; 网络安全竞赛样题&#xff08;一&#xff09; &#xff08;总分1000分&#xff09; 目录 模块A 基础设施设置与安全加固 A-1任务一 登录安全加固 A-2任务二 数据库安全策略 A-3任务三 流量完整性 A-4任…

Oracle的JDBC在读取数据时报“流已关闭”异常

在从oracle数据库中读取数据的时候有时候会报“流已关闭”的异常&#xff0c;查询官网发现这是oracle的一个bug&#xff0c;原因是表中有long数据类型的字段。 如果oracle数据库表中有long类型的字段&#xff0c;在从结果集中读取数据的时候&#xff0c;如果long类型的字段不在…

[MySQL--基础]多表查询

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;生活中最大的挑战就是发现自己是谁。然后&#xff0c;坚定不移地成为那个人。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法 MySQL数据库 多表查询 前言多表关系概述&#x1f…

为什么 SQL 不适合图数据库

背景 “为什么你们的图形产品不支持 SQL 或类似 SQL 的查询语言&#xff1f;” 过去&#xff0c;我们的一些客户经常问这个问题&#xff0c;但随着时间的推移&#xff0c;这个问题变得越来越少。 尽管一度被忽视&#xff0c;但图数据库拥有无缝设计并适应其底层数据结构的查询…

leetcode每日一题36

91.解码方法 变形的蜗牛爬塔问题&#xff0c;动态规划 五部曲走起 确定dp数组及下标含义 f(n)代表当前在字符串的第n位时的解码方法数&#xff0c;而因为可以一次性分割&#xff08;解码&#xff09;1位或两位&#xff0c;那么在n位时&#xff0c;假设其上一位为a&#xff0c…

四层LVS与七层Nginx负载均衡的区别

一、四层负载均衡与七层负载均衡&#xff1a; &#xff08;1&#xff09;四层负载均衡&#xff1a; 四层负载均衡工作在 OSI 七层模型的第四层&#xff08;传输层&#xff09;&#xff0c;指的是负载均衡设备通过报文中的目标IP地址、端口和负载均衡算法&#xff0c;选择到达的…

Android防破解重签名方案研究

最近发现有人破解我们的应用&#xff0c;于是研究了一下如何在应用被破解以后&#xff0c;让应用退出的简单实现方案。 很多应用可能会选择加固的方式来实现&#xff0c;但是在海外Google Play上是不允许加固的&#xff0c;因此需要其他策略。 应用被破解以后一般会被修改然后…

赛事回顾 | 首届“智航杯“全国无人机智能算法竞赛落幕

11月28日&#xff0c;首届“智航杯”全国无人机智能算法竞赛实物赛在海南省三亚市成功落下帷幕。此次竞赛自2023年4月启动以来&#xff0c;共有来自全国145所高等院校和50多所企事业单位的1253支团队、3655人报名参赛&#xff0c;最终有6支队伍脱颖而出&#xff0c;入围了实物赛…

Elasticsearch:评估 RAG - 指标之旅

作者&#xff1a;Quentin Herreros&#xff0c;Thomas Veasey&#xff0c;Thanos Papaoikonomou 2020年&#xff0c;Meta发表了一篇题为 “知识密集型NLP任务的检索增强生成” 的论文。 本文介绍了一种通过利用外部数据库将语言模型 (LLM) 知识扩展到初始训练数据之外的方法。 …

阶段四:数据分析与机器学习(掌握NumPy和Pandas库,用于数据处理和分析)

Python的NumPy和Pandas库是数据处理和分析的重要工具。NumPy(Numerical Python)提供了高性能的数值计算工具,适用于大规模多维数组和矩阵的运算。Pandas则提供了强大的数据结构和数据分析工具,使得数据处理和分析变得更加便捷。以下是掌握NumPy和Pandas库的一些建议: 熟悉…

11.9每日一题(无穷比无穷型的极限:提出并消去无穷因子、抓大头、拆多项式)

注&#xff1a;1、 为x的绝对值&#xff0c;因为x趋向负无穷&#xff0c;所以为 -x 2、用有理运算法则拆分式子时&#xff0c;注意判断极限是否存在

Python 网络爬虫(二):HTTP 基础知识

《Python入门核心技术》专栏总目录・点这里 文章目录 1. HTTP 协议简述2. HTTP 请求过程3. HTTP 的结构3.1 请求行3.2 请求头3.3 请求体3.4 状态行3.5 响应头3.6 响应体 4. Cookie 状态管理5. HTTP 请求示例6. 总结 大家好&#xff0c;我是水滴~~ 在准备学习网络爬虫之前&…

React都有哪些hooks?

useState&#xff1a;用于在函数组件中使用状态&#xff08;state&#xff09;。它返回一个数组&#xff0c;第一个元素是当前状态的值&#xff0c;第二个元素是更新状态的函数。可以通过调用这个函数来更新状态的值。 useEffect&#xff1a;用于在函数组件中执行副作用操作&a…

springboot慢性胃炎管理系统-计算机毕设 附源码 35541

SpringBoot慢性胃炎管理系统 摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;医疗行业当然也不例外。慢性胃炎管理系统是以实际运用为开发背景&#xff0c;运用软件工程…

PLC通过485Modbus转Profinet网关与温控表通讯在发酵罐的应用

前提&#xff1a;在自动化控制系统中&#xff0c;PLC通常需要和各种设备进行数据通讯&#xff0c;其中就包括温控表。而这些设备之间的通讯常常需要通过485Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;来实现。 方案&#xff1a;在一些应用场合中&#xff0c;Profi…

Spring Boot 3 整合 Spring Cache 与 Redis 缓存实战

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

2023/11/30JAVAweb学习(postman,各种参数,统一响应数据,三层架构,分层解耦,bean组件扫描,Bean注入及解决方式)

数组json形式 想切换实现类,只需要只在你需要的类上添加 Component 如果在同一层,可以更改扫描范围,但是不推荐这种方法 注入时存在多个同类型bean解决方式