Browse Source

```
feat(api-select): 添加 getOptions 属性以获取选项数据

新增 `getOptions` 回调函数属性,用于在 ApiSelect 组件中暴露当前下拉选项,
便于外部组件获取和使用这些选项数据。

fix(form): 修改 setFieldsValue 返回类型为 Promise

将 `setFieldsValue` 方法的返回类型从 void 改为 Promise<void>,确保与异步操作兼容,
提升表单赋值逻辑的一致性和可等待性。

feat(env): 切换测试环境服务地址并注释本地配置

更新 `.env.test` 文件中的后端服务地址,启用服务器地址并注释掉本地 IP 配置,
保证测试环境连接到正确后端接口。

feat(components): 注册 NGridItem 组件类型

在 `components.d.ts` 中注册 `NGridItem` 类型,解决 TypeScript 提示问题,
完善 naive-ui 组件的类型支持。

feat(fright-config): 增加渠道选项动态控制及横向滚动

在运费配置页面中引入 `ChannelOptions` 数据,并通过 `getOptions` 获取选项列表;
同时添加横向滚动容器防止内容溢出。

feat(delivery-modal): 处理订单发货前售后状态校验

增加对“售后处理中”商品的状态判断,在发货时提示用户存在未完成售后的商品,
避免错误发货。并在弹窗关闭后重置相关状态。

fix(normal-modal): 订单详情弹窗关闭后清空数据

在订单详情弹窗关闭后清除 `orderInfo` 数据,防止下次打开显示旧数据,
提高用户体验和界面安全性。

feat(desk-category): 搜索表单自动填充店铺 ID 并查询

在桌台分类页面中,当选择门店下拉框加载完成后自动设置默认店铺 ID,
并触发一次数据查询,优化初始化流程。

perf(user-manage): 简化用户名长度验证方式

替换原有的自定义规则验证器为原生 `minlength` 和 `maxlength` 属性,
简化代码结构并增强维护性。

refactor(user-manage): 重构邮箱和手机验证逻辑

统一使用 validator 函数进行邮箱和手机号格式校验,强化输入控制,
并修复可能存在的正则匹配边界情况。
```

zhangtao 5 days ago
parent
commit
46169cc5f6

+ 2 - 2
.env.test

@@ -1,9 +1,9 @@
 # backend service base url, test environment
 # VITE_SERVICE_BASE_URL=http://74949mkfh190.vicp.fun
-VITE_SERVICE_BASE_URL=http://192.168.1.206:8114 #付
+# VITE_SERVICE_BASE_URL=http://192.168.1.206:8114 #付
 # VITE_SERVICE_BASE_URL=http://192.168.0.157:8114 #王
 # VITE_SERVICE_BASE_URL=https://mock.apifox.cn/m1/3109515-0-default
-# VITE_SERVICE_BASE_URL=https://shop.platform.zswlgz.com #服务器
+VITE_SERVICE_BASE_URL=https://shop.platform.zswlgz.com #服务器
 
 
 # other backend service base url, test environment

+ 3 - 0
src/components/zt/ApiSelect/api-select.vue

