自定义多级联动选择器指南(uni-app)

news2024/10/24 0:18:10
  • 多端支持:可以运行在H5、APP、微信小程序还是支付宝小程序,都可以轻松使用改组件。
  • 自定义配置:您可以根据需要配置选择器的级数,使其适应不同的数据结构和用例。
  • 无限级联:此组件支持无限级联选择,使您能够创建具有复杂数据结构的选择器。
参数类型描述默认值必选
titlestring标题‘’
layernumber控制几级联动1
dataarray数据:[{name: ‘’, id: ‘’, children: [{name: ‘’, id: ‘’}]}][]

在这里插入图片描述

接口返回数据结构:

[
    {id: 47, name: "工厂1", parentId: 0, type: 0},
    {id: 48, name: "区域1", parentId: 47, type: 0},
    {id: 19, name: "设备1", parentId: 48, type: 1}
]

处理后数据结构:

[{ 
    id: 47, name: "工厂1", parentId: 0, type: 0,
    children: [{ 
        id: 48, name: "区域1", parentId: 47, type: 0,
        children: [
            { id: 19, name: "设备1", parentId: 48, type: 1 }
        ] 
    }] 
}]

引用示例:

<template>
	<view class="container">
		<view @click="bindDevice">选择设备</view>
        <cascade-picker ref="picker" :title="cascadePicker.title" :layer="cascadePicker.layer" :data="cascadePicker.data" @callback="pickerCallback"></cascade-picker>
    </view>
</template>
<script>
import cascadePicker from '@/components/cascade-picker/cascade-picker.vue';
import {handleTree} from "@/utils/tree";
export default {
	components: {
    	cascadePicker
	},
    data() {
        return {
			deviceArr: [],
			deviceId: '',
            cascadePicker: {
                title: 'picker标题',
                layer: null,
                data: []
          }
		}
	},
	onLoad() {
        this.getDeviceSimpleList()
      },
	methods: {
		// 获取设备列表
        getDeviceSimpleList() {
          getDeviceSimpleList().then(res => {
            this.deviceArr = handleTree(res.data)
          })
        },
		// 打开设备选择器
        bindDevice() {
          const _this = this;
          _this.cascadePicker.data = _this.deviceArr;
          _this.$refs.picker.open().then(function() {
            console.log('打开成功');
          });
        },
        // picker多级联动回调
        pickerCallback(e) {
          const {data} = e;
          const lastItem = data[data.length - 1];
          this.deviceId = lastItem.id;
        }
    }
}
</script>

@/utils/tree文件中的handleTree方法

/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 * @param {*} rootId 根Id 默认 0
 */
export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children', rootId = 0) {
  rootId = rootId || Math.min.apply(Math, data.map(item => {
    return item[parentId]
  })) || 0
  //对源数据深度克隆
  const cloneData = JSON.parse(JSON.stringify(data))
  //循环所有项
  const treeData = cloneData.filter(father => {
    let branchArr = cloneData.filter(child => {
      //返回每一项的子级数组
      return father[id] === child[parentId]
    });
    branchArr.length > 0 ? father.children = branchArr : '';
    //返回第一层
    return father[parentId] === rootId;
  });
  return treeData !== '' ? treeData : data;
}

cascade-picker组件完整代码:

components文件夹下创建cascade-picker文件夹,然后在此文件夹下创建cascade-picker.vue - 多级联动组件

