Bug问题

news2025/6/7 7:36:22

一、list 页面

import React, { useEffect, useState } from 'react';
import { shallowEqual, useHistory, useSelector } from 'dva';
import { Button, message } from 'choerodon-ui/pro';
import formatterCollections from 'hzero-front/lib/utils/intl/formatterCollections';
import {
  commonModelPrompt,
  languageConfig,
  prdTemCode,
} from '@/language/language';
import { ButtonColor } from 'choerodon-ui/pro/lib/button/enum';
import {
  getPortalConfig,
  getPortalSyncConfig,
  postPortalConfig,
} from '@/api/portalConfig/main';
import { pubPath } from '@/utils/utils';
import { useDefaultPage } from '@ino/ltc-component-paas';
import { DefaultPageMode } from '@ino/ltc-component-paas/lib/component/defaultPage/enum';
import Tabs from '@/components/Tabs/index';
import { TabItem, TableData } from '@/interface/portalConfig/main';
import { queryMapIdpValue } from 'services/api';
import { renderSync } from '@/pages/portalConfig/list/hook';
import { dataSourceTabs, languageTabs } from '@/pages/portalConfig/list';
import {
  mergeConfigData,
  portalConfigAddComponentLevelAndSort,
} from '@/utils/portalConfig/main';
import Banner from '../components/Banner/main';
import Resources from '../components/Resources/main';
import FooterBanner from '../components/FooterBanner/main';
import { loadingModel } from './hook';
import '@/assets/styles/c7n.less';
import styles from './main.less';
import { configDefault } from './store';