@@ -51,6 +51,9 @@ async function fetchApi() {
   const res = await api(params);
   options.value = unref(bindValue).pagination ? [...options.value, ...res.data.records] : get(res, props.resultFeild);
   fetchLoading.value = false;
+  if (props.getOptions) {
+    props.getOptions(options.value);
+  }
   if (unref(bindValue).pagination) {
     total.value = res.data.total;
   }

+ 4 - 0
src/components/zt/ApiSelect/type/index.ts

@@ -42,4 +42,8 @@ export interface ApiSelectProps extends /* @vue-ignore */ SelectProps {
    * 分页条数字段(对标后端需要字段)
    */
   pageSizeFeild?: string;
+  /**
+   * 获取options
+   */
+  getOptions?: (options: any) => any;
 }

+ 1 - 1
src/components/zt/Form/types/form.ts

@@ -87,7 +87,7 @@ export interface FormActionType {
   submit: () => Promise<any>;
   setFormProps: (formProps: Partial<FormProps>) => Promise<void>;
   setSchema: (schemaProps: Partial<FormSchema[]>) => Promise<void>;
-  setFieldsValue: (values: Recordable) => void;
+  setFieldsValue: (values: Recordable) => Promise<void>;
   clearValidate: (name?: string | string[]) => Promise<void>;
   getFieldsValue: () => Recordable;
   resetFields: () => Promise<void>;

+ 4 - 0
src/typings/api.d.ts

@@ -1222,6 +1222,10 @@ declare namespace Api {
        * 使用积分
        */
       useScore?: number;
+      /**
+       * 售后处理中
+       */
+      refundIngCount?: number;
       [property: string]: any;
     }
     interface UserAddrOrder {

+ 1 - 0
src/typings/components.d.ts

@@ -83,6 +83,7 @@ declare module 'vue' {
     NFormItem: typeof import('naive-ui')['NFormItem']
     NGi: typeof import('naive-ui')['NGi']
     NGrid: typeof import('naive-ui')['NGrid']
+    NGridItem: typeof import('naive-ui')['NGridItem']
     NIcon: typeof import('naive-ui')['NIcon']
     NImage: typeof import('naive-ui')['NImage']
     NInput: typeof import('naive-ui')['NInput']

+ 11 - 2
src/views/config/fright-config/index.vue

@@ -1,4 +1,5 @@
 <script setup lang="tsx">
+import { ref } from 'vue';
 import { NDynamicInput, NInputNumber } from 'naive-ui';
 import { fetchGetAllChannelList } from '@/service/api/goods/store-goods';
 import { useTabStore } from '@/store/modules/tab';
@@ -9,6 +10,7 @@ interface Options {
   value: any;
   index: number;
 }
+const ChannelOptions = ref<Api.goods.Channel[]>([]);
 const [registerForm, { getFieldsValue, validate }] = useForm({
   schemas: [
     {
@@ -17,7 +19,7 @@ const [registerForm, { getFieldsValue, validate }] = useForm({
       label: '配送费/运费',
       render({ model, field }) {
         return (
-          <div>
+          <div class={'flex flex-wrap items-center'}>
             <div class={'h38px flex items-center'}>
               <div class={'w-200px text-center'}> 企业 </div>
               <div class={'ml-3 w-200px text-center'}> 费用(元/每20kg) </div>
@@ -27,6 +29,7 @@ const [registerForm, { getFieldsValue, validate }] = useForm({
               onUpdate:value={value => (model[field] = value)}
               v-slots={(row: Options) => handleCreatInput(model, field, row)}
               onCreate={() => handleAdd()}
+              max={ChannelOptions.value.length}
             ></NDynamicInput>
           </div>
         );
@@ -53,6 +56,7 @@ function handleCreatInput(model: any, field: any, row: Options) {
           api={fetchGetAllChannelList}
           labelFeild="channelName"
           valueFeild="id"
+          getOptions={handleGetOptions}
           onUpdate:value={vlaue => {
             model[field][row.index].Enterprise = vlaue;
           }}
@@ -80,6 +84,9 @@ function handleAdd() {
     Cost: 1
   };
 }
+function handleGetOptions(options: Api.goods.Channel[]) {
+  ChannelOptions.value = options;
+}
 async function save() {
   await validate();
   const form = getFieldsValue();
@@ -90,7 +97,9 @@ async function save() {
 <template>
   <LayoutTable>
     <NCard title="配送费&运费配置" :bordered="false" size="small" segmented class="card-wrapper">
-      <BasicForm @register-form="registerForm"></BasicForm>
+      <NScrollbar x-scrollable>
+        <BasicForm @register-form="registerForm"></BasicForm>
+      </NScrollbar>
       <template #footer>
         <NSpace justify="end">
           <NButton size="small" @click="close">关闭</NButton>

+ 19 - 7
src/views/delivery/normal-order/component/delivery-modal.vue

@@ -1,5 +1,5 @@
 <script setup lang="tsx">
-import { nextTick, ref } from 'vue';
+import { nextTick, ref, unref } from 'vue';
 import { fetchDeliveryOrder, fetchGetNomalOrderInfo } from '@/service/api/delivery/normal-orde';
 import { useModal } from '@/components/zt/Modal/hooks/useModal';
 import { useForm } from '@/components/zt/Form/hooks/useForm';
@@ -26,6 +26,7 @@ const emit = defineEmits<{
 }>();
 
 const orderInfo = ref<Api.delivery.deliveryOrder>();
+const isRefundIng = ref(false);
 const ShipmentColumns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
   {
     title: '商品',
@@ -48,13 +49,20 @@ const ShipmentColumns: NaiveUI.TableColumn<Api.delivery.OrderItemElement>[] = [
     key: 'num',
     width: 100,
     render: row => {
-      const count = Number(row.prodCount) - row.refundSuccessCount;
+      const count = Number(row.prodCount) - Number(row.refundSuccessCount);
+      if (row.refundIngCount) {
+        isRefundIng.value = true;
+      }
       return (
         <div class={'flex items-center justify-center'}>
-          <div>
-            {count}{' '}
-            {row.refundSuccessCount ? <n-tag type="success">已扣除退款成功:{row.refundSuccessCount}</n-tag> : ''}
-          </div>
+          {count} {row.refundSuccessCount ? <n-tag type="success">已扣除退款成功:{row.refundSuccessCount}</n-tag> : ''}
+          {row.refundIngCount ? (
+            <n-tag type="error" class={'ml2'}>
+              售后处理:{row.refundIngCount}
+            </n-tag>
+          ) : (
+            ''
+          )}
         </div>
       );
     }
@@ -98,6 +106,10 @@ async function handleOpenOrder(orderNumber: string) {
   }
 }
 async function handleSubmit() {
+  if (unref(isRefundIng)) {
+    window.$message?.error('存在售后处理中的商品,请处理后再发货');
+    return;
+  }
   await validate();
   setSubLoading(true);
   const form: Recordable = {
@@ -118,7 +130,7 @@ async function handleSubmit() {
 </script>
 
 <template>
-  <BasicModal @register="registerModal" @ok="handleSubmit">
+  <BasicModal @register="registerModal" @ok="handleSubmit" @after-leave="isRefundIng = false">
     <div v-if="orderInfo">
       <NCard :bordered="false" title="商品信息">
         <NDataTable :scroll-x="800" :columns="ShipmentColumns" :data="orderInfo.orderItems" :bordered="false" />

+ 2 - 2
src/views/delivery/normal-order/component/normal-modal.vue

@@ -82,7 +82,7 @@ function handleCopy() {
 
 <template>
   <div>
-    <BasicModal @register="registerModal">
+    <BasicModal @register="registerModal" @after-leave="orderInfo = undefined">
       <div v-if="orderInfo">
         <NFlex justify="space-between" align="center">
           <NFlex>
@@ -187,7 +187,7 @@ function handleCopy() {
               </NTr>
               <NTr>
                 <NTd>积分</NTd>
-                <NTd>{{ orderInfo.offsetPoints }}</NTd>
+                <NTd>{{ orderInfo.offsetPoints || 0 }}</NTd>
               </NTr>
               <NTr>
                 <NTd v-if="orderInfo.hbOrderStatus == orderStatusEnum.WAIT_PAY">需付款</NTd>

+ 5 - 1
src/views/goods/desk-category/index.vue

@@ -12,7 +12,7 @@ const appStore = useAppStore();
 const deskData = ref<Api.goods.ShopCategory[]>([]);
 const loading = ref(false);
 const relatedGoodsModalRef = useTemplateRef('relatedGoodsModalRef');
-const [registerSearchForm, { getFieldsValue: getSearchForm }] = useForm({
+const [registerSearchForm, { getFieldsValue: getSearchForm, setFieldsValue }] = useForm({
   schemas: [
     {
       field: 'name',
@@ -31,6 +31,10 @@ const [registerSearchForm, { getFieldsValue: getSearchForm }] = useForm({
           nextTick(() => {
             getData();
           });
+        },
+        getOptions: async (options: any) => {
+          await setFieldsValue({ shopId: options[0].shopId });
+          getData();
         }
       }
     }

+ 22 - 24
src/views/manage/user/index.vue

@@ -132,26 +132,10 @@ const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValu
           label: '用户名',
           component: 'NInput',
           required: true,
-          rules: [
-            {
-              required: true,
-              message: '请输入用户名',
-              trigger: ['blur', 'input'],
-              validator: (rule, value) => {
-                return new Promise<void>((resolve, reject) => {
-                  if (value.length < 2) {
-                    console.log(rule);
-
-                    reject(new Error('用户名不能低于2位'));
-                  }
-                  if (value.length > 20) {
-                    reject(new Error('用户名不能高于20位'));
-                  }
-                  resolve();
-                });
-              }
-            }
-          ]
+          componentProps: {
+            maxlength: 20,
+            minlength: 2
+          }
         },
         {
           field: 'deptIds',
@@ -194,10 +178,17 @@ const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValu
           required: true,
           rules: [
             {
-              pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
               type: 'email',
               message: '请输入正确的邮箱地址',
-              trigger: ['blur', 'input']
+              trigger: ['blur', 'input', 'change'],
+              validator: (_fomItem, value, callback) => {
+                if (!value) {
+                  callback(new Error('请输入邮箱'));
+                } else if (!/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(value)) {
+                  callback(new Error('请输入正确的邮箱'));
+                }
+                return true;
+              }
             }
           ]
         },
@@ -211,9 +202,16 @@ const [registerModalForm, { openModal, closeModal, getFieldsValue, setFieldsValu
           },
           rules: [
             {
-              pattern: /^1[3456789]\d{9}$/,
               message: '请输入正确的手机号',
-              trigger: ['blur', 'input']
+              trigger: ['blur', 'input'],
+              validator: (_fomItem, value, callback) => {
+                if (!value) {
+                  callback(new Error('请输入手机号'));
+                } else if (!/^1[3456789]\d{9}$/.test(value)) {
+                  callback(new Error('请输入正确的手机号'));
+                }
+                return true;
+              }
             }
           ]
         },