<template name="cascade-picker">
  <view class="aui-picker" v-if="SHOW" :class="{'aui-picker-in': FADE==1,'aui-picker-out': FADE==0}">
    <view class="aui-mask" @click.stop="close"></view>
    <view class="aui-picker-main">
      <view class="aui-picker-header">
        <view class="aui-picker-header-icon" @click.stop="close">取消</view>
        <view class="aui-picker-title" v-if="title">{{ title }}</view>
        <view class="aui-picker-header-icon aui-picker-confirm" @click.stop="_confirm">确认</view>
      </view>
      <view class="aui-picker-nav">
        <view class="aui-picker-navitem"
              v-if="nav.length>0"
              v-for="(item, index) in nav"
              :key="index"
              :data-index="index"
              :class="[index==navCurrentIndex ? 'active' : '', 'aui-picker-navitem-'+index]"
              :style="{margin: nav.length>2 ? '0 10px 0 0' : '0 30px 0 0'}"
              @click.stop="_changeNav($event)"
        >{{ item.name }}
        </view>
        <view class="aui-picker-navitem"
              :key="nav.length"
              :data-index="nav.length"
              :class="[nav.length==navCurrentIndex ? 'active' : '', 'aui-picker-navitem-'+nav.length]"
              :style="{margin: nav.length>2 ? '0 10px 0 0' : '0 30px 0 0'}"
              @click.stop="_changeNav($event)">请选择
        </view>
        <view class="aui-picker-navborder" :style="{left: navBorderLeft+'px'}"></view>
      </view>
      <view class="aui-picker-content">
        <view class="aui-picker-lists">
          <view class="aui-picker-list"
                v-for="(list, index) in queryItems.length + 1"
                :key="index"
                :data-index="index"
                :class="[index==navCurrentIndex ? 'active' : '']"
          >
            <view class="aui-picker-list-warp" v-if="index == 0">
              <view class="aui-picker-item"
                    v-for="(item, key) in items"
                    v-if="item.pid=='0'"
                    :key="key"
                    :data-pindex="index"
                    :data-index="key"
                    :data-id="item.id"
                    :data-pid="item.pid"
                    :data-name="item.name"
                    :data-type="item.type"
                    :class="{'active': result.length>index && result[index].id==item.id}"
                    :style="{'background': touchConfig.index==key && touchConfig.pindex==index ? touchConfig.style.background : ''}"
                    @click.stop="_chooseItem($event)"
                    @touchstart="_btnTouchStart($event)"
                    @touchmove="_btnTouchEnd($event)"
                    @touchend="_btnTouchEnd($event)"
              >{{ item.name }}
              </view>
            </view>
            <view class="aui-picker-list-warp" v-else>
              <view class="aui-picker-item"
                    v-for="(item, key) in queryItems[index-1]"
                    :key="key"
                    :data-pindex="index"
                    :data-index="key"
                    :data-id="item.id"
                    :data-pid="item.pid"
                    :data-name="item.name"
                    :data-type="item.type"
                    :class="{'active': result.length>index && result[index].id==item.id}"
                    :style="{'background': touchConfig.index==key && touchConfig.pindex==index ? touchConfig.style.background : ''}"
                    @click.stop="_chooseItem($event)"
                    @touchstart="_btnTouchStart($event)"
                    @touchmove="_btnTouchEnd($event)"
                    @touchend="_btnTouchEnd($event)"
              >{{ item.name }}
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'cascade-picker',
  props: {
    title: { //标题
      type: String,
      default: ''
    },
    layer: { //控制几级联动,默认无限级(跟随数据有无下级)
      type: Number,
      default: null
    },
    data: { //数据 如:[{id: '', name: '', children: [{id: '', name: ''}]}]
      type: Array,
      default() {
        return [
          // [{id: '', name: '', children: [{id: '', name: ''}]}]
        ]
      }
    }
  },
  data() {
    return {
      SHOW: false,
      FADE: -1,
      nav: [],
      items: [], // 第一级数据列表
      queryItems: [], // 各级数据列表
      navCurrentIndex: 0, // 当前选中的导航项索引
      navBorderLeft: 35, // 导航栏的边框左侧位置
      result: [],
      touchConfig: {
        index: -1,
        pindex: -1,
        style: {
          color: '#214579',
          background: '#EFEFEF'
        }
      },
      selectedData: [] // 用于回显数据的属性
    }
  },
  watch: {
    data() {
      const _this = this;
      const data = _this.data;
      _this.items = _this._flatten(data, '0')
    }
  },
  methods: {
    // 打开
    open() {
      const _this = this;
      _this.reset(); //打开时重置picker
      return new Promise(function (resolve, reject) {
        _this.SHOW = true;
        _this.FADE = 1;
        resolve();
      });
    },
    // 关闭
    close() {
      const _this = this;
      return new Promise(function (resolve, reject) {
        _this.FADE = 0;
        const _hidetimer = setTimeout(() => {
          _this.SHOW = false;
          _this.FADE = -1;
          clearTimeout(_hidetimer);
          resolve();
        }, 100)
      });
    },
    //重置
    reset() {
      const _this = this;
      _this.queryItems = [];
      _this.nav = [];
      _this.navBorderLeft = 35;
      _this.navCurrentIndex = 0;
      _this.result = [];
    },
    //导航栏切换
    _changeNav(e) {
      const _this = this;
      const index = Number(e.currentTarget.dataset.index);
      _this.navCurrentIndex = index;
      const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-" + index);
      _el.boundingClientRect(data => {
        _this.navBorderLeft = data.left + 20;
      }).exec();
    },
    //数据选择
    _chooseItem(e) {
      const _this = this;
      const id = e.currentTarget.dataset.id;
      const name = e.currentTarget.dataset.name;
      const pid = e.currentTarget.dataset.pid;
      const type = e.currentTarget.dataset.type;
      const _arr = [];
      _this.result[_this.navCurrentIndex] = {id: id, name: name, pid: pid, type: type};
      if (
        (!_this._isDefine(_this.layer) && _this._isDefine(_this._deepQuery(_this.data, id).children))
        ||
        (_this.navCurrentIndex < (Number(_this.layer) - 1) && _this._isDefine(_this._deepQuery(_this.data, id).children))
      ) { //有下级数据
        _this._deepQuery(_this.data, id).children.forEach(function (item, index) {
          _arr.push({id: item.id, name: item.name, pid: id, type: item.type});
        });
        if (_this.navCurrentIndex == _this.queryItems.length) { //选择数据
          _this.queryItems.push(_arr);
          _this.nav.push({name: name});
        } else { //重新选择数据
          _this.queryItems.splice(_this.navCurrentIndex + 1, 1);
          _this.nav.splice(_this.navCurrentIndex + 1, 1);
          _this.queryItems.splice(_this.navCurrentIndex, 1, _arr);
          _this.nav.splice(_this.navCurrentIndex, 1, {name: name});
        }
        _this.navCurrentIndex = _this.navCurrentIndex + 1;
        const _el = uni.createSelectorQuery().in(this).select(".aui-picker-navitem-" + _this.navCurrentIndex);
        setTimeout(() => {
          _el.boundingClientRect(data => {
            _this.navBorderLeft = data.left + 20;
          }).exec();
        }, 100)
      } else { //无下级数据且最后一级数据的type为1时,则可以确认关闭
        _this._confirm();
      }
    },
    _confirm() {
      const _this = this;
      const lastItem = _this.result[_this.result.length - 1];
      if (lastItem && lastItem.type === 1) {
        _this.close().then(() => {
          _this.$emit("callback", {status: 0, data: _this.result});
        });
      } else {
        uni.$u.toast('请选择设备')
      }
    },
    //递归遍历——将树形结构数据转化为数组格式
    _flatten(tree, pid) {
      return tree.reduce((arr, {id, name, type, children = []}) =>
        arr.concat([{id, name, pid, type}], this._flatten(children, id)), [])
    },
    //根据id查询对应的数据(如查询id=10100对应的对象)
    _deepQuery(tree, id) {
      let isGet = false;
      let retNode = null;

      function deepSearch(tree, id) {
        for (let i = 0; i < tree.length; i++) {
          if (tree[i].children && tree[i].children.length > 0) {
            deepSearch(tree[i].children, id);
          }
          if (id === tree[i].id || isGet) {
            isGet || (retNode = tree[i]);
            isGet = true;
            break;
          }
        }
      }

      deepSearch(tree, id);
      return retNode;
    },
    /***判断字符串是否为空
     @param {string} str 变量
     @example: aui.isDefine("变量");
     */
    _isDefine(str) {
      if (str == null || str == "" || str == "undefined" || str == undefined || str == "null" || str == "(null)" || str == 'NULL' || typeof (str) == 'undefined') {
        return false;
      } else {
        str = str + "";
        str = str.replace(/\s/g, "");
        if (str == "") {
          return false;
        }
        return true;
      }
    },
    _btnTouchStart(e) {
      const _this = this,
        index = Number(e.currentTarget.dataset.index),
        pindex = Number(e.currentTarget.dataset.pindex);
      _this.touchConfig.index = index;
      _this.touchConfig.pindex = pindex;
    },
    _btnTouchEnd(e) {
      const _this = this,
        index = Number(e.currentTarget.dataset.index),
        pindex = Number(e.currentTarget.dataset.pindex);
      _this.touchConfig.index = -1;
      _this.touchConfig.pindex = -1;
    },
  }
}
</script>