const PortanConfig = () => {
  const history = useHistory();

  /** 定义提交成功页面 */
  const openDefaultSuccess = useDefaultPage({
    mode: DefaultPageMode.handleSuccess,
    renderDom: {
      current: document.getElementsByClassName(
        'hzero-common-layout-content',
      )[0],
    },
    isClose: true,
    closeWait: 3,
    onClose: () => {
      fetchConfig();
    },
  });

  /** 跳转到:没有权限页面 */
  const openNoPermissions = useDefaultPage({
    mode: DefaultPageMode.noPermissions,
    renderDom: {
      current: document.getElementsByClassName(
        'hzero-common-layout-content',
      )[0],
    },
    isClose: true,
    closeWait: 3,
    onClose: () => {
      fetchConfig();
    },
  });

  /** 系统错误 */
  const openDefault = useDefaultPage({
    mode: DefaultPageMode.sysError,
    renderDom: {
      current: document.getElementsByClassName(
        'hzero-common-layout-content',
      )[0],
    },
    isClose: true,
    closeWait: 3,
    onClose: () => {
      fetchConfig();
    },
  });

  const [loading, setLoading] = useState(true); // 初始状态设为true,确保开始时显示loading
  const [isDataLoaded, setIsDataLoaded] = useState(false); // 新增状态,用于判断数据是否已加载

  const [tabKey, setTabKey] = useState<string>('zh_CN'); // 语言模式
  const [dataSource, setDataSource] = useState<string>('pc'); // 数据源(PC/移动)
  const [operation, setOperation] = useState<boolean>(false); // 操作模式
  const [rule, setRule] = useState([]); // 添加规则上限
  const [configData, setConfigData] = useState<TableData[]>(configDefault); // 配置数据

  /** 当前tab页 */
  const activeTabKey = useSelector(
    (state: any) => state?.global?.activeTabKey,
    shallowEqual,
  );

  /** 获取配置 */
  const fetchConfig = async (sourse?: string) => {
    setLoading(true);

    try {
      const res = await getPortalConfig({
        language: tabKey,
        dataSource: sourse ? sourse : dataSource,
        componentPlate: 'DEVELOPER',
      });

      if (res.failed) {
        message.error(
          languageConfig('tips.fetchError', '获取配置失败'),
          1.5,
          'top',
        );
        setIsDataLoaded(false);
        res?.code === '401' ? openNoPermissions.open() : openDefault.open();
        return;
      }

      // console.log('获取配置', dataSource, res);
      // console.log('原始数据', configData);

      // 合并数据
      const result = mergeConfigData(configDefault, res || []);

      setConfigData(result);
      setIsDataLoaded(true);
    } catch (error) {
      message.error(
        languageConfig('tips.fetchError', '获取配置失败'),
        1.5,
        'top',
      );
      setIsDataLoaded(false);
    } finally {
      setLoading(false);
    }
  };

  /** 移动端:一键同步 */
  const handleSync = async () => {
    setLoading(true);
    const res = await getPortalSyncConfig({
      language: tabKey,
      componentPlate: 'DEVELOPER',
      componentSys: 'app',
    });
    if (res.failed) {
      message.error(
        languageConfig('tips.syncFetchDataError', '同步失败'),
        1.5,
        'top',
      );
      setIsDataLoaded(false);
      res?.code === '401' ? openNoPermissions.open() : openDefault.open();
    } else {
      setConfigData(res || []);
      setIsDataLoaded(true);
    }
    setLoading(false);
  };

  /** tabs 切换 */
  const handleTabChange = (selectedTab: TabItem) => {
    // console.log('当前选中的标签数据:', selectedTab);
    setTabKey(selectedTab.key);
    setDataSource('pc');
    setLoading(true); // 切换 tabs 时设置 loading 为 true
    setOperation(false); // 切换 tabs 时设置 operation 为 false
  };

  /** 数据源: 移动端、PC端切换 */
  const handleTabTypeChange = () => {
    setDataSource(dataSource === 'pc' ? 'app' : 'pc');
    setOperation(false); // 切换 tabs 时设置 operation 为 false
  };

  /** 提交 */
  const handleSubmit = async () => {
    // 1、componentLevel 和 componentSort
    const submitParams = portalConfigAddComponentLevelAndSort(configData);
    console.log('submitParams', submitParams);

    // 2、提交数据
    setLoading(true);
    const params = {
      componentSys: dataSource,
      containerComponentList: submitParams,
    };
    const res = await postPortalConfig(tabKey, params);
    setLoading(false);
    if (res.failed) {
      message.warning(res.message, undefined, undefined, 'top');
      return;
    }
    message.success(languageConfig('tips.success', '保存成功'), 1.5, 'top');

    // 3、跳转到成功页面
    setOperation(false); // 操作状态关闭
    openDefaultSuccess.open(); // 跳转到成功页面
  };

  /** 回调 */
  const handleAction = (val: TableData[]) => {
    // console.log('这里是回调', val);
    setConfigData(val);
  };

  useEffect(() => {
    if (activeTabKey) {
      fetchConfig();
    }
  }, [activeTabKey, tabKey, dataSource]);

  // 获取数量上限
  useEffect(() => {
    const fetchRule = async () => {
      const res = await queryMapIdpValue(['INO_TAI_HOME_PAGE_MODULE']);
      // console.log('rule', res[0]);
      setRule(res?.[0] || []);
    };
    fetchRule();
  }, []);

  return (
    <>
      {loading && !isDataLoaded && loadingModel()}

      <div
        className="ltc-c7n-style"
        style={{
          overflow: 'auto',
          height: '100%',
        }}
      >
        <div className={styles.portalConfig}>
          <div className={styles.portalConfig_content}>
            {/* Tabs:切换中英文 */}
            <div className={styles.portalConfig_content_tabs}>
              <Tabs tabs={languageTabs} onTabChange={handleTabChange} />
              <div
                className={styles.portalConfig_content_tabs_preview}
                onClick={() => history.push(`${pubPath}/tal/preview/${tabKey}`)}
              >
                <img
                  src={require('@/assets/imgs/portalConfig/icon_preview.png')}
                  alt={'icon_preview'}
                />
                {languageConfig('btn.preview', '预览')}
              </div>
            </div>

            {/* 配置项:内容 */}
            <div className={styles.portalConfig_content_config}>
              {/* tabs: PC/移动端配置 */}
              <div className={styles.portalConfig_content_config_typeTabs}>
                <Tabs
                  type="card"
                  tabs={dataSourceTabs}
                  activeKey={dataSource}
                  onTabChange={handleTabTypeChange}
                />
                {renderSync(dataSource, operation, () => handleSync())}
              </div>

              <Banner
                operation={operation}
                configData={configData}
                ruleData={rule}
                dataSource={dataSource}
                onAction={val => handleAction(val)}
              />
              {/* <Resources
                operation={operation}
                configData={configData}
                ruleData={rule}
                dataSource={dataSource}
                onAction={val => handleAction(val)}
              />
              <FooterBanner
                operation={operation}
                configData={configData}
                ruleData={rule}
                dataSource={dataSource}
                onAction={val => handleAction(val)}
              /> */}
            </div>
          </div>

          {/* 底部:提交按钮 */}
          <div className={styles.portalConfig_operation}>
            {operation ? (
              <>
                <Button color={ButtonColor.primary} onClick={handleSubmit}>
                  {languageConfig('btn.submit', '提交')}
                </Button>
                <Button
                  color={ButtonColor.default}
                  onClick={() => {
                    setOperation(!operation);
                    fetchConfig();
                  }}
                >
                  {languageConfig('btn.cancel', '取消')}
                </Button>
              </>
            ) : (
              <Button
                color={ButtonColor.primary}
                onClick={() => setOperation(!operation)}
              >
                {languageConfig('btn.edit', '编辑')}
              </Button>
            )}
          </div>
        </div>
      </div>
    </>
  );
};

