index.vue 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <template>
  2. <div class="actions">
  3. <span
  4. v-for="(action, i) in items"
  5. :class="{ active: equal(selected, action), disabled: action.disabled }"
  6. :key="action.key || i"
  7. @click="clickHandler(action)"
  8. >
  9. <ui-icon :type="action.icon" class="icon" :tip="action.text" />
  10. </span>
  11. </div>
  12. </template>
  13. <script lang="ts" setup>
  14. import { useActive } from "@/hook";
  15. import { ref, toRaw, watchEffect, onBeforeUnmount, nextTick, watch } from "vue";
  16. export type ActionsItem<T = any> = {
  17. icon: string;
  18. key?: T;
  19. text: string;
  20. disabled?: boolean;
  21. action?: () => (() => void) | void;
  22. };
  23. export type ActionsProps = {
  24. items: ActionsItem[];
  25. current?: ActionsItem | null;
  26. single?: boolean;
  27. };
  28. const props = defineProps<ActionsProps>();
  29. const emit = defineEmits<{ (e: "update:current", data: ActionsItem | null): void }>();
  30. const equal = (a: ActionsItem | null, b: ActionsItem | null) => toRaw(a) === toRaw(b);
  31. const selected = ref<ActionsItem | null>(null);
  32. const clickHandler = (select: ActionsItem) => {
  33. selected.value = equal(selected.value, select) ? null : select;
  34. emit("update:current", selected.value);
  35. if (props.single) {
  36. nextTick(() => selected.value && clickHandler(selected.value));
  37. }
  38. };
  39. watch(
  40. () => props.current,
  41. () => {
  42. if (!props.current && selected.value) {
  43. clickHandler(selected.value);
  44. }
  45. }
  46. );
  47. watch(
  48. selected,
  49. (_n, _o, onCleanup) => {
  50. if (selected.value?.action) {
  51. const cleanup = selected.value.action();
  52. cleanup && onCleanup(cleanup);
  53. }
  54. },
  55. { flush: "sync" }
  56. );
  57. onBeforeUnmount(() => {
  58. selected.value = null;
  59. });
  60. </script>
  61. <style lang="scss" scoped>
  62. .actions {
  63. display: flex;
  64. gap: 3px;
  65. background: rgba(27, 27, 28, 0.8);
  66. box-shadow: inset 0px 0px 0px 2px rgba(255, 255, 255, 0.1);
  67. border-radius: 4px 4px 4px 4px;
  68. padding: 4px 10px;
  69. span {
  70. flex: 1;
  71. height: 32px;
  72. width: 32px;
  73. border-radius: 4px 4px 4px 4px;
  74. opacity: 1;
  75. display: flex;
  76. align-items: center;
  77. justify-content: center;
  78. color: rgba(255, 255, 255, 0.6);
  79. font-size: 14px;
  80. cursor: pointer;
  81. transition: all 0.3s ease;
  82. .icon {
  83. font-size: 22px;
  84. }
  85. &:hover,
  86. &.active {
  87. background: rgba(0, 200, 175, 0.16);
  88. color: #00c8af;
  89. }
  90. }
  91. }
  92. </style>