<style lang="scss" scoped>
/* ====================
  多级联动弹窗
 =====================*/
.aui-picker {
  width: 100vw;
  height: 100vh;
  //opacity: 0;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  /* display: none; */
}

// 遮罩层
.aui-mask {
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, .3);
  position: absolute;
  left: 0;
  top: 0;
  z-index: 999;
}

.aui-picker.aui-picker-in {
  -moz-animation: aui-fade-in .1s ease-out forwards;
  -ms-animation: aui-fade-in .1s ease-out forwards;
  -webkit-animation: aui-fade-in .1s ease-out forwards;
  animation: aui-fade-in .1s ease-out forwards;
}

.aui-picker.aui-picker-out {
  -moz-animation: aui-fade-out .1s ease-out forwards;
  -ms-animation: aui-fade-out .1s ease-out forwards;
  -webkit-animation: aui-fade-out .1s ease-out forwards;
  animation: aui-fade-out .1s ease-out forwards;
}

.aui-picker-main {
  width: 100vw;
  height: 50vh;
  background: #FFF;
  //border-radius: 15px 15px 0 0;
  position: absolute;
  left: 0px;
  bottom: 0;
  z-index: 999;
}

.aui-picker.aui-picker-in .aui-picker-main {
  -moz-animation: aui-slide-up-screen .2s ease-out forwards;
  -ms-animation: aui-slide-up-screen .2s ease-out forwards;
  -webkit-animation: aui-slide-up-screen .2s ease-out forwards;
  animation: aui-slide-up-screen .2s ease-out forwards;
}

