Prechádzať zdrojové kódy

供应商信息和界面展示

Lijingyang 1 mesiac pred
rodič
commit
7dc41ad738

+ 7 - 0
src/api/system/addressarea/index.ts

@@ -78,3 +78,10 @@ export const delAddressarea = (id: string | number | Array<string | number>) =>
   });
 };
 
+export const getChinaArea = (): AxiosPromise<AddressareaVO[]> => {
+  return request({
+    url: '/system/addressarea/getchina/area',
+    method: 'get'
+  });
+};
+

+ 260 - 82
src/views/supplier/author/edit.vue

@@ -22,6 +22,7 @@
                 filterable
                 style="width: 100%;"
                 @change="handleBrandChange"
+                @clear="handleBrandClear"
               >
                 <el-option
                   v-for="brand in brandList"
@@ -40,6 +41,11 @@
               />
             </el-form-item>
           </el-col>
+          <el-col :span="8">
+            <el-form-item label="商标注册人:">
+              <el-input v-model="formData.brandRegistrant" readonly style="width: 100%;" placeholder="商标注册人" class="readonly-input readonly-not-allowed" />
+            </el-form-item>
+          </el-col>
           <el-col :span="8">
             <el-form-item label="授权类型:">
               <el-select 
@@ -58,6 +64,7 @@
               </el-select>
             </el-form-item>
           </el-col>
+          
         </el-row>
 
         <!-- 第一行:三个分类下拉框 -->
@@ -204,16 +211,23 @@
         <!-- 授权链路 -->
         <el-form-item label="授权链路:">
           <div class="auth-chain-container">
-            <!-- 授权链路表格 -->
-            <el-table :data="authChainTableData" border style="width: 100%; margin-bottom: 16px;">
-              <el-table-column 
-                v-for="(level, index) in authChainLevels" 
-                :key="index"
-                :label="level.label" 
-                align="center"
-                min-width="200"
-              >
-                <template #default>
+            <!-- 授权链路自定义表格 -->
+            <div class="auth-chain-custom-table">
+              <div class="auth-chain-header">
+                <div 
+                  v-for="level in authChainLevels" 
+                  :key="'h-' + level.id"
+                  class="auth-chain-cell header-cell"
+                >
+                  {{ level.label }}
+                </div>
+              </div>
+              <div class="auth-chain-row">
+                <div 
+                  v-for="level in authChainLevels" 
+                  :key="'c-' + level.id"
+                  class="auth-chain-cell"
+                >
                   <el-input 
                     v-if="level.editable"
                     v-model="level.value" 
@@ -221,13 +235,13 @@
                     clearable
                   />
                   <span v-else>{{ level.value }}</span>
-                </template>
-              </el-table-column>
-            </el-table>
+                </div>
+              </div>
+            </div>
             
             <!-- 操作按钮 -->
             <div class="auth-chain-actions">
-              <el-button type="primary" @click="handleAddAuthLevel">添加品牌授权人</el-button>
+              <el-button type="primary" @click="handleAddAuthLevel" :disabled="authChainLevels.length >= 5">添加品牌授权人</el-button>
               <el-button type="danger" @click="handleDeleteAuthLevel" :disabled="authChainLevels.length <= 2">删除</el-button>
             </div>
           </div>
@@ -357,7 +371,6 @@ import { ElMessage, ElMessageBox } from 'element-plus';
 import { listBrand } from '@/api/product/brand';
 import { listCategory } from '@/api/product/category';
 import type { CategoryVO } from '@/api/product/category/types';
-import { chinaAreaList } from '@/api/system/addressarea';
 import { listByIds } from '@/api/system/oss';
 import { addSupplierauthorize, getBrandAuthorizeDetail, updateSupplierauthorize } from '@/api/supplier/supplierauthorize';
 import type { SupplierauthorizeForm } from '@/api/supplier/supplierauthorize/types';
@@ -366,6 +379,7 @@ import { listAuthorizetypeLevel } from '@/api/supplier/authorizetypeLevel';
 import type { AuthorizetypeLevelVO } from '@/api/supplier/authorizetypeLevel/types';
 import { useUserStore } from '@/store/modules/user';
 import FileUpload from '@/components/FileUpload/index.vue';
+import { regionData } from 'element-china-area-data';
 
 const router = useRouter();
 const route = useRoute();
