Selaa lähdekoodia

feat(login): 增强密码输入安全性和用户体验

- 为密码输入框增加最大长度限制(16位)
- 新增密码输入处理函数,过滤特殊符号和空格
- 在忘记密码和注册表单中添加密码规则提示文字
- 样式调整以支持密码提示信息展示
- 实现统一的密码输入验证逻辑

fix(store): 修复经营种类数据绑定与展示问题

- 修改经营种类字段名称从 businessTypes 为 businessTypesList
- 调整 checkbox 组件值绑定属性
- 优化经营板块变更处理逻辑
- 完善门店详情数据回显功能
- 更新表单校验规则匹配新的字段名

style(store): 调整营业时间页面布局结构

- 移除最小高度限制,改为固定高度并启用弹性布局
- 为底部操作栏添加自适应间距
- 优化页面元素的伸缩属性设置
- 改进右侧表单项标签宽度样式
- 增强整体布局的响应性表现
spy 3 viikkoa sitten
vanhempi
commit
16935a4421

+ 38 - 0
src/views/login/index.vue

@@ -35,7 +35,9 @@
                 placeholder="请输入密码"
                 show-password
                 autocomplete="new-password"
+                maxlength="16"
                 clearable
+                @input="handlePasswordInput($event, 'passwordForm', 'password')"
               />
             </el-form-item>
             <el-form-item prop="captcha" label="登录验证" label-position="top">
@@ -504,8 +506,11 @@
             placeholder="请输入新密码"
             show-password
             autocomplete="new-password"
+            maxlength="16"
             clearable
+            @input="handlePasswordInput($event, 'forgetForm', 'newPassword')"
           />
+          <div class="password-tip">密码长度为6-16位,必须包含字母和数字,不允许空格和特殊符号</div>
         </el-form-item>
       </el-form>
       <template #footer>
@@ -540,8 +545,11 @@
             placeholder="请输入密码"
             show-password
             autocomplete="new-password"
+            maxlength="16"
             clearable
+            @input="handlePasswordInput($event, 'registerForm', 'password')"
           />
+          <div class="password-tip">密码长度为6-16位,必须包含字母和数字,不允许空格和特殊符号</div>
         </el-form-item>
         <el-form-item prop="confirmPassword" label="确认密码" label-position="top">
           <el-input
@@ -550,7 +558,9 @@
             placeholder="请再次输入密码"
             show-password
             autocomplete="new-password"
+            maxlength="16"
             clearable
+            @input="handlePasswordInput($event, 'registerForm', 'confirmPassword')"
           />
         </el-form-item>
       </el-form>
