index.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. <script setup lang="ts">
  2. import { NAlert, NCard, NSplit, NScrollbar, NButton } from 'naive-ui'
  3. import { ref, watch } from 'vue'
  4. import { VueDraggable } from 'vue-draggable-plus'
  5. import { ContentWrapper, EmptyPlaceholder } from '@/components'
  6. import { useInjection } from '@/composables'
  7. import { mediaQueryInjectionKey } from '@/injection'
  8. import type { UseDraggableReturn } from 'vue-draggable-plus'
  9. defineOptions({
  10. name: 'DragDrop',
  11. })
  12. let codeToHtml: any
  13. const { isMediumScreen } = useInjection(mediaQueryInjectionKey)
  14. const APP_NAME = import.meta.env.VITE_APP_NAME
  15. const baseListCodeHighlight = ref()
  16. const gridListCodeHighlight = ref()
  17. const cloneList2CodeHighlight = ref()
  18. const baseList = ref(
  19. Object.keys(Array.from({ length: 4 }).fill(0)).map((item, index) => ({
  20. name: item,
  21. id: index,
  22. })),
  23. )
  24. const gridList = ref(
  25. Object.keys(Array.from({ length: 50 }).fill(0)).map((item, index) => ({
  26. name: Number(item) + 4,
  27. id: index + 4,
  28. })),
  29. )
  30. const taskList = ref(
  31. Object.keys(Array.from({ length: 5 }).fill(0)).map((item) => ({
  32. name: `任务-${item}`,
  33. id: `任务-${item}`,
  34. })),
  35. )
  36. const cloneTaskList = ref<{ name: string; id: string; key: string }[]>([])
  37. const baseDragRef = ref<UseDraggableReturn>()
  38. const gridDragRef = ref<UseDraggableReturn>()
  39. const taskDragRef = ref<UseDraggableReturn>()
  40. const cloneTaskListDragRef = ref<UseDraggableReturn>()
  41. function cloneTask(element: Record<'name' | 'id', string>) {
  42. return {
  43. name: `${element.name}`,
  44. id: `${element.id}`,
  45. key: element.name + new Date().getTime(),
  46. }
  47. }
  48. function removeTask(element: Record<'name' | 'id' | 'key', string>) {
  49. const find = cloneTaskList.value.find((item) => item.key === element.key)
  50. if (find) {
  51. cloneTaskList.value = cloneTaskList.value.filter((item) => item.key !== element.key)
  52. }
  53. }
  54. watch(
  55. [baseList, gridList, cloneTaskList],
  56. async (newVal) => {
  57. const [baseList, gridList, cloneList2] = newVal
  58. if (!codeToHtml) {
  59. // @ts-ignore
  60. const shiki = await import('https://cdn.jsdelivr.net/npm/shiki@3.7.0/+esm')
  61. codeToHtml = shiki.codeToHtml
  62. }
  63. codeToHtml(JSON.stringify(baseList, null, 2), {
  64. lang: 'json',
  65. themes: {
  66. dark: 'dark-plus',
  67. light: 'min-light',
  68. },
  69. })
  70. .then((result: string) => (baseListCodeHighlight.value = result))
  71. .catch(() => (baseListCodeHighlight.value = JSON.stringify(baseList, null, 2)))
  72. codeToHtml(JSON.stringify(gridList, null, 2), {
  73. lang: 'json',
  74. themes: {
  75. dark: 'dark-plus',
  76. light: 'min-light',
  77. },
  78. })
  79. .then((result: string) => (gridListCodeHighlight.value = result))
  80. .catch(() => (gridListCodeHighlight.value = JSON.stringify(gridList, null, 2)))
  81. codeToHtml(JSON.stringify(cloneList2, null, 2), {
  82. lang: 'json',
  83. themes: {
  84. dark: 'dark-plus',
  85. light: 'min-light',
  86. },
  87. })
  88. .then((result: string) => (cloneList2CodeHighlight.value = result))
  89. .catch(() => (cloneList2CodeHighlight.value = JSON.stringify(cloneList2, null, 2)))
  90. },
  91. {
  92. immediate: true,
  93. },
  94. )
  95. </script>
  96. <template>
  97. <ContentWrapper content-class="flex flex-col gap-y-2">
  98. <NAlert
  99. type="info"
  100. closable
  101. >
  102. {{ APP_NAME }} 的 Tabs 栏的拖拽模块使用了
  103. vue-draggable-plus,在一些拖拽的场景下,它也许可以帮助到你
  104. </NAlert>
  105. <NCard
  106. title="基础使用"
  107. :size="isMediumScreen ? 'small' : undefined"
  108. >
  109. <NSplit
  110. :direction="isMediumScreen ? 'vertical' : 'horizontal'"
  111. :pane1-class="isMediumScreen ? 'pb-4' : 'pr-8'"
  112. :pane2-class="isMediumScreen ? 'pt-4' : 'pl-8'"
  113. :style="{
  114. height: isMediumScreen ? '580px' : '280px',
  115. }"
  116. >
  117. <template #1>
  118. <NScrollbar>
  119. <VueDraggable
  120. ref="baseDragRef"
  121. v-model="baseList"
  122. :animation="150"
  123. :scrollSensitivity="100"
  124. ghostClass="ghost"
  125. group="drag1"
  126. class="flex flex-col gap-2 rounded bg-neutral-500/5 p-4 select-none"
  127. >
  128. <div
  129. v-for="{ id, name } in baseList"
  130. :key="id"
  131. class="flex h-14 cursor-move items-center justify-center rounded bg-neutral-500/8 p-3"
  132. >
  133. {{ name }}
  134. </div>
  135. </VueDraggable>
  136. </NScrollbar>
  137. </template>
  138. <template #2>
  139. <NScrollbar>
  140. <div v-html="baseListCodeHighlight"></div>
  141. </NScrollbar>
  142. </template>
  143. <template #resize-trigger>
  144. <div
  145. class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] max-lg:h-px max-lg:w-full dark:bg-neutral-700"
  146. ></div>
  147. </template>
  148. </NSplit>
  149. </NCard>
  150. <NCard
  151. title="网格布局"
  152. :size="isMediumScreen ? 'small' : undefined"
  153. >
  154. <div class="mb-4">
  155. 你可以把<span class="text-primary">网格布局</span>的元素拖进<span class="text-primary"
  156. >基础使用</span
  157. >中,它们可以相互拖放
  158. </div>
  159. <NSplit
  160. :direction="isMediumScreen ? 'vertical' : 'horizontal'"
  161. :pane1-class="isMediumScreen ? 'pb-4' : 'pr-8'"
  162. :pane2-class="isMediumScreen ? 'pt-4' : 'pl-8'"
  163. :style="{
  164. height: isMediumScreen ? '680px' : '280px',
  165. }"
  166. :default-size="0.7"
  167. >
  168. <template #1>
  169. <NScrollbar>
  170. <VueDraggable
  171. ref="gridDragRef"
  172. v-model="gridList"
  173. :animation="150"
  174. ghostClass="ghost"
  175. group="drag1"
  176. class="m-auto grid grid-cols-8 gap-2 rounded bg-neutral-500/5 p-4 select-none max-lg:grid-cols-4"
  177. >
  178. <div
  179. v-for="{ id, name } in gridList"
  180. :key="id"
  181. class="flex h-14 cursor-move items-center justify-center rounded bg-neutral-500/8 p-3"
  182. >
  183. {{ name }}
  184. </div>
  185. </VueDraggable>
  186. </NScrollbar>
  187. </template>
  188. <template #2>
  189. <NScrollbar>
  190. <div v-html="gridListCodeHighlight"></div>
  191. </NScrollbar>
  192. </template>
  193. <template #resize-trigger>
  194. <div
  195. class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] max-lg:h-px max-lg:w-full dark:bg-neutral-700"
  196. ></div>
  197. </template>
  198. </NSplit>
  199. </NCard>
  200. <NCard
  201. title="克隆使用"
  202. :size="isMediumScreen ? 'small' : undefined"
  203. >
  204. <NSplit
  205. :direction="isMediumScreen ? 'vertical' : 'horizontal'"
  206. :pane1-class="isMediumScreen ? 'pb-4' : 'pr-8'"
  207. :pane2-class="isMediumScreen ? 'pt-4' : 'pl-8'"
  208. :style="{
  209. height: isMediumScreen ? '780px' : '280px',
  210. }"
  211. :default-size="0.7"
  212. >
  213. <template #1>
  214. <div class="flex h-full gap-4 max-lg:flex-col">
  215. <NScrollbar>
  216. <VueDraggable
  217. ref="taskDragRef"
  218. v-model="taskList"
  219. :animation="150"
  220. :scrollSensitivity="100"
  221. ghostClass="ghost"
  222. :group="{ name: 'clone', pull: 'clone', put: false }"
  223. class="flex flex-col gap-2 rounded bg-neutral-500/5 p-4 select-none"
  224. :clone="cloneTask"
  225. >
  226. <div
  227. v-for="{ id, name } in taskList"
  228. :key="id"
  229. class="flex h-14 cursor-move items-center justify-center rounded bg-neutral-500/8 p-3"
  230. >
  231. {{ name }}
  232. </div>
  233. </VueDraggable>
  234. </NScrollbar>
  235. <NScrollbar>
  236. <EmptyPlaceholder
  237. :show="cloneTaskList.length <= 0"
  238. :description="`把${isMediumScreen ? '上' : '左'}边的任务拖拽到这里`"
  239. />
  240. <VueDraggable
  241. ref="cloneTaskListDragRef"
  242. v-model="cloneTaskList"
  243. :animation="150"
  244. :scrollSensitivity="100"
  245. ghostClass="ghost"
  246. group="clone"
  247. class="flex h-full flex-col gap-2 rounded bg-neutral-500/5 p-4 select-none"
  248. style="min-height: 300px"
  249. >
  250. <div
  251. v-for="item in cloneTaskList"
  252. :key="item.key"
  253. class="flex h-14 cursor-move items-center justify-between rounded bg-neutral-500/8 px-4 py-3"
  254. >
  255. <span>{{ item.name }}</span>
  256. <NButton
  257. quaternary
  258. circle
  259. size="small"
  260. @click="removeTask(item)"
  261. >
  262. <template #icon>
  263. <span class="iconify ph--x"></span>
  264. </template>
  265. </NButton>
  266. </div>
  267. </VueDraggable>
  268. </NScrollbar>
  269. </div>
  270. </template>
  271. <template #2>
  272. <NScrollbar>
  273. <div v-html="cloneList2CodeHighlight"></div>
  274. </NScrollbar>
  275. </template>
  276. <template #resize-trigger>
  277. <div
  278. class="h-full w-px cursor-col-resize bg-neutral-200 transition-[background-color] dark:bg-neutral-700"
  279. ></div>
  280. </template>
  281. </NSplit>
  282. </NCard>
  283. </ContentWrapper>
  284. </template>