export default formatterCollections({
  code: [prdTemCode, commonModelPrompt],
})(PortanConfig);

二、Banner

import React, { useMemo, useState } from 'react';
import {
  languageConfig,
  PICTURE_FORMAT,
  PICTURE_MAX_SIZE,
} from '@/language/language';
import Title from '@/components/Title';
import { ModuleCreateProps, TableData } from '@/interface/portalConfig/main';
import {
  portalConfigFetchMaxLimit,
  portanConfigUpdateModuleData,
  replaceConfigData,
} from '@/utils/portalConfig/main';
import styles from '../../list/main.less';
import { renderRuleLimit, renderTitle } from '../../list/hook';
import { findZoneSize } from '../../list/store';
import Create from './create/main';
import View from './create/view';

const Banner = (props: ModuleCreateProps) => {
  const { operation, configData, ruleData, dataSource, onAction } = props;

  const [show, setShow] = useState<boolean>(true); // 管理收缩状态

  /** 获取配置数据 */
  const bannerList = useMemo(() => {
    // 1、找到这个组件数据
    const bannerModule = configData.find(
      item => item.componentModule === 'developer.bannerConfig',
    );

    if (!bannerModule?.childrenList) return [];

    // 2、获取组件下'配置项'数据
    const bannerChild = bannerModule.childrenList.find(
      child => child.componentModule === 'developer.bannerConfig.banner',
    );

    return bannerChild?.childrenList || [];
  }, [configData]);

  /** 回调:用于接收子页面传递过来的数据 */
  const handleChildDataUpdate = (newData: TableData[]) => {
    // console.log('bannerl回调', newData);
    // console.log('configData', configData);

    // 1、给模块添加'提示'标头
    const resultData = newData?.map((item, index) => ({
      ...item,
      componentName: `${languageConfig(
        'developer.banner.label.bannerImage',
        'banner图片',
      )}${index + 1}`,
    }));

    // 2、更新:从父组件'configData'中获取的本模块数据
    const bannerModule = configData.find(
      item => item.componentModule === 'developer.bannerConfig',
    );
    const updatedTableData = portanConfigUpdateModuleData(
      bannerModule,
      resultData,
      'developer.bannerConfig.banner',
    );
    // console.log('更新后的 tableData:', updatedTableData);

    // 2、合并:更新过后的'本模块'和父组件中配置数据进行合并
    const result = replaceConfigData(configData, updatedTableData);
    onAction(result);
  };

  return (
    <div className={styles.portalConfig_banner}>
      <Title
        title={languageConfig(
          'developer.banner.label.bannerConfig',
          'banner配置',
        )}
        desc={
          <div>
            <img
              src={require('@/assets/imgs/portalConfig/demo_banner.png')}
              alt={'banner示例'}
            />
          </div>
        }
        isExpanded={show}
        onToggle={() => setShow(!show)}
      />

      {show && (
        <div className={styles.portalConfig_card}>
          {/* 左侧:标题、规则限制 */}
          <div className={styles.portalConfig_card_left}>
            {renderTitle(
              languageConfig(
                'developer.banner.label.bannerImage',
                'banner图片',
              ),
            )}
            {renderRuleLimit(ruleData, 'developer.bannerConfig.banner')}
          </div>
          {/* 右侧 */}
          <div className={styles.portalConfig_card_right}>
            {operation ? (
              <Create
                list={bannerList}
                maxNum={portalConfigFetchMaxLimit(
                  ruleData,
                  'developer.bannerConfig.banner',
                )}
                tips={
                  <>
                    {PICTURE_FORMAT}
                    <br />
                    {findZoneSize('developer.bannerConfig', dataSource)}
                    <br />
                    {PICTURE_MAX_SIZE}
                    <br />
                  </>
                }
                onSelect={handleChildDataUpdate}
              />
            ) : (
              <View detail={bannerList} />
            )}
          </div>
        </div>
      )}
    </div>
  );
};

