├── packages ├── devui-vue │ ├── docs │ │ ├── index.md │ │ ├── vite.config.ts │ │ ├── .vitepress │ │ │ ├── theme │ │ │ │ ├── register-components.js │ │ │ │ └── index.ts │ │ │ └── config.ts │ │ └── components │ │ │ └── tree │ │ │ └── index.md │ ├── lib │ │ └── devui-vue.js │ ├── __tests__ │ │ └── devui-vue.test.js │ ├── README.md │ ├── jest.config.js │ ├── devui │ │ ├── vue-devui.ts │ │ └── tree │ │ │ ├── index.ts │ │ │ ├── src │ │ │ ├── components │ │ │ │ ├── icon-open.tsx │ │ │ │ └── icon-close.tsx │ │ │ ├── tree-types.ts │ │ │ ├── composables │ │ │ │ ├── use-toggle.ts │ │ │ │ └── use-highlight.ts │ │ │ ├── tree.scss │ │ │ └── tree.tsx │ │ │ └── __tests__ │ │ │ └── tree.spec.ts │ └── package.json └── devui-cli │ ├── lib │ └── devui-cli.js │ ├── __tests__ │ └── devui-cli.test.js │ ├── README.md │ ├── index.js │ ├── package.json │ └── commands │ ├── build.js │ └── create.js ├── .gitignore ├── .vscode └── extensions.json ├── lerna.json ├── package.json └── README.md /packages/devui-vue/docs/index.md: -------------------------------------------------------------------------------- 1 | # Hello mini-vue-devui -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/devui-cli/lib/devui-cli.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = devuiCli; 4 | 5 | function devuiCli() { 6 | // TODO 7 | } 8 | -------------------------------------------------------------------------------- /packages/devui-vue/lib/devui-vue.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = devuiVue; 4 | 5 | function devuiVue() { 6 | // TODO 7 | } 8 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0", 6 | "npmClient": "yarn", 7 | "useWorkspaces": true 8 | } 9 | -------------------------------------------------------------------------------- /packages/devui-cli/__tests__/devui-cli.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const devuiCli = require('..'); 4 | 5 | describe('devui-cli', () => { 6 | it('needs tests'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/devui-vue/__tests__/devui-vue.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const devuiVue = require('..'); 4 | 5 | describe('devui-vue', () => { 6 | it('needs tests'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/devui-cli/README.md: -------------------------------------------------------------------------------- 1 | # `devui-cli` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const devuiCli = require('devui-cli'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/devui-vue/README.md: -------------------------------------------------------------------------------- 1 | # `devui-vue` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const devuiVue = require('devui-vue'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/devui-vue/docs/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vueJsx from '@vitejs/plugin-vue-jsx' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vueJsx()] 7 | }) 8 | -------------------------------------------------------------------------------- /packages/devui-vue/docs/.vitepress/theme/register-components.js: -------------------------------------------------------------------------------- 1 | import Demo from 'vitepress-theme-demoblock/components/Demo.vue' 2 | import DemoBlock from 'vitepress-theme-demoblock/components/DemoBlock.vue' 3 | export function registerComponents(app) { 4 | app.component('Demo', Demo) 5 | app.component('DemoBlock', DemoBlock) 6 | } 7 | -------------------------------------------------------------------------------- /packages/devui-vue/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'jest-environment-jsdom', 3 | transform: { 4 | '^.+\\.(ts|tsx|js|jsx)$': [ 5 | 'babel-jest', { 6 | presets: ['@babel/preset-env', '@babel/preset-typescript'], 7 | plugins: ['@vue/babel-plugin-jsx'] 8 | } 9 | ] 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/vue-devui.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import TreeInstall, { Tree } from './tree' 3 | 4 | const installs = [ 5 | TreeInstall, 6 | ] 7 | 8 | export { 9 | Tree, 10 | } 11 | 12 | export default { 13 | version: '0.0.1', 14 | // 实现vue3插件,需要实现一个install方法 15 | // 将来接收一个App实例,createApp() 16 | install(app: App): void { 17 | installs.forEach((p) => app.use(p as any)) 18 | } 19 | } -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import Tree from './src/tree' 3 | 4 | // 声明为插件,可以引入组件 5 | Tree.install = function(app: App): void { 6 | app.component(Tree.name, Tree) 7 | } 8 | 9 | export { Tree } 10 | 11 | // 单独引入Tree,为后面按需做基础 12 | export default { 13 | title: 'Tree 树', 14 | category: '数据展示', 15 | status: '20%', 16 | install(app: App): void { 17 | app.use(Tree as any) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /packages/devui-vue/docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import Theme from "vitepress/dist/client/theme-default"; 2 | import Tree from "../../../devui/tree"; 3 | 4 | // 主题样式 5 | import 'vitepress-theme-demoblock/theme/styles/index.css' 6 | // 插件的组件,主要是demo组件 7 | import { registerComponents } from "./register-components.js"; 8 | 9 | export default { 10 | ...Theme, 11 | enhanceApp({ app }) { 12 | app.use(Tree); 13 | 14 | // 注册demoblock插件需要用到的两个组件 15 | registerComponents(app); 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/devui-cli/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { Command } from "commander"; 3 | import { onCreate } from "./commands/create"; 4 | 5 | // 创建命令对象 6 | const program = new Command(); 7 | 8 | // 注册命令、参数、回调 9 | program 10 | // 注册 create 命令 11 | .command("create") 12 | // 添加命令描述 13 | .description("创建一个组件模板或配置文件") 14 | // 添加命令参数 -t | --type 表示该参数必填,[type] 表示选填 15 | .option("-t --type ", `创建类型,可选值:component, lib-entry`) 16 | // 注册命令回调 17 | .action(onCreate); 18 | 19 | // 执行命令行参数解析 20 | program.parse(); 21 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/components/icon-open.tsx: -------------------------------------------------------------------------------- 1 | const IconOpen = (props: any) => { 2 | return ( 3 | 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default IconOpen 20 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/tree-types.ts: -------------------------------------------------------------------------------- 1 | import type { PropType, ExtractPropTypes } from "vue"; 2 | 3 | export interface TreeItem { 4 | label: string; 5 | children?: TreeData; 6 | disableToggle?: boolean; 7 | disabled?: boolean; 8 | checked?: boolean, // 是否勾选 9 | [key: string]: any; 10 | } 11 | 12 | export type TreeData = Array; 13 | 14 | export const treeProps = { 15 | data: { 16 | type: Array as PropType, 17 | default: () => [], 18 | }, 19 | // 新增 20 | checkable: { 21 | type: Boolean, 22 | default: false 23 | }, 24 | } as const; 25 | 26 | export type TreeProps = ExtractPropTypes; 27 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/components/icon-close.tsx: -------------------------------------------------------------------------------- 1 | const IconClose = (props: any) => { 2 | return ( 3 | 11 | 12 | 13 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export default IconClose 23 | -------------------------------------------------------------------------------- /packages/devui-vue/docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { demoBlockPlugin } from 'vitepress-theme-demoblock' 2 | 3 | const sidebar = { 4 | "/": [ 5 | { text: "快速开始", link: "/" }, 6 | { 7 | text: "通用", 8 | }, 9 | { 10 | text: "导航", 11 | }, 12 | { 13 | text: "反馈", 14 | }, 15 | { 16 | text: "数据录入", 17 | }, 18 | { 19 | text: "数据展示", 20 | children: [{ text: "Tree 树", link: "/components/tree/" }], 21 | }, 22 | { 23 | text: "布局", 24 | }, 25 | ], 26 | }; 27 | 28 | const config = { 29 | themeConfig: { 30 | sidebar, 31 | }, 32 | markdown: { 33 | config: (md) => { 34 | // 这里可以使用 markdown-it 插件,vitepress-theme-demoblock就是基于此开发的 35 | md.use(demoBlockPlugin) 36 | } 37 | } 38 | 39 | }; 40 | 41 | export default config; 42 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/composables/use-toggle.ts: -------------------------------------------------------------------------------- 1 | import { ref } from "vue"; 2 | import { TreeData, TreeItem } from "../tree-types"; 3 | 4 | export default function useToggle(data: TreeData): any { 5 | const openedTree = (tree: any) => { 6 | return tree.reduce( 7 | (acc: TreeItem, item: TreeItem) => 8 | item.open 9 | ? acc.concat(item, openedTree(item.children)) 10 | : acc.concat(item), 11 | [] 12 | ); 13 | }; 14 | 15 | const openedData = ref(openedTree(data)); // 响应式对象 16 | 17 | // 触发打开状态 18 | const toggle = (item: TreeItem) => { 19 | if (!item.children) return; 20 | // 如果设置禁止打开则跳过 21 | if (item.disableToggle) return; 22 | item.open = !item.open; 23 | 24 | openedData.value = openedTree(data); 25 | }; 26 | 27 | return { 28 | openedData, 29 | toggle, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-vue-devui", 3 | "version": "0.0.0", 4 | "main": "vue-devui.umd.js", 5 | "module": "vue-devui.es.js", 6 | "private": true, 7 | "scripts": { 8 | "dev": "lerna exec --scope vue-devui yarn dev", 9 | "build": "lerna exec --scope vue-devui yarn build", 10 | "build:lib": "lerna exec --scope vue-devui yarn build:lib" 11 | }, 12 | "dependencies": { 13 | "vue": "^3.2.16" 14 | }, 15 | "devDependencies": { 16 | "@babel/preset-env": "^7.15.8", 17 | "@babel/preset-typescript": "^7.15.0", 18 | "@types/jest": "^27.0.2", 19 | "@vitejs/plugin-vue": "^1.9.3", 20 | "@vitejs/plugin-vue-jsx": "^1.2.0", 21 | "@vue/babel-plugin-jsx": "^1.1.1", 22 | "@vue/test-utils": "^2.0.0-rc.16", 23 | "babel-jest": "^27.3.1", 24 | "commander": "^8.3.0", 25 | "esbuild": "^0.13.10", 26 | "fs-extra": "^10.0.0", 27 | "inquirer": "^8.2.0", 28 | "jest": "^27.3.1", 29 | "kolorist": "^1.5.0", 30 | "lerna": "^4.0.0", 31 | "sass": "^1.43.4", 32 | "typescript": "^4.4.3", 33 | "vite": "^2.6.4", 34 | "vitepress": "^0.20.0", 35 | "vitepress-theme-demoblock": "^1.2.6", 36 | "vue-template-compiler": "^2.6.14", 37 | "vue-tsc": "^0.3.0" 38 | }, 39 | "workspaces": [ 40 | "packages/*" 41 | ], 42 | "license": "MIT" 43 | } 44 | -------------------------------------------------------------------------------- /packages/devui-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devui-cli", 3 | "version": "0.0.0", 4 | "description": "Now I’m the model of a modern major general / The venerated Virginian veteran whose men are all / Lining up, to put me up on a pedestal / Writin’ letters to relatives / Embellishin’ my elegance and eloquence / But the elephant is in the room / The truth is in ya face when ya hear the British cannons go / BOOM", 5 | "keywords": [], 6 | "author": "57code ", 7 | "license": "ISC", 8 | "main": "lib/devui-cli.js", 9 | "directories": { 10 | "lib": "lib", 11 | "test": "__tests__" 12 | }, 13 | "files": [ 14 | "lib" 15 | ], 16 | "publishConfig": { 17 | "registry": "https://registry.npm.taobao.org/" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/57code/mini-vue-devui.git" 22 | }, 23 | "scripts": { 24 | "cli:dev": "esbuild --bundle ./index.js --format=cjs --platform=node --outdir=./lib --watch", 25 | "cli:build": "esbuild --bundle ./index.js --format=cjs --platform=node --outdir=./lib", 26 | "cli": "node ./lib/index.js create", 27 | "build:components": "node ../devui-cli/commands/build.js", 28 | "build:lib": "yarn build:components && cp package.json build && cp README.md build" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/57code/mini-vue-devui/issues" 32 | }, 33 | "homepage": "https://github.com/57code/mini-vue-devui#readme" 34 | } 35 | -------------------------------------------------------------------------------- /packages/devui-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-devui", 3 | "version": "0.0.0", 4 | "main": "vue-devui.umd.js", 5 | "module": "vue-devui.es.js", 6 | "private": true, 7 | "scripts": { 8 | "dev": "vitepress dev docs", 9 | "build": "vue-tsc --noEmit && vite build", 10 | "serve": "vite preview", 11 | "docs:dev": "vitepress dev docs", 12 | "docs:build": "vitepress build docs", 13 | "docs:serve": "vitepress serve docs", 14 | "register:components": "vitepress-rc", 15 | "test": "jest" 16 | }, 17 | "dependencies": { 18 | "vue": "^3.2.16" 19 | }, 20 | "devDependencies": { 21 | "@babel/preset-env": "^7.15.8", 22 | "@babel/preset-typescript": "^7.15.0", 23 | "@types/jest": "^27.0.2", 24 | "@vitejs/plugin-vue": "^1.9.3", 25 | "@vitejs/plugin-vue-jsx": "^1.2.0", 26 | "@vue/babel-plugin-jsx": "^1.1.1", 27 | "@vue/test-utils": "^2.0.0-rc.16", 28 | "babel-jest": "^27.3.1", 29 | "commander": "^8.3.0", 30 | "esbuild": "^0.13.10", 31 | "fs-extra": "^10.0.0", 32 | "inquirer": "^8.2.0", 33 | "jest": "^27.3.1", 34 | "kolorist": "^1.5.0", 35 | "lerna": "^4.0.0", 36 | "sass": "^1.43.4", 37 | "typescript": "^4.4.3", 38 | "vite": "^2.6.4", 39 | "vitepress": "^0.20.0", 40 | "vitepress-theme-demoblock": "^1.2.6", 41 | "vue-template-compiler": "^2.6.14", 42 | "vue-tsc": "^0.3.0" 43 | }, 44 | "workspaces": [ 45 | "packages/*" 46 | ], 47 | "license": "MIT" 48 | } 49 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/composables/use-highlight.ts: -------------------------------------------------------------------------------- 1 | import { ref, Ref } from 'vue' 2 | 3 | interface TypeHighlightClass { 4 | [key: string]: 'active' | '' | 'devui-tree_isDisabledNode' 5 | } 6 | type TypeUseHighlightNode = () => { 7 | nodeClassNameReflect: Ref 8 | handleClickOnNode: (index: string) => void 9 | handleInitNodeClassNameReflect: (isDisabled: boolean, ...keys: Array) => string 10 | } 11 | 12 | const HIGHLIGHT_CLASS = 'active' 13 | const IS_DISABLED_FLAG = 'devui-tree_isDisabledNode' 14 | const useHighlightNode: TypeUseHighlightNode = () => { 15 | const nodeClassNameReflectRef = ref({}) 16 | const handleInit = (isDisabled = false, ...keys) => { 17 | const key = keys.join('-') 18 | nodeClassNameReflectRef.value[key] = isDisabled ? IS_DISABLED_FLAG : (nodeClassNameReflectRef.value[key] || '') 19 | return key 20 | } 21 | const handleClick = (key) => { 22 | if (nodeClassNameReflectRef.value[key] === IS_DISABLED_FLAG) { 23 | return 24 | } 25 | nodeClassNameReflectRef.value = 26 | Object.fromEntries( 27 | Object 28 | .entries(nodeClassNameReflectRef.value) 29 | .map(([k]) => [k, k === key ? HIGHLIGHT_CLASS : '']) 30 | ) 31 | } 32 | return { 33 | nodeClassNameReflect: nodeClassNameReflectRef, 34 | handleClickOnNode: handleClick, 35 | handleInitNodeClassNameReflect: handleInit, 36 | } 37 | } 38 | export default useHighlightNode 39 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/__tests__/tree.spec.ts: -------------------------------------------------------------------------------- 1 | // Step 1: 定义一个测试套 Test Suite 2 | // Step 1: 定义一个测试套 Test Suite 3 | // describe('tree', () => { 4 | // // Step 2: 定义一个单元测试 Test 5 | // // i think 'tree should render correctly' 6 | // it('should render correctly', () => { 7 | // // Step 3: 编写测试断言,期望(expect)1等于1 8 | // expect(1).toEqual(1) 9 | // }) 10 | // }) 11 | 12 | import { mount } from '@vue/test-utils' 13 | import DTree from '../src/tree' 14 | 15 | describe('tree', () => { 16 | it('should render correctly', () => { 17 | const wrapper = mount({ 18 | components: { DTree }, 19 | template: ` 20 | 21 | `, 22 | setup() { 23 | const data = [{ 24 | label: '一级 1', level: 1, 25 | children: [{ 26 | label: '二级 1-1', level: 2, 27 | children: [{ 28 | label: '三级 1-1-1', level: 3, 29 | }] 30 | }] 31 | }, { 32 | label: '一级 2', level: 1, 33 | open: true, // 新增 34 | children: [{ 35 | label: '二级 2-1', level: 2, 36 | children: [{ 37 | label: '三级 2-1-1', level: 3, 38 | }] 39 | }, { 40 | label: '二级 2-2', level: 2, 41 | children: [{ 42 | label: '三级 2-2-1', level: 3, 43 | }] 44 | }] 45 | }, { 46 | label: '一级 3', level: 1, 47 | open: true, // 新增 48 | children: [{ 49 | label: '二级 3-1', level: 2, 50 | children: [{ 51 | label: '三级 3-1-1', level: 3, 52 | }] 53 | }, { 54 | label: '二级 3-2', level: 2, 55 | open: true, // 新增 56 | children: [{ 57 | label: '三级 3-2-1', level: 3, 58 | }] 59 | }] 60 | }, { 61 | label: '一级 4', level: 1, 62 | }] 63 | 64 | return { 65 | data 66 | } 67 | } 68 | }) 69 | 70 | expect(wrapper.classes()).toContain('devui-tree') 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /packages/devui-vue/docs/components/tree/index.md: -------------------------------------------------------------------------------- 1 | # Tree 树 2 | 3 | :::demo 渲染一棵基本树 4 | 5 | ```vue 6 | 36 | 37 | 91 | ``` 92 | ::: 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mini-vue-devui 2 | 3 | 愿景:让每一个前端都能做出属于自己的组件库。 4 | 5 | ## 前言 6 | 7 | 大家好,我是[村长](https://space.bilibili.com/480140591),欢迎关注我的公众号「[村长学前端](https://gitee.com/57code/picgo/raw/master/%E6%89%AB%E7%A0%81_%E6%90%9C%E7%B4%A2%E8%81%94%E5%90%88%E4%BC%A0%E6%92%AD%E6%A0%B7%E5%BC%8F-%E6%A0%87%E5%87%86%E8%89%B2%E7%89%88.png)」一起学习。 8 | 9 | ## 朴素的请求 10 | 请给本项目和[Vue DevUI](https://gitee.com/devui/vue-devui)点个star鼓励一下村长和kagol吧! 11 | 12 | 13 | ## 项目描述 14 | 15 | 此项目是华为开源组件库[Vue DevUI](https://gitee.com/devui/vue-devui)的mini版,是我和[DevUI](https://devui.design/)项目负责人[Kagol](https://github.com/kagol)老师一起做的B站直播节目【[我要做开源](https://space.bilibili.com/480140591/channel/seriesdetail?sid=411659)】中产出的学习项目,所以它不能用于实际项目开发。它的主要作用是带大家学习做开源的方法和如何建设一个组件库。这是一个长期的过程:我们要搭建项目基础架构,解决开发过程中遇到的各种各样的问题,设计和实现一些典型的组件。所以你完全可以把自己掌握学到的知识提交上来,一起完善这个项目。 16 | 17 | ## 快速开始 18 | 19 | ### 第一步:clone 源代码 20 | ``` 21 | git clone https://github.com/57code/mini-vue-devui.git 22 | ``` 23 | 24 | ### 第二步:安装依赖 25 | 26 | 全局安装`yarn`和`lerna` 27 | ``` 28 | npm i -g yarn lerna 29 | ``` 30 | 31 | 安装项目依赖 32 | ``` 33 | yarn 34 | ``` 35 | 36 | ### 第三步:本地启动 37 | ``` 38 | lerna exec --scope mini-vue-devui yarn dev 39 | ``` 40 | 41 | ## 使用 mini-vue-devui 42 | 43 | ### 第一步:创建一个`vite`+`vue3`的工程 44 | ``` 45 | yarn create vite vite-project --template vue 46 | ``` 47 | 48 | ### 第二步:安装 mini-vue-devui 49 | ``` 50 | yarn add mini-vue-devui 51 | ``` 52 | 53 | ### 第三步:使用 mini-vue-devui 54 | 55 | 修改`src/main.ts`文件 56 | ``` 57 | // 引入 MiniDevUI 58 | import MiniDevUI from 'mini-vue-devui' 59 | 60 | createApp(App) 61 | .use(MiniDevUI) // 使用 MiniDevUI 62 | .mount('#app') 63 | ``` 64 | 65 | ## 历次直播 66 | 67 | 为了让大家更方便的观看学习,我给大家准备了该系列视频列表: 68 | 69 | [【我要做开源】Vue DevUI开源指南](https://space.bilibili.com/480140591/channel/seriesdetail?sid=411659) 70 | 71 | 欢迎小伙们快乐学习的同时动动小手,三连一波鼓励一下村长吧! 72 | 73 | 74 | ## 文档链接 75 | 76 | 下面是Kagol在掘金发布的直播相关文档,大家学习之余,多多点赞鼓励他吧! 77 | 78 | [组件库从0到1](https://juejin.cn/column/6961051124031815687) 79 | 80 | ## 致谢 81 | 82 | [DevUI](https://devui.design/)团队的很多小伙伴都加入到我们直播分享中来,他们不仅亲自编写文档,还上场给大家做干货分享,真心感谢你们,下面是参加分享的小伙伴列表: 83 | 84 | kagol:[github](https://github.com/kagol)、[掘金](https://juejin.cn/user/712139267650141) 85 | 86 | wailen:[github](https://github.com/SituC)、[掘金](https://juejin.cn/user/2928754707411629) 87 | 88 | iel:[github](https://github.com/RootWater)、[掘金](https://juejin.cn/user/1538972011203662) 89 | 90 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/tree.scss: -------------------------------------------------------------------------------- 1 | $devui-text-weak: var(--devui-text-weak, #575d6c); 2 | $devui-font-size: var(--devui-font-size, 12px); 3 | $devui-list-item-selected-bg: var(--devui-list-item-selected-bg, #e9edfa); 4 | $devui-list-item-hover-bg: var(--devui-list-item-hover-bg, #f2f5fc); 5 | $devui-border-radius: var(--devui-border-radius, 2px); 6 | $devui-animation-duration-fast: var(--devui-animation-duration-fast, 100ms); 7 | $devui-animation-ease-in-smooth: var( 8 | --devui-animation-ease-in-smooth, 9 | cubic-bezier(0.645, 0.045, 0.355, 1) 10 | ); 11 | $devui-disabled-text: var(--devui-disabled-text, #adb0b8); 12 | 13 | .devui-text-ellipsis { 14 | text-overflow: ellipsis; 15 | overflow: hidden; 16 | white-space: nowrap; 17 | } 18 | 19 | .devui-tree-node { 20 | color: $devui-text-weak; 21 | line-height: 1.5; 22 | white-space: nowrap; 23 | position: relative; 24 | 25 | .devui-tree-node__content { 26 | display: inline-flex; 27 | align-items: center; 28 | font-size: $devui-font-size; 29 | padding-right: 10px; 30 | width: 100%; 31 | border-radius: $devui-border-radius; 32 | padding-left: 6px; 33 | transition: color $devui-animation-duration-fast 34 | $devui-animation-ease-in-smooth, 35 | background-color $devui-animation-duration-fast 36 | $devui-animation-ease-in-smooth; 37 | 38 | &.active { 39 | background-color: $devui-list-item-selected-bg; 40 | text-decoration: none; 41 | border-color: transparent; 42 | } 43 | 44 | &:not(.active):hover { 45 | background-color: $devui-list-item-hover-bg; 46 | } 47 | } 48 | 49 | .devui-tree-node__content--value-wrapper { 50 | display: inline-flex; 51 | align-items: center; 52 | height: 30px; 53 | width: 100%; 54 | 55 | .toggle-disabled { 56 | cursor: not-allowed; 57 | 58 | svg.svg-icon rect { 59 | stroke: $devui-disabled-text; 60 | } 61 | 62 | svg.svg-icon path { 63 | fill: $devui-disabled-text; 64 | } 65 | } 66 | } 67 | 68 | .devui-tree-node__title { 69 | @extend .devui-text-ellipsis; 70 | 71 | margin-left: 5px; 72 | display: inline-block; 73 | border: 1px dashed transparent; 74 | border-radius: $devui-border-radius; 75 | max-width: 100%; 76 | 77 | &:not(.disabled) { 78 | cursor: pointer; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/devui-cli/commands/build.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const { defineConfig, build } = require("vite"); 4 | const vue = require("@vitejs/plugin-vue"); 5 | const vueJsx = require("@vitejs/plugin-vue-jsx"); 6 | const fsExtra = require("fs-extra"); 7 | 8 | const entryDir = path.resolve(__dirname, "../../devui-vue/devui"); 9 | const outputDir = path.resolve(__dirname, "../../../build"); 10 | 11 | // 单组件按需构建 12 | const buildSingle = async (name) => { 13 | await build( 14 | defineConfig({ 15 | ...baseConfig, 16 | build: { 17 | rollupOptions, 18 | lib: { 19 | entry: path.resolve(entryDir, name), 20 | name: "index", 21 | fileName: "index", 22 | formats: ["es", "umd"], 23 | }, 24 | outDir: path.resolve(outputDir, name), 25 | }, 26 | }) 27 | ); 28 | }; 29 | // 生成组件的 package.json 文件 30 | const createPackageJson = (name) => { 31 | const fileStr = `{ 32 | "name": "${name}", 33 | "version": "0.0.0", 34 | "main": "index.umd.js", 35 | "module": "index.es.js", 36 | "style": "style.css" 37 | }`; 38 | fsExtra.outputFile( 39 | path.resolve(outputDir, `${name}/package.json`), 40 | fileStr, 41 | "utf-8" 42 | ); 43 | }; 44 | // 打包配置 45 | const baseConfig = defineConfig({ 46 | configFile: false, 47 | publicDir: false, 48 | plugins: [vue(), vueJsx()], 49 | }); 50 | 51 | const rollupOptions = { 52 | external: ["vue", "vue-router"], 53 | output: { 54 | globals: { 55 | vue: "Vue", 56 | }, 57 | }, 58 | }; 59 | 60 | //全量构建 61 | const buildAll = async () => { 62 | await build( 63 | defineConfig({ 64 | ...baseConfig, 65 | build: { 66 | rollupOptions, 67 | lib: { 68 | entry: path.resolve(entryDir, "vue-devui.ts"), 69 | name: "vue-devui", 70 | fileName: "vue-devui", 71 | formats: ["es", "umd"], 72 | }, 73 | outDir: outputDir, 74 | }, 75 | }) 76 | ); 77 | }; 78 | 79 | const buildLib = async () => { 80 | // 全量打包 81 | await buildAll(); 82 | 83 | // 打包单个组件 84 | // 获取组件名称组成的数组 85 | const components = fs.readdirSync(entryDir).filter((name) => { 86 | const componentDir = path.resolve(entryDir, name); 87 | const isDir = fs.lstatSync(componentDir).isDirectory(); 88 | return isDir && fs.readdirSync(componentDir).includes("index.ts"); 89 | }); 90 | 91 | // 循环一个一个组件构建 92 | for (const name of components) { 93 | // 构建单组件 94 | await buildSingle(name); 95 | 96 | // 生成组件的 package.json 文件 97 | createPackageJson(name); 98 | } 99 | }; 100 | 101 | buildLib(); 102 | -------------------------------------------------------------------------------- /packages/devui-cli/commands/create.js: -------------------------------------------------------------------------------- 1 | import inquirer from "inquirer"; 2 | import { red } from "kolorist"; 3 | 4 | // create type 支持项 5 | const CREATE_TYPES = ["component", "lib-entry"]; 6 | // 文档分类 7 | const DOCS_CATEGORIES = [ 8 | "通用", 9 | "导航", 10 | "反馈", 11 | "数据录入", 12 | "数据展示", 13 | "布局", 14 | ]; 15 | 16 | export async function onCreate(cmd = {}) { 17 | let { type } = cmd; 18 | 19 | // 如果没有在命令参数里带入 type 那么就询问一次 20 | if (!type) { 21 | const result = await inquirer.prompt([ 22 | { 23 | // 用于获取后的属性名 24 | name: "type", 25 | // 交互方式为列表单选 26 | type: "list", 27 | // 提示信息 28 | message: "(必填)请选择创建类型:", 29 | // 选项列表 30 | choices: CREATE_TYPES, 31 | // 默认值,这里是索引下标 32 | default: 0, 33 | }, 34 | ]); 35 | // 赋值 type 36 | type = result.type; 37 | } 38 | 39 | // 如果获取的类型不在我们支持范围内,那么输出错误提示并重新选择 40 | if (CREATE_TYPES.every((t) => type !== t)) { 41 | console.log( 42 | red( 43 | `当前类型仅支持:${CREATE_TYPES.join( 44 | ", " 45 | )},收到不在支持范围内的 "${type}",请重新选择!` 46 | ) 47 | ); 48 | return onCreate(); 49 | } 50 | 51 | try { 52 | switch (type) { 53 | case "component": 54 | // 如果是组件,我们还需要收集一些信息 55 | const info = await inquirer.prompt([ 56 | { 57 | name: "name", 58 | type: "input", 59 | message: "(必填)请输入组件 name ,将用作目录及文件名:", 60 | validate: (value) => { 61 | if (value.trim() === "") { 62 | return "组件 name 是必填项!"; 63 | } 64 | return true; 65 | }, 66 | }, 67 | { 68 | name: "title", 69 | type: "input", 70 | message: "(必填)请输入组件中文名称,将用作文档列表显示:", 71 | validate: (value) => { 72 | if (value.trim() === "") { 73 | return "组件名称是必填项!"; 74 | } 75 | return true; 76 | }, 77 | }, 78 | { 79 | name: "category", 80 | type: "list", 81 | message: "(必填)请选择组件分类,将用作文档列表分类:", 82 | choices: DOCS_CATEGORIES, 83 | default: 0, 84 | }, 85 | ]); 86 | 87 | createComponent(info); 88 | break; 89 | case "lib-entry": 90 | createLibEntry(); 91 | break; 92 | default: 93 | break; 94 | } 95 | } catch (e) { 96 | console.log(red("✖") + e.toString()); 97 | process.exit(1); 98 | } 99 | } 100 | 101 | function createComponent(info) { 102 | // 输出收集到的组件信息 103 | console.log(info); 104 | } 105 | 106 | function createLibEntry() { 107 | console.log("create lib-entry file."); 108 | } 109 | -------------------------------------------------------------------------------- /packages/devui-vue/devui/tree/src/tree.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, toRefs } from 'vue' 2 | import { treeProps, TreeProps, TreeData, TreeItem } from './tree-types' 3 | import IconOpen from './components/icon-open' 4 | import IconClose from './components/icon-close' 5 | import useToggle from './composables/use-toggle' 6 | import './tree.scss' 7 | import useHighlightNode from './composables/use-highlight' 8 | 9 | export default defineComponent({ 10 | name: 'DTree', 11 | props: treeProps, 12 | emits: [], 13 | setup(props: TreeProps, { slots }) { 14 | const { data, checkable } = toRefs(props) 15 | const { openedData, toggle } = useToggle(data.value) 16 | const { nodeClassNameReflect, handleInitNodeClassNameReflect, handleClickOnNode } = useHighlightNode() 17 | 18 | // 增加缩进的展位元素 19 | const Indent = () => { 20 | return 21 | } 22 | 23 | const renderIcon = (item: TreeItem) => { 24 | return item.children 25 | ? toggle(item)}> 28 | { 29 | // 增加插槽逻辑 30 | slots.icon 31 | ? slots.icon(item) 32 | : item.open 33 | ? 34 | : 35 | } 36 | 37 | : 38 | } 39 | 40 | const renderNode = (item: TreeItem) => { 41 | const { key = '', label, disabled, open, level } = item 42 | const nodeId = handleInitNodeClassNameReflect(disabled, key, label) // 获取nodeId 43 | 44 | return ( 45 |
49 |
handleClickOnNode(nodeId)}> 51 |
52 | {/* 折叠图标 */} 53 | {renderIcon(item)} 54 | {/* 复选框 */} 55 | {/* { checkable.value && } */} 56 | { checkable.value && } 57 | {/* 文本 */} 58 | 61 | {item.label} 62 | 63 |
64 |
65 |
66 | ) 67 | } 68 | 69 | return () => { 70 | return ( 71 |
72 | {openedData.value.map((item: TreeItem) => renderNode(item))} 73 |
74 | ) 75 | } 76 | } 77 | }) 78 | 79 | --------------------------------------------------------------------------------