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

贵阳网站建设方案维护seo软文推广

贵阳网站建设方案维护,seo软文推广,php 上传移动到网站根目录,郑州市的实惠推广网站一周调试终于实现了类 slack 类别、频道拖动调整位置功能。 历经四个版本迭代。 实现了类似slack 类别、频道拖动调整功能 从vue->react ;更喜欢React的生态及编程风格,新项目用React来重构了。 1.zustand全局状态 2.DndKit 拖动 功能视频&…

一周调试终于实现了类 slack  类别、频道拖动调整位置功能。
历经四个版本迭代。

实现了类似slack 类别、频道拖动调整功能
从vue->react ;更喜欢React的生态及编程风格,新项目用React来重构了。

1.zustand全局状态

2.DndKit  拖动

功能视频:

dndKit 实现类似slack 类别、频道拖动调整位置功能

React DndKit 实现类似slack 类别、频道拖动调整位置功能_哔哩哔哩_bilibili
 

1.ChannelList.tsx

// ChannelList.tsx
import React, { useState } from 'react';
import useChannelsStore from "@/Stores/useChannelListStore";
import { DndContext, closestCenter, DragOverlay, pointerWithin, ragEndEvent, DragOverEvent, DragStartEvent, DragMoveEvent, DragEndEvent } from "@dnd-kit/core";
import { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { GripVertical } from "lucide-react";
import { ChevronDown, ChevronRight } from "lucide-react"; // 图标库
import { Channel } from "@/Stores/useChannelListStore"interface ChannelProps {id: string;name: string;selected: boolean
}const ChannelItem: React.FC<ChannelProps> = ({ id, name, selected }) => {const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id, data: { type: 'channel' } });return (<divref={setNodeRef}{...attributes}{...listeners}style={{transform: CSS.Transform.toString(transform),transition,opacity: isDragging ? 0.5 : 1,cursor: "grab",}}><div className={` w-full  rounded-lg pl-1 ${selected ? "bg-gray-300 dark:bg-gray-700 font-bold" : ""} `} ># {name}</div></div>)
}interface CategoryProps {id: string;name: string;channels: Channel[];channelIds: string[];active: string | undefined;
}
const Category: React.FC<CategoryProps> = ({ id, name, channels, channelIds, active }) => {const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id, data: { type: 'category' }, });const [collapsed, setCollapsed] = useState(true); // 控制折叠状态const selectChannel = channels.find(channel => channel.id === active);return (<divref={setNodeRef}{...attributes}style={{transform: CSS.Transform.toString(transform),transition,opacity: isDragging ? 0.5 : 1,cursor: "grab",}}><div className="flex flex-row  text-nowrap   group"><div className=" flex flex-1 flex-row items-center cursor-pointer " onClick={() => setCollapsed(!collapsed)}>{collapsed ? <ChevronDown size={22} /> : <ChevronRight size={22} />}<div className="flex-1 ">{name}</div></div><div{...listeners}  // 绑定拖拽事件到这个点style={{cursor: "grab",}}className="opacity-0 group-hover:opacity-100 transition-opacity duration-200 cursor-grab"><GripVertical width={18} /></div></div>{collapsed ? (<SortableContext items={channelIds} strategy={verticalListSortingStrategy}>{channels.map((channel) => (<div  key={channel.id}  className='ml-2 m-1  rounded-lg  hover:dark:bg-gray-700   hover:bg-gray-300 cursor-pointer'><ChannelItem                                       id={channel.id}name={channel.name}selected={channel.id === active}/></div>))}</SortableContext>) : (channels.find(channel => channel.id === active) && (<div className="pl-1 ml-2 mr-1 mt-1 font-bold  rounded-lg   dark:bg-gray-700  bg-gray-300 cursor-pointer"># {selectChannel?.name}</div>))}</div>)}const ChannelList: React.FC = () => {const { categories, categoryIds, channelIds, setCategories } = useChannelsStore();const [activeItem, setActiveItem] = useState<{ id: string; name: string; type: string } | undefined>();const [moving, setMoving] = useState(false); // 移动时候渲染const handleDragStart = (event: DragStartEvent) => {//当前选中id ,以便组件中高亮显示const activeData = event.active.data.current as { id: string; name: string } | undefined;if (activeData) {setActiveItem({id: String(event.active.id),name: activeData.name,type: activeData?.type, // 类型});}}const handleDragOver = (event: DragOverEvent) => {setMoving(false)const { active, over } = event;if (!over) return;const activeId = active.id as string;const overId = over.id as string;// 处理类别排序if (activeItem?.type === "category") {setCategories((prevCategories) => {const oldIndex = prevCategories.findIndex((cat) => cat.id === activeId);const newIndex = prevCategories.findIndex((cat) => cat.id === overId);if (oldIndex === -1 || newIndex === -1) return prevCategories;return arrayMove([...prevCategories], oldIndex, newIndex);});return;}if (activeItem?.type === "channel") {setCategories((prevCategories) => {const newCategories = [...prevCategories];const fromCategory = newCategories.find((cat) =>cat.channels.some((ch) => ch.id === activeId));const toCategory = newCategories.find((cat) =>cat.channels.some((ch) => ch.id === overId));if (!fromCategory || !toCategory) return prevCategories;const fromCategoryId = fromCategory.id;const toCategoryId = toCategory.id;if (fromCategory !== toCategory) {const fromCat = newCategories.find((cat) => cat.id === fromCategoryId);const toCat = newCategories.find((cat) => cat.id === toCategoryId);if (!fromCat || !toCat) return prevCategories;const channelIndex = fromCat.channels.findIndex((ch) => ch.id === activeId);if (channelIndex === -1) return prevCategories;const [movedChannel] = fromCat.channels.splice(channelIndex, 1);toCat.channels = [...toCat.channels, movedChannel];return newCategories;} else {const fromCat = newCategories.find((cat) => cat.id === fromCategoryId);if (!fromCat) return prevCategories;const channelIndex = fromCat.channels.findIndex((ch) => ch.id === activeId);const targetIndex = fromCat.channels.findIndex((ch) => ch.id === overId);if (channelIndex !== targetIndex) {fromCat.channels = [...arrayMove([...fromCat.channels], channelIndex, targetIndex)];return newCategories;}return prevCategories;}});}}const handleDragEnd = (event: DragEndEvent) => {setMoving(false)const { active, over } = event;if (!over) return;}const handleDragMove = (event: DragEndEvent) => {setMoving(true)}const renderDragOverlay = (activeItem: { id: string; name: string; type: string }) => {switch (activeItem.type) {case "category":{const category = categories.find(category => category.id === activeItem.id);return (category && (<div><Categorykey={category.id}id={category.id}name={category.name}channels={category.channels}channelIds={[]}active={''} /></div>))}case "channel":{const channel = categories.flatMap(category => category.channels).find(channel => channel.id === activeItem.id);return (channel && (<div><ChannelItem id={channel.id} name={channel.name} selected={true} /></div>))}default:return null;}};return (<DndContextcollisionDetection={pointerWithin}onDragStart={handleDragStart}onDragEnd={handleDragEnd}onDragOver={handleDragOver}onDragMove={handleDragMove}><SortableContext items={categoryIds} strategy={verticalListSortingStrategy}>{categories.map((category) => (<Categorykey={category.id}id={category.id}name={category.name}channels={category.channels}channelIds={channelIds}active={activeItem?.id} />))}</SortableContext><DragOverlay>{moving && activeItem?.type && renderDragOverlay(activeItem)}</DragOverlay></DndContext>);
};export default ChannelList;