export default Banner;

三、create

import React, { useCallback } from 'react';
import {
  Button,
  Form,
  Icon,
  useDataSet,
  message,
  Attachment,
  TextField,
} from 'choerodon-ui/pro';
import { onBeforeUpload } from '@/utils/utils';
import { languageConfig } from '@/language/language';
import { LabelLayout } from 'choerodon-ui/pro/lib/form/enum';
import { FuncType } from 'choerodon-ui/pro/lib/button/enum';
import { Record } from 'choerodon-ui/dataset';
import '@/assets/styles/c7n.less';
import styles from '../../../list/main.less';
import { tableFields } from './store';

interface CreateProps {
  list: any[];
  maxNum: string | number; // 添加最大限制
  tips: React.ReactNode; // 提示文案
  onSelect: (val: any) => void; // 回调
}

const BannerCreate = (props: CreateProps) => {
  const { list = [], maxNum, tips = '', onSelect } = props;

  // ds
  const dataDs = useDataSet(() => {
    return {
      autoCreate: true,
      fields: tableFields(),
      data: list.map(item => new Record(item)),
      events: {
        update: () => {
          onSelect?.(dataDs.toData());
        },
      },
    };
  }, [list, onSelect]);

  /** 上传状态变化的处理 */
  const onUploadSuccess = (index, file) => {
    // console.log('上传状态变化的处理:info', index, file);
    if (file.fileUrl) {
      // 图片url添加
      dataDs?.get(index)?.set('componentPicture', file.fileUrl);
    }
  };

  /** 新增 */
  const handleAdd = async () => {
    // 1、限制上限,如里maxNumber 为'-' 表示不限制上限
    if (maxNum !== '-' && dataDs.length >= Number(maxNum)) {
      message.error(
        `${languageConfig(
          'uploadBanner.label.menuMaxNumPleaseDeleteRetry',
          'banner图片超出最大限制,请删除后重试:',
        )}${languageConfig(
          'uploadBanner.label.maxLength',
          '最多支持',
        )}${maxNum}${languageConfig('uploadBanner.label.unit', '条')}`,
        1.5,
        'top',
      );
      return;
    }

    // 3、创建新记录
    const newRecordData = {
      componentModule: 'developer.bannerConfig.banner.item',
    };
    // console.log('newRecordData', newRecordData);
    dataDs.push(new Record(newRecordData, dataDs));
    // console.log('karla', dataDs.toData());

    onSelect?.(dataDs.toData());
  };

  /** 删除 */
  const handleDelete = useCallback(
    async (index: number) => {
      try {
        await dataDs.delete(dataDs.get(index), false);
        // 手动触发 onSelect,确保父组件收到更新
        if (typeof onSelect === 'function') {
          onSelect(dataDs.toData());
        }
      } catch (error) {
        console.error('Delete failed:', error);
        message.error('删除记录时发生错误,请重试');
      }
    },
    [dataDs, onSelect],
  );

  return (
    <div className="ltc-c7n-style">
      {/* Add */}
      <div className={styles.portalConfig_card_right_add} onClick={handleAdd}>
        <Icon type="add" />
        {languageConfig(
          'developer.banner.label.bannerImageAdd',
          '添加banner图片',
        )}
      </div>

      {/* Form */}
      {dataDs.map((item: any, index: number) => {
        return (
          <div key={item} className={styles.portalConfig_card_right_item}>
            {/* 标识 */}
            <div className={styles.portalConfig_card_right_item_label}>
              {languageConfig(
                'developer.banner.label.bannerImage',
                'banner图片',
              )}
              {index + 1}
            </div>

            <Form
              columns={1}
              labelLayout={LabelLayout.none}
              record={dataDs.get(index)}
              style={{ flex: 1 }}
            >
              <Form.Item>
                <div
                  style={{
                    display: 'flex',
                    gap: '16px',
                    background: '#F5F5F5',
                  }}
                >
                  {/* 图片 */}
                  <div className={styles.portalConfig_upload}>
                    <Attachment
                      name="remark"
                      labelLayout={'float'}
                      listType="picture-card"
                      max={1}
                      beforeUpload={onBeforeUpload}
                      onUploadSuccess={file => onUploadSuccess(index, file)}
                      onRemove={() => {
                        dataDs.get(index)?.set('componentPicture', '');
                        dataDs.get(index)?.set('remark', '');
                      }}
                    />
                  </div>

                  {/* Tips */}
                  <div className={styles.portalConfig_tips}>{tips}</div>

                  {/* 删除:大于1条时显示  */}
                  <div
                    style={{
                      marginTop: '40px',
                    }}
                  >
                    {dataDs.toData()?.length > 1 && (
                      <Button
                        funcType={FuncType.link}
                        onClick={() => {
                          handleDelete(index);
                        }}
                        className={styles.uploadConfig_card_right_delete}
                      >
                        {languageConfig('btn.remove', '移除')}
                      </Button>
                    )}
                  </div>
                </div>
              </Form.Item>

              <Form.Item>
                <div
                  style={{
                    display: 'flex',
                    gap: '8px',
                  }}
                >
                  {/* 网页链接 */}
                  <div>
                    <TextField
                      name="componentLink"
                      clearButton
                      style={{ width: '652px' }}
                    />
                  </div>
                </div>
              </Form.Item>
            </Form>
          </div>
        );
      })}
    </div>
  );
};