@@ -723,6 +733,28 @@ const registerRules = reactive({
 
 // 注册协议同意状态
 const registerAgreed = ref(false);
+
+// 处理密码输入,过滤特殊符号和空格,限制16位
+const handlePasswordInput = (value: string, formName: string, fieldName: string) => {
+  // 只保留字母和数字,移除特殊符号和空格
+  const filteredValue = value.replace(/[^a-zA-Z0-9]/g, "");
+  // 限制最大长度为16(maxlength 已经限制,这里再确保一下)
+  const finalValue = filteredValue.slice(0, 16);
+
+  // 更新对应的表单字段
+  if (formName === "passwordForm") {
+    passwordForm.password = finalValue;
+  } else if (formName === "forgetForm") {
+    forgetForm.value.newPassword = finalValue;
+  } else if (formName === "registerForm") {
+    if (fieldName === "password") {
+      registerForm.password = finalValue;
+    } else if (fieldName === "confirmPassword") {
+      registerForm.confirmPassword = finalValue;
+    }
+  }
+};
+
 // 刷新图片验证码
 const refreshCaptcha = async () => {
   captchaImage.value = "";
@@ -1299,6 +1331,12 @@ onBeforeUnmount(() => {
     border: 1px solid #d0d3d6;
     border-radius: 10px;
   }
+  .password-tip {
+    margin-top: 8px;
+    font-size: 12px;
+    line-height: 1.5;
+    color: #909399;
+  }
 }
 .captcha-wrapper {
   display: flex;

+ 31 - 13
src/views/storeDecoration/basicStoreInformation/index.vue

@@ -149,6 +149,16 @@
               </el-option>
             </el-select>
           </el-form-item>
+          <!-- 经纬度展示 -->
+          <el-form-item label="经纬度展示">
+            <el-input v-model="formData.storePosition" disabled class="readonly-input">
+              <template #prefix>
+                <el-icon>
+                  <Lock />
+                </el-icon>
+              </template>
+            </el-input>
+          </el-form-item>
           <!-- 经营板块 -->
           <el-form-item label="经营板块" prop="">
             <el-radio-group v-model="formData.businessSection" disabled>
@@ -162,11 +172,9 @@
 
           <!-- 经营种类 -->
           <el-form-item label="经营种类" prop="">
-            <el-checkbox-group v-model="formData.businessTypes" disabled style="display: flex; flex-wrap: wrap; width: 100%">
+            <el-checkbox-group v-model="formData.businessTypesList" disabled style="display: flex; flex-wrap: wrap; width: 100%">
               <div class="businessSection-item" v-for="type in businessTypeList" :key="type.id || type.value">
-                <el-checkbox :value="type.id || type.value" :label="type.dictDetail">
-                  {{ type.dictDetail }}
-                </el-checkbox>
+                <el-checkbox :value="type.dictId || type.value" :label="type.dictDetail" />
               </div>
             </el-checkbox-group>
           </el-form-item>
@@ -244,7 +252,7 @@ const formData = reactive({
   storeBlurb: "",
   queryAddress: "",
   businessSection: "",
-  businessTypes: [] as string[],
+  businessTypesList: [] as string[],
   expirationTime: "",
   foodLicenceExpirationTime: "",
   storePosition: "",
@@ -326,7 +334,7 @@ const rules = reactive<FormRules>({
   storeBlurb: [{ required: true, message: "请输入门店简介", trigger: "blur" }],
   queryAddress: [{ required: true, message: "请输入地址进行经纬度查询", trigger: "blur" }],
   businessSection: [{ required: true, message: "请选择经营板块", trigger: "change" }],
-  businessTypes: [{ required: true, message: "请选择经营种类", trigger: "change", type: "array" }]
+  businessTypesList: [{ required: true, message: "请选择经营种类", trigger: "change", type: "array" }]
 });
 
 // 地区选项
@@ -509,10 +517,10 @@ const getBusinessSectionData = async () => {
 };
 
 // 监听经营板块变化,获取对应的经营种类
-const handleBusinessSectionChange = async (sectionId: string) => {
+const handleBusinessSectionChange = async (sectionId: string, init?: boolean) => {
   if (!sectionId) {
     businessTypeList.value = [];
-    formData.businessTypes = [];
+    formData.businessTypesList = [];
     return;
   }
   try {
@@ -535,7 +543,9 @@ const handleBusinessSectionChange = async (sectionId: string) => {
       businessTypeList.value = [];
     }
     // 清空已选择的经营种类
-    formData.businessTypes = [];
+    // if (!init) {
+    //   formData.businessTypesList = [];
+    // }
   } catch (error: any) {
     // 忽略请求取消错误(这是正常的,当有重复请求时会被取消)
     if (error?.code === "ERR_CANCELED" || error?.name === "CanceledError") {
@@ -544,7 +554,7 @@ const handleBusinessSectionChange = async (sectionId: string) => {
     }
     console.error("获取经营种类失败:", error);
     businessTypeList.value = [];
-    formData.businessTypes = [];
+    formData.businessTypesList = [];
   }
 };
 
@@ -595,6 +605,7 @@ const handleSubmit = async () => {
         storeTel: formData.storeTel,
         storeAddress: formData.storeAddress,
         storeArea: formData.storeArea,
+        storeBlurb: formData.storeBlurb,
         queryAddress: formData.queryAddress,
         administrativeRegionProvinceAdcode: formData.administrativeRegionProvinceAdcode ?? "",
         administrativeRegionCityAdcode: formData.administrativeRegionCityAdcode ?? "",
@@ -688,8 +699,10 @@ const getStoreDetailData = async () => {
         // 设置 businessSection(此时 watch 不会触发,因为 isLoadingDetail 为 true)
         formData.businessSection = sectionId;
         // 设置已选择的经营种类
-        if (storeData.businessTypes) {
-          formData.businessTypes = Array.isArray(storeData.businessTypes) ? storeData.businessTypes : [storeData.businessTypes];
+        if (storeData.businessTypesList) {
+          formData.businessTypesList = Array.isArray(storeData.businessTypesList)
+            ? storeData.businessTypesList
+            : [storeData.businessTypesList];
         }
       }
     }
@@ -720,7 +733,7 @@ onMounted(async () => {
   await getBusinessSectionData();
   // 如果默认选中了经营板块,自动加载对应的经营种类
   if (formData.businessSection) {
-    await handleBusinessSectionChange(formData.businessSection);
+    await handleBusinessSectionChange(formData.businessSection, true);
   }
   await getStoreDetailData();
 });
@@ -741,6 +754,11 @@ onMounted(async () => {
         flex: 1;
         min-width: 0;
       }
+      .form-right {
+        :deep(.el-form-item__label) {
+          width: 180px !important;
+        }
+      }
     }
     .radio-item {
       width: 100%;

+ 8 - 2
src/views/storeDecoration/businessHours/index.vue

@@ -629,7 +629,10 @@ const handleSave = async () => {
 
 <style scoped lang="scss">
 .business-hours-container {
-  min-height: 100%;
+  display: flex;
+  flex-direction: column;
+  //min-height: 100vh;
+  height: 100%;
   padding: 20px;
   background-color: white;
   .page-title {
@@ -639,6 +642,7 @@ const handleSave = async () => {
     color: #000000;
   }
   .section {
+    flex-shrink: 0;
     margin-bottom: 32px;
     .section-title {
       margin-bottom: 16px;
@@ -668,7 +672,9 @@ const handleSave = async () => {
   .footer {
     display: flex;
     justify-content: center;
-    margin-top: 40px;
+    padding-top: 40px;
+    padding-bottom: 20px;
+    margin-top: auto;
   }
 }
 .day-buttons,