select-card-item.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <script setup lang="ts">
  2. import type { Recordable } from '@vben/types';
  3. import { computed, type PropType, useSlots } from 'vue';
  4. import { Tooltip } from 'ant-design-vue';
  5. import { Icon } from '#/components/icon';
  6. import defaultImg from './images/head-default.png';
  7. import FemaleImg from './images/head-female.png';
  8. import MaleImg from './images/head-male.png';
  9. import RoleImg from './images/role.png';
  10. defineOptions({
  11. name: 'SelectCardItem',
  12. });
  13. const props = defineProps({
  14. model: {
  15. type: Object as PropType<Recordable<any>>,
  16. default: () => ({}),
  17. },
  18. fieldNames: {
  19. type: Array as PropType<Array<any>>,
  20. default: () => [
  21. { title: '名称', value: 'title', maxLength: 12 },
  22. { title: '电话', value: 'phone', maxLength: 8 },
  23. ],
  24. },
  25. config: {
  26. type: Object as PropType<{
  27. bgcolor: string;
  28. color: string;
  29. fillColor: string;
  30. type: string;
  31. }>,
  32. default: () => ({
  33. type: 'none',
  34. fillColor: '#f1ecfe',
  35. bgcolor: '#f5f1fd',
  36. color: '#b389ff',
  37. }),
  38. },
  39. disabled: {
  40. type: Boolean as PropType<boolean>,
  41. default: false,
  42. },
  43. showTree: {
  44. type: Boolean as PropType<boolean>,
  45. default: false,
  46. },
  47. });
  48. const hasCheckSlot = computed(() => {
  49. return !!useSlots().check;
  50. });
  51. const getImage = computed(() => {
  52. switch (props.config.type) {
  53. case 'role': {
  54. return RoleImg;
  55. }
  56. case 'user': {
  57. return props.model.sex === 1 ? MaleImg : FemaleImg;
  58. }
  59. default: {
  60. return defaultImg;
  61. }
  62. }
  63. });
  64. const getFillColor = computed(() => {
  65. if (props.config.type === 'user') {
  66. return props.model.sex === 1 ? '#e9f0fe' : '#ffedf5';
  67. } else {
  68. return props.config.fillColor || '#f1ecfe';
  69. }
  70. });
  71. const getFontColor = computed(() => {
  72. if (props.config.type === 'user') {
  73. return props.model.sex === 1 ? '#3c7eff' : '#ffd1d7';
  74. } else {
  75. return props.config.color || '#b389ff';
  76. }
  77. });
  78. const getBgcolor = computed(() => {
  79. if (props.config.type === 'user') {
  80. return props.model.sex === 1 ? '#f3f8ff' : '#fef6fa';
  81. } else {
  82. return props.config.bgcolor || '#f5f1fd';
  83. }
  84. });
  85. const itemleftwidth = computed(() => {
  86. return props.showTree ? '30%' : '25%';
  87. });
  88. </script>
  89. <template>
  90. <div class="select-card-item">
  91. <div class="select-card-item-box">
  92. <div class="select-card-item-left">
  93. <img :src="getImage" />
  94. </div>
  95. <div class="z-10">
  96. <div
  97. v-for="(item, index) in fieldNames"
  98. :key="index"
  99. class="select-card-item-right flex items-center"
  100. >
  101. <div class="select-card-item-right-title">
  102. {{ item.title }}
  103. </div>
  104. <Tooltip
  105. v-if="
  106. model[item.value] &&
  107. model[item.value].length > model[item.value].maxLength
  108. "
  109. :title="model[item.value]"
  110. >
  111. <div class="select-card-item-right-name">
  112. {{
  113. `${model[item.value].slice(0, model[item.value].maxLength)}...`
  114. }}
  115. </div>
  116. </Tooltip>
  117. <div v-else class="select-card-item-right-name">
  118. {{ model[item.value] || '-' }}
  119. </div>
  120. </div>
  121. </div>
  122. <!-- <div v-if="props.disabled">
  123. <div class="fixed-checked"> 禁用 </div>
  124. </div> -->
  125. <div v-if="hasCheckSlot" class="fixed-checked">
  126. <slot name="check"></slot>
  127. </div>
  128. <div class="fixed-icon">
  129. <Icon :color="getFillColor" class="size-16" icon="fa6-solid:user-tie" />
  130. </div>
  131. </div>
  132. </div>
  133. </template>
  134. <style lang="less" scoped>
  135. .select-card-item {
  136. width: 30%;
  137. margin-bottom: 20px;
  138. margin-left: 20px;
  139. overflow: hidden;
  140. border-radius: 8px;
  141. border-color: transparent;
  142. background: v-bind(getBgcolor);
  143. &:hover {
  144. border: 1px dotted v-bind(getFontColor);
  145. }
  146. &-box {
  147. display: flex;
  148. position: relative;
  149. margin: 14px;
  150. }
  151. &-left {
  152. width: v-bind(itemleftwidth);
  153. margin-right: 14px;
  154. img {
  155. width: 100%;
  156. height: 100%;
  157. }
  158. }
  159. &-right {
  160. &-title {
  161. margin: 10px 10px 4px 0;
  162. opacity: 0.8;
  163. color: #999;
  164. font-size: 12px;
  165. font-weight: bold;
  166. }
  167. &-name {
  168. margin: 8px 0 4px;
  169. opacity: 0.8;
  170. color: #303133;
  171. font-size: 14px;
  172. font-weight: bold;
  173. }
  174. }
  175. .fixed-checked {
  176. position: absolute;
  177. z-index: 1;
  178. right: -6px;
  179. bottom: -4px;
  180. }
  181. .fixed-icon {
  182. position: absolute;
  183. z-index: 0;
  184. top: -24px;
  185. right: -24px;
  186. transform: rotate(48deg);
  187. }
  188. }
  189. :deep(.ant-checkbox-inner) {
  190. border-color: v-bind(getFontColor);
  191. }
  192. :deep(.ant-checkbox-checked .ant-checkbox-inner) {
  193. border-color: v-bind(getFontColor);
  194. background-color: v-bind(getFontColor);
  195. }
  196. :deep(.ant-checkbox-checked::after),
  197. :deep(.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover),
  198. :deep(.ant-checkbox-inner),
  199. :deep(.ant-checkbox:hover),
  200. :deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
  201. border-color: v-bind(getFontColor);
  202. }
  203. .picked {
  204. border-width: 1px;
  205. border-style: dotted;
  206. border-color: v-bind(getFontColor);
  207. }
  208. .not-picked {
  209. border-width: 1px;
  210. border-style: dotted;
  211. }
  212. </style>