export default BannerCreate;

store.js

import { languageConfig } from '@/language/language';
import { bucketInfo } from '@/utils/utils';
import { FieldType } from 'choerodon-ui/dataset/data-set/enum';

/** ds table */
export const tableFields = () => {
  return [
    {
      name: 'remark',
      type: FieldType.string,
      // bucketName: 'inovance-tai-pub-test',
      // bucketDirectory: '/portalConfig',
      // storageCode: 'INOTAL',
      bucketName: bucketInfo.bucketName,
      bucketDirectory: bucketInfo.bucketDirectory,
      storageCode: bucketInfo.storageCode,
      defaultValue: '',
    },
    {
      name: 'componentLink',
      type: FieldType.string,
      label: languageConfig('portalConfig.menu.label.link', '网页链接'),
      placeholder: languageConfig(
        'portalConfig.menu.placeholder.pleaseInputLink',
        '请输入网页链接',
      ),
      defaultValue: '',
    },
    {
      name: 'componentModule',
      type: FieldType.string,
      defaultValue: 'developer.bannerConfig.banner.item',
    },
  ];
};

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

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

相关文章

【数据结构】5. 双向链表

文章目录 一、链表的分类1、双向链表的结构 二、双向链表的实现0、准备工作1、初始化2、打印3、尾插4、头插5、尾删6、头删7、查找8、在指定位置之后插入数据9、删除指定位置10、销毁 一、链表的分类 链表总共分为8种&#xff0c;具体的分组方式如图所示&#xff1a; 带头指的…