.aui-picker.aui-picker-out .aui-picker-main {
  -moz-animation: aui-slide-down-screen .2s ease-out forwards;
  -ms-animation: aui-slide-down-screen .2s ease-out forwards;
  -webkit-animation: aui-slide-down-screen .2s ease-out forwards;
  animation: aui-slide-down-screen .2s ease-out forwards;
}

.aui-picker-header {
  width: 100%;
  min-height: 50px;
  position: relative;
  z-index: 999;
  display: flex;
  justify-content: space-between;
  align-items: center;

  &-icon {
    font-size: 15px;
    color: #aaa;
    padding: 0 15px;
  }

  .aui-picker-confirm {
    height: 50px;
    line-height: 50px;
    text-align: center;
    font-size: 15px;
    color: $custom-content-color;
    padding: 0 15px;
  }
}

.aui-picker-header::after {
  content: '';
  width: 100%;
  height: 1px;
  background: rgba(100, 100, 100, .3);
  -moz-transform: scaleY(.3);
  -ms-transform: scaleY(.3);
  -webkit-transform: scaleY(.3);
  transform: scaleY(.3);
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 999;
}

.aui-picker-title {
  line-height: 20px;
  text-align: center;
  font-size: 17px;
  color: #333;
  padding: 15px;
  box-sizing: border-box;
  position: absolute;
  left: 50px;
  right: 50px;
  top: 0;
}

.aui-picker-content {
  width: 100%;
  height: -webkit-calc(100% - 100px);
  height: calc(100% - 100px);
}

.aui-picker-nav {
  width: 100%;
  height: 50px;
  text-align: left;
  padding: 0 15px;
  margin: 0 0 1px 0;
  justify-content: flex-start;
  white-space: nowrap;
  box-sizing: border-box;
  position: relative;
  overflow-x: scroll;
  overflow-y: hidden;
}

.aui-picker-nav::after {
  content: '';
  width: 100%;
  height: 1px;
  background: rgba(100, 100, 100, .3);
  -moz-transform: scaleY(.3);
  -ms-transform: scaleY(.3);
  -webkit-transform: scaleY(.3);
  transform: scaleY(.3);
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 999;
}