@@ -387,6 +401,7 @@ const formData = ref({
   brandId: '',
   brandName: '',
   brandLogo: '', // 品牌LOGO
+  brandRegistrant: '', // 商标注册人
   authorizeTypeId: '', // 授权类型ID
   authorizeLevel: '', // 授权等级
   category1: '', // 第一行的一级分类
@@ -403,20 +418,25 @@ const authorizeTypeList = ref<AuthorizetypeLevelVO[]>([]);
 
 // 授权链路
 interface AuthChainLevel {
+  id: number;         // 唯一标识
   label: string;      // 列标题(0级授权、1级授权...)
   value: string;      // 授权人名称
   editable: boolean;  // 是否可编辑
   placeholder: string; // 输入框提示
 }
 
+let authLevelIdCounter = 0;
+
 const authChainLevels = ref<AuthChainLevel[]>([
   {
+    id: authLevelIdCounter++,
     label: '0级授权',
     value: '',
     editable: true,
     placeholder: '请输入授权人'
   },
   {
+    id: authLevelIdCounter++,
     label: '1级授权',
     value: '优易达(武汉)有限公司',
     editable: false,
@@ -424,8 +444,12 @@ const authChainLevels = ref<AuthChainLevel[]>([
   }
 ]);
 
-// 表格数据(只有一行,用于显示)
-const authChainTableData = ref([{}]);
+const refreshAuthChainEditable = () => {
+  if (!authChainLevels.value?.length) return;
+  authChainLevels.value.forEach((lvl, idx) => (lvl.editable = idx !== authChainLevels.value.length - 1));
+};
+
+refreshAuthChainEditable();
 
 // 第一行的分类列表
 const category1List = ref<CategoryVO[]>([]); // 一级分类
@@ -488,13 +512,41 @@ const selectedAreaIds = ref<string[]>([]); // 选中的区域ID列表(用于
 
 const cascaderProps = {
   multiple: true,
-  value: 'id', // 使用id而不是areaCode
-  label: 'areaName',
+  value: 'value', // 使用 component 自带的 value(即行政区划代码如 110000)
+  label: 'label',
   children: 'children',
   checkStrictly: false,
   emitPath: true
 };
 
+const toProvinceCityOptions = (options: any[]) => {
+  return (options || []).map((province) => {
+    const provinceValue6 = String(province.value ?? '');
+    const provinceValue = provinceValue6.length >= 2 ? provinceValue6.slice(0, 2) : provinceValue6;
+
+    const cities = (province.children || []).map((city: any) => {
+      const cityValue6 = String(city.value ?? '');
+      const cityValue = cityValue6.length >= 4 ? cityValue6.slice(0, 4) : cityValue6;
+      const { children, ...rest } = city;
+      return {
+        ...rest,
+        value: cityValue
+      };
+    });
+
+    return {
+      ...province,
+      value: provinceValue,
+      children: cities
+    };
+  });
+};
+
+const isCityCode = (val: any) => {
+  const s = String(val ?? '').trim();
+  return /^\d{4}$/.test(s);
+};
+
 /** 返回 */
 const goBack = () => {
   router.back();
@@ -514,6 +566,7 @@ const loadDetailData = async () => {
     // 回显基本信息
     formData.value.brandName = data.brandName || '';
     formData.value.brandLogo = data.brandLogo || '';
+    formData.value.brandRegistrant = data.brandRegistrant || (data as any).registrationCertificate || '';
     formData.value.authorizeTypeId = Number(data.authorizeType) || '';
     formData.value.authorizeLevel = data.authorizeLevel || '';
     
@@ -582,9 +635,10 @@ const loadDetailData = async () => {
       
       licensors.forEach((licensor, index) => {
         authChainLevels.value.push({
+          id: authLevelIdCounter++,
           label: `${index}级授权`,
           value: licensor.trim(),
-          editable: licensor.trim() !== '优易达(武汉)有限公司',
+          editable: index !== licensors.length - 1,
           placeholder: '请输入授权人'
         });
       });
@@ -600,16 +654,11 @@ const loadDetailData = async () => {
       // 需要根据区域ID在 areaOptions 中找到对应的省市关系
       selectedAreas.value = await buildCascaderValues(areaIds);
       
-      // 显示区域名称
-      areaList.value = [{
-        province: data.province || '',
-        city: data.city || ''
-      }];
+      areaList.value = buildAreaRows(selectedAreas.value);
       console.log('授权区域回显:', { 
         areaIds, 
         selectedAreas: selectedAreas.value,
-        province: data.province, 
-        city: data.city 
+        rows: areaList.value
       });
     }
     
@@ -672,6 +721,13 @@ const loadDetailData = async () => {
   }
 };
 
+const handleBrandClear = () => {
+  formData.value.brandId = '';
+  formData.value.brandName = '';
+  formData.value.brandLogo = '';
+  formData.value.brandRegistrant = '';
+};
+
 
 
 /** 根据三级分类ID加载分类信息 */
@@ -724,15 +780,24 @@ const handleAuthorizeTypeChange = (id: string | number) => {
 
 /** 品牌选择改变 */
 const handleBrandChange = (brandName: string) => {
+  if (!brandName) {
+    formData.value.brandId = '';
+    formData.value.brandName = '';
+    formData.value.brandLogo = '';
+    formData.value.brandRegistrant = '';
+    return;
+  }
   const selectedBrand = brandList.value.find(b => b.brandName === brandName);
   if (selectedBrand) {
     formData.value.brandId = selectedBrand.id;
     formData.value.brandName = selectedBrand.brandName;
     formData.value.brandLogo = selectedBrand.brandLogo || ''; // 保存品牌LOGO
+    formData.value.brandRegistrant = selectedBrand.registrationCertificate || selectedBrand.brandRegistrant || '';
     console.log('选择品牌:', { 
       brandId: formData.value.brandId, 
       brandName: formData.value.brandName,
-      brandLogo: formData.value.brandLogo
+      brandLogo: formData.value.brandLogo,
+      brandRegistrant: formData.value.brandRegistrant
     });
   }
 };
@@ -940,10 +1005,15 @@ const handleDeletePerson = () => {
 
 /** 添加授权级别 */
 const handleAddAuthLevel = () => {
+  if (authChainLevels.value.length >= 5) {
+    ElMessage.warning('授权链路最多支持5个级别');
+    return;
+  }
   // 在最前面插入新的0级授权
   // 所有现有级别的标签都要+1
   const newLevels: AuthChainLevel[] = [
     {
+      id: authLevelIdCounter++,
       label: '0级授权',
       value: '',
       editable: true,
@@ -954,13 +1024,18 @@ const handleAddAuthLevel = () => {
   // 将现有的级别标签都+1
   authChainLevels.value.forEach((level, index) => {
     newLevels.push({
+      id: level.id, // 保持唯一身份标识避免页面组件渲染错误
       label: `${index + 1}级授权`,
       value: level.value,
-      editable: level.editable && level.value !== '优易达(武汉)有限公司', // 优易达始终不可编辑
+      editable: true,
       placeholder: level.placeholder
     });
   });
-  
+
+  // 仅最后一级不可编辑
+  if (newLevels.length) {
+    newLevels.forEach((lvl, idx) => (lvl.editable = idx !== newLevels.length - 1));
+  }
   authChainLevels.value = newLevels;
   ElMessage.success('添加成功');
 };
@@ -978,8 +1053,14 @@ const handleDeleteAuthLevel = () => {
   // 重新调整所有级别的标签
   authChainLevels.value = authChainLevels.value.map((level, index) => ({
     ...level,
-    label: `${index}级授权`
+    label: `${index}级授权`,
+    editable: true
   }));
+
+  // 仅最后一级不可编辑
+  if (authChainLevels.value.length) {
+    authChainLevels.value.forEach((lvl, idx) => (lvl.editable = idx !== authChainLevels.value.length - 1));
+  }
   
   ElMessage.success('删除成功');
 };
@@ -1111,30 +1192,35 @@ const handleDeleteFile = (row: any) => {
 const buildCascaderValues = async (areaIds: string[]): Promise<any[]> => {
   const cascaderValues: any[] = [];
   
-  // 遍历所有区域ID,找到它们在区域树中的位置
-  areaIds.forEach(areaId => {
-    const numId = Number(areaId);
-    
-    // 在省级数据中查找
-    for (const province of areaOptions.value) {
-      if (province.id === numId) {
-        // 这是一个省级ID
-        cascaderValues.push([numId]);
-        return;
+  // 递归寻找属于叶子节点的路径
+  const findLeafPath = (options: any[], targetId: string, currentPath: string[]): boolean => {
+    for (const option of options) {
+      const path = [...currentPath, option.value];
+      if (option.value === targetId) {
+        // 找到了对应的ID,判断是否为叶子节点
+        // 因为 Cascader 的 checkStrictly: false, 父节点的显式勾选会导致级联其下所有节点
+        // 只有是叶子节点(即没有 children),我们才作为有效路径提供给 Cascader 回显
+        if (!option.children || option.children.length === 0) {
+          cascaderValues.push(path);
+        }
+        return true; 
       }
-      
-      // 在市级数据中查找
-      if (province.children) {
-        for (const city of province.children) {
-          if (city.id === numId) {
-            // 这是一个市级ID,需要包含省级ID
-            cascaderValues.push([province.id, numId]);
-            return;
-          }
+      if (option.children && option.children.length > 0) {
+        if (findLeafPath(option.children, targetId, path)) {
+          return true;
         }
       }
     }
-  });
+    return false;
+  };
+
+  // 遍历所有区域ID,仅当它代表叶子节点时才添加路径去激活回显
+  areaIds
+    .map((areaId) => String(areaId).trim())
+    .filter((strId) => strId && isCityCode(strId))
+    .forEach((cityId) => {
+      findLeafPath(areaOptions.value, cityId, []);
+    });
   
   console.log('构建级联选择器值:', { areaIds, cascaderValues });
   return cascaderValues;
@@ -1148,9 +1234,8 @@ const handleSelectArea = () => {
 /** 初始化授权区域选项数据 */
 const initAreaOptions = async () => {
   try {
-    const res = await chinaAreaList();
-    areaOptions.value = res.data || [];
-    console.log('授权区域选项数据加载完成:', areaOptions.value.length);
+    areaOptions.value = toProvinceCityOptions(regionData as any);
+    console.log('授权区域选项数据加载完成(使用 element-china-area-data):', areaOptions.value.length);
   } catch (e) {
     console.error('获取授权区域数据失败:', e);
   }
@@ -1164,10 +1249,9 @@ const handleAreaSubmit = async () => {
     console.log('=== 授权区域提交 ===');
     console.log('selectedAreas.value:', selectedAreas.value);
     
-    // 从选中的路径中提取区域信息(用于显示)
-    const regionData = extractRegionData(selectedAreas.value);
+    const areaRows = buildAreaRows(selectedAreas.value);
     
-    // 提取所有选中的区域ID(包括省和市的ID)
+    // 提取所有选中的区域ID(包括省和市的ID),使用后端短编码格式(11、1101)
     const areaIds: string[] = [];
     selectedAreas.value.forEach(path => {
       if (Array.isArray(path)) {
@@ -1175,8 +1259,9 @@ const handleAreaSubmit = async () => {
         console.log('处理路径:', path);
         path.forEach(id => {
           console.log('提取ID:', id, '类型:', typeof id);
-          if (id && !areaIds.includes(String(id))) {
-            areaIds.push(String(id));
+          const strId = String(id).trim();
+          if (strId && !areaIds.includes(strId)) {
+            areaIds.push(strId);
           }
         });
       }
@@ -1185,12 +1270,9 @@ const handleAreaSubmit = async () => {
     selectedAreaIds.value = areaIds;
 
     // 更新授权区域列表显示
-    areaList.value = [{
-      province: regionData.provinces,
-      city: regionData.cities
-    }];
+    areaList.value = areaRows;
 
-    console.log('选中的授权区域名称:', regionData);
+    console.log('选中的授权区域名称:', areaRows);
     console.log('选中的区域ID列表(用于提交):', selectedAreaIds.value);
     console.log('=== 授权区域提交完成 ===');
     ElMessage.success('授权区域设置成功');
@@ -1203,6 +1285,45 @@ const handleAreaSubmit = async () => {
   }
 };
 
+const buildAreaRows = (selectedPaths: any[]) => {
+  const provinceMap = new Map<string, Set<string>>();
+
+  const normalizeCityDisplayName = (provinceName: string, cityName: string) => {
+    const p = String(provinceName || '');
+    const c = String(cityName || '');
+    if (!c) return '';
+    if (c === '市辖区') {
+      return p.endsWith('市') ? p : `${p}市`;
+    }
+    return c;
+  };
+
+  selectedPaths.forEach((path) => {
+    if (Array.isArray(path) && path.length >= 2) {
+      const [provinceId, cityId] = path;
+      const provinceName = findRegionNameById(provinceId, areaOptions.value);
+      const province = areaOptions.value.find((p) => p.value === String(provinceId));
+      const cityName = province && cityId ? findRegionNameById(cityId, province.children || []) : '';
+
+      if (!provinceName) return;
+      if (!provinceMap.has(provinceName)) {
+        provinceMap.set(provinceName, new Set());
+      }
+      const displayCityName = normalizeCityDisplayName(provinceName, cityName);
+      if (displayCityName) {
+        provinceMap.get(provinceName)!.add(displayCityName);
+      }
+    }
+  });
+
+  return Array.from(provinceMap.entries()).map(([province, cities]) => {
+    return {
+      province,
+      city: Array.from(cities).join(',')
+    };
+  });
+};
+
 /** 从级联选择器的值中提取区域数据 */
 const extractRegionData = (selectedPaths: any[]) => {
   // 用于存储省-市的映射关系
@@ -1212,9 +1333,9 @@ const extractRegionData = (selectedPaths: any[]) => {
     if (Array.isArray(path) && path.length >= 2) {
       const [provinceId, cityId] = path;
       
-      // 查找对应的名称(通过id查找)
+      // 查找对应的名称(通过value查找)
       const provinceName = findRegionNameById(provinceId, areaOptions.value);
-      const province = areaOptions.value.find(p => p.id === provinceId);
+      const province = areaOptions.value.find(p => p.value === provinceId);
       const cityName = province && cityId ? findRegionNameById(cityId, province.children || []) : '';
 
       if (provinceName) {
@@ -1222,8 +1343,11 @@ const extractRegionData = (selectedPaths: any[]) => {
           provinceMap.set(provinceName, new Set());
         }
         
-        if (cityName) {
+        // 直辖市的处理(市辖区转为原来的名称)
+        if (cityName && cityName !== '市辖区') {
           provinceMap.get(provinceName)!.add(cityName);
+        } else if (cityName === '市辖区') {
+          provinceMap.get(provinceName)!.add(provinceName);
         }
       }
     }
@@ -1244,10 +1368,11 @@ const extractRegionData = (selectedPaths: any[]) => {
   };
 };
 
-/** 根据id查找区域名称 */
+/** 根据value查找区域名称 */
 const findRegionNameById = (id: string | number, regions: any[]): string => {
-  const region = regions.find(r => r.id === id);
-  return region ? region.areaName : '';
+  const targetId = String(id);
+  const region = regions.find(r => r.value === targetId);
+  return region ? region.label : '';
 };
 
 /** 提交 */
@@ -1339,6 +1464,7 @@ const handleSubmit = async () => {
       brandId: formData.value.brandId,
       brandName: formData.value.brandName,
       brandLogo: formData.value.brandLogo,
+      brandRegistrant: formData.value.brandRegistrant,
       authorizeType: formData.value.authorizeTypeId, // 授权类型ID
       authorizeLevel: formData.value.authorizeLevel, // 授权等级
       categoryIds: categoryIds, // 三级分类ID列表
@@ -1518,30 +1644,82 @@ onMounted(async () => {
   gap: 12px;
 }
 
+.auth-chain-custom-table {
+  width: 100%;
+  margin-bottom: 16px;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  overflow-x: auto;
+}
+
+.auth-chain-header {
+  display: flex;
+  background-color: #f5f7fa;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.auth-chain-row {
+  display: flex;
+}
+
+.auth-chain-cell {
+  flex: 1;
+  min-width: 200px;
+  padding: 12px;
+  text-align: center;
+  border-right: 1px solid #ebeef5;
+  box-sizing: border-box;
+}
+
+.auth-chain-cell:last-child {
+  border-right: none;
+}
+
+.header-cell {
+  font-weight: bold;
+  color: #909399;
+  font-size: 14px;
+}
+
 /* 只读输入框样式 - 更美观的设计 */
 .readonly-input :deep(.el-input__wrapper) {
-  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important;
-  border: 1px solid #dee2e6 !important;
-  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1) !important;
-  cursor: not-allowed !important;
+  background: var(--el-input-bg-color) !important;
+  border: 1px solid var(--el-border-color) !important;
+  box-shadow: none !important;
+  cursor: default !important;
 }
 
 .readonly-input :deep(.el-input__inner) {
   background: transparent !important;
-  color: #495057 !important;
-  font-weight: 500 !important;
-  cursor: not-allowed !important;
-  text-align: center !important;
+  color: var(--el-text-color-regular) !important;
+  font-weight: 400 !important;
+
+  /* 文字居中显示 */
+  text-align: left !important;
 }
 
 .readonly-input :deep(.el-input__wrapper):hover {
-  border-color: #adb5bd !important;
-  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.15) !important;
+  border-color: var(--el-border-color) !important;
+  box-shadow: none !important;
 }
 
 .readonly-input :deep(.el-input__wrapper):focus-within {
-  border-color: #6c757d !important;
-  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.15), 0 0 0 2px rgba(108, 117, 125, 0.1) !important;
+  border-color: var(--el-border-color) !important;
+  box-shadow: none !important;
+}
+
+.auth-form :deep(.el-input.is-disabled .el-input__wrapper),
+.auth-form :deep(.el-select .el-input.is-disabled .el-input__wrapper) {
+  cursor: default;
+}
+
+.readonly-not-allowed :deep(.el-input__wrapper) {
+  cursor: not-allowed !important;
+  pointer-events: auto;
+}
+
+.readonly-not-allowed :deep(.el-input__inner) {
+  cursor: default !important;
 }
 
 /* 分类区域样式 */

+ 279 - 79
src/views/supplier/author/index.vue

@@ -26,10 +26,39 @@
         </el-select>
       </el-form-item>
       <el-form-item label="省">
-        <el-input v-model="searchParams.province" placeholder="请输入省名称" clearable style="width: 200px;" />
+        <el-select
+          v-model="searchParams.province"
+          placeholder="请选择省"
+          clearable
+          filterable
+          style="width: 200px;"
+          @change="handleProvinceChange"
+          @clear="handleProvinceClear"
+        >
+          <el-option
+            v-for="p in provinceOptions"
+            :key="p.value"
+            :label="p.label"
+            :value="p.value"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item label="市">
-        <el-input v-model="searchParams.city" placeholder="请输入市名称" clearable style="width: 200px;" />
+        <el-select
+          v-model="searchParams.city"
+          placeholder="请选择市"
+          clearable
+          filterable
+          :disabled="!searchParams.province"
+          style="width: 200px;"
+        >
+          <el-option
+            v-for="c in cityOptions"
+            :key="c.value"
+            :label="c.label"
+            :value="c.value"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
@@ -37,66 +66,90 @@
       </el-form-item>
     </el-form>
 
-    <!-- 产品线管理列表 -->
-    <div class="table-header">
-      <span class="table-title">产品线管理列表</span>
-      <el-button type="primary" @click="handleAdd">添加授权</el-button>
-    </div>
-
-    <!-- 授权详情信息列表 -->
-    <el-table v-loading="loading" :data="authorizationList" border style="width: 100%">
-      <el-table-column prop="brandName" label="品牌名称" align="center" />
-      <el-table-column label="一级类目" align="center">
-        <template #default="scope">
-          <span>{{ scope.row.categorysMap?.oneLevelName || '-' }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="二级类目" align="center">
-        <template #default="scope">
-          <span>{{ scope.row.categorysMap?.twoLevelName || '-' }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="三级类目" align="center">
-        <template #default="scope">
-          <span>{{ scope.row.categorysMap?.threeLevelName || '-' }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column prop="authorizationEndTime" label="资质到期日期" align="center">
-        <template #default="scope">
-          <span>{{ scope.row.authorizationEndTime ? formatDate(scope.row.authorizationEndTime) : '-' }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column prop="province" label="授权区域(省)" align="center" />
-      <el-table-column prop="city" label="授权区域(市)" align="center" />
-      <el-table-column prop="authorizedStatus" label="状态" align="center">
-        <template #default="scope">
-          <span>{{ getAuthorizedStatusText(scope.row.authorizedStatus) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" width="120">
-        <template #default="scope">
-          <el-button link type="primary" @click="handleView(scope.row)">编辑</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 空状态 -->
-    <div v-if="authorizationList.length === 0 && !loading" class="empty-state">
-      暂无授权详情信息
-    </div>
-
-    <!-- 分页 -->
-    <div class="pagination-container">
-      <el-pagination
-        v-model:current-page="pagination.pageNum"
-        v-model:page-size="pagination.pageSize"
-        :page-sizes="[10, 20, 50, 100]"
-        :total="pagination.total"
-        layout="total, sizes, prev, pager, next, jumper"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-      />
-    </div>
+    <!-- 产品线管理列表以及分页卡片 -->
+    <el-card shadow="never" class="table-card">
+      <template #header>
+        <div class="table-header-content">
+          <span class="table-title">产品线管理列表</span>
+          <el-button type="primary" @click="handleAdd">添加授权</el-button>
+        </div>
+      </template>
+
+      <!-- 授权详情信息列表 -->
+      <el-table v-loading="loading" :data="authorizationList" border style="width: 100%">
+        <el-table-column prop="brandName" label="品牌名称" align="center" />
+        <el-table-column label="一级类目" align="center">
+          <template #default="scope">
+            <span>{{ scope.row.categorysMap?.oneLevelName || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="二级类目" align="center">
+          <template #default="scope">
+            <span>{{ scope.row.categorysMap?.twoLevelName || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="三级类目" align="center">
+          <template #default="scope">
+            <span>{{ scope.row.categorysMap?.threeLevelName || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="authorizationEndTime" label="资质到期日期" align="center">
+          <template #default="scope">
+            <span>{{ scope.row.authorizationEndTime ? formatDate(scope.row.authorizationEndTime) : '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="授权区域(省)" align="center">
+          <template #default="scope">
+            <el-tooltip
+              :content="formatAuthorizedProvinces(scope.row)"
+              placement="top"
+              :disabled="formatAuthorizedProvinces(scope.row) === '-'"
+            >
+              <span class="cell-ellipsis">{{ formatAuthorizedProvinces(scope.row) }}</span>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column label="授权区域(市)" align="center">
+          <template #default="scope">
+            <el-tooltip
+              :content="formatAuthorizedCities(scope.row)"
+              placement="top"
+              :disabled="formatAuthorizedCities(scope.row) === '-'"
+            >
+              <span class="cell-ellipsis">{{ formatAuthorizedCities(scope.row) }}</span>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column prop="authorizedStatus" label="状态" align="center">
+          <template #default="scope">
+            <span>{{ getAuthorizedStatusText(scope.row.authorizedStatus) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="120">
+          <template #default="scope">
+            <el-button link type="primary" @click="handleView(scope.row)">编辑</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 空状态 -->
+      <div v-if="authorizationList.length === 0 && !loading" class="empty-state">
+        暂无授权详情信息
+      </div>
+
+      <!-- 分页 -->
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="pagination.pageNum"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
   </div>
 </template>
 
@@ -118,6 +171,11 @@ const userStore = useUserStore();
 const authorizationList = ref<any[]>([]);
 const loading = ref(false);
 
+// 授权区域(省/市)回显:使用 element-china-area-data
+const areaOptions = ref<any[]>([]);
+const provinceOptions = ref<any[]>([]);
+const cityOptions = ref<any[]>([]);
+
 // 品牌相关
 const brandList = ref<any[]>([]);
 const brandLoading = ref(false);
@@ -131,8 +189,8 @@ const isSearchMode = ref(false); // 是否在搜索模式
 // 搜索参数
 const searchParams = ref({
   brandName: '',
-  province: '', // 省名称
-  city: ''      // 市名称
+  province: '', // 省ID(2位)
+  city: ''      // 市ID(4位)
 });
 
 // 分页参数
@@ -250,6 +308,145 @@ const loadMoreBrands = async () => {
   await getBrandList(false);
 };
 
+const toProvinceCityOptions = (options: any[]) => {
+  return (options || []).map((province) => {
+    const provinceValue6 = String(province.value ?? '');
+    const provinceValue = provinceValue6.length >= 2 ? provinceValue6.slice(0, 2) : provinceValue6;
+
+    const cities = (province.children || []).map((city: any) => {
+      const cityValue6 = String(city.value ?? '');
+      const cityValue = cityValue6.length >= 4 ? cityValue6.slice(0, 4) : cityValue6;
+      const { children, ...rest } = city;
+      return {
+        ...rest,
+        value: cityValue
+      };
+    });
+
+    return {
+      ...province,
+      value: provinceValue,
+      children: cities
+    };
+  });
+};
+
+/** 根据value查找区域名称 */
+const findRegionNameById = (id: string | number, regions: any[]): string => {
+  const targetId = String(id ?? '').trim();
+  if (!targetId) return '';
+  const region = (regions || []).find((r: any) => String(r.value) === targetId);
+  return region?.label || '';
+};
+
+const isProvinceCode = (val: any) => {
+  const s = String(val ?? '').trim();
+  return /^\d{2}$/.test(s);
+};
+
+const isCityCode = (val: any) => {
+  const s = String(val ?? '').trim();
+  return /^\d{4}$/.test(s);
+};
+
+const normalizeCityDisplayName = (provinceName: string, cityName: string) => {
+  const p = String(provinceName || '');
+  const c = String(cityName || '');
+  if (!c) return '';
+  if (c === '市辖区') {
+    return p.endsWith('市') ? p : `${p}市`;
+  }
+  return c;
+};
+
+/** 从行数据中提取授权区域ID列表(兼容 authorizedArea / province / city) */
+const getRowAreaIds = (row: any): string[] => {
+  if (row?.authorizedArea) {
+    return String(row.authorizedArea)
+      .split(',')
+      .map((s) => String(s ?? '').trim())
+      .filter(Boolean);
+  }
+
+  const ids: string[] = [];
+  const p = String(row?.province ?? '').trim();
+  const c = String(row?.city ?? '').trim();
+  if (p) ids.push(p);
+  if (c) ids.push(c);
+  return ids;
+};
+
+/** 授权区域(省)回显 */
+const formatAuthorizedProvinces = (row: any) => {
+  const ids = getRowAreaIds(row);
+  const provinceIds = ids.filter((id) => isProvinceCode(id));
+  const provinceNames = provinceIds
+    .map((pid) => findRegionNameById(pid, areaOptions.value))
+    .filter(Boolean);
+  return provinceNames.length ? Array.from(new Set(provinceNames)).join(',') : '-';
+};
+
+/** 授权区域(市)回显 */
+const formatAuthorizedCities = (row: any) => {
+  const ids = getRowAreaIds(row);
+  const cityIds = ids.filter((id) => isCityCode(id));
+  if (!cityIds.length) return '-';
+
+  const provinceMap = new Map<string, Set<string>>();
+  cityIds.forEach((cid) => {
+    const provinceId = String(cid).slice(0, 2);
+    const provinceName = findRegionNameById(provinceId, areaOptions.value);
+    if (!provinceName) return;
+
+    const province = areaOptions.value.find((p: any) => String(p.value) === String(provinceId));
+    const cityName = province ? findRegionNameById(cid, province.children || []) : '';
+    const displayCityName = normalizeCityDisplayName(provinceName, cityName);
+    if (!displayCityName) return;
+
+    if (!provinceMap.has(provinceName)) {
+      provinceMap.set(provinceName, new Set());
+    }
+    provinceMap.get(provinceName)!.add(displayCityName);
+  });
+
+  const allCities: string[] = [];
+  provinceMap.forEach((cities) => {
+    allCities.push(...Array.from(cities));
+  });
+  return allCities.length ? allCities.join(',') : '-';
+};
+
+/** 初始化授权区域选项数据 */
+const initAreaOptions = async () => {
+  try {
+    const mod: any = await import('element-china-area-data');
+    areaOptions.value = toProvinceCityOptions(mod?.regionData as any);
+    provinceOptions.value = areaOptions.value || [];
+  } catch (e) {
+    console.error('获取授权区域数据失败:', e);
+  }
+};
+
+const refreshCityOptions = () => {
+  const pid = String(searchParams.value.province ?? '').trim();
+  if (!pid) {
+    cityOptions.value = [];
+    return;
+  }
+  const province = provinceOptions.value.find((p: any) => String(p.value) === pid);
+  cityOptions.value = province?.children || [];
+};
+
+const handleProvinceChange = () => {
+  searchParams.value.city = '';
+  refreshCityOptions();
+};
+
+const handleProvinceClear = () => {
+  searchParams.value.city = '';
+  refreshCityOptions();
+};
+
 /** 获取授权详情列表 */
 const getAuthorizationList = async () => {
   let supplierId = userStore.supplierId;
@@ -270,8 +467,8 @@ const getAuthorizationList = async () => {
       pageNum: pagination.value.pageNum,
       pageSize: pagination.value.pageSize
     });
-    authorizationList.value = res.data || [];
-    pagination.value.total = authorizationList.value.length;
+    authorizationList.value = res.rows || [];
+    pagination.value.total = res.total || 0;
     console.log('授权详情列表:', authorizationList.value);
   } catch (e) {
     console.error('获取授权详情列表失败:', e);
@@ -294,6 +491,7 @@ const handleReset = () => {
     province: '',
     city: ''
   };
+  refreshCityOptions();
   pagination.value.pageNum = 1;
   getAuthorizationList();
 };
@@ -349,6 +547,7 @@ onMounted(async () => {
   // 并行加载品牌列表和授权详情列表
   await Promise.all([
     getBrandList(true),
+    initAreaOptions(),
     getAuthorizationList()
   ]);
 });
@@ -368,14 +567,14 @@ onMounted(async () => {
   border-radius: 4px;
 }
 
-.table-header {
+.table-card {
+  margin-top: 0px;
+}
+
+.table-header-content {
   display: flex;
   justify-content: space-between;
   align-items: center;
-  background: #fff;
-  padding: 16px 20px;
-  margin-bottom: 0;
-  border-radius: 4px 4px 0 0;
 }
 
 .table-title {
@@ -384,22 +583,23 @@ onMounted(async () => {
   color: #333;
 }
 
-.el-table {
-  border-radius: 0 0 4px 4px;
-}
-
 .empty-state {
   text-align: center;
   padding: 40px;
   color: #999;
-  background: #fff;
 }
 
 .pagination-container {
   display: flex;
   justify-content: flex-end;
-  padding: 20px;
-  background: #fff;
-  border-radius: 0 0 4px 4px;
+  padding-top: 20px;
+}
+
+.cell-ellipsis {
+  display: block;
+  width: 100%;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
 }
 </style>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 131 - 1095
src/views/supplier/info/index.vue


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov