From 6f2565a9d27c7414f307662901802b33d5d9eed4 Mon Sep 17 00:00:00 2001 From: 1549469775 <1549469775@qq.com> Date: Mon, 20 Jun 2022 16:07:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E4=BA=86=E4=B8=80=E5=A0=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vitepress/theme/index.js | 5 +- docs/demo-example.vue | 24 ++++- packages/build/buildComponent.ts | 5 +- packages/build/util.ts | 8 +- packages/components/button/button.vue | 22 +++++ packages/components/button/index.ts | 7 +- packages/components/button/index.vue | 25 ----- packages/components/captcha/captcha.vue | 96 +++++++++++++++++++ packages/components/captcha/index.ts | 7 +- packages/components/captcha/index.vue | 96 ------------------- packages/components/components.ts | 10 +- packages/components/gulpfile.ts | 0 packages/components/tree/index.ts | 14 +++ packages/components/tree/node.vue | 154 ++++++++++++++++++++++++++++++ packages/components/tree/tree-drag.vue | 34 +++++++ packages/components/tree/tree.vue | 127 +++++++++++++++++++++++++ packages/components/tree/type.ts | 32 +++++++ packages/components/tree/util.ts | 155 +++++++++++++++++++++++++++++++ packages/playground/src/App.vue | 34 +++---- packages/playground/src/dev/captcha.vue | 27 ++++++ packages/playground/src/dev/tree.vue | 31 +++++++ packages/theme-chalk/src/base.scss | 3 - packages/theme-chalk/src/button.scss | 6 +- packages/theme-chalk/src/captcha.scss | 2 +- packages/theme-chalk/src/common/var.scss | 2 +- packages/theme-chalk/src/index.scss | 1 + packages/theme-chalk/src/tree.scss | 72 ++++++++++++++ 27 files changed, 829 insertions(+), 170 deletions(-) create mode 100644 packages/components/button/button.vue delete mode 100644 packages/components/button/index.vue create mode 100644 packages/components/captcha/captcha.vue delete mode 100644 packages/components/captcha/index.vue delete mode 100644 packages/components/gulpfile.ts create mode 100644 packages/components/tree/index.ts create mode 100644 packages/components/tree/node.vue create mode 100644 packages/components/tree/tree-drag.vue create mode 100644 packages/components/tree/tree.vue create mode 100644 packages/components/tree/type.ts create mode 100644 packages/components/tree/util.ts create mode 100644 packages/playground/src/dev/captcha.vue create mode 100644 packages/playground/src/dev/tree.vue create mode 100644 packages/theme-chalk/src/tree.scss diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js index d16446d..82aa986 100644 --- a/docs/.vitepress/theme/index.js +++ b/docs/.vitepress/theme/index.js @@ -1,5 +1,8 @@ import DefaultTheme from 'vitepress/dist/client/theme-default' -import PrincessUI from "@princess-ui/components" + +import PrincessUI from "princess-ui" +import "princess-ui/theme-chalk/index.css" + export default { ...DefaultTheme, diff --git a/docs/demo-example.vue b/docs/demo-example.vue index 5a7c228..0160cc9 100644 --- a/docs/demo-example.vue +++ b/docs/demo-example.vue @@ -12,18 +12,40 @@ <span>{{ count }}</span> </p> <button @click="onClick">count++</button> + <ps-tree :list="list"></ps-tree> </div> </template> <script setup> import { ref } from 'vue' // import VTypical from 'vue-typical' - +import { convertTreeData } from "princess-ui/lib/tree" const count = ref(0) function onClick() { count.value++ } + +const list = ref(convertTreeData([ + { + key: 1, + title: "1", + children: [ + { + key: 5, + title: "5" + }, + ] + }, + { + key: 2, + title: "2" + }, + { + key: 3, + title: "3" + }, +])) </script> <style> diff --git a/packages/build/buildComponent.ts b/packages/build/buildComponent.ts index 7eeb985..5d9a4c0 100644 --- a/packages/build/buildComponent.ts +++ b/packages/build/buildComponent.ts @@ -3,11 +3,13 @@ import vue from "@vitejs/plugin-vue" import path from "path" import dts from "vite-plugin-dts" import { replaceCodePlugin } from "vite-plugin-replace"; +import fs from "fs-extra"; // import libInjectCss from "./libInjectCss" import _ from "lodash" import { getOutput, getPath, getPkgs } from "@princess-ui/share"; export default function (prefix: string, component: string, name: string, opts?: {}) { + const isExistVue = fs.pathExistsSync(`components/${component}/index.vue`) return build({ logLevel: "error", plugins: [ @@ -17,7 +19,8 @@ export default function (prefix: string, component: string, name: string, opts?: tsConfigFilePath: getPath("tsconfig.json"), outputDir: getOutput(`lib/${component}`), cleanVueFileName: true, - include: [`components/${component}/index.vue`], + // include: [isExistVue?`components/${component}/index.vue`:`components/${component}/${component}.vue`, `components/${component}/index.ts`], + include: [`components/${component}`], staticImport: true, }), replaceCodePlugin({ diff --git a/packages/build/util.ts b/packages/build/util.ts index 0ceefda..e92d777 100644 --- a/packages/build/util.ts +++ b/packages/build/util.ts @@ -14,7 +14,7 @@ export async function getComponents() { } export async function generateComponents(components?: string[]) { - const componetnsStr: string[] = [`// 该文件为自动生成,请勿修改!!!`] + // const componetnsStr: string[] = [`// 该文件为自动生成,请勿修改!!!`] const newComp: string[] = [] const typeArray: string[] = [] let typeStr = `declare module 'vue' { @@ -28,12 +28,12 @@ export { }` } components.forEach(name => { const n = "Ps" + _.upperFirst(_.kebabCase(name)) - componetnsStr.push(`import ${n} from "./${name}"`) + // componetnsStr.push(`import ${n} from "./${name}"`) newComp.push(n) typeArray.push(n + `: typeof import('princess-ui')['${n}']`) }) - componetnsStr.push(`export { \n ${newComp.join(",\n ")} \n}`) + // componetnsStr.push(`export { \n ${newComp.join(",\n ")} \n}`) typeStr = typeStr.replace("__placeholder__", typeArray.join(',\n ')) await fs.writeFile(getOutput("components.d.ts"), typeStr) - await fs.writeFile(getPkgs("components/components.ts"), componetnsStr.join("\n")) + // await fs.writeFile(getPkgs("components/components.ts"), componetnsStr.join("\n")) } diff --git a/packages/components/button/button.vue b/packages/components/button/button.vue new file mode 100644 index 0000000..4adbc9e --- /dev/null +++ b/packages/components/button/button.vue @@ -0,0 +1,22 @@ +<template> + <div>ffffffffffff-{{ test }}</div> +</template> + +<script lang="ts"> +export default defineComponent({ + name: "ps-button" +}) +</script> + +<script lang="ts" setup> +import { defineComponent, onBeforeUnmount, ref } from "vue" +const props = defineProps<{ + test: string +}>() +console.log(props); + +const emits = defineEmits<{ + +}>() + +</script> diff --git a/packages/components/button/index.ts b/packages/components/button/index.ts index 7282283..fe6221e 100644 --- a/packages/components/button/index.ts +++ b/packages/components/button/index.ts @@ -1,4 +1,9 @@ -import PsButton from "./index.vue" +import { App } from "vue" +import PsButton from "./button.vue" + +PsButton.install = function(app: App) { + app.component(PsButton.name || "ps-button", PsButton) +} export { PsButton diff --git a/packages/components/button/index.vue b/packages/components/button/index.vue deleted file mode 100644 index 52ce227..0000000 --- a/packages/components/button/index.vue +++ /dev/null @@ -1,25 +0,0 @@ -<template> - <div>ffffffffffff</div> -</template> - -<script lang="ts"> -export default defineComponent({ - name: "ps-button" -}) -</script> - -<script lang="ts" setup> -import { defineComponent, onBeforeUnmount, ref } from "vue" -// const props = withDefaults( -// defineProps<{ - -// }>(), -// { - -// }, -// ) -// const emits = defineEmits<{ - -// }>() - -</script> diff --git a/packages/components/captcha/captcha.vue b/packages/components/captcha/captcha.vue new file mode 100644 index 0000000..67949bf --- /dev/null +++ b/packages/components/captcha/captcha.vue @@ -0,0 +1,96 @@ +<template> + <div class="ps-send" :class="{ + ['ps-send--'+size]: size? true: false, + ['ps-send--border']: border + }" @click="onClick" :disabled="isDisabled" + :loading="isLoading" type="button" size="small"> + {{ text }} + </div> +</template> + +<script lang="ts"> +export default defineComponent({ + name: "ps-captcha" +}) +</script> + +<script lang="ts" setup> +import { defineComponent, onBeforeUnmount, ref } from "vue" +const props = withDefaults( + defineProps<{ + duration?: number + initText?: string + runText?: string + loadingText?: string + resetText?: string + border?: boolean + size?: "small" | "big" + }>(), + { + runText: "{%s}s 后重新发送", + initText: "获取验证码", + loadingText: "正在发送", + resetText: "重新获取", + border: false, + duration: 60, + }, +) +const emits = defineEmits<{ + (event: "update:modelValue", show: boolean): void + (event: "send", start: () => void, done: (isDone?: boolean) => void): void +}>() + +const text = ref(props.initText) +const isDisabled = ref(false) +const isLoading = ref(false) +let timeID: any + +onBeforeUnmount(() => { + stop() +}) + +function stop() { + clearInterval(timeID) + text.value = props.resetText + isLoading.value = false + isDisabled.value = false + emits("update:modelValue", false) +} +//获取格式化 +function getText(second: string | number): string { + return props.runText.replace(/\{([^{]*?)%s(.*?)\}/g, String(second)) +} +function run() { + isLoading.value = false + let number = props.duration + text.value = getText(number) + clearInterval(timeID) + timeID = setInterval(() => { + number-- + text.value = getText(number) + if (number <= 0) { + stop() + } + }, 1000) +} + +function onClick() { + if (isDisabled.value) return + if (isLoading.value) return + emits( + "send", + () => { + isDisabled.value = true + isLoading.value = true + text.value = props.loadingText + }, + (isDone: boolean = true) => { + if (isDone) { + run() + } else { + stop() + } + }, + ) +} +</script> diff --git a/packages/components/captcha/index.ts b/packages/components/captcha/index.ts index e1759b7..e709ec5 100644 --- a/packages/components/captcha/index.ts +++ b/packages/components/captcha/index.ts @@ -1,4 +1,9 @@ -import PsCaptcha from "./index.vue" +import { App } from "vue" +import PsCaptcha from "./captcha.vue" + +PsCaptcha.install = function(app: App) { + app.component(PsCaptcha.name || "ps-captcha", PsCaptcha) +} export { PsCaptcha diff --git a/packages/components/captcha/index.vue b/packages/components/captcha/index.vue deleted file mode 100644 index 67949bf..0000000 --- a/packages/components/captcha/index.vue +++ /dev/null @@ -1,96 +0,0 @@ -<template> - <div class="ps-send" :class="{ - ['ps-send--'+size]: size? true: false, - ['ps-send--border']: border - }" @click="onClick" :disabled="isDisabled" - :loading="isLoading" type="button" size="small"> - {{ text }} - </div> -</template> - -<script lang="ts"> -export default defineComponent({ - name: "ps-captcha" -}) -</script> - -<script lang="ts" setup> -import { defineComponent, onBeforeUnmount, ref } from "vue" -const props = withDefaults( - defineProps<{ - duration?: number - initText?: string - runText?: string - loadingText?: string - resetText?: string - border?: boolean - size?: "small" | "big" - }>(), - { - runText: "{%s}s 后重新发送", - initText: "获取验证码", - loadingText: "正在发送", - resetText: "重新获取", - border: false, - duration: 60, - }, -) -const emits = defineEmits<{ - (event: "update:modelValue", show: boolean): void - (event: "send", start: () => void, done: (isDone?: boolean) => void): void -}>() - -const text = ref(props.initText) -const isDisabled = ref(false) -const isLoading = ref(false) -let timeID: any - -onBeforeUnmount(() => { - stop() -}) - -function stop() { - clearInterval(timeID) - text.value = props.resetText - isLoading.value = false - isDisabled.value = false - emits("update:modelValue", false) -} -//获取格式化 -function getText(second: string | number): string { - return props.runText.replace(/\{([^{]*?)%s(.*?)\}/g, String(second)) -} -function run() { - isLoading.value = false - let number = props.duration - text.value = getText(number) - clearInterval(timeID) - timeID = setInterval(() => { - number-- - text.value = getText(number) - if (number <= 0) { - stop() - } - }, 1000) -} - -function onClick() { - if (isDisabled.value) return - if (isLoading.value) return - emits( - "send", - () => { - isDisabled.value = true - isLoading.value = true - text.value = props.loadingText - }, - (isDone: boolean = true) => { - if (isDone) { - run() - } else { - stop() - } - }, - ) -} -</script> diff --git a/packages/components/components.ts b/packages/components/components.ts index 7619fd2..4594a74 100644 --- a/packages/components/components.ts +++ b/packages/components/components.ts @@ -1,7 +1,3 @@ -// 该文件为自动生成,请勿修改!!! -import PsButton from "./button" -import PsCaptcha from "./captcha" -export { - PsButton, - PsCaptcha -} \ No newline at end of file +export * from "./button" +export * from "./captcha" +export * from "./tree" \ No newline at end of file diff --git a/packages/components/gulpfile.ts b/packages/components/gulpfile.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/components/tree/index.ts b/packages/components/tree/index.ts new file mode 100644 index 0000000..8e4d091 --- /dev/null +++ b/packages/components/tree/index.ts @@ -0,0 +1,14 @@ +import { App } from "vue" +import PsTree from "./tree.vue" + +export * from "./util" +export * from "./type" + +PsTree.install = function(app: App) { + app.component(PsTree.name || "ps-tree", PsTree) +} + +export { + PsTree, +} +export default PsTree \ No newline at end of file diff --git a/packages/components/tree/node.vue b/packages/components/tree/node.vue new file mode 100644 index 0000000..d8c3bee --- /dev/null +++ b/packages/components/tree/node.vue @@ -0,0 +1,154 @@ +<template> + <div class="ps-tree-node component" :class="[{ draging: status === ENiuTreeStatus.DragIng }]"> + <div class="ps-tree-node-wrapper" ref="nodeEL" :draggable="draggable" :onDragstart="onDragstart" + :onDragend="onDragend" :onDrop="onDrop" :onDragover="onDragover" :onDragleave="onDragleave" + @click.stop="emit('click', data)"> + <slot :data="data" :deep="level" :dataSourceKey="dataSourceKey" :status="status"> + <div :style="{ + marginLeft: level * 10 + 'px', + 'position': 'relative', + zIndex: 10 + }"> + {{ data.title }} - {{ level * 10 + 'px' }} + </div> + <NiuTreeDrag v-bind="props" :status="status" :style="{ + marginLeft: level * 10 + 'px', + }"></NiuTreeDrag> + </slot> + </div> + <div class="ps-tree-sub-node" v-if="(opts.justOpen || data.isExpand) && data.children && data.children.length"> + <template v-for="(item, index) in data.children" :key="item.key"> + <node @onDragstart="(e: INiuTreeKey) => emit('onDragstart', e)" + @onDragend="(e: INiuTreeKey) => emit('onDragend', e)" + @onDrop="(e: INiuTreeKey, s?: ENiuTreeStatus) => emit('onDrop', e, s)" + @onDragover="(e: INiuTreeKey) => emit('onDragover', e)" + @onDragleave="(e: INiuTreeKey) => emit('onDragleave', e)" + @click="(e: INiuTreeData) => emit('click', e)" :data-source-key="dataSourceKey" :data="item" + :list="list" :level="level + 1"> + <template + #default="{ data, deep, dataSourceKey, status }: { data: INiuTreeData, deep: number, dataSourceKey: INiuTreeKey, status: ENiuTreeStatus }"> + <slot :data="data" :deep="deep" :dataSourceKey="dataSourceKey" :status="status"> + <div :style="{ + marginLeft: deep * 10 + 'px', + 'position': 'relative', + zIndex: 10 + }"> + {{ data.title }} - {{ deep * 10 + 'px' }} + </div> + <NiuTreeDrag v-bind="props" :status="status" :style="{ + marginLeft: deep * 10 + 'px', + }"></NiuTreeDrag> + </slot> + </template> + </node> + </template> + </div> + </div> +</template> + +<script lang="ts" setup> +import { isChildOf } from './util' +import NiuTreeDrag from './tree-drag.vue'; +import { ENiuTreeStatus, INiuTreeData, INiuTreeKey } from './type'; +const props = withDefaults( + defineProps<{ + data: INiuTreeData + list: INiuTreeData[] + dataSourceKey?: INiuTreeKey + level?: number + }>(), + { + level: 0, + } +) +const opts = inject("tree:opts", { + justOpen: false +}) +const emit = defineEmits<{ + (e: 'click', data: INiuTreeData): void + (e: 'onDragstart', key: INiuTreeKey): void + (e: 'onDragend', key: INiuTreeKey): void + (e: 'onDrop', key: INiuTreeKey, status?: ENiuTreeStatus): void + (e: 'onDragover', key: INiuTreeKey): void + (e: 'onDragleave', key: INiuTreeKey): void +}>() + +const draggable = ref(true) +const status = ref<ENiuTreeStatus>() +const nodeEL = ref<HTMLDivElement>() +provide("draggable", draggable) +function onDragstart(event: DragEvent) { + // 开始拖拽 + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'move' + event.dataTransfer.effectAllowed = 'move' + if (nodeEL.value) { + let clone = nodeEL.value.cloneNode(true) as HTMLDivElement + clone.id = 'dragging_node' + clone.style.display = 'inline-block' + clone.style.position = 'absolute' + clone.style.zIndex = '100000' + clone.style.width = '100px' + clone.style.top = '-100000px' + document.body.append(clone) + event.dataTransfer.setDragImage(clone, -4, -4) + } + emit('onDragstart', props.data.key) + status.value = ENiuTreeStatus.DragIng + } +} +function onDragend() { + // 结束拖拽 + let clone = document.getElementById('dragging_node') + clone?.remove() + status.value = undefined + emit('onDragend', props.data.key) +} +function onDrop(e: DragEvent) { + e.preventDefault() + emit('onDrop', props.data.key, status.value) + status.value = undefined +} + +function onDragover(event: DragEvent) { + event.preventDefault() + if (!props.dataSourceKey) return + if (props.dataSourceKey === props.data.key) return + if ( + props.dataSourceKey && + isChildOf(props.data.key, props.dataSourceKey, props.list) + ) + return + const y = event.offsetY + const h = (event.currentTarget as HTMLDivElement).offsetHeight + if (y < h / 3) { + status.value = ENiuTreeStatus.DragUp + } else if (y <= (h * 2) / 3 && y >= h / 3 && props.data.children) { + status.value = ENiuTreeStatus.DragIn + } else if (y > (h * 2) / 3) { + status.value = ENiuTreeStatus.DragDown + } else { + status.value = undefined + } + emit('onDragover', props.data.key) +} +function onDragleave() { + if (!props.dataSourceKey) return + if (props.dataSourceKey === props.data.key) return + if ( + props.dataSourceKey && + isChildOf(props.data.key, props.dataSourceKey, props.list) + ) + return + + // 拖拽离开元素上 + status.value = undefined + emit('onDragleave', props.data.key) +} +</script> +<script lang="ts"> +import { defineComponent, inject, ref, provide } from 'vue' +export default defineComponent({ + name: 'ps-tree-node', +}) +</script> \ No newline at end of file diff --git a/packages/components/tree/tree-drag.vue b/packages/components/tree/tree-drag.vue new file mode 100644 index 0000000..637cf8c --- /dev/null +++ b/packages/components/tree/tree-drag.vue @@ -0,0 +1,34 @@ +<template> + <div :class="[ + { + 'ps-tree-drag-up': + dataSourceKey != data.key && + dataSourceKey != undefined && + !isChildOf(data.key, dataSourceKey, list) && + status === ENiuTreeStatus.DragUp, + 'ps-tree-drag-in': + dataSourceKey != data.key && + dataSourceKey && + !isChildOf(data.key, dataSourceKey, list) && + status === ENiuTreeStatus.DragIn, + 'ps-tree-drag-down': + dataSourceKey != data.key && + dataSourceKey && + !isChildOf(data.key, dataSourceKey, list) && + status === ENiuTreeStatus.DragDown, + }, + ]"></div> +</template> +<script lang="ts" setup> +import { isChildOf } from "./util"; +import { ENiuTreeStatus, INiuTreeData, INiuTreeKey } from "./type"; +const props = withDefaults( + defineProps<{ + data: INiuTreeData + list: INiuTreeData[] + dataSourceKey?: INiuTreeKey + status?: ENiuTreeStatus + }>(), + {}, +) +</script> diff --git a/packages/components/tree/tree.vue b/packages/components/tree/tree.vue new file mode 100644 index 0000000..4d920fc --- /dev/null +++ b/packages/components/tree/tree.vue @@ -0,0 +1,127 @@ +<template> + <div class="ps-tree component"> + <template v-for="(item, index) in list" :key="item.key"> + <node @onDragstart="onDragstart" @onDragEnd="onDragEnd" @onDrop="onDrop" :data-source-key="dataSourceKey" + :data="item" :list="list" :level="level" @click="(item) => clickNode(item)"> + <template + #default="{ data, deep, dataSourceKey, status }: { data: INiuTreeData, deep: number, dataSourceKey: INiuTreeKey, status: ENiuTreeStatus }"> + <slot :data="data" :deep="deep" :dataSourceKey="dataSourceKey" :status="status"></slot> + </template> + </node> + </template> + </div> +</template> +<script lang="ts" setup> +import { INiuTreeData, INiuTreeKey, ENiuTreeStatus } from './type' +import { + findByKey, + forEachTree, + insertAfterByKey, + insertBeforeByKey, + isChildOf, + removeByKey, +} from './util' + +const props = withDefaults( + defineProps<{ + list: INiuTreeData[] + justOpen?: boolean + autoExpand?: boolean + justOpenOne?: boolean + level?: number + }>(), + { + justOpenOne: false, + justOpen: false, + autoExpand: false, + level: 0, + } +) + +provide("tree:opts", props) + +function clickNode(item: INiuTreeData) { + if (props.justOpenOne) { + forEachTree(props.list, (node: INiuTreeData) => { + node.isExpand = false + }) + } + if (item.isFolder) { + item.isExpand = !item.isExpand + emit("change") + } + +} + +const emit = defineEmits<{ + (e: 'change'): void +}>() + +const dataSourceKey = ref() +function onDragstart(key: INiuTreeKey) { + dataSourceKey.value = key +} +function onDragEnd(key: INiuTreeKey) { + dataSourceKey.value = undefined +} +function onDrop(key: INiuTreeKey, status?: ENiuTreeStatus) { + if (!dataSourceKey.value) return + if (!key) return + let data = findByKey(dataSourceKey.value, props.list) + let targetData = findByKey(key, props.list) + switch (status) { + case ENiuTreeStatus.DragIn: + if ( + data && + targetData && + dataSourceKey.value != key && + !isChildOf(key, dataSourceKey.value, props.list) && + targetData.children + ) { + removeByKey(dataSourceKey.value, props.list) + targetData.children.push(data) + if (props.autoExpand) { + targetData.isExpand = true + } + emit("change") + } + break + case ENiuTreeStatus.DragDown: + // 按索引往列表添加节点 + if ( + data && + targetData && + dataSourceKey.value != key && + !isChildOf(key, dataSourceKey.value, props.list) + ) { + removeByKey(dataSourceKey.value, props.list) + insertAfterByKey(key, data, props.list) + emit("change") + } + break + case ENiuTreeStatus.DragUp: + // 按索引往列表添加节点 + if ( + data && + targetData && + dataSourceKey.value != key && + !isChildOf(key, dataSourceKey.value, props.list) + ) { + removeByKey(dataSourceKey.value, props.list) + insertBeforeByKey(key, data, props.list) + emit("change") + } + break + } + dataSourceKey.value = undefined +} +</script> +<script lang="ts"> +import { defineComponent, provide, ref } from "vue" +import node from './node.vue' + +export default defineComponent({ + name: 'ps-tree', +}) +</script> + diff --git a/packages/components/tree/type.ts b/packages/components/tree/type.ts new file mode 100644 index 0000000..2a15d95 --- /dev/null +++ b/packages/components/tree/type.ts @@ -0,0 +1,32 @@ +export interface INiuTreeProps { + key: INiuTreeKey // 唯一键值 + title: string // 标题 + isExpand?: boolean // 标题 + isEdit?: boolean + isDel?: boolean // 是否被删除了 + isNew?: boolean, + data?: any // 节点数据 + children?: INiuTreeProps[] // 子节点 +} + +export type INiuTreeKey = string | number + +export interface INiuTreeData<T = any> { + key: INiuTreeKey + title: string + isFolder: boolean + isExpand: boolean + isFile: boolean + isNew?: boolean, + isDel?: boolean, + data?: T + isEdit: boolean + children?: INiuTreeData[] +} + +export enum ENiuTreeStatus { + DragUp = 'drag-up', + DragIn = 'drag-in', + DragDown = 'drag-down', + DragIng = 'drag-ing', +} \ No newline at end of file diff --git a/packages/components/tree/util.ts b/packages/components/tree/util.ts new file mode 100644 index 0000000..75dea1f --- /dev/null +++ b/packages/components/tree/util.ts @@ -0,0 +1,155 @@ +import { INiuTreeData, INiuTreeKey, INiuTreeProps } from "./type" + +export function convertTreeData(items: INiuTreeProps[]) { + return convertData<INiuTreeProps, INiuTreeData>(items) +} +export function convert(item: INiuTreeProps) { + return convertData<INiuTreeProps, INiuTreeData>([item])[0] +} + +export function convertData< + T extends { children?: T[] }, + S extends { children?: S[] } +>(data: T[] | S[]): S[] { + const transformData = data.map((item: any) => { + const children = item.children && convertData(item.children) + const result = { + ...item, + isEdit: item?.isEdit?item.isEdit:false, + isNew: item?.isNew?item.isNew:false, + isFolder: !!children, + isExpand: item?.isExpand?item.isExpand:false, // 默认全部收缩 + isFile: !children, + children, + } as S + return result + }) + return transformData +} +export function flatTreeData(treeData: INiuTreeData[]): INiuTreeData[] { + let res: INiuTreeData[] = [] + treeData.forEach((data) => { + res.push(data) + if (data.children) { + res = res.concat(flatTreeData(data.children)) + } + }) + return res +} +export function isChildOf( + a_key: INiuTreeKey, + b_key: INiuTreeKey, + treeData: INiuTreeData[] +) { + if (a_key === b_key) return false + + let target_node = findByKey(b_key, treeData) + if (!target_node || !Array.isArray(target_node.children)) return false + + return ( + flatTreeData(target_node.children).findIndex((i) => i.key === a_key) > + -1 + ) +} + +export function forEachTree(tree: INiuTreeData[], cb: (node: INiuTreeData)=>void) { + tree.forEach(v=>{ + cb && cb(v) + if(v.children && v.children.length){ + forEachTree(v.children, cb) + } + }) +} + +export function findByKey( + key: INiuTreeKey, + treeData: INiuTreeData[] +): INiuTreeData | undefined { + for (let i = 0; i < treeData.length; i++) { + const data = treeData[i] + if (data.key === key) { + return data + } + if (data.children && data.children.length) { + let result = findByKey(key, data.children) + if (result) { + return result + } + } + } +} +export function findByKeyParent( + key: INiuTreeKey, + treeData: INiuTreeData[] +): INiuTreeData | undefined { + for (let i = 0; i < treeData.length; i++) { + const data = treeData[i] + if (data.children?.map(v=>v.key).includes(key)) { + return data + } + if (data.children && data.children.length) { + let result = findByKeyParent(key, data.children) + if (result) { + return result + } + } + } +} +export function insertBeforeByKey( + key: INiuTreeKey, + node: INiuTreeData, + treeData: INiuTreeData[] +) { + if (!treeData || !treeData.length) { + return + } + for (let i = 0; i < treeData.length; i++) { + let data = treeData[i] + console.log(key) + + if (data.key === key) { + console.log(node) + treeData.splice(i, 0, node) + break + } + if (data && data.children) { + insertBeforeByKey(key, node, data.children) + } + } +} +export function insertAfterByKey( + key: INiuTreeKey, + node: INiuTreeData, + treeData: INiuTreeData[] +) { + if (!treeData || !treeData.length) { + return + } + for (let i = 0; i < treeData.length; i++) { + let data = treeData[i] + if (data.key === key) { + treeData.splice(i + 1, 0, node) + break + } + if (data && data.children) { + insertAfterByKey(key, node, data.children) + } + } +} +// https://blog.csdn.net/baidu_36095053/article/details/121649810 +export function removeByKey(key: INiuTreeKey, treeData: INiuTreeData[], cb?:(node: INiuTreeData)=>void) { + if (!treeData || !treeData.length) { + return + } + for (let i = 0; i < treeData.length; i++) { + let data = treeData[i] + if (data.key === key) { + cb && cb(data) + treeData.splice(i, 1) + break + } + if (data && data.children) { + removeByKey(key, data.children) + } + } +} diff --git a/packages/playground/src/App.vue b/packages/playground/src/App.vue index cb52b26..6ecd6a1 100644 --- a/packages/playground/src/App.vue +++ b/packages/playground/src/App.vue @@ -1,28 +1,17 @@ <script setup lang="ts"> -function send(start: () => void, done: (isDone: boolean) => void) { - start() - setTimeout(() => { - done(true) - }, 2500); -} +import DevCaptcha from "./dev/captcha.vue" +import DevTree from "./dev/tree.vue" +// import a, * as b from "@princess-ui/components" +// import c, * as d from "princess-ui" +// console.log(a); +// console.log(b); +// console.log(c); +// console.log(d); </script> <template> - <Panel name="验证码"> - <div class="bg-white px-10px py-5px rounded-8px flex"> - <input type="text" class="outline-0 flex-1" placeholder="请输入验证码"> - <ps-captcha @send="send" :duration="5">sada</ps-captcha> - </div> - </Panel> - <Panel name="带边框的验证码" desc="带了个小边框"> - <div class="bg-white px-10px py-5px rounded-8px flex"> - <input type="text" class="outline-0 flex-1" placeholder="请输入验证码"> - <ps-captcha @send="send" border :duration="5">sada</ps-captcha> - </div> - </Panel> - <Panel name="按钮"> - <ps-button></ps-button> - </Panel> + <DevCaptcha></DevCaptcha> + <DevTree></DevTree> </template> <style> @@ -39,11 +28,10 @@ body { align-items: center; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - text-align: center; height: 100vh; } -.card{ +.card { margin-bottom: 25px; } </style> diff --git a/packages/playground/src/dev/captcha.vue b/packages/playground/src/dev/captcha.vue new file mode 100644 index 0000000..c97e3dc --- /dev/null +++ b/packages/playground/src/dev/captcha.vue @@ -0,0 +1,27 @@ +<script setup lang="ts"> +function send(start: () => void, done: (isDone: boolean) => void) { + start() + setTimeout(() => { + done(true) + }, 2500); +} + +</script> + +<template> + <Panel name="验证码"> + <div class="bg-white px-10px py-5px rounded-8px flex"> + <input type="text" class="outline-0 flex-1" placeholder="请输入验证码"> + <ps-captcha @send="send" :duration="5">sada</ps-captcha> + </div> + </Panel> + <Panel name="带边框的验证码" desc="带了个小边框"> + <div class="bg-white px-10px py-5px rounded-8px flex"> + <input type="text" class="outline-0 flex-1" placeholder="请输入验证码"> + <ps-captcha @send="send" border :duration="5">sada</ps-captcha> + </div> + </Panel> + <Panel name="按钮"> + <ps-button></ps-button> + </Panel> +</template> diff --git a/packages/playground/src/dev/tree.vue b/packages/playground/src/dev/tree.vue new file mode 100644 index 0000000..e62cac0 --- /dev/null +++ b/packages/playground/src/dev/tree.vue @@ -0,0 +1,31 @@ +<script setup lang="ts"> +import { ref } from "vue" +import PsTree, { convertTreeData } from "@princess-ui/components/tree" + +const list = ref(convertTreeData([ + { + key: 1, + title: "1", + children: [ + { + key: 5, + title: "5" + }, + ] + }, + { + key: 2, + title: "2" + }, + { + key: 3, + title: "3" + }, +])) +</script> + +<template> + <Panel name="Tree"> + <ps-tree :list="list"></ps-tree> + </Panel> +</template> diff --git a/packages/theme-chalk/src/base.scss b/packages/theme-chalk/src/base.scss index 29745e9..e69de29 100644 --- a/packages/theme-chalk/src/base.scss +++ b/packages/theme-chalk/src/base.scss @@ -1,3 +0,0 @@ -*{ - color: gainsboro; -} \ No newline at end of file diff --git a/packages/theme-chalk/src/button.scss b/packages/theme-chalk/src/button.scss index 981e602..b634ca7 100644 --- a/packages/theme-chalk/src/button.scss +++ b/packages/theme-chalk/src/button.scss @@ -1,5 +1 @@ -@use "common/var.scss"; - -div{ - color: darkkhaki; -} \ No newline at end of file +@import 'common/var.scss'; \ No newline at end of file diff --git a/packages/theme-chalk/src/captcha.scss b/packages/theme-chalk/src/captcha.scss index 1fa1d67..89c9216 100644 --- a/packages/theme-chalk/src/captcha.scss +++ b/packages/theme-chalk/src/captcha.scss @@ -1,4 +1,4 @@ -@use 'common/var.scss'; +@import 'common/var.scss'; .ps-send { user-select: none; diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 8b13789..7b33766 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -1 +1 @@ - +$primary-color: red;//#32b0f877 diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index 7e4c069..6718ebb 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -1,2 +1,3 @@ @import "./button.scss"; @import "./captcha.scss"; +@import "./tree.scss"; diff --git a/packages/theme-chalk/src/tree.scss b/packages/theme-chalk/src/tree.scss new file mode 100644 index 0000000..0677b45 --- /dev/null +++ b/packages/theme-chalk/src/tree.scss @@ -0,0 +1,72 @@ +@import 'common/var.scss'; + +.ps-tree-node.component { + position: relative; + + &.draging { + opacity: 0.6; + } + + .ps-tree-node-wrapper { + position: relative; + } +} + +.ps-tree-drag-up { + background-color: $primary-color !important; + position: absolute; + top: -1px; + left: 5px; + right: 8px; + height: 2px; + pointer-events: none; + z-index: 9; + + &::before { + content: ''; + width: 6px; + height: 6px; + background-color: $primary-color; + border-radius: 50%; + position: absolute; + left: 0; + top: 50%; + margin-top: -3px; + margin-left: -3px; + } +} + +.ps-tree-drag-down { + background-color: $primary-color !important; + position: absolute; + bottom: -1px; + left: 5px; + right: 8px; + height: 2px; + pointer-events: none; + z-index: 9; + + &::before { + content: ''; + width: 6px; + height: 6px; + background-color: $primary-color; + border-radius: 50%; + position: absolute; + left: 0; + top: 50%; + margin-top: -3px; + margin-left: -3px; + } +} + +.ps-tree-drag-in { + background-color: $primary-color !important; + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + pointer-events: none; + z-index: 9; +}