【Linux手册】冯诺依曼体系结构

目录 前言 五大组件 数据信号 存储器&#xff08;内存&#xff09;有必要吗 常见面试题 前言 冯诺依曼体系结构是当代计算机基本架构&#xff0c;冯诺依曼体系有五大组件&#xff0c;通过这五大组件直观的描述了计算机的工作原理&#xff1b;学习冯诺依曼体系可以让给我们更…

Mobile App UI自动化locator

在开展mobile app UI层自动化测试时&#xff0c;编写目标元素的locator是比较耗时的一个环节&#xff0c;弄清楚locator背后的逻辑&#xff0c;可以有效降低UI层测试维护成本。此篇博客以webdriverioappium作为UI自动化工具为例子&#xff0c;看看有哪些selector方法&#xff0…

(LeetCode 每日一题) 1061. 按字典序排列最小的等效字符串 (并查集)

题目&#xff1a;1061. 按字典序排列最小的等效字符串 思路&#xff1a;使用并查集&#xff0c;来将等价的字符连起来&#xff0c;形成一棵树。这棵树最小的字母&#xff0c;就代表整颗树&#xff0c;时间复杂度0(n)&#xff0c;细节看注释。 C版本&#xff1a; class Solutio…

linux 安装mysql8.0;支持国产麒麟,统信uos系统

一&#xff1a;使用我已经改好的mysql linux mysql8.0解压可用&#xff0c;点我下载 也在国产麒麟系统&#xff0c;统信uos系统也测试过&#xff0c;可用&#xff1b; 下载后&#xff0c;上传mysql.tar.gz 然后使用root角色去执行几个命令即可&#xff1b;数据库密码&#xf…

C#实现远程锁屏

前言 这是一次提前下班没有锁屏进而引发的一次思考后的产物&#xff0c;思考的主要场景是当人离开电脑后&#xff0c;怎么能控制电脑锁屏&#xff0c;避免屏幕上的聊天记录被曝光。 首先想到通过系统的电源计划设置闲置超时时间熄屏&#xff0c;这可能是最接近场景的解决方案&a…

SpringBoot3整合MySQL8的注意事项

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 注意事项 1、请添加添加如下依赖&#xff1a; <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><…

智语心桥:当AI遇上“星星的孩子”,科技如何点亮沟通之路?

目录: 引言:当科技的温度,遇见“星星的孩子”“智语心桥”:一座为孤独症儿童搭建的AI沟通之桥核心技术探秘:AI如何赋能“读心”与“对话”?个性化魔法:AI如何实现“千人千面”的精准干预?应用场景畅想:从家庭到机构,AI的全方位支持为什么是“智语心桥”?——价值、可…

itop-3568开发板机器视觉opencv开发手册-图像绘制-画线

本小节代码在配套资料“iTOP-3568 开发板\03_【iTOP-RK3568 开发板】指南教程 \04_OpenCV 开发配套资料\11”目录下&#xff0c;如下图所示&#xff1a; cv2.line 函数功能&#xff1a; 绘制一条直线。 函数原型&#xff1a; cv2.line(img,pt1,pt2,color,thicknessNone,lin…

sudo docker exec -it backend bash 以交互方式(interactive)进入正在运行的 Docker 容器的命令行环境

sudo docker exec -it backend bash&#x1f50d; 总体作用 这条命令的作用是&#xff1a; 以交互方式&#xff08;interactive&#xff09;进入名为 backend 的正在运行的 Docker 容器的命令行环境。 你会进入容器的“终端”&#xff0c;就像登录到一个 Linux 系统一样&#…

浅析EXCEL自动连接PowerBI的模板

