index.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <template>
  2. <div class="web-link-input-wrapper">
  3. <el-input
  4. v-model="localValue"
  5. :placeholder="placeholder"
  6. :disabled="disabled"
  7. clearable
  8. :style="inputStyle"
  9. @change="handleInputChange"
  10. @keydown="handleKeydown"
  11. @focus="
  12. (e: FocusEvent) => {
  13. if (e.target) (e.target as HTMLInputElement).select();
  14. }
  15. "
  16. >
  17. <template #append>
  18. <el-button type="primary" size="small" :disabled="disabled" @click="handleSelectLink"> 选择 </el-button>
  19. </template>
  20. </el-input>
  21. <LinkSelector
  22. ref="linkSelectorRef"
  23. v-model:visible="selectorVisible"
  24. v-model="localValue"
  25. @confirm="handleLinkConfirm"
  26. @close="handleSelectorClose"
  27. />
  28. </div>
  29. </template>
  30. <script setup lang="ts">
  31. import { ref, watch } from 'vue';
  32. import LinkSelector from '@/components/LinkSelector/index.vue';
  33. // 定义属性
  34. interface Props {
  35. modelValue?: string;
  36. placeholder?: string;
  37. disabled?: boolean;
  38. inputStyle?: Record<string, any>;
  39. }
  40. // 定义事件
  41. interface Emits {
  42. (e: 'update:modelValue', value: string): void;
  43. (e: 'change', value: string): void;
  44. }
  45. const props = withDefaults(defineProps<Props>(), {
  46. modelValue: '',
  47. placeholder: '请输入链接或点击选择',
  48. disabled: false,
  49. inputStyle: () => ({})
  50. });
  51. const emit = defineEmits<Emits>();
  52. // 响应式数据
  53. const linkSelectorRef = ref();
  54. const selectorVisible = ref(false);
  55. const localValue = ref(props.modelValue);
  56. // 监听props变化
  57. watch(
  58. () => props.modelValue,
  59. (newValue) => {
  60. if (newValue !== localValue.value) {
  61. localValue.value = newValue;
  62. }
  63. },
  64. { immediate: true }
  65. );
  66. // 监听localValue变化,实现双向绑定
  67. watch(localValue, (newValue) => {
  68. if (newValue !== props.modelValue) {
  69. emit('update:modelValue', newValue);
  70. }
  71. });
  72. // 处理输入框变化
  73. const handleInputChange = () => {
  74. emit('change', localValue.value);
  75. };
  76. // 打开链接选择器
  77. const handleSelectLink = () => {
  78. // 优先使用组件暴露的open方法
  79. if (linkSelectorRef.value && linkSelectorRef.value.open) {
  80. linkSelectorRef.value.open();
  81. } else {
  82. // 兼容方案
  83. selectorVisible.value = true;
  84. }
  85. };
  86. // 处理键盘事件
  87. const handleKeydown = (event: KeyboardEvent) => {
  88. // 当输入框获取焦点时,按回车键可以打开选择器
  89. if (event.code === 'Enter' && !props.disabled) {
  90. handleSelectLink();
  91. event.preventDefault();
  92. }
  93. };
  94. // 处理链接选择确认
  95. const handleLinkConfirm = (value: string, linkItem?: any) => {
  96. localValue.value = value;
  97. emit('update:modelValue', value);
  98. emit('change', value);
  99. selectorVisible.value = false;
  100. };
  101. // 处理选择器关闭
  102. const handleSelectorClose = () => {
  103. selectorVisible.value = false;
  104. };
  105. // 暴露方法给父组件
  106. defineExpose({
  107. openSelector: () => {
  108. handleSelectLink();
  109. },
  110. focusInput: () => {
  111. // 可以在后续实现输入框聚焦功能
  112. }
  113. });
  114. </script>
  115. <style lang="scss" scoped>
  116. .web-link-input-wrapper {
  117. position: relative;
  118. width: 100%;
  119. .el-input {
  120. width: 100%;
  121. }
  122. .el-input-group__append {
  123. padding: 0 15px;
  124. background-color: transparent;
  125. border-left: none;
  126. box-shadow: none;
  127. }
  128. .el-button {
  129. transition: all 0.3s ease;
  130. &:hover:not(:disabled) {
  131. transform: translateY(-1px);
  132. box-shadow: 0 2px 8px rgba(40, 180, 133, 0.3);
  133. }
  134. &:active:not(:disabled) {
  135. transform: translateY(0);
  136. }
  137. }
  138. // 适配不同状态下的样式
  139. .el-input.is-disabled {
  140. .el-input-group__append {
  141. background-color: #f5f7fa;
  142. }
  143. }
  144. // 添加一些间距和布局优化
  145. display: inline-flex;
  146. flex-direction: column;
  147. gap: 4px;
  148. }
  149. </style>