SharedCard.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. <template>
  2. <ui-card
  3. class="hover:ring-2 flex flex-col group hover:ring-accent dark:hover:ring-gray-200"
  4. >
  5. <slot name="header">
  6. <div class="flex items-center mb-4">
  7. <ui-img
  8. v-if="data.icon?.startsWith('http')"
  9. :src="data.icon"
  10. class="overflow-hidden rounded-lg"
  11. style="height: 40px; width: 40px"
  12. alt="Can not display"
  13. />
  14. <span v-else class="p-2 rounded-lg bg-box-transparent">
  15. <v-remixicon :name="data.icon || icon" />
  16. </span>
  17. <div class="flex-grow"></div>
  18. <button
  19. class="invisible group-hover:visible"
  20. @click="$emit('execute', data)"
  21. >
  22. <v-remixicon name="riPlayLine" />
  23. </button>
  24. <ui-popover v-if="showDetails" class="h-6 ml-2">
  25. <template #trigger>
  26. <button>
  27. <v-remixicon name="riMoreLine" />
  28. </button>
  29. </template>
  30. <ui-list class="space-y-1" style="min-width: 150px">
  31. <ui-list-item
  32. v-for="item in menu"
  33. :key="item.id"
  34. v-close-popover
  35. class="cursor-pointer"
  36. @click="$emit('menuSelected', { id: item.id, data })"
  37. >
  38. <v-remixicon :name="item.icon" class="mr-2 -ml-1" />
  39. <span class="capitalize">{{ item.name }}</span>
  40. </ui-list-item>
  41. </ui-list>
  42. </ui-popover>
  43. </div>
  44. </slot>
  45. <div class="cursor-pointer flex-1" @click="$emit('click', data)">
  46. <p class="line-clamp font-semibold leading-tight">
  47. {{ data.name }}
  48. </p>
  49. <p
  50. v-show="data.description"
  51. class="text-gray-600 dark:text-gray-200 line-clamp leading-tight mb-1"
  52. >
  53. {{ data.description }}
  54. </p>
  55. </div>
  56. <div class="flex items-center text-gray-600 dark:text-gray-200">
  57. <p class="flex-1">{{ state.date }}</p>
  58. <slot name="footer-content" />
  59. <v-remixicon
  60. v-if="state.triggerText"
  61. v-tooltip="state.triggerText"
  62. :class="{ 'ml-2': $slots['footer-content'] }"
  63. name="riFlashlightLine"
  64. size="20"
  65. />
  66. </div>
  67. </ui-card>
  68. </template>
  69. <script setup>
  70. import { onMounted, shallowReactive } from 'vue';
  71. import { useI18n } from 'vue-i18n';
  72. import dayjs from '@/lib/dayjs';
  73. import triggerText from '@/utils/trigger-text';
  74. const props = defineProps({
  75. data: {
  76. type: Object,
  77. default: () => ({}),
  78. },
  79. icon: {
  80. type: String,
  81. default: 'riGlobalLine',
  82. },
  83. showDetails: {
  84. type: Boolean,
  85. default: true,
  86. },
  87. menu: {
  88. type: Array,
  89. default: () => [],
  90. },
  91. });
  92. defineEmits(['execute', 'click', 'menuSelected']);
  93. const { t } = useI18n();
  94. const state = shallowReactive({
  95. triggerText: null,
  96. date: dayjs(props.data.createdAt).fromNow(),
  97. });
  98. onMounted(async () => {
  99. const { trigger, id } = props.data;
  100. if (!trigger) return;
  101. state.triggerText = await triggerText(trigger, t, id);
  102. });
  103. </script>