浅析EXCEL自动连接PowerBI的模板 之前我分享过&#xff1a;PowerBI链接EXCEL实现自动化报表 &#xff0c;其中一个关键工具就是提到的EXCEL链接模板&#xff0c;即宏工作薄。 今天就大概来聊一聊这个宏工作簿的底层原理是啥&#xff0c;怎么实现的。 第一步&#xff1a; 打开…

java32

1.反射 获取类&#xff1a; 获取构造方法&#xff1a; 获取权限修饰符&#xff1a; 获取参数信息&#xff1a; 利用反射出来的构造器来创建对象&#xff1a; 获取成员变量&#xff1a; 获取成员方法&#xff1a; 综合练习&#xff1a; 动态代理&#xff1a;

【Redis】zset 类型

zset 一. zset 类型介绍二. zset 命令zaddzcard、zcountzrange、zrevrange、zrangebyscorezpopmax、zpopminzrank、zrevrank、zscorezrem、zremrangebyrank、zremrangebyscorezincrby阻塞版本命令&#xff1a;bzpopmax、bzpopmin集合间操作&#xff1a;zinterstore、zunionstor…

从Gartner报告看Atlassian在生成式AI领域的创新路径与实践价值

本文来源atlassian.com&#xff0c;由Atlassian全球白金合作伙伴——龙智翻译整理。 二十余年来&#xff0c;Atlassian始终是创新领域的领军者。凭借对团队协作本质的深刻理解&#xff0c;Atlassian在AI时代仍持续引领协作方式的革新。如今&#xff0c;这一领先地位再次获得权威…

Kafka 安装教程(支持 Windows / Linux / macOS)

一、下载 1、kafka官网下载地址:https://kafka.apache.org/downloads 根据实际情况下载对应的版本 2、JDK的版本最好是17+ JDK下载地址:https://www.oracle.com/java/technologies/javase/jdk17-0-13-later-archive-downloads.html 二、安装 前置条件 安装 Java(至少 Jav…

OpenCV种的cv::Mat与Qt种的QImage类型相互转换

一、首先了解cv::Mat结构体 cv::Mat::step与QImage转换有着较大的关系。 step的几个类别区分: step:矩阵第一行元素的字节数step[0]:矩阵第一行元素的字节数step[1]:矩阵中一个元素的字节数step1(0):矩阵中一行有几个通道数step1(1):一个元素有几个通道数(channel()) cv::Ma…

前端没有“秦始皇“,但可以做跨端的王[特殊字符]

前端各领域的 “百家争鸣” 框架之争&#xff1a;有 React、Vue、Angular 等多种框架。它们各有优缺点&#xff0c;开发者之间还存在鄙视链&#xff0c;比如 Vue 嫌 React 难用&#xff0c;React 嫌 Vue 不够灵活。样式处理&#xff1a; CSS 预处理器&#xff1a;像 Sass、Les…

mongodb源码分析session异步接受asyncSourceMessage()客户端流变Message对象

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制&#xff0c;ASIOSession和connection是循环接受客户端命令&#xff0c;状态转变流程是&#xff1a;State::Created 》 State::Source 》State::…

【数据分析】什么是鲁棒性?

引言 —— 为什么我们需要“抗折腾”的系统&#xff1f; 当你乘坐的飞机穿越雷暴区时机体剧烈颠簸&#xff0c;自动驾驶汽车在暴雨中稳稳避开障碍物&#xff0c;或是手机从口袋摔落后依然流畅运行——这些场景背后&#xff0c;都藏着一个工程领域的“隐形守护者”&#xff1a;…

Qt/C++学习系列之QGroupBox控件的简单使用

Qt/C学习系列之QGroupBox控件的简单使用 前言样式使用代码层面初始化控件事件过滤器点击事件处理 总结 前言 最近在练手一个项目&#xff0c;项目中有不同功能的划分&#xff0c;为了功能分区一目了然&#xff0c;我使用到QGroupBox控件&#xff0c;也是在界面排版布局中最常用…