.aui-picker-navitem {
  width: 80px;
  line-height: 50px;
  font-size: 16px;
  margin: 0 30px 0 0;
  text-align: center;
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.aui-picker-navitem.active {
  color: $custom-content-color;
}

.aui-picker-navborder {
  width: 40px;
  height: 3px;
  background: $custom-content-color;
  border-radius: 5px;
  transition: left .15s;
  position: absolute;
  left: 40px;
  bottom: 0;
}

.aui-picker-lists {
  width: 100%;
  height: 100%;
  justify-content: space-around;
  white-space: nowrap;
}

.aui-picker-list {
  width: 100%;
  height: 100%;
  overflow: hidden;
  overflow-y: scroll;
  display: none;
  vertical-align: top;
}

.aui-picker-list.active {
  display: inline-block;
}

.aui-picker-list-warp {
  width: 100%;
  height: auto;
  box-sizing: border-box;
  padding: 15px 0;
  display: inline-block;
}

.aui-picker-item {
  width: 100%;
  height: 50px;
  line-height: 50px;
  padding: 0 15px;
  box-sizing: border-box;
  font-size: 15px;
  color: #333;
  position: relative;
}

.aui-picker-item.active {
  color: $custom-content-color;
}

.aui-picker-item.active::after {
  content: '✔';
  font-size: 15px;
  color: $custom-content-color;
  position: absolute;
  top: 0px;
  right: 10px;
}

</style>
总结

自定义多级联动选择器组件将有助于您在uni-app中创建灵活的选择器,以满足各种不同平台和级数的需求。如果有任何问题、反馈或需要进一步的帮助,请不要犹豫,在下面的评论中提出。期待听到您的声音,以便改进和完善这个组件。

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

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

相关文章

MySQL8.0主从同步报ERROR 13121错误解决方法

由于平台虚拟机宿主机迁移&#xff0c;导致一套MySQL主从库从节点故障&#xff0c;从节点服务终止&#xff0c;在服务启动后&#xff0c;恢复从节点同步服务&#xff0c;发现了如下报错&#xff1a; mysql> show slave status\G; *************************** 1. row *****…

整合全文检索引擎 Lucene 添加站内搜索子模块

整合全文检索引擎 Lucene: 添加站内搜索子模块 1. 什么是 Lucene ? 有啥优势&#xff1f; Lucene 是一个开源的全文检索引擎库&#xff0c;由 Apache 基金会维护&#xff0c;官网地址&#xff1a;https://lucene.apache.org/ 。它提供了丰富的文本处理和搜索功能&#xff0c…

IO、存储、硬盘:解析文件系统和File类

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! 在计算机科学中&#xff0c;IO&#xff08;输入/输出&#xff09;、存储、硬盘和文件系统是构成计算机数据处理和存储的基础。本文将探讨这些概念&#xff0c;特别是文件系统的工作原理和相关知识。 输入/输出…

【C++篇】探索STL之美:熟悉使用String类

CSDN 文章目录 前言 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&…

吴恩达深度学习笔记(8)

计算机视觉 包括&#xff1a;图像分类也叫做图像识别、目标检测等 一个小的图像可能1M&#xff0c;但是他的像素是一个超级大向量&#xff0c;如果直接深度学习那么运算量会很大&#xff0c;因此需要运用卷积运算。 卷积运算是卷积神经网络的基础单元之一。下面用边缘检测理…

【Qt】控件——Qt多元素控件、常见的多元素控件、多元素控件的使用、List Widget、Table Widget、Tree Widget

文章目录 QtQt多元素控件List WidgetTable WidgetTree Widget Qt Qt多元素控件 List Widget 使用 QListWidget 能够显示一个纵向的列表。 属性说明currentRow当前被选中的是第几行。count一共有多少行。sortingEnabled是否允许排序。isWrapping是否允许换行。itemAlignment元素…

软件架构的 “4+1” 视图(附视图案例)

简介 Philippe Kruchten 在《IEEE Software》上发表的《The 41 View Model of Architecture》论文中提出了“41”视图方法&#xff0c;该方法被RUP采纳。“41”视图模型通过逻辑视图、进程视图&#xff08;也称为处理视图&#xff09;、物理视图、开发视图 和 场景视图 五个视…

YOLOv9改进,YOLOv9引入FLAttention注意力机制(ICCV2023),并二次创新RepNCSPELAN4结构

摘要 自我注意的二次计算复杂度在将 Transformer 模型应用于视觉任务时,这是一个长期存在的问题。除了减少注意力区域外,线性注意力也被认为是避免过多计算成本的有效解决方案。通过使用精心设计的映射函数来逼近 Softmax,线性注意力可以在自注意力操作中切换计算顺序并实现…

HTTP Proxy环境下部署Microsoft Entra Connect和Health Agents

在企业环境中&#xff0c;时常需要通过使用HTTP Proxy访问Internet&#xff0c;在使用HTTP Proxy访问Internet的环境中部署Microsoft Entra Connect和Microsoft Entra Connect Health Agents可能会遇到一些额外的配置步骤&#xff0c;以便这些服务能够正常连接到Internet。 一…

再Android10上实现检测AHD摄像头是否接入

项目有个需要&#xff0c;需要知道tp9951是否接入AHD摄像头 1&#xff0c;驱动层可以通过读取寄存器的值来检测是否接入AHD摄像头 tp9951_write_reg(0x40, 0x00); //select decoder page tp9951_write_reg(0x41, ch); val tp9951_read_reg(TP_INPUT_STATUS_REG);…

vscode默认添加python项目的源目录路径到执行环境(解决ModuleNotFoundError: No module named问题)

0. 问题描述 vscode中编写python脚本&#xff0c;导入工程目录下的其他模块&#xff0c;出现ModuleNotFoundError: No module named 错误 在test2的ccc.py文件中执行print(sys.path) 查看路径 返回结果发现并无’/home/xxx/first_demo’的路径&#xff0c;所以test2下面的文…

Vscode + EIDE +CortexDebug 调试Stm32(记录)

{// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请访问: https://go.microsoft.com/fwlink/?linkid830387"version": "0.2.0","configurations": [{"cwd": "${workspaceRoot…

w~自动驾驶合集9

我自己的原文哦~ https://blog.51cto.com/whaosoft/12320882 #自动驾驶数据集全面调研 自动驾驶技术在硬件和深度学习方法的最新进展中迅速发展&#xff0c;并展现出令人期待的性能。高质量的数据集对于开发可靠的自动驾驶算法至关重要。先前的数据集调研试图回顾这些数据集&…

Djang学习- URL反转

代码中url书写规范&#xff1a; 、 url反向解析 urls: path(test/url, views.test_url),path(test_result/<int:age>, views.test_result, name"rl") views: def test_url(request):return render(request, test_url.html)def test_result(request,age):re…

Vue3学习:vite项目中图片不能显示,报错 require is not defined

今天做了一个案例“给你喜欢的人送花”&#xff0c;如果喜欢谁&#xff0c;就给谁送花&#xff0c;最多可以送5朵。运行效果如下。 这个项目是使用 npm create vitelatest 命令创建的。 包括2个组件&#xff1a; 根组件App.vue子组件HelloVote.vue。 目录结构如图所示&#x…

资讯 | 财富通科技政务协同办公管理软件通过麒麟软件适配认证

2024年9月25日&#xff0c;财富通科技研发的政务协同办公管理软件成功通过中国国产操作系统麒麟软件的适配认证。本次认证是继公司区块链产品“基于区块链的企业及人员资质数字证书服务平台”认证以后得第二次认证。这一成就标志着财富通科技在推动国产软件生态建设方面迈出了坚…

基于SSM+微信小程序的家庭记账本管理系统(家庭1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 1、管理员端功能有首页、个人中心、用户管理&#xff0c;消费详情管理、收入详情管理、系统管理等。 2、用户端功能有首页、消费详情、收入详情、论坛信息、我的等功能。 2、项目技术 …

数据结构作业day2

作业一&#xff1a;结构体数组存储学生信息&#xff08;姓名&#xff0c;年龄&#xff0c;分数&#xff09;&#xff0c;完成输入学生信息&#xff0c;输出学生信息&#xff0c;求学生成绩之和&#xff0c;求最低学生成绩。 main.c #include "test.h"int main(int …

GitLab 老旧版本如何升级?

极狐GitLab 正式对外推出 GitLab 专业升级服务 https://dl.gitlab.cn/cm33bsfv&#xff01; 专业的技术人员为您的 GitLab 老旧版本实例进行专业升级&#xff01;服务详情可以在官网查看详细解读&#xff01; 那些因为老旧版本而被攻击的例子 话不多说&#xff0c;直接上图&a…

视频转GIF,5个简单好用的转换工具任你选!

GIF&#xff08;Graphics Interchange Format&#xff09;&#xff0c;作为一种轻量级的图像文件格式&#xff0c;因其能够存储多帧图像形成简单动画而广受欢迎。无论是动画影视片段&#xff0c;还是日常生活的小确幸&#xff0c;GIF都能将其定格为生动的视觉语言。当我们想要将…