ColorPicker.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <el-popover
  3. v-model:visible="visible"
  4. class="inline-block"
  5. trigger="click"
  6. :show-arrow="false"
  7. :hide-after="50"
  8. placement="bottom-start"
  9. :width="250"
  10. popper-class="color-picker-popover"
  11. >
  12. <div class="relative">
  13. <div class="color-box">
  14. <p>选择颜色</p>
  15. <ul class="flex flex-wrap p-extra-small color-list">
  16. <li
  17. v-for="color in defaultColors"
  18. :key="color"
  19. class="color-item"
  20. :class="{ active: customColor === color }"
  21. :style="{ '--color': color }"
  22. @click="checkColor(color)"
  23. ></li>
  24. </ul>
  25. </div>
  26. <div class="color-box">
  27. <p>自定义颜色</p>
  28. <ul class="flex flex-wrap p-extra-small color-list">
  29. <li
  30. v-for="color in historyColors"
  31. :key="color"
  32. class="color-item"
  33. :class="{ active: customColor === color }"
  34. :style="{ '--color': color }"
  35. @click="checkColor(color)"
  36. ></li>
  37. </ul>
  38. </div>
  39. <div class="custom-color-button" @click="onCustomPicker">选择自定义颜色</div>
  40. <div class="flex items-center justify-between m-t-base confirm-buttons">
  41. <el-button size="small" type="primary" @click="onConfirm">确定</el-button>
  42. <el-button size="small" type="primary" plain @click="toggleVisible(false)">取消</el-button>
  43. </div>
  44. <input ref="colorInput" v-model="customColor" class="color-input" type="color" />
  45. </div>
  46. <template #reference><slot></slot></template>
  47. </el-popover>
  48. </template>
  49. <script setup lang="ts" name="ColorPicker">
  50. import { reactive, ref, watch } from 'vue'
  51. import { debounce } from 'lodash-es'
  52. import { ElPopover, ElButton } from 'element-plus'
  53. import { localStorage } from '@/plugins/storage'
  54. const props = defineProps<{
  55. modelValue: string
  56. }>()
  57. const emits = defineEmits<{ (e: 'update:modelValue', color: string): void }>()
  58. const visible = ref<boolean>(false)
  59. const defaultColors = [
  60. '#fe7e7d',
  61. '#feff7e',
  62. '#81ff81',
  63. '#01fe7f',
  64. '#80fffe',
  65. '#0080ff',
  66. '#fd7fc3',
  67. '#ff7ffd',
  68. '#fe0002',
  69. '#fffc00',
  70. '#80fe00',
  71. '#00ff43',
  72. '#04fdfd',
  73. '#007fc1',
  74. '#7f81b7',
  75. '#fe02f3',
  76. '#803f40',
  77. '#fb823e',
  78. '#05fc03',
  79. '#018184',
  80. '#043e7b',
  81. '#817eff',
  82. '#810141',
  83. '#fb0182',
  84. ]
  85. const historyColors = ref<string[]>(localStorage.get('HISTORY_CUSTOM_COLOR') || [])
  86. const customColor = ref<string>(props.modelValue)
  87. watch(props, () => {
  88. customColor.value = props.modelValue
  89. })
  90. watch(
  91. customColor,
  92. debounce(() => {
  93. if (customColor.value && !defaultColors.concat(historyColors.value).includes(customColor.value)) {
  94. if (historyColors.value.length >= 16) {
  95. historyColors.value = [customColor.value].concat(historyColors.value.slice(0, 15))
  96. } else {
  97. historyColors.value.unshift(customColor.value)
  98. }
  99. }
  100. }, 50)
  101. )
  102. watch(
  103. historyColors,
  104. debounce(() => {
  105. localStorage.set('HISTORY_CUSTOM_COLOR', [...new Set(historyColors.value.slice(0, 16))])
  106. }, 50)
  107. )
  108. const checkColor = (color: string) => {
  109. customColor.value = color
  110. }
  111. const colorInput = ref<HTMLInputElement>()
  112. const onCustomPicker = () => {
  113. colorInput.value?.click()
  114. }
  115. const toggleVisible = (v: boolean) => {
  116. visible.value = v
  117. }
  118. const onConfirm = () => {
  119. toggleVisible(false)
  120. emits('update:modelValue', customColor.value)
  121. }
  122. </script>
  123. <style scoped lang="scss">
  124. .color-box {
  125. color: $NormalColor;
  126. .color-list {
  127. margin-left: -6px;
  128. .color-item {
  129. width: 20px;
  130. height: 20px;
  131. background-color: var(--color);
  132. margin-left: 6px;
  133. margin-bottom: 9px;
  134. transition: scale 0.1s linear;
  135. &:hover {
  136. transform: scale(1.15);
  137. }
  138. &.active {
  139. outline: 1px dashed red;
  140. }
  141. }
  142. }
  143. }
  144. .custom-color-button {
  145. color: $NormalColor;
  146. height: 32px;
  147. line-height: 32px;
  148. border-radius: 2px;
  149. margin: 6px 0;
  150. background-color: rgba(0, 0, 0, 0.1);
  151. text-align: center;
  152. cursor: pointer;
  153. &:hover {
  154. background-color: rgba(0, 0, 0, 0.08);
  155. }
  156. }
  157. .confirm-buttons {
  158. .el-button {
  159. width: 100px;
  160. }
  161. }
  162. .color-input {
  163. width: 0;
  164. height: 0;
  165. border: none;
  166. outline: none;
  167. opacity: 0;
  168. position: absolute;
  169. right: -30px;
  170. top: 0;
  171. }
  172. </style>