Browse Source

✨ feat: 邮箱输入自动补全

Pchen. 3 weeks ago
parent
commit
834960e523

+ 31 - 0
src/components/EmailAutoComplete/index.vue

@@ -0,0 +1,31 @@
+<script setup>
+import { useEmailAutocomplete } from '@/utils/emailAutocomplete'
+
+defineOptions({ inheritAttrs: false })
+
+defineProps({
+  modelValue: {
+    type: String,
+    default: '',
+  },
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const { emailOptions, handleEmailSearch } = useEmailAutocomplete()
+</script>
+
+<template>
+  <a-auto-complete
+    :data="emailOptions"
+    :model-value="modelValue"
+    allow-clear
+    v-bind="$attrs"
+    @search="handleEmailSearch"
+    @update:model-value="emit('update:modelValue', $event)"
+  >
+    <template v-for="(_, name) in $slots" #[name]="slotData">
+      <slot :name="name" v-bind="slotData || {}" />
+    </template>
+  </a-auto-complete>
+</template>

+ 2 - 0
src/components/index.js

@@ -15,6 +15,7 @@ import {
 import Chart from './Chart/index.vue'
 
 import Breadcrumb from './Breadcrumb/index.vue'
+import EmailAutoComplete from './EmailAutoComplete/index.vue'
 
 use([
   CanvasRenderer,
@@ -39,5 +40,6 @@ export default {
   install(Vue) {
     Vue.component('Chart', Chart)
     Vue.component('Breadcrumb', Breadcrumb)
+    Vue.component('EmailAutoComplete', EmailAutoComplete)
   },
 }

+ 2 - 2
src/pages/Login/components/register.vue

@@ -38,11 +38,11 @@
 
             <a-form-item field="email" hide-label :rules="[{ type: 'email', required: true, message: '请填写正确的邮箱地址' }]"
                 :validate-trigger="['change']">
-                <a-input placeholder="请输入邮箱" allow-clear v-model="form.email">
+                <EmailAutoComplete placeholder="请输入邮箱" allow-clear v-model="form.email">
                     <template #prefix>
                         <icon-email />
                     </template>
-                </a-input>
+                </EmailAutoComplete>
             </a-form-item>
 
 

+ 2 - 2
src/pages/User/setting/components/basic-information.vue

@@ -21,11 +21,11 @@
       <a-form size="large" ref="emailFormRef" :model="emailForm" class="form" @submit="changeEmail">
         <a-form-item field="email" label="邮箱" :rules="[{ type: 'email', required: true, message: '请填写正确的邮箱地址' }]"
           :validate-trigger="['change']">
-          <a-input placeholder="请输入邮箱" allow-clear v-model="emailForm.email">
+          <EmailAutoComplete placeholder="请输入邮箱" allow-clear v-model="emailForm.email">
             <template #prefix>
               <icon-email />
             </template>
-          </a-input>
+          </EmailAutoComplete>
         </a-form-item>
 
         <a-form-item field="captcha" label="图片验证码" :rules="[{ length: 4, required: true, message: '请正确填写图片验证码' }]">

+ 1 - 1
src/pages/admin/goods/orderList.vue

@@ -25,7 +25,7 @@
                             </a-col>
                             <a-col :span="6">
                                 <a-form-item field="user_email" label="用户邮箱">
-                                    <a-input v-model="queryData.user_email" placeholder="支持模糊搜索" allow-clear />
+                                    <EmailAutoComplete v-model="queryData.user_email" placeholder="支持模糊搜索" allow-clear />
                                 </a-form-item>
                             </a-col>
                             <a-col :span="6">

+ 2 - 2
src/pages/admin/lepaoAccount/accountList.vue

@@ -25,7 +25,7 @@
                             </a-col>
                             <a-col :span="6">
                                 <a-form-item field="email" label="用户邮箱">
-                                    <a-input v-model="queryData.email" allow-clear />
+                                    <EmailAutoComplete v-model="queryData.email" allow-clear />
                                 </a-form-item>
                             </a-col>
                             <a-col :span="6">
@@ -213,7 +213,7 @@
                 </a-radio-group>
             </a-form-item>
             <a-form-item field="email" label="通知邮箱" v-if="form.notice_type === 'email'">
-                <a-input v-model="form.email" placeholder="用于接收乐跑失败、登录失效的通知" />
+                <EmailAutoComplete v-model="form.email" placeholder="用于接收乐跑失败、登录失效的通知" allow-clear />
             </a-form-item>
             <a-form-item field="area" label="乐跑跑区">
                 <a-select v-model="form.area" placeholder="请选择乐跑跑区" default-value="">

+ 1 - 1
src/pages/admin/lepaoRecords/lepaoRecords.vue

@@ -26,7 +26,7 @@
               </a-col>
               <a-col :span="12">
                 <a-form-item field="email" label="通知邮箱">
-                  <a-input v-model="queryData.email" placeholder="请输入通知邮箱" allow-clear />
+                  <EmailAutoComplete v-model="queryData.email" placeholder="请输入通知邮箱" allow-clear />
                 </a-form-item>
               </a-col>
               <a-col :span="12">

+ 1 - 1
src/pages/admin/user/userList.vue

@@ -15,7 +15,7 @@
                             </a-col>
                             <a-col :span="8">
                                 <a-form-item field="email" label="用户邮箱">
-                                    <a-input v-model="queryData.email" />
+                                    <EmailAutoComplete v-model="queryData.email" allow-clear />
                                 </a-form-item>
                             </a-col>
                             <a-col :span="8">

+ 2 - 32
src/pages/lepao/accountList/index.vue

@@ -59,7 +59,7 @@
               </a-col>
               <a-col :span="6">
                 <a-form-item field="email" label="邮箱">
-                  <a-input v-model="queryDataForm.email" allow-clear />
+                  <EmailAutoComplete v-model="queryDataForm.email" allow-clear />
                 </a-form-item>
               </a-col>
               <a-col :span="6">
@@ -316,8 +316,7 @@
         </a-radio-group>
       </a-form-item>
       <a-form-item field="email" label="通知邮箱" v-if="form.notice_type === 'email'">
-        <a-auto-complete :data="email" @search="handleSearch" v-model="form.email" placeholder="用于接收乐跑失败、登录失效的通知"
-          allow-clear />
+        <EmailAutoComplete v-model="form.email" placeholder="用于接收乐跑失败、登录失效的通知" allow-clear />
       </a-form-item>
       <a-form-item field="area" label="乐跑跑区">
         <a-select v-model="form.area" placeholder="请选择乐跑跑区" default-value="">
@@ -373,7 +372,6 @@ import { hasPermission } from '@/utils/permission'
 
 const notice = ref('')
 
-const email = ref([])
 const faceInfo = ref({})
 const bindBotRef = ref(null)
 const accountDetailRef = ref(null)
@@ -417,34 +415,6 @@ const pagination = reactive({
   pagesize: 20
 })
 
-const handleSearch = (value) => {
-  const emailSuffix = ["qq.com", "ctbu.edu.cn", "163.com"]
-  const input = (value || "").trim()
-
-  if (!input) {
-    email.value = []
-    return
-  }
-
-  // 没有输入 @,直接拼接所有后缀
-  if (!input.includes("@")) {
-    email.value = emailSuffix.map(suffix => `${input}@${suffix}`)
-    return
-  }
-
-  // 输入了 @ 但结尾是 @,拼接所有后缀
-  if (input.endsWith("@")) {
-    email.value = emailSuffix.map(suffix => `${input}${suffix}`)
-    return
-  }
-
-  // 输入了 @ 且有部分后缀,智能匹配
-  const [prefix, suffixPart] = input.split("@")
-  email.value = emailSuffix
-    .filter(suffix => suffix.startsWith(suffixPart))
-    .map(suffix => `${prefix}@${suffix}`)
-}
-
 const area = ["兰花湖校区跑区", "主校区北跑区", "主校区南跑区", "重庆工商大学茶园校区"]
 const state = [
   { label: '全部', value: -1 }, { label: '需登录', value: 0 }, { label: '正常', value: 1 }, { label: '状态异常', value: 2 }

+ 1 - 1
src/pages/lepao/lepaoRecords/index.vue

@@ -21,7 +21,7 @@
               </a-col>
               <a-col :span="12">
                 <a-form-item field="email" label="通知邮箱">
-                  <a-input v-model="queryData.email" placeholder="请输入通知邮箱" allow-clear />
+                  <EmailAutoComplete v-model="queryData.email" placeholder="请输入通知邮箱" allow-clear />
                 </a-form-item>
               </a-col>
               <a-col :span="12">

+ 1 - 31
src/pages/power/accountList.vue

@@ -72,8 +72,7 @@
         <a-input-number v-model="form.lowest" placeholder="请选择提醒阈值" :step="1" :precision="2" />
       </a-form-item>
       <a-form-item field="email" label="通知邮箱">
-        <a-auto-complete :data="email" @search="handleSearch" v-model="form.email" placeholder="用于接收电费变更通知"
-          allow-clear />
+        <EmailAutoComplete v-model="form.email" placeholder="用于接收电费变更通知" allow-clear />
       </a-form-item>
       <a-form-item field="notes" label="备注">
         <a-textarea v-model="form.notes" no-trim placeholder="添加对账号的备注(非必填)" :max-length="{ length: 50 }" allow-clear
@@ -290,35 +289,6 @@ const GetPowerData = async (type, pid = '') => {
   }
 }
 
-const email = ref([])
-const handleSearch = (value) => {
-  const emailSuffix = ["qq.com", "ctbu.edu.cn", "163.com"]
-  const input = (value || "").trim()
-
-  if (!input) {
-    email.value = []
-    return
-  }
-
-  // 没有输入 @,直接拼接所有后缀
-  if (!input.includes("@")) {
-    email.value = emailSuffix.map(suffix => `${input}@${suffix}`)
-    return
-  }
-
-  // 输入了 @ 但结尾是 @,拼接所有后缀
-  if (input.endsWith("@")) {
-    email.value = emailSuffix.map(suffix => `${input}${suffix}`)
-    return
-  }
-
-  // 输入了 @ 且有部分后缀,智能匹配
-  const [prefix, suffixPart] = input.split("@")
-  email.value = emailSuffix
-    .filter(suffix => suffix.startsWith(suffixPart))
-    .map(suffix => `${prefix}@${suffix}`)
-}
-
 const editAccount = (item) => {
   if (item) {
     form.id = item.id

+ 1 - 1
src/pages/service/createOrder.vue

@@ -21,7 +21,7 @@
                 </a-form-item>
 
                 <a-form-item field="email" label="通知邮箱">
-                    <a-input v-model="form.email" :max-length="30" allow-clear placeholder="请填写您的邮箱,客服回复后会通过邮件通知您" />
+                    <EmailAutoComplete v-model="form.email" :max-length="30" allow-clear placeholder="请填写您的邮箱,客服回复后会通过邮件通知您" />
                 </a-form-item>
 
                 <a-alert v-if="!hasPermission('action.service.createOrder')" type="warning" style="margin-bottom: 16px;">

+ 50 - 0
src/utils/emailAutocomplete.js

@@ -0,0 +1,50 @@
+import { ref } from 'vue'
+
+/** 邮箱后缀候选列表 */
+export const EMAIL_SUFFIXES = ['qq.com', 'ctbu.edu.cn', '163.com']
+
+/**
+ * 根据当前输入生成邮箱自动补全候选
+ * @param {string} value 输入值
+ * @param {string[]} [suffixes=EMAIL_SUFFIXES] 后缀列表
+ * @returns {string[]}
+ */
+export function buildEmailSuggestions(value, suffixes = EMAIL_SUFFIXES) {
+  const input = (value || '').trim()
+  if (!input) return []
+
+  if (!input.includes('@')) {
+    return suffixes.map((suffix) => `${input}@${suffix}`)
+  }
+
+  if (input.endsWith('@')) {
+    return suffixes.map((suffix) => `${input}${suffix}`)
+  }
+
+  const [prefix, suffixPart] = input.split('@')
+  return suffixes
+    .filter((suffix) => suffix.startsWith(suffixPart))
+    .map((suffix) => `${prefix}@${suffix}`)
+}
+
+/**
+ * 邮箱自动补全 composable
+ * @param {string[]} [suffixes=EMAIL_SUFFIXES]
+ */
+export function useEmailAutocomplete(suffixes = EMAIL_SUFFIXES) {
+  const emailOptions = ref([])
+
+  const handleEmailSearch = (value) => {
+    emailOptions.value = buildEmailSuggestions(value, suffixes)
+  }
+
+  const clearEmailOptions = () => {
+    emailOptions.value = []
+  }
+
+  return {
+    emailOptions,
+    handleEmailSearch,
+    clearEmailOptions,
+  }
+}