浏览代码

regionTree组件化

liuxf 4 月之前
父节点
当前提交
523973c55b
共有 3 个文件被更改,包括 261 次插入528 次删除
  1. 105 28
      src/components/PublicRange.vue
  2. 4 6
      src/components/RegionNode.vue
  3. 152 494
      src/views/fcbi/sales/sumSettings.vue

+ 105 - 28
src/components/PublicRange.vue

@@ -1,6 +1,5 @@
 <template>
   <div class="public-range-container">
-    <!-- 统一使用带标题容器的结构,确保标题对齐 -->
     <!-- FC区域 - 支持可插拔控制 -->
     <div class="fc-bt-area-form" v-if="showFc">
       <div class="section-title">FC</div>
@@ -43,6 +42,7 @@
             class="region-tree-container"
             :treeData="regionTree"
             @check-change="handleCheckChange"
+            :disabled="disabled"
         />
       </div>
     </div>
@@ -50,9 +50,10 @@
 </template>
 
 <script setup>
-import { defineProps, defineEmits, watch } from 'vue';
+import { defineProps, defineEmits, watch, ref } from 'vue';
 import RegionTree from './RegionTree.vue';
 
+// 定义属性
 const props = defineProps({
   modelValue: {
     type: Object,
@@ -78,39 +79,122 @@ const props = defineProps({
     required: true,
     default: () => []
   },
-  // 業種显示控制
   showBusinessType: {
     type: Boolean,
     default: false
   },
-  // FC显示控制
   showFc: {
     type: Boolean,
     default: false
   },
-  // エリア显示控制
   showArea: {
     type: Boolean,
     default: true
+  },
+  disabled: {
+    type: Boolean,
+    default: false
   }
 });
 
+// 定义事件
 const emits = defineEmits([
   'update:modelValue',
   'check-change'
 ]);
 
+// 内部维护的选中区域列表
+const selectedRegions = ref([]);
+
+// 监听地域树数据变化,初始化选中状态
+watch(() => props.regionTree, (newVal) => {
+  if (newVal?.length) {
+    updateSelectedRegions(newVal);
+  }
+}, { deep: true });
+
+// 监听modelValue变化,同步到父组件
+watch(
+    () => props.modelValue,
+    (newValue) => {
+      emits('update:modelValue', newValue);
+    },
+    { deep: true }
+);
+
+// 处理区域树勾选变化
 const handleCheckChange = (regionId, isChecked) => {
+  const node = findRegionById(props.regionTree, regionId);
+  if (node) {
+    node.checked = isChecked;
+    updateSelectedRegions(props.regionTree); // 重新收集选中区域
+  }
   emits('check-change', regionId, isChecked);
 };
 
-watch(
-  () => props.modelValue,
-  (newValue) => {
-    emits('update:modelValue', newValue);
-  },
-  { deep: true }
-);
+// 递归查找指定ID的地域节点
+const findRegionById = (nodes, id) => {
+  if (!nodes?.length) return null;
+  for (const node of nodes) {
+    if (node.id === id) {
+      return node;
+    }
+    if (node.children && node.children.length > 0) {
+      const found = findRegionById(node.children, id);
+      if (found) return found;
+    }
+  }
+  return null;
+};
+
+// 更新选中的区域列表
+const updateSelectedRegions = (nodes) => {
+  if (!nodes?.length) return;
+  selectedRegions.value = [];
+  collectSelectedRegions(nodes, true); // 跳过顶级父节点
+  // 同步到modelValue的regions字段
+  props.modelValue.regions = [...selectedRegions.value];
+  emits('update:modelValue', { ...props.modelValue });
+};
+
+// 递归收集选中的子区域(跳过父节点)
+const collectSelectedRegions = (nodes, isParent = false) => {
+  nodes.forEach(node => {
+    if (isParent) {
+      // 父节点只处理子节点,不自身收集
+      if (node.children && node.children.length > 0) {
+        collectSelectedRegions(node.children, false);
+      }
+      return;
+    }
+    // 收集选中的子节点
+    if (node.checked) {
+      selectedRegions.value.push({
+        regionCode: node.regionCode
+      });
+    }
+    // 递归处理子节点
+    if (node.children && node.children.length > 0) {
+      collectSelectedRegions(node.children, false);
+    }
+  });
+};
+
+// 重置地域树的勾选状态
+const resetRegionTreeCheck = (nodes = props.regionTree) => {
+  nodes.forEach(node => {
+    node.checked = false;
+    if (node.children?.length) {
+      resetRegionTreeCheck(node.children);
+    }
+  });
+  updateSelectedRegions(nodes); // 重置后同步状态
+};
+
+// 暴露重置方法给父组件
+defineExpose({
+  resetRegionTreeCheck
+});
 </script>
 
 <style scoped>
@@ -118,48 +202,41 @@ watch(
   margin-top: 10px;
 }
 
-/* 统一的区块样式,标题和内容横向排列 */
 .fc-bt-area-form {
   display: flex;
-  align-items: flex-start; /* 标题和内容顶部对齐 */
+  align-items: flex-start;
   margin-top: 5px;
 }
 
-/* 标题容器 - 固定宽度确保垂直对齐 */
 .section-title {
-  width: 100px; /* 固定宽度,确保三个标题垂直对齐 */
+  width: 100px;
   font-size: 14px;
   color: #606266;
   font-weight: 700;
-  text-align: left; /* 首字母对齐关键:左对齐 */
-  padding-top: 4px; /* 与复选框组件基线对齐 */
+  text-align: left;
+  padding-top: 4px;
 }
 
-/* 内容区域 - 占剩余宽度 */
 .section-content {
-  flex: 1; /* 内容区域自适应剩余宽度 */
+  flex: 1;
   align-items: flex-start;
 }
 
 .area-header-row {
-  flex: 1; /* 内容区域自适应剩余宽度 */
+  flex: 1;
   display: flex;
   align-items: flex-start;
 }
 
-/* 区域树特殊样式调整 */
 .region-tree-container {
-  margin-top: 2px; /* 微调与标题的垂直对齐 */
-  margin-left: -4px; /* 微调与标题的垂直对齐 */
-
+  margin-top: 2px;
+  margin-left: -4px;
 }
 
-/* 响应式调整 */
 @media (max-width: 768px) {
   .section-title {
-    width: 100px !important;/* 移动端标题宽度微调 */
+    width: 100px !important;
     margin-right: 0;
   }
-
 }
 </style>

+ 4 - 6
src/components/RegionNode.vue

@@ -6,7 +6,7 @@
       <div class="checkbox-wrapper absolute">
         <input
             type="checkbox"
-            :id="region.id"
+            :id="region.regionId"
             :checked="region.checked"
             :indeterminate="region.indeterminate"
             @change="toggleRegion"
@@ -39,7 +39,7 @@
 </template>
 
 <script setup>
-import { defineProps, defineEmits} from 'vue';
+import {defineEmits, defineProps} from 'vue';
 
 const props = defineProps({
   region: {
@@ -70,7 +70,7 @@ initNodeState(props.region);
 const toggleRegion = () => {
   const newChecked = !props.region.checked;
   updateNodeChecked(props.region, newChecked);
-  emits('check-change', props.region.id, newChecked);
+  emits('check-change', props.region.regionId, newChecked);
 };
 
 // 处理子节点选中状态变更
@@ -106,9 +106,7 @@ const updateParentState = (parentNode) => {
   if (!parentNode.children || parentNode.children.length === 0) return;
 
   // 子ノードが選択されている限り、親ノードはchecked:trueとマークされます。
-  const hasCheckedChild = parentNode.children.some(child => child.checked);
-
-  parentNode.checked = hasCheckedChild;
+  parentNode.checked = parentNode.children.some(child => child.checked);
   // indeterminateを必要とせずにfalseにする
   parentNode.indeterminate = false;
 };

+ 152 - 494
src/views/fcbi/sales/sumSettings.vue

@@ -1,8 +1,7 @@
-<!-- 売上集計条件指定 -->
 <template>
   <div class="app-container">
     <div class="form-container">
-      <!-- 対象区域:使用el-form-item的label属性替代独立标题 -->
+      <!-- 対象区域 -->
       <el-form-item
           label="対象"
           class="section-container"
@@ -37,27 +36,26 @@
               <el-radio class="common-radio" :value="TARGET_PERIOD_TYPE.ANNUAL" v-model="queryParams.targetPeriodType">
                 年度指定
               </el-radio>
-                <div class="date-group">
-                  <el-select class="date-select-year" v-model="queryParams.annual" placeholder=""
-                             :disabled="queryParams.targetPeriodType !== TARGET_PERIOD_TYPE.ANNUAL">
-                    <el-option v-for="year in years" :key="year" :label="year" :value="year"></el-option>
-                  </el-select>
-                  <el-text class="date-label">年度</el-text>
+              <div class="date-group">
+                <el-select class="date-select-year" v-model="queryParams.annual" placeholder=""
+                           :disabled="queryParams.targetPeriodType !== TARGET_PERIOD_TYPE.ANNUAL">
+                  <el-option v-for="year in years" :key="year" :label="year" :value="year"></el-option>
+                </el-select>
+                <el-text class="date-label">年度</el-text>
               </div>
             </div>
           </div>
         </div>
       </el-form-item>
 
-      <!-- 集計種別区域:同样使用el-form-item的label属性 -->
+      <!-- 集計種別区域 -->
       <el-form-item
           class="section-container"
           label="集計種別"
           label-width="100px"
       >
-        <div class="content-group"> <!-- 复用content-group,确保内容区域基准一致 -->
+        <div class="content-group">
           <el-radio-group class="aggregation-radio-horizontal" v-model="queryParams.aggregationType">
-            <!-- 为每个radio添加common-radio类,与“対象”区域的单选框样式统一 -->
             <el-radio class="common-radio" :value="AGGREGATION_TYPE.FC">FC別集計</el-radio>
             <el-radio class="common-radio" :value="AGGREGATION_TYPE.AREA">エリア別集計</el-radio>
             <el-radio class="common-radio" :value="AGGREGATION_TYPE.STORE">店舗別集計</el-radio>
@@ -67,13 +65,14 @@
 
       <el-divider class="custom-divider" type="primary"></el-divider>
 
+      <!-- FC/エリア範囲选择(使用整合后的PublicRange) -->
       <div class="section-fc-area">
         <PublicRange
+            ref="publicRangeRef"
             v-model="queryParams"
             :region-tree="regionTree"
             :yamada-fc-brand="yamadaFcBrand"
             :yamada-business-type="yamadaBusinessType"
-            @check-change="handleCheckChange"
             :show-fc="true"
             :show-business-type="true"
         />
@@ -88,35 +87,33 @@
 </template>
 
 <script name="purchaseSaleSettings" setup>
-// 1. 基础依赖导入
 import { reactive, ref, onMounted, getCurrentInstance, watch, toRefs } from 'vue';
 import { useRouter } from 'vue-router';
 import { getRegionTree } from "@/api/fcbi/survey.js";
 import PublicRange from '../../../components/PublicRange.vue';
 import useSalesStore from '@/store/modules/sales'
-// 定数ファイルから集計タイプ(FC / エリア / 店舗別)と対象期間タイプ(年度 / 月指定)の定数をインポート
 import { AGGREGATION_TYPE, TARGET_PERIOD_TYPE } from '@/constants';
 
-// 2. 组件实例与状态管理
+// 组件实例与状态管理
 const { proxy } = getCurrentInstance();
 const salesStore = useSalesStore();
 const router = useRouter();
+const publicRangeRef = ref(null); // 用于调用PublicRange的重置方法
 
-// 3. 基础数据与状态定义
+// 基础数据与状态定义
 const error = ref('');
 const regionTree = ref([]); // 地域ツリーデータ
-const selectedRegions = ref([]); // 選択された地域コードの配列
 
 // 年月选择器数据
 const currentYear = new Date().getFullYear();
 const years = Array.from({ length: 11 }, (_, i) => currentYear + i);
 const months = Array.from({ length: 12 }, (_, i) => i + 1);
 
-// 4. 数据字典获取
+// 数据字典获取
 const { yamada_fc_brand: yamadaFcBrand } = proxy.useDict('yamada_fc_brand');
 const { yamada_business_type: yamadaBusinessType } = proxy.useDict('yamada_business_type');
 
-// 5. 暴露模板使用的常量
+// 暴露模板使用的常量
 defineExpose({ AGGREGATION_TYPE, TARGET_PERIOD_TYPE });
 
 // フォームの入力データ
@@ -129,48 +126,29 @@ const data = reactive({
     annual: null, // 指定年度
     brandCode: [],
     businessTypeCode: [],
-    regions: []
+    regions: [] // 由PublicRange组件维护的选中区域
   }
 });
 
 const { queryParams } = toRefs(data);
-// 6. 初始化与生命周期
+
+// 初始化与生命周期
 onMounted(async () => {
   try {
     const response = await getRegionTree();
     if (response?.success) {
-      const regions = response.data.regions || [];
-      regionTree.value = regions;
-      if (regions.length) {
-        updateSelectedRegions(regions);
-      }
+      regionTree.value = response.data.regions || [];
     } else {
       error.value = response?.message || '地域データの取得に失敗しました';
       proxy.$message.error(error.value);
-      console.error('地域データの取得に失敗しました:', response);
     }
   } catch (err) {
-    const errorMsg = '地域データの取得中にエラーが発生しました';
-    proxy.$message.error(errorMsg);
-    console.error('地域データの取得中にエラーが発生しました:', err);
+    proxy.$message.error('地域データの取得中にエラーが発生しました');
+    console.error(err);
   }
 });
 
-// 7. 监听器
-/**
- * 地域ツリーのデータが更新されたときに、選択された地域を更新します
- */
-watch(() => regionTree.value, (newVal) => {
-  if (newVal?.length) {
-    updateSelectedRegions(newVal);
-  }
-}, { deep: true });
-
-/**
- * 対象期間タイプが変更されたときに、関連する日付フィールドをリセットします
- * - 月指定の場合は年度指定フィールドをクリア
- * - 年度指定の場合は月指定フィールドをクリア
- */
+// 监听器:対象期間タイプ变化时重置相关字段
 watch(
     () => queryParams.value.targetPeriodType,
     (newVal) => {
@@ -184,133 +162,19 @@ watch(
     { immediate: true }
 );
 
-// 8. 地域树处理方法(核心工具方法)
-/**
- * 指定されたIDを持つ地域ノードをツリーから再帰的に検索します
- * @param {Array} nodes - 検索対象の地域ノード配列
- * @param {string|number} id - 検索する地域のID
- * @returns {Object|null} 見つかった地域ノード(見つからない場合はnull)
- */
-const findRegionById = (nodes, id) => {
-  if (!nodes?.length) return null;
-
-  for (const node of nodes) {
-    if (node.id === id) {
-      return node;
-    }
-    if (node.children && node.children.length > 0) {
-      const found = findRegionById(node.children, id);
-      if (found) {
-        return found;
-      }
-    }
-  }
-  return null;
-};
-
-/**
- * 地域ツリーから選択された地域を再帰的に収集し、選択状態を更新します
- * 親ノードは収集対象外とし、サブノードのみを対象とします
- * @param {Array} nodes - 地域ツリーのノード配列
- */
-const updateSelectedRegions = (nodes) => {
-  if (!nodes?.length) return;
-  selectedRegions.value = [];
-  // 最上位ノードは親ノードとしてマークされ、再帰的に子ノードを処理します
-  collectSelectedRegions(nodes, true);
-  queryParams.value.regions = [...selectedRegions.value];
-};
-
-/**
- * 選択されたサブエリアオブジェクトを再帰的に収集します(親ノードをスキップ)
- * @param {Array} nodes - 処理対象の地域ノード配列
- * @param {boolean} isParent - 親ノードかどうかを示すフラグ(trueの場合は子ノードのみ処理)
- */
-const collectSelectedRegions = (nodes, isParent = false) => {
-  nodes.forEach(node => {
-    // 親ノード(上位ノード)の場合は、収集をスキップして再帰的に子ノードを処理します
-    if (isParent) {
-      if (node.children && node.children.length > 0) {
-        collectSelectedRegions(node.children, false);
-      }
-      return;
-    }
-    if (node.checked) {
-      selectedRegions.value.push({
-        regionCode: node.regionCode
-      });
-    }
-    if (node.children && node.children.length > 0) {
-      collectSelectedRegions(node.children, false);
-    }
-  });
-};
-
-/**
- * 地域ツリーのチェック状態を再帰的にリセットします
- * すべてのノードのcheckedプロパティをfalseに設定します
- * @param {Array} nodes - 地域ツリーのノード配列
- */
-const resetRegionTreeCheck = (nodes) => {
-  nodes.forEach(node => {
-    node.checked = false;
-    if (node.children?.length) {
-      resetRegionTreeCheck(node.children);
-    }
-  });
-};
-
-// 9. 事件处理方法
-/**
- * 地域のチェック状態が変更されたときのハンドラー関数です
- * @param {string|number} regionId - チェック状態が変更された地域のID
- * @param {boolean} isChecked - チェック状態(true:チェックされた、false:チェックが外れた)
- */
-const handleCheckChange = (regionId, isChecked) => {
-  const region = findRegionById(regionTree.value, regionId);
-  if (!region) return;
-
-  if (isChecked) {
-    if (!selectedRegions.value.some(r => r.regionCode === region.regionCode)) {
-      selectedRegions.value.push({ regionCode: region.regionCode });
-    }
-  } else {
-    selectedRegions.value = selectedRegions.value.filter(r => r.regionCode !== region.regionCode);
-  }
-  queryParams.value.regions = [...selectedRegions.value];
-};
-
-// 10. 表单验证与提交
-/**
- * 検索条件を検証する関数です
- * 必須項目の入力状態を確認し、未入力の場合はエラーメッセージを返します
- * @returns {Array} エラーメッセージの配列(検証が通った場合は空配列)
- */
+// 表单验证
 const validateForm = () => {
   const errors = [];
-
   if (queryParams.value.targetPeriodType === TARGET_PERIOD_TYPE.MONTHLY) {
-    if (!queryParams.value.startYear) {
-      errors.push('「対象 月指定」の年を選択してください');
-    }
-    if (!queryParams.value.startMonth) {
-      errors.push('「対象 月指定」の月を選択してください');
-    }
+    if (!queryParams.value.startYear) errors.push('「対象 月指定」の年を選択してください');
+    if (!queryParams.value.startMonth) errors.push('「対象 月指定」の月を選択してください');
   } else if (queryParams.value.targetPeriodType === TARGET_PERIOD_TYPE.ANNUAL) {
-    if (!queryParams.value.annual) {
-      errors.push('「対象 年度指定」の年度を選択してください');
-    }
+    if (!queryParams.value.annual) errors.push('「対象 年度指定」の年度を選択してください');
   }
-
   return errors;
 };
 
-/**
- * 集計ボタンをクリックしたときの処理関数です
- * 1. フォームのバリデーションを実行
- * 2. 集計条件に基づいてsalesFlagを設定
- * 3. 集計データをストアに保存し、結果画面に遷移
- */
+// 集計处理
 const handleAggregate = () => {
   const errors = validateForm();
   if (errors.length) {
@@ -318,6 +182,7 @@ const handleAggregate = () => {
     return;
   }
 
+  // 计算salesFlag(根据选择的条件)
   let salesFlag = 0;
   const isMonthly = queryParams.value.targetPeriodType === TARGET_PERIOD_TYPE.MONTHLY;
   const { aggregationType } = queryParams.value;
@@ -338,439 +203,232 @@ const handleAggregate = () => {
       return;
   }
 
+  // 准备传递给结果页的参数
   const transferData = {
     targetPeriodType: queryParams.value.targetPeriodType,
     startYear: isMonthly ? queryParams.value.startYear : null,
     startMonth: isMonthly ? queryParams.value.startMonth : null,
     annual: !isMonthly ? queryParams.value.annual : null,
     brandCodes: queryParams.value.brandCode,
-    regionCodes: queryParams.value.regions.map(r => r.regionCode),
+    regionCodes: queryParams.value.regions.map(r => r.regionCode), // 提取区域编码
     aggregationType: queryParams.value.aggregationType,
     salesFlag: salesFlag
   };
+
+  // 保存参数到状态管理并跳转结果页
   salesStore.setSalesData(transferData);
   router.push({ name: 'salesSumResult' });
 };
 
-/**
- * リセットボタンをクリックしたときの処理関数です
- * フォームのすべての入力値と選択状態を初期状態にリセットします
- */
+// 重置表单
 const resetForm = () => {
+  // 重置基础参数
   queryParams.value.targetPeriodType = TARGET_PERIOD_TYPE.MONTHLY;
   queryParams.value.aggregationType = AGGREGATION_TYPE.FC;
   queryParams.value.startYear = null;
   queryParams.value.startMonth = null;
   queryParams.value.annual = null;
   queryParams.value.brandCode = [];
-  queryParams.value.regions = [];
-  selectedRegions.value = [];
-  if (regionTree.value.length) {
-    resetRegionTreeCheck(regionTree.value);
+  queryParams.value.businessTypeCode = [];
+
+  // 调用PublicRange组件的重置方法(重置区域选择)
+  if (publicRangeRef.value) {
+    publicRangeRef.value.resetRegionTreeCheck();
   }
 };
 </script>
 
 <style scoped>
-/* 基础容器样式 - 最外层布局控制 */
+/* 基础容器样式 */
 .form-container {
-  margin-top: 0;  /* 清除顶部默认外边距,避免与父容器产生额外间距 */
-  padding: 0 !important; /* 清除默认内边距,确保布局基准线一致 */
+  margin-top: 0;
+  padding: 0 !important;
 }
 
-/* 布局容器 - 统一标题与内容的排列结构 */
+/* 布局容器 */
 .section-container {
-  display: flex; /* 使用Flex布局,使标题与内容横向排列 */
-  align-items: flex-start; /* 顶部对齐,确保各区域垂直基准一致 */
-  margin-bottom: 10px; /* 区域间垂直间距,区分不同设置项 */
-  gap: 10px; /* 标题与内容区的固定水平间距 */
-  margin-left: 0 !important; /* 清除el-form-item默认左间距 */
+  display: flex;
+  align-items: flex-start;
+  margin-bottom: 10px;
+  gap: 10px;
+  margin-left: 0 !important;
 }
 
-/* 通过深度选择器控制el-form-item的label样式,替代原.form-section */
+/* 标题样式 */
 :deep(.el-form-item__label) {
-  width: 100px !important; /* 固定宽度,确保标题左侧起点一致 */
-  font-size: 14px !important; /* 统一字体大小,保持视觉一致性 */
-  line-height: 1.4 !important; /* 统一行高,确保文字基线对齐 */
-  padding-left: 15px !important; /* 左对齐标题,节省空间并统一缩进 */
-  padding-right: 0 !important; /* 清除右侧内边距 */
-  margin: 0 !important; /* 清除默认外边距,避免布局偏移 */
-  margin-top: 5px !important; /* 与单选框保持垂直居中对齐 */
-  text-align: left !important; /* 强制左对齐,统一标题显示方式 */
-  letter-spacing: normal !important; /* 清除异常字间距,确保文字排列均匀 */
-  display: inline-block !important; /* 确保宽高计算准确,不影响周围元素 */
-  box-sizing: border-box !important; /* 宽度计算包含边框和内边距,避免尺寸偏差 */
-  position: relative !important; /* 建立稳定定位基准,便于子元素定位 */
-  top: 0 !important; /* 清除定位偏移,确保顶部对齐 */
-  //color: #606266 !important; /* 标题文字颜色,使用中性色调增强可读性 */
-  font-weight: 700 !important; /* 标题文字加粗,突出显示设置项类别 */
-}
-
-/* 分隔线样式 - 区域间视觉分隔 */
+  width: 100px !important;
+  font-size: 14px !important;
+  line-height: 1.4 !important;
+  padding-left: 15px !important;
+  padding-right: 0 !important;
+  margin: 0 !important;
+  margin-top: 5px !important;
+  text-align: left !important;
+  display: inline-block !important;
+  box-sizing: border-box !important;
+  font-weight: 700 !important;
+}
+
+/* 分隔线样式 */
 .custom-divider {
   background-color: var(--color-primary);
-  height: 2px; /* 设置分隔线高度,确保视觉效果 */
-  margin: 12px 0; /* 上下外边距,与周围内容保持距离 */
+  height: 2px;
+  margin: 12px 0;
 }
 
-
-/* 标题首字符样式 - 确保左侧无额外间距 */
-:deep(.el-form-item__label)::first-letter {
-  margin-left: 0 !important; /* 清除首字符可能的默认左间距,确保对齐精确 */
-}
-
-/* 公共区域标题样式 - 控制子组件标题对齐 */
-:deep(.public-range-title) {
-  text-align: left !important;  /* 强制左对齐,与其他标题保持一致 */
-  padding-left: 20px; /* 左侧内边距,与其他标题保持对齐 */
-}
-
-/* 内容区域容器 - 标题右侧的内容部分 */
+/* 内容区域容器 */
 .content-group {
-  flex: 1; /* 占满剩余宽度,自适应不同屏幕尺寸 */
-  margin: 0 !important; /* 清除element-ui默认外边距,避免布局偏差 */
-  padding-left: 0 !important; /* 统一内容区左侧缩进,确保对齐一致 */
+  flex: 1;
+  margin: 0 !important;
+  padding-left: 0 !important;
 }
 
-/* 单选组布局 - 垂直排列(月指定/年度指定) */
+/* 单选组布局(垂直) */
 .period-selector-vertical {
-  display: flex; /* 使用Flex布局 */
-  flex-direction: column; /* 垂直排列子元素 */
-  gap: 5px; /* 月指定与年度指定选项的垂直间距,区分不同选项 */
-  padding: 0 !important; /* 清除默认内边距 */
-  margin: 0 !important; /* 清除默认外边距 */
+  display: flex;
+  flex-direction: column;
+  gap: 5px;
+  padding: 0 !important;
+  margin: 0 !important;
 }
 
-/* 单选组布局 - 水平排列(集計種別) */
+/* 单选组布局(水平) */
 .aggregation-radio-horizontal {
-  display: flex; /* 使用Flex布局 */
-  gap: 20px; /* 集計種別选项间的水平间距,区分不同选项 */
-  align-items: center; /* 垂直居中对齐选项 */
-  flex-wrap: wrap; /* 小屏幕下自动折行避免溢出 */
-  margin: 0 !important; /* 清除默认外边距 */
-  padding: 0 !important; /* 清除默认内边距 */
+  display: flex;
+  gap: 20px;
+  align-items: center;
+  flex-wrap: wrap;
+  margin: 0 !important;
+  padding: 0 !important;
 }
 
-/* 单选框基础样式 - 统一所有单选框的对齐 */
+/* 单选框基础样式 */
 .common-radio {
-  flex-shrink: 0 !important; /* 禁止压缩,保持宽度稳定 */
-  margin: 0 !important; /* 清除默认外边距,避免布局偏移 */
-  padding: 0 0 0 20px !important; /* 左侧预留空间放置单选框圆圈 */
-  text-align: left !important; /* 文字左对齐,统一显示方式 */
-  position: relative !important; /* 建立定位基准,便于单选框圆圈定位 */
+  flex-shrink: 0 !important;
+  margin: 0 !important;
+  padding: 0 0 0 20px !important;
+  text-align: left !important;
+  position: relative !important;
 }
 
-/* 集計種別 区域的宽度 */
+/* 集計種別单选框宽度 */
 .aggregation-radio-horizontal .common-radio {
   width: 100px !important;
 }
 
-/* 月指定单选框样式 - 与年度指定保持一致 */
+/* 月/年度指定单选框宽度 */
 .section-monthly .common-radio,
 .section-annual .common-radio {
   width: 80px !important;
 }
 
-/* 单选框内部圆圈定位 - 核心对齐控制 */
+/* 单选框内部圆圈定位 */
 :deep(.common-radio .el-radio__input) {
-  position: absolute !important; /* 脱离文档流,基于父元素定位 */
-  left: 0 !important; /* 固定在左侧,所有单选框左对齐 */
-  top: 50% !important; /* 垂直方向居中定位 */
-  transform: translateY(-50%) !important; /* 精确垂直居中对齐 */
-  margin: 0 !important; /* 清除默认外边距,避免定位偏差 */
+  position: absolute !important;
+  left: 0 !important;
+  top: 50% !important;
+  transform: translateY(-50%) !important;
+  margin: 0 !important;
 }
 
-/* 单选框文字样式 - 控制与圆圈的间距 */
+/* 单选框文字样式 */
 :deep(.common-radio .el-radio__label) {
-  padding-left: 5px !important; /* 文字与圆圈的固定间距,保持一致 */
-  margin: 0 !important; /* 清除默认外边距 */
+  padding-left: 5px !important;
+  margin: 0 !important;
 }
 
-/* 日期选择器样式 - 年下拉框 */
+/* 日期选择器样式(年) */
 .date-select-year {
-  width: 100px !important; /* 固定宽度 */
-  min-width: 100px !important; /* 最小宽度限制 */
-  max-width: 100px !important; /* 最大宽度限制 */
-  padding: 0 8px !important; /* 减少内边距,节省空间 */
-}
-
-/* 月指定日期组样式 - 清除默认边距 */
-.section-monthly .date-group {
-  margin: 0 !important; /* 清除默认外边距 */
-  padding: 0 !important; /* 清除默认内边距 */
+  width: 100px !important;
+  min-width: 100px !important;
+  max-width: 100px !important;
+  padding: 0 8px !important;
 }
 
-/* 日期选择组合容器 - 年+月的整体布局 */
+/* 日期选择组合容器 */
 .date-groups-wrapper {
-  display: flex; /* 使用Flex布局 */
-  align-items: center; /* 垂直居中对齐子元素 */
-  gap: 8px; /* 年和月下拉列表之间的间隔 */
-  flex-wrap: wrap; /* 小屏幕上自动折行,避免溢出 */
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: wrap;
 }
 
-/* 日期项容器 - 下拉框+标签的组合 */
+/* 日期项容器 */
 .date-group {
-  display: flex; /* 使用Flex布局 */
-  align-items: center; /* 垂直居中对齐子元素 */
-  gap: 0; /* 下拉框与标签的间距 */
-  white-space: nowrap; /* 防止内部元素折行,保持在同一行 */
+  display: flex;
+  align-items: center;
+  gap: 0;
+  white-space: nowrap;
 }
 
-/* 日期选择器样式 - 月下拉框(略窄) */
+/* 日期选择器样式(月) */
 .date-select-month {
-  width: 80px !important; /* 固定宽度,比年下拉框略窄 */
-  min-width: 60px !important; /* 最小宽度限制 */
-  max-width: 90px !important; /* 最大宽度限制 */
-  padding: 0 8px !important; /* 减少内边距,节省空间 */
+  width: 80px !important;
+  min-width: 60px !important;
+  max-width: 90px !important;
+  padding: 0 8px !important;
 }
 
-/* 日期标签样式 - "年"、"月"、"年度"文字 */
+/* 日期标签样式 */
 .date-label {
-  white-space: nowrap; /* 禁止文字折行,保持在同一行 */
-  line-height: 1; /* 行高为1,紧凑显示 */
-  display: inline-flex; /*  inline-flex布局,便于对齐 */
-  align-items: center; /* 垂直居中对齐 */
-}
-
-/* 单选按钮与下拉框组合容器 - 确保不折行 */
-.section-annual {
-  display: flex; /* 使用Flex布局 */
-  align-items: center; /* 垂直居中对齐子元素 */
-  gap: 8px; /* 子元素间的水平间距 */
-  width: 100%; /* 占满父容器宽度 */
-  min-width: 0; /* 允许内容收缩 */
-  flex-wrap: nowrap; /* 强制不换行,保持选项与输入框在同一行 */
+  white-space: nowrap;
+  line-height: 1;
+  display: inline-flex;
+  align-items: center;
 }
 
-/* 年度指定整体容器样式 - 控制不折行 */
+/* 年度指定整体容器样式 */
 .annual-group {
-  display: flex; /* 使用Flex布局 */
-  align-items: center; /* 垂直居中对齐子元素 */
-  gap: 0 !important; /* 强制消除单选框与下拉列表间距 */
-  width: 100%; /* 占满父容器宽度 */
-  flex-wrap: nowrap; /* 强制不折行,保持在同一行 */
-  min-width: 300px; /* 确保有足够宽度容纳内容,避免挤压 */
-}
-
-/* 年度日期组合容器样式 - 控制内部间距 */
-.annual-group .date-group {
-  flex: 1; /* 继承原annual-row的flex:1属性 */
-  min-width: 0; /* 继承原annual-row的收缩特性 */
-  margin: 0;
-  padding: 2px 0; /* 继承原annual-row的内边距 */
-}
-
-/* 年度下拉框样式 - 清除右侧边距 */
-.annual-group .date-select-year {
-  margin-right: 0 !important; /* 清除下拉框右侧外边距 */
-}
-
-/* 年度标签样式 - 调整左侧间距 */
-.annual-group .date-label {
-  margin-left: 0 !important; /* 微调间距,与"年"标签对齐 */
+  display: flex;
+  align-items: center;
+  gap: 0 !important;
+  width: 100%;
+  flex-wrap: nowrap;
+  min-width: 300px;
 }
 
-/* 公共区域容器 - FC/区域选择部分的布局 */
+/* 公共区域容器 */
 .section-fc-area {
-  margin-left: 16px; /* 与其他内容区保持左对齐,统一页面缩进 */
+  margin-left: 16px;
 }
 
-/* 按钮区域样式 - 集計和リセット按钮容器 */
+/* 按钮区域样式 */
 .section-btn {
-  padding-left: calc(15px + 100px + 10px); /* 与标题对齐:15px偏移+100px标题宽度+10px间距 */
-  margin-top: 25px; /* 顶部外边距,与上方内容保持距离 */
-  display: flex; /* 使用Flex布局 */
-  gap: 10px; /* 按钮间距,区分不同按钮 */
-  align-items: center; /* 垂直居中对齐按钮 */
-  justify-content: start; /* 子元素左对齐,符合操作习惯 */
-  flex-wrap: wrap; /* 小屏幕下按钮换行,避免溢出 */
+  padding-left: calc(15px + 100px + 10px);
+  margin-top: 25px;
+  display: flex;
+  gap: 10px;
+  align-items: center;
+  justify-content: start;
+  flex-wrap: wrap;
 }
 
-/* 公共按钮样式 */
-.base-btn {
+/* 按钮样式 */
+.btn-aggregation, .btn-reset {
   width: 120px;
   height: 32px;
   padding: 6px 10px;
   box-sizing: border-box;
   white-space: nowrap;
 }
-/* 差异化样式 */
-.btn-aggregation {
-  @extend .base-btn;
-  margin-left: 0;
-}
 .btn-reset {
-  @extend .base-btn;
   margin-left: 10px;
 }
 
-/* 媒体查询 - 大屏幕(≥1024px) */
-@media (min-width: 1024px) {
-  .section-container {
-    gap: 15px; /* 增大间距提升可读性 */
-  }
-
-  .aggregation-radio-horizontal {
-    gap: 30px; /* 增大选项间距,提升可读性 */
-  }
-
-  /* 统一年度指定区域布局 */
-  .annual-group {
-    gap: 0 !important; /* 与月指定的date-groups-wrapper保持相同间距(8px) */
-    align-items: center !important; /* 确保垂直居中对齐 */
-  }
-
-  /* 年度下拉框位置调整 */
-  .annual-group .date-group {
-    gap: 0 !important; /* 覆盖大屏下的可能间距 */
-    margin-left: 0 !important; /* 用容器左移替代内部元素位移,保持布局整体性 */
-  }
-
-  .annual-group .date-select-year {
-    vertical-align: middle; /* 垂直居中对齐 */
-  }
-
-  /* 日期标签对齐调整 */
-  .date-group .date-label {
-    margin-left: 4px !important; /* 统一标签与下拉框的间距 */
-  }
-
-  .annual-group .date-group .date-label {
-    margin-left: 2px !important; /* 微调年度标签位置,确保与年标签对齐 */
-  }
-}
-
-/* 媒体查询 - 平板端(768px-1023px) */
-@media (min-width: 768px) and (max-width: 1023px) {
-  .date-group {
-    gap: 4px; /* 缩小间距,适应中等屏幕 */
-  }
-
-  /* 仅调整年度指定行的布局,与月指定对齐 */
-  .annual-group {
-    gap: 5px !important; /* 与月指定的section-annual保持相同间距 */
-  }
-
-  /* 年度下拉框容器与月指定的date-group保持一致的左偏移 */
-  .annual-group .date-group {
-    margin-left: 0 !important; /* 清除额外左偏移 */
-  }
-
-  /* 确保年度下拉框与年下拉框宽度/位置一致 */
-  .annual-group .date-select-year {
-    margin-left: 0 !important; /* 清除默认左外边距 */
-  }
-
-  .section-btn {
-    padding-left: calc(15px + 100px + 10px); /* 与标题对齐,动态计算左侧内边距 */
-  }
-}
-
-/* 响应式样式 - 移动端(<768px) */
+/* 响应式调整 */
 @media (max-width: 767px) {
-  /* 标题样式调整 */
-  :deep(.el-form-item__label) {
-    width: 80px !important; /* 保持宽度为80px */
-    white-space: nowrap !important; /* 禁止换行,确保标题完整显示 */
-    margin-left: 0 !important; /* 清除左侧外边距干扰 */
-    padding-left: 20px !important; /* 固定左侧内边距 */
-    min-width: 80px !important; /* 强制最小宽度 */
-    max-width: 80px !important; /* 强制最大宽度 */
-    flex-shrink: 0 !important; /* 禁止宽度被压缩 */
-    box-sizing: content-box !important; /* 宽度计算不包含内边距 */
-  }
-
-  .date-group {
-    align-items: center; /* 垂直居中对齐 */
-    gap: 0; /* 缩小间距 */
-  }
-
-  /* 年度下拉框区域固定宽度,防止被挤压 */
-  .date-group > .date-select-year:first-child {
-    flex-shrink: 0; /* 禁止压缩 */
-  }
-
-  /* 年标签与年下拉框绑定,不单独折行 */
-  .date-group > .date-label:nth-child(2) {
-    margin-right: 8px; /* 右侧外边距 */
-    flex-shrink: 0; /* 禁止压缩 */
-    font-size: 14px; /* 日期标签字体微调 */
-  }
-
-  .aggregation-radio-horizontal {
-    gap: 10px; /* 缩小水平间距,适应小屏幕 */
-  }
-
-  /* 在小屏幕上,让日期组垂直排列 */
   .date-groups-wrapper {
-    flex-direction: column; /* 垂直排列 */
-    align-items: flex-start; /* 左对齐 */
-    width: 100%; /* 占满宽度 */
-    padding-left: 0 !important; /* 清除左内边距 */
-  }
-
-  /* 年度指定整体容器样式 - 增强不折行控制 */
-  .annual-group {
-    display: flex; /* Flex布局 */
-    align-items: center; /* 垂直居中 */
-    gap: 0 !important; /* 减少年度指定单选框与下拉列表的间隔 */
-    width: 100%; /* 占满宽度 */
-    flex-wrap: nowrap; /* 强制不折行 */
-    min-width: 300px; /* 确保有足够宽度容纳内容 */
-    overflow-x: auto; /* 内容过宽时显示横向滚动条 */
-    padding-bottom: 5px; /* 为滚动条预留空间 */
-    scrollbar-width: thin; /* 优化滚动条样式 */
+    flex-direction: column;
+    align-items: flex-start;
+    width: 100%;
+    padding-left: 0 !important;
   }
-
-  /* 年度下拉框与年下拉框保持完全一致的位置 */
-  .annual-group .date-group {
-    margin-left: 0 !important; /* 与月指定的date-group左对齐 */
-    gap: 0 !important; /* 与月指定的date-group内部间距一致 */
-  }
-
-  /* 年度下拉框宽度/位置与年下拉框完全匹配 */
-  .annual-group .date-select-year {
-    margin-left: 0 !important; /* 清除默认左外边距 */
-  }
-
-  /* 优化滚动条样式 */
-  .annual-group::-webkit-scrollbar {
-    height: 4px; /* 滚动条高度 */
-  }
-
-  /* 调整单选框宽度,为下拉框留出更多空间 */
-  .section-annual .common-radio {
-    padding-right: 0 !important; /* 移除右侧内边距 */
-  }
-
-  /* 关键修正:让月指定的年下拉框容器与年度下拉框对齐 */
-  .section-monthly .date-groups-wrapper {
-    margin-left: 0 !important; /* 清除可能的默认左偏移 */
-    padding-left: 0 !important; /* 清除左内边距 */
-  }
-
-  /* 强制月指定的年下拉框与年度下拉框左对齐基准一致 */
-  .section-monthly .date-group:first-child {
-    margin-left: 0 !important; /* 与年度下拉框的date-group保持相同左偏移 */
-    position: relative; /* 相对定位 */
-    left: 0 !important; /* 消除任何隐性定位偏移 */
-  }
-
-  /* 月下拉列表行与年下拉列表对齐 */
   .section-monthly .date-groups-wrapper .date-group:nth-child(2) {
-    margin-left: 80px !important; /* 匹配"月指定"单选框宽度,确保左对齐 */
-    margin-top: 8px; /* 添加微小间距,避免拥挤 */
+    margin-left: 80px !important;
+    margin-top: 8px;
   }
-
   .section-btn {
-    padding-left: 20px; /* 调整左侧内边距,避免溢出 */
-    justify-content: flex-start; /* 左对齐避免溢出 */
+    padding-left: 20px;
   }
-
-  /* 保持按钮宽度一致 */
-  .btn-aggregation, .btn-reset {
-    flex-shrink: 0; /* 禁止按钮宽度被压缩 */
-  }
-
 }
 </style>