2.useChannelsStore.ts

//useChannelsStore.ts
import { create } from "zustand";// 频道接口
export interface Channel {id: string;name: string;
}// 频道类型接口
export interface Category {id: string;name: string;channels: Channel[];
}// 初始化频道类型
const initialChannelTypes: Category[] = [{id: "text",name: "文字",channels: [{ id: "1", name: "文字频道1" },{ id: "2", name: "文字频道2" },],},{id: "void",name: "语音",channels: [{ id: "3", name: "语音频道1" },{ id: "4", name: "语音频道2" },],},{id: "prv",name: "私密",channels: [{ id: "5", name: "私密频道1" },{ id: "6", name: "私密频道2" },],},
];interface ChannelsStore {categories: Category[];channelIds: string[];categoryIds: string[];setCategories: (update: Category[] | ((prev: Category[]) => Category[])) => void;}const useChannelsStore = create<ChannelsStore>((set) => ({categories: initialChannelTypes,channelIds: initialChannelTypes.flatMap((channelType) =>channelType.channels.map((channel) => channel.id)),categoryIds: initialChannelTypes.map((category) => category.id),setCategories: (update) => set((state) => ({categories: typeof update === "function" ? update(state.categories) : update,}))
}));export default useChannelsStore;

http://www.dtcms.com/wzjs/364834.html

相关文章:

  • 地板网站源码公众号免费推广平台
  • wordpress手机接口seo怎么做优化排名
  • 企业网站推广成功案例免费网站电视剧全免费
  • 在微信中做网站百度搜索引擎网址格式
  • 求推荐在哪个网站做德语翻译员百度站长资源
  • 网络公司做网站价格免费网上销售平台
  • 自己做的网站怎么放上网优化大师怎么强力卸载
  • 做网站没有数据库国外免费网站域名服务器
  • 网站做快速排名是怎么做的呢怎样建立个人网站
  • 做视频网站 投入图片外链生成工具在线
  • 企业网站建设怎么样做会计分录京津冀协同发展
  • 杭州滨江网站建设深圳网络推广哪家好
  • 设计公司网站页面设计怎么优化网站排名
  • wordpress foxplayer杭州百度快照优化公司
  • 广州微信网站建设哪家好朋友圈信息流广告投放价格
  • 成都响应式网站开发茶叶网络推广方案
  • 网站建设考虑事项杭州网站建设方案优化
  • 常州网站快速排名优化广告联盟代理平台
  • 关键词没有排名的网站怎么做百度信息流推广技巧
  • 做网站要找什么软件商旅100网页版
  • 网站怎么做动态图片网站建站网站
  • 跟公司产品做网站百度公司在哪里
  • 做类似知乎网站大数据营销推广精准粉
  • wap网站还用吗西安网站建设制作公司
  • 自己做网站 搜索功能开发北京seoqq群
  • 钱包钱夹移动网站建设百度贴吧入口
  • 乐安网站建设深圳网站营销seo电话
  • 建设银行积分兑换网站站长网站seo查询
  • 做网站时候如果添加微信代码网络推广是以企业产品或服务
  • wordpress里验证谷歌站长推广赚钱app哪个靠谱