|
|
@@ -0,0 +1,424 @@
|
|
|
+<!-- 売上集計条件指定 -->
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <div class="form-container">
|
|
|
+ <div class="section-container">
|
|
|
+ <div class="form-section">対象 </div>
|
|
|
+ <el-form-item class="radio-group-vertical">
|
|
|
+ <div class="radio-container">
|
|
|
+ <el-radio
|
|
|
+ label="immediate"
|
|
|
+ v-model="queryParams.releaseType"
|
|
|
+ class="vertical-radio"
|
|
|
+ >月指定</el-radio>
|
|
|
+ <div class="scheduled-row">
|
|
|
+ <!-- 期間指定时显示的日期选择(同行) -->
|
|
|
+ <div class="date-group" >
|
|
|
+ <!-- 日期选择器代码保持不变 -->
|
|
|
+ <el-select v-model="queryParams.monthYear" placeholder="" class="date-select" :disabled="queryParams.releaseType === 'scheduled'">
|
|
|
+ <el-option v-for="year in years" :key="year" :label="year" :value="year"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <span class="date-label">年</span>
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.startMonth"
|
|
|
+ placeholder=""
|
|
|
+ class="date-select-small"
|
|
|
+ :disabled="queryParams.releaseType === 'scheduled'"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="month in months"
|
|
|
+ :key="month"
|
|
|
+ :label="month"
|
|
|
+ :value="month"
|
|
|
+ ></el-option>
|
|
|
+ </el-select>
|
|
|
+ <span class="date-label">月</span>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button @click="resetForm" v-hasPermi="['fcbi:survey:form']" class="append-button">集計条件リセット</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-radio
|
|
|
+ label="scheduled"
|
|
|
+ v-model="queryParams.releaseType"
|
|
|
+ class="scheduled-radio"
|
|
|
+ >年度指定</el-radio>
|
|
|
+ <div class="scheduled-row">
|
|
|
+ <!-- 期間指定时显示的日期选择(同行) -->
|
|
|
+ <div class="date-group" >
|
|
|
+ <!-- 日期选择器代码保持不变 -->
|
|
|
+ <el-select v-model="queryParams.startYear" placeholder="" class="date-select" :disabled="queryParams.releaseType === 'immediate'">
|
|
|
+ <el-option v-for="year in years" :key="year" :label="year" :value="year"></el-option>
|
|
|
+ </el-select>
|
|
|
+ <span class="date-label">年度</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <el-form-item label="集計種別" style="margin-left: 50px;margin-top: -15px">
|
|
|
+ <el-radio-group v-model="queryParams.aggregationType" class="custom-radio-group">
|
|
|
+ <el-radio label="FC_CHECKBOX">FC別集計</el-radio>
|
|
|
+ <el-radio label="TYPE_CHECKBOX">エリア別集計</el-radio>
|
|
|
+ <el-radio label="STORE_CHECKBOX">店舗別集計</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <div class="divider"></div>
|
|
|
+ <PublicRange
|
|
|
+ v-model="queryParams"
|
|
|
+ :region-tree="regionTree"
|
|
|
+ :yamada-fc-brand="yamada_fc_brand"
|
|
|
+ :yamada-business-type="yamada_business_type"
|
|
|
+ @check-change="handleCheckChange"
|
|
|
+ :show-business-type="false"
|
|
|
+ />
|
|
|
+ <div class="btn-section" style="padding-left: 78px; margin-top: 25px;">
|
|
|
+ <button class="operate-btn" @click="Confirm" v-hasPermi="['fcbi:sales:sum']">集計</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script name="SurveyExport" setup>
|
|
|
+import { getRegionTree } from "@/api/fcbi/survey.js";
|
|
|
+import {reactive, ref, onMounted, getCurrentInstance, watch, toRefs} from 'vue';
|
|
|
+import PublicRange from '../../../components/PublicRange.vue';
|
|
|
+import { useSurveyStore } from '@/store/surveyStore';
|
|
|
+import { useSurveySalesStore } from '@/store/surveyStore';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
+const surveyStore = useSurveyStore();
|
|
|
+const surveySalesStore = useSurveySalesStore();
|
|
|
+const router = useRouter();
|
|
|
+
|
|
|
+/**
|
|
|
+ * データ辞書から品牌と業務タイプのデータを取得します
|
|
|
+ */
|
|
|
+const { yamada_fc_brand } = proxy.useDict('yamada_fc_brand');
|
|
|
+const { yamada_business_type } = proxy.useDict('yamada_business_type');
|
|
|
+
|
|
|
+/**
|
|
|
+ * 地域ツリーデータと選択された地域コードの配列を保持します
|
|
|
+ */
|
|
|
+const regionTree = ref([]);
|
|
|
+const selectedRegions = ref([]);
|
|
|
+
|
|
|
+/**
|
|
|
+ * フォームの入力データを保持するオブジェクトです
|
|
|
+ */
|
|
|
+const data = reactive({
|
|
|
+ // 検索条件を格納するオブジェクト
|
|
|
+ queryParams: {
|
|
|
+ brandCode: [],
|
|
|
+ businessTypeCode: [],
|
|
|
+ releaseType: 'immediate',
|
|
|
+ aggregationType: 'FC_CHECKBOX',
|
|
|
+ monthYear: null,
|
|
|
+ startMonth: null,
|
|
|
+ startYear: null,
|
|
|
+ regions: []
|
|
|
+ }
|
|
|
+});
|
|
|
+const { queryParams} = toRefs(data);
|
|
|
+
|
|
|
+const currentYear = new Date().getFullYear();
|
|
|
+const years = Array.from({ length: 11 }, (_, i) => currentYear + i);
|
|
|
+const months = Array.from({ length: 12 }, (_, i) => i + 1);
|
|
|
+/**
|
|
|
+ * 地域ツリーのデータが更新されたときに、選択された地域を更新します
|
|
|
+ */
|
|
|
+watch(regionTree, () => {
|
|
|
+ if (regionTree.value.length > 0) {
|
|
|
+ updateSelectedRegions(regionTree.value);
|
|
|
+ }
|
|
|
+}, { deep: true });
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => queryParams.value.releaseType,
|
|
|
+ (newVal) => {
|
|
|
+ if (newVal === 'immediate') {
|
|
|
+ queryParams.value.startYear = null;
|
|
|
+ } else {
|
|
|
+ queryParams.value.monthYear = null;
|
|
|
+ queryParams.value.startMonth = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+);
|
|
|
+
|
|
|
+/**
|
|
|
+ * コンポーネントのマウント後に地域ツリーデータを初期化します
|
|
|
+ */
|
|
|
+onMounted(async () => {
|
|
|
+ const response = await getRegionTree();
|
|
|
+ if (response?.success) {
|
|
|
+ const regions = response.data.regions || []
|
|
|
+ regionTree.value = regions
|
|
|
+ surveyStore.setRegionTree(regions)
|
|
|
+ if (regionTree.value.length > 0) {
|
|
|
+ updateSelectedRegions(regionTree.value);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ error.value = response?.message || '地域データの取得に失敗しました';
|
|
|
+ console.error('地域データの取得に失敗しました:', response);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+/**
|
|
|
+ * 地域のチェック状態が変更されたときのハンドラー関数です
|
|
|
+ */
|
|
|
+const handleCheckChange = (regionId, isChecked) => {
|
|
|
+ const region = findRegionById(regionTree.value, regionId);
|
|
|
+ if (region) {
|
|
|
+ if (isChecked) {
|
|
|
+ if (!selectedRegions.value.some(r => r.region_code === region.region_code)) {
|
|
|
+ selectedRegions.value.push({
|
|
|
+ region_code: region.region_code
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ selectedRegions.value = selectedRegions.value.filter(r => r.region_code !== region.region_code);
|
|
|
+ }
|
|
|
+ queryParams.value.regions = selectedRegions.value;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 指定されたIDを持つ地域ノードをツリーから再帰的に検索します
|
|
|
+ */
|
|
|
+const findRegionById = (nodes, id) => {
|
|
|
+ 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) => {
|
|
|
+ selectedRegions.value = [];
|
|
|
+ // 最上位ノードは親ノードとしてマークされ、再帰的に子ノードを処理します
|
|
|
+ collectSelectedRegions(nodes, true);
|
|
|
+ queryParams.value.regions = selectedRegions.value;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 選択されたすべてのサブエリアオブジェクトを再帰的に収集します(親ノードをスキップ)
|
|
|
+ */
|
|
|
+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({
|
|
|
+ region_code: node.region_code
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
+ collectSelectedRegions(node.children, false);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const Confirm = () => {
|
|
|
+
|
|
|
+ let isValid = true;
|
|
|
+
|
|
|
+ if (queryParams.value.releaseType === 'immediate') {
|
|
|
+ if (!queryParams.value.monthYear) {
|
|
|
+ isValid = false;
|
|
|
+ proxy.$message.warning('月指定の年を選択してください');
|
|
|
+ }
|
|
|
+ if (!queryParams.value.startMonth) {
|
|
|
+ isValid = false;
|
|
|
+ proxy.$message.warning('月指定の月を選択してください');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (queryParams.value.releaseType === 'scheduled') {
|
|
|
+ if (!queryParams.value.startYear) {
|
|
|
+ isValid = false;
|
|
|
+ proxy.$message.warning('年度指定の年度を選択してください');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isValid) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (queryParams.value.aggregationType === 'FC_CHECKBOX') {
|
|
|
+ let salesFlag = 0;
|
|
|
+ const hasArea = queryParams.value.regions && queryParams.value.regions.length > 0; // エリア是否至少选一个
|
|
|
+ const isMonth = queryParams.value.releaseType === 'immediate'; // 是否是月指定
|
|
|
+
|
|
|
+ if (isMonth) {
|
|
|
+ salesFlag = hasArea ? 1 : 2;
|
|
|
+ } else {
|
|
|
+ salesFlag = hasArea ? 3 : 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ const transferData = {
|
|
|
+ monthYear: isMonth ? queryParams.value.monthYear : null,
|
|
|
+ startMonth: isMonth ? queryParams.value.startMonth : null,
|
|
|
+ startYear: !isMonth ? queryParams.value.startYear : null,
|
|
|
+ // FC选择值
|
|
|
+ brandCodes: queryParams.value.brandCode,
|
|
|
+ // エリア选择值
|
|
|
+ regionCodes: queryParams.value.regions.map(region => region.region_code),
|
|
|
+ salesFlag
|
|
|
+ };
|
|
|
+
|
|
|
+ surveySalesStore.setSalesData(transferData);
|
|
|
+
|
|
|
+ router.push({ name: 'salesSumResult' });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const resetForm = () => {
|
|
|
+ // 1. 重置核心参数
|
|
|
+ queryParams.value.releaseType = 'immediate';
|
|
|
+ queryParams.value.aggregationType = 'FC_CHECKBOX';
|
|
|
+ queryParams.value.monthYear = null;
|
|
|
+ queryParams.value.startMonth = null;
|
|
|
+ queryParams.value.startYear = null;
|
|
|
+ queryParams.value.brandCode = [];
|
|
|
+
|
|
|
+ selectedRegions.value = [];
|
|
|
+ queryParams.value.regions = [];
|
|
|
+ if (regionTree.value.length > 0) {
|
|
|
+ resetRegionTreeCheck(regionTree.value);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const resetRegionTreeCheck = (nodes) => {
|
|
|
+ nodes.forEach(node => {
|
|
|
+ node.checked = false;
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
+ resetRegionTreeCheck(node.children);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.form-container {
|
|
|
+ margin-top: 40px;
|
|
|
+}
|
|
|
+.operate-btn {
|
|
|
+ background-color: #0080c7;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 6px 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ width: 240px;
|
|
|
+ margin-left: 90px;
|
|
|
+}
|
|
|
+@media (max-width:48em) {
|
|
|
+ .operate-btn {
|
|
|
+ width: 100px;
|
|
|
+ margin-left: 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.radio-group-vertical {
|
|
|
+ margin-left: -20px;
|
|
|
+}
|
|
|
+@media (max-width:48em) {
|
|
|
+ .radio-group-vertical {
|
|
|
+ margin-left: 7%;
|
|
|
+ }
|
|
|
+}
|
|
|
+.radio-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+.vertical-radio {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.scheduled-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-left: 88px;
|
|
|
+ margin-top: -45px;
|
|
|
+}
|
|
|
+@media (max-width:48em) {
|
|
|
+ .scheduled-row {
|
|
|
+ margin-left: 13%;
|
|
|
+ width: 80%;
|
|
|
+ }
|
|
|
+}
|
|
|
+.date-group {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-left: 8px;
|
|
|
+}
|
|
|
+.date-select {
|
|
|
+ min-width: 80px;
|
|
|
+}
|
|
|
+.date-select-small {
|
|
|
+ min-width: 60px;
|
|
|
+}
|
|
|
+.date-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+.section-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.form-section {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: 700;
|
|
|
+ min-width: 80px;
|
|
|
+ margin-top: -55px;
|
|
|
+ margin-left: 60px;
|
|
|
+}
|
|
|
+@media (max-width:48em) {
|
|
|
+ .section-container {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+ .form-section {
|
|
|
+ padding-right: 0;
|
|
|
+ margin-left: 7%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.custom-radio-group {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: center;
|
|
|
+ margin-left: 2px !important;
|
|
|
+}
|
|
|
+.divider {
|
|
|
+ height: 1px;
|
|
|
+ background-color: #0080c7;
|
|
|
+ margin: 10px 27px;
|
|
|
+}
|
|
|
+.append-button{
|
|
|
+ background-color: #0080c7;
|
|
|
+ color: white;
|
|
|
+ margin-left: 100px;
|
|
|
+}
|
|
|
+</style>
|