ソースを参照

アンケート回答集計結果画面作成

quyx@nextosd.com 5 ヶ月 前
コミット
8d69d40a4f

+ 8 - 0
src/api/fcbi/survey.js

@@ -50,3 +50,11 @@ export function getRegionTree() {
     method: 'get'
   })
 }
+// アンケート回答集計結果の取得
+export function listSurveyDetails(query) {
+  return request({
+    url: '/system/survey/detailsList',
+    method: 'get',
+    params: query
+  })
+}

+ 14 - 0
src/router/index.js

@@ -139,6 +139,20 @@ export const constantRoutes = [
             }
         ]
     },
+    {
+        path: '/surveyAdmin',
+        component: Layout,
+        hidden: true,
+        redirect: 'noredirect',
+        children: [
+            {
+                path: 'surveyResults/:surveyCode',
+                component: () => import('@/views/fcbi/survey/results'),
+                name: 'surveyResults',
+                meta: {title: 'アンケート回答集計結果', icon: 'user'}
+            }
+        ]
+    },
 ]
 
 // ダイナミックルート、ユーザー権限に基づいて動的に読み込みます

+ 4 - 1
src/utils/messageInfo.js

@@ -16,5 +16,8 @@ export default {
     'E0024': 'ファイルのサイズは{0}以下を選択してください。',
     'E0036': '入力要の場合、キーワードが入力されていました。',
     'E0047': 'ファイルは変更されているので、もう一度選択してください。',
-    'E0048': '確認用のパスワードと新パスワードが一致しません。'
+    'E0048': '確認用のパスワードと新パスワードが一致しません。',
+    'Y0001': '{0}を少なくとも1つ選択してください。',
+    'Y0002': '日付範囲は完全に入力してください(年、月、日をすべて選択)。',
+    'Y0003': '開始日付は終了日付より後の日付を選択できません。'
 }

+ 56 - 30
src/views/fcbi/survey/form.vue

@@ -292,6 +292,7 @@ import RegionTree from '../../../components/RegionTree.vue';
 import { ElMessage } from "element-plus";
 import { useRouter } from 'vue-router';
 import { useSurveyStore } from '@/store/surveyStore'
+import {formatMsg} from "@/utils/yamada.js"
 
 const { proxy } = getCurrentInstance();
 const surveyStore = useSurveyStore()
@@ -701,33 +702,63 @@ const collectSelectedRegions = (nodes, isParent = false) => {
 
 // 確認按钮点击处理函数
 const Confirm = async () => {
-  if (!queryParams.value.title) {
-    ElMessage.error('タイトルを入力してください');
-    return;
+  // 错误信息集合
+  const errorMessages = [];
+
+  // 标题校验
+  if (!queryParams.value.title || queryParams.value.title.trim() === '') {
+    errorMessages.push('タイトルを入力してください');
   }
-  if (queryParams.value.releaseType === 'scheduled') {
-    if (!queryParams.value.startYear || !queryParams.value.startMonth || !queryParams.value.startDay ||
-        !queryParams.value.endYear || !queryParams.value.endMonth || !queryParams.value.endDay) {
-      ElMessage.error('期間指定の場合は全ての日付を入力してください');
-      return;
-    }
+
+  //FC复选框组校验
+  if (!queryParams.value.brandCode || queryParams.value.brandCode.length === 0) {
+    errorMessages.push(formatMsg('Y0001', "FC"));
   }
-  if (regionTree.value.length > 0) {
-    updateSelectedRegions(regionTree.value);
+
+  //業種复选框组校验
+  if (!queryParams.value.businessTypeCode || queryParams.value.businessTypeCode.length === 0) {
+    errorMessages.push(formatMsg('Y0001', "業種"));
   }
-  formatDate();
+
+  //エリア复选框组校验
+  if (!queryParams.value.regions || queryParams.value.regions.length === 0) {
+    errorMessages.push(formatMsg('Y0001', "エリア"));
+  }
+
+  // 公開期間校验(期間指定时)
   if (queryParams.value.releaseType === 'scheduled') {
-    // 确保日期格式正确(YYYY-MM-DD)
-    const startDate = new Date(queryParams.publicStartTime);
-    const endDate = new Date(queryParams.publicEndTime);
+    if (!queryParams.value.startYear || !queryParams.value.startMonth || !queryParams.value.startDay) {
+      errorMessages.push('公開開始日の全ての項目を入力してください');
+    }
+    if (!queryParams.value.endYear || !queryParams.value.endMonth || !queryParams.value.endDay) {
+      errorMessages.push('公開終了日の全ての項目を入力してください');
+    }
 
-    // 检查日期是否有效且开始时间大于结束时间
-    if (!isNaN(startDate.getTime()) && !isNaN(endDate.getTime()) && startDate > endDate) {
-      ElMessage.error('公開開始日時は公開終了日時より後に設定できません');
-      return; // 中断提交
+    // 日期有效性校验
+    if (queryParams.value.startYear && queryParams.value.startMonth && queryParams.value.startDay &&
+        queryParams.value.endYear && queryParams.value.endMonth && queryParams.value.endDay) {
+      const startDate = new Date(`${queryParams.value.startYear}-${queryParams.value.startMonth}-${queryParams.value.startDay}`);
+      const endDate = new Date(`${queryParams.value.endYear}-${queryParams.value.endMonth}-${queryParams.value.endDay}`);
+      if (startDate > endDate) {
+        errorMessages.push('公開開始日時は公開終了日時より後に設定できません');
+      }
     }
   }
 
+  // 批量显示错误信息
+  if (errorMessages.length > 0) {
+    // 清空之前的错误提示(避免重复显示)
+    document.querySelectorAll('.el-message--error').forEach(el => el.remove());
+
+    // 依次显示所有错误
+    errorMessages.forEach((msg, index) => {
+      setTimeout(() => {
+        ElMessage.error(msg);
+      }, index * 300);
+    });
+    return;
+  }
+
   let hasError = false;
   const questionsData = data.questions.map(question => {
     if (!question.questionText || question.questionText.trim() === '') {
@@ -747,10 +778,8 @@ const Confirm = async () => {
       sortOrder: question.sortOrder
     };
 
-    // CHECKBOXタイプの問題を処理するオプション
     if (question.questionType === 'CHECK_BOX') {
       questionData.checkboxItems = question.checkboxItems.map((item, itemIndex) => {
-        // 新增:校验optionText是否为空
         if (!item.optionText || item.optionText.trim() === '') {
           ElMessage.warning(`設問文${question.sortOrder}の選択肢${itemIndex + 1}を入力してください`);
           hasError = true;
@@ -762,10 +791,8 @@ const Confirm = async () => {
       });
     }
 
-    // RADIOタイプの問題を処理するオプション
     if (question.questionType === 'RADIO_BUTTON') {
       questionData.radioItems = question.radioItems.map((item, itemIndex) => {
-        // 新增:校验optionText是否为空
         if (!item.optionText || item.optionText.trim() === '') {
           ElMessage.warning(`設問文${question.sortOrder}の選択肢${itemIndex + 1}を入力してください`);
           hasError = true;
@@ -777,9 +804,7 @@ const Confirm = async () => {
       });
     }
 
-    // SELECTタイプの問題:動的生成オプション
     if (question.questionType === 'NUMBER_SELECT') {
-      // 先判断start或end是否为空(包括空字符串、纯空格)
       const isStartEmpty = !question.numberSelectStart || question.numberSelectStart.trim() === '';
       const isEndEmpty = !question.numberSelectEnd || question.numberSelectEnd.trim() === '';
 
@@ -797,22 +822,20 @@ const Confirm = async () => {
         return null;
       }
 
-
       const start = parseInt(question.numberSelectStart);
       const end = parseInt(question.numberSelectEnd);
 
       if (!isNaN(start) && !isNaN(end) && start <= end) {
-        // startからendへのオプションの動的生成
         questionData.selectOptions = Array.from({ length: end - start + 1 }, (_, i) => ({
           optionText: (start + i).toString(),
           sortOrder: i + 1
         }));
       } else {
         ElMessage.warning(`SELECTタイプ問題の範囲が無効です:${start}~${end}`);
-        hasError = true; // 标记有错误
-        return null; // 临时返回null
+        hasError = true;
+        return null;
       }
-      // 新增:単位「あり」时的输入框空值校验
+
       if (question.unitHas && (!question.questionUnit || question.questionUnit.trim() === '')) {
         ElMessage.warning(`設問文${question.sortOrder}の単位を入力してください`);
         hasError = true;
@@ -822,9 +845,11 @@ const Confirm = async () => {
 
     return questionData;
   });
+
   if (hasError) {
     return;
   }
+
   const validQuestionsData = questionsData.filter(q => q !== null);
   const params = {
     title: queryParams.value.title,
@@ -839,6 +864,7 @@ const Confirm = async () => {
     questions: validQuestionsData,
     regions: queryParams.value.regions
   };
+
   addSurvey(params).then((res) => {
     if (res.success) {
       surveyStore.updateFormData({

+ 186 - 49
src/views/fcbi/survey/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="app-container">
     <el-form ref="queryRef" :inline="true" :model="queryParams">
-      <el-form-item label="アンケート番号" prop="surveyCode">
+      <el-form-item label="アンケートコード" prop="surveyCode">
         <el-input v-model="queryParams.surveyCode"
                   clearable maxlength="13" @keyup.enter="handleQuery"/>
       </el-form-item>
@@ -14,31 +14,88 @@
         <el-button  v-hasPermi="['fcbi:survey:form']" class="append-button">アンケート作成</el-button>
       </el-form-item>
       <el-row v-show="initLoadingCompleted" :gutter="10" class="mb8">
-        <el-col :span="4">
+        <el-col :span="11">
           <el-form-item label="回答日付">
             <div class="date-range-container">
-              <!-- 开始日 -->
-              <el-date-picker
-                  v-model="queryParams.startDate"
-                  type="date"
-                  placeholder="開始日"
-                  format="YYYY-MM-DD"
-                  value-format="YYYY-MM-DD"
-                  style="width: 261px;"
-              />
-              <span class="range-separator">~</span>
-              <el-date-picker
-                  v-model="queryParams.endDate"
-                  type="date"
-                  placeholder="終了日"
-                  format="YYYY-MM-DD"
-                  value-format="YYYY-MM-DD"
-                  style="width: 261px;"
-              />
+              <el-select v-model="queryParams.startYear" placeholder="" class="date-select"  @change="formatDate">
+                <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"
+                  @change="formatDate"
+              >
+                <el-option
+                    v-for="month in months"
+                    :key="month"
+                    :label="month"
+                    :value="month"
+                ></el-option>
+              </el-select>
+              <span class="date-label">月</span>
+              <el-select
+                  v-model="queryParams.startDay"
+                  placeholder=""
+                  class="date-select-small"
+                  @change="formatDate"
+              >
+                <el-option
+                    v-for="day in days"
+                    :key="day"
+                    :label="day"
+                    :value="day"
+                ></el-option>
+              </el-select>
+              <span class="date-label">日</span>
+              <span class="date-label">~</span>
+              <el-select
+                  v-model="queryParams.endYear"
+                  placeholder=""
+                  class="date-select"
+                  @change="formatDate"
+              >
+                <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.endMonth"
+                  placeholder=""
+                  class="date-select-small"
+                  @change="formatDate"
+              >
+                <el-option
+                    v-for="month in months"
+                    :key="month"
+                    :label="month"
+                    :value="month"
+                ></el-option>
+              </el-select>
+              <span class="date-label">月</span>
+              <el-select
+                  v-model="queryParams.endDay"
+                  placeholder=""
+                  class="date-select-small"
+                  @change="formatDate"
+              >
+                <el-option
+                    v-for="day in days"
+                    :key="day"
+                    :label="day"
+                    :value="day"
+                ></el-option>
+              </el-select>
+              <span class="date-label">日</span>
             </div>
           </el-form-item>
         </el-col>
-        <el-form-item  class="range-button" >
+        <el-form-item>
           <el-button icon="Search" type="primary" @click="handleQuery">検索</el-button>
           <el-button icon="Refresh" @click="resetQuery">クリア</el-button>
         </el-form-item>
@@ -50,14 +107,18 @@
           :show-overflow-tooltip="true"
           align="center"
           header-align="center"
-          label="アンケート番号"
+          label="アンケートコード"
           min-width="50"
       >
         <template #default="scope">
-          <el-link   :underline="true"
-          >
+          <template v-if="scope.row.answerFlag === 1">
+            <el-link class="custom-btn" @click="goToSurveyDetail(scope.row)">
+              {{ scope.row.surveyCode }}
+            </el-link>
+          </template>
+          <template v-else>
             {{ scope.row.surveyCode }}
-          </el-link>
+          </template>
         </template>
       </el-table-column>
       <el-table-column :show-overflow-tooltip="true" align="left" header-align="center" label="アンケートタイトル" min-width="60"
@@ -92,7 +153,7 @@
                 v-model:limit="queryParams.pageSize"
                 v-model:page="queryParams.pageNum"
                 :total="listTotalCnt"
-                @pagination="getList"/>
+                @pagination="getSearchList"/>
   </div>
 </template>
 
@@ -100,14 +161,16 @@
 
 import { listSurvey} from "@/api/fcbi/survey.js"
 import { useRouter } from 'vue-router';
+import {formatMsg} from "@/utils/yamada.js"
+import {ElMessage} from "element-plus";
 
-// 認証項目のテーブルデータを格納するリアクティブ変数
+// 認証項目のテーブルデータを格納するリアクティブ
 const certificationItemList = ref([]);
-// データ読み込み中のローディング状態を管理するリアクティブ変数
+// データ読み込み中のローディング状態を管理するリアクティブ
 const listLoading = ref(false);
-// 検索結果の総件数を格納するリアクティブ変数
+// 検索結果の総件数を格納するリアクティブ
 const listTotalCnt = ref(0);
-// 初期ロードが完了したかどうかを示すリアクティブ変数
+// 初期ロードが完了したかどうかを示すリアクティブ
 const initLoadingCompleted = ref(false);
 const {proxy} = getCurrentInstance();
 const router = useRouter();
@@ -120,9 +183,15 @@ const data = reactive({
   // 検索条件を格納するオブジェクト
   queryParams: {
     pageNum: 1,
-    pageSize: 100,
-    startDate: null,
-    endDate: null,
+    pageSize: 10,
+    startYear: '',
+    startMonth: '',
+    startDay: '',
+    endYear: '',
+    endMonth: '',
+    endDay: '',
+    publicStartTime: '',
+    publicEndTime: '',
     surveyCode: null,
     title: null,
   },
@@ -131,16 +200,77 @@ const data = reactive({
 // リアクティブオブジェクトからプロパティを参照可能なオブジェクトに変換
 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);
+const days = Array.from({ length: 31 }, (_, i) => i + 1);
+
+const formatDate = () => {
+  const publicStartTime = `${queryParams.value.startYear}-${String(queryParams.value.startMonth).padStart(2, '0')}-${String(queryParams.value.startDay).padStart(2, '0')}`;
+  const publicEndTime = `${queryParams.value.endYear}-${String(queryParams.value.endMonth).padStart(2, '0')}-${String(queryParams.value.endDay).padStart(2, '0')}`;
+  queryParams.publicStartTime = publicStartTime;
+  queryParams.publicEndTime = publicEndTime;
+};
+
+/**
+ * 日付範囲の完全性と有効性を検証します
+ * - 6つの値のうちいずれか1つに値が設定されている場合は、すべての値を入力する必要があります
+ * - すべての値が入力された場合、開始日は終了日よりも前でなければなりません
+ */
+const validateDateRange = () => {
+  const { startYear, startMonth, startDay, endYear, endMonth, endDay } = queryParams.value;
+
+  // すべての日付フィールドの値を収集します
+  const dateFields = [startYear, startMonth, startDay, endYear, endMonth, endDay];
+  // いずれかのフィールドに値が設定されているかどうかを判断します
+  const hasAnyValue = dateFields.some(value => value !== '');
+
+  // 値が設定されているフィールドが存在する場合、すべてのフィールドに値が設定されているかどうかを確認します
+  if (hasAnyValue) {
+    const isComplete = dateFields.every(value => value !== '');
+    if (!isComplete) {
+      ElMessage.error(formatMsg('Y0002'));
+      return false;
+    }
+
+    // すべてのフィールドに値が設定されている場合、開始日が終了日以前であることを検証します
+    const startDate = new Date(startYear, startMonth - 1, startDay);
+    const endDate = new Date(endYear, endMonth - 1, endDay);
+
+    if (startDate > endDate) {
+      ElMessage.error(formatMsg('Y0003'));
+      return false;
+    }
+  }
+
+  return true;
+};
+
 /**
  * 検索ボタンのクリックイベントハンドラー
  * 検索条件を初期ページに設定して検索を実行する
  */
 function handleQuery() {
+  if (!validateDateRange()) return;
   queryParams.value.pageNum = 1;
   getSearchList();
 }
 
-// 格式化公開期間列的显示内容
+const goToSurveyDetail = (row) => {
+  router.push({
+    name: 'surveyResults',
+    params: {
+      surveyCode: row.surveyCode
+    }
+  });
+};
+
+/**
+ * 公開期間をフォーマットする関数
+ * 公開モードに応じて表示形式を変換する
+ * - INSTANTモードの場合は「即時公開」を返す
+ * - PERIODモードの場合は公開開始日と終了日を「YYYYMMDD~YYYYMMDD」形式で返す
+ */
 function formatPublicPeriod(row) {
   const { publicMode, publicStartTime, publicEndTime } = row;
 
@@ -174,8 +304,8 @@ function getSearchList() {
     pageSize: queryParams.value.pageSize,
     surveyCode: queryParams.value.surveyCode,
     title: queryParams.value.title,
-    publicStartTime: queryParams.value.startDate,
-    publicEndTime: queryParams.value.endDate,
+    publicStartTime: queryParams.publicStartTime,
+    publicEndTime: queryParams.publicEndTime,
   };
   listSurvey(params).then(response => {
     certificationItemList.value = response.rows;
@@ -189,8 +319,14 @@ function getSearchList() {
  */
 function resetQuery() {
   proxy.resetForm("queryRef");
-  queryParams.value.startDate = null;
-  queryParams.value.endDate = null;
+  queryParams.value.startYear = '';
+  queryParams.value.startMonth = '';
+  queryParams.value.startDay = '';
+  queryParams.value.endYear = '';
+  queryParams.value.endMonth = '';
+  queryParams.value.endDay = '';
+  queryParams.publicStartTime = '';
+  queryParams.publicEndTime = '';
   handleQuery();
 }
 
@@ -209,10 +345,6 @@ getSearchList();
   color: white;
 }
 
-.date-picker{
-  margin-left: 12%;
-}
-
 .circle-checkbox .el-checkbox__inner {
   border-radius: 50%;
 }
@@ -220,14 +352,19 @@ getSearchList();
 .date-range-container {
   display: flex;
   align-items: center;
-  margin-left: 8%;
+  margin-left: 9%;
+  gap: 8px;
 }
-
-.range-separator {
-  margin: 0 8px;
-  font-weight: bold;
+.custom-btn {
+  color: #0066cc;
+  text-decoration: underline;
+}
+/* 年份选择器宽度 */
+.date-select {
+  min-width: 80px;
 }
-.range-button{
-  margin-left: 25%;
+/* 月/日选择器宽度 */
+.date-select-small {
+  min-width: 60px;
 }
 </style>

+ 351 - 0
src/views/fcbi/survey/results.vue

@@ -0,0 +1,351 @@
+<template>
+  <div class="app-container">
+    <el-form ref="queryRef" :inline="true" :model="queryParams">
+      <el-form-item label="アンケートコード" prop="surveyCode">
+        <el-input v-model="queryParams.surveyCode"
+                  clearable maxlength="13" @keyup.enter="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="FC会員名" prop="fcMemberName">
+        <el-input v-model="queryParams.fcMemberName"
+                  clearable
+                  maxlength="255" @keyup.enter="handleQuery"/>
+      </el-form-item>
+      <el-form-item>
+        <el-button class="append-button" @click="handleExport">CSV出力</el-button>
+      </el-form-item>
+      <el-row v-show="initLoadingCompleted" :gutter="10" class="mb8">
+        <el-col :span="11">
+          <el-form-item label="回答日付">
+            <div class="date-range-container">
+                  <el-select v-model="queryParams.startYear" placeholder="" class="date-select"  @change="formatDate">
+                    <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"
+                      @change="formatDate"
+                  >
+                    <el-option
+                        v-for="month in months"
+                        :key="month"
+                        :label="month"
+                        :value="month"
+                    ></el-option>
+                  </el-select>
+                  <span class="date-label">月</span>
+                  <el-select
+                      v-model="queryParams.startDay"
+                      placeholder=""
+                      class="date-select-small"
+                      @change="formatDate"
+                  >
+                    <el-option
+                        v-for="day in days"
+                        :key="day"
+                        :label="day"
+                        :value="day"
+                    ></el-option>
+                  </el-select>
+                  <span class="date-label">日</span>
+                  <span class="date-label">~</span>
+                  <el-select
+                      v-model="queryParams.endYear"
+                      placeholder=""
+                      class="date-select"
+                      @change="formatDate"
+                  >
+                    <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.endMonth"
+                      placeholder=""
+                      class="date-select-small"
+                      @change="formatDate"
+                  >
+                    <el-option
+                        v-for="month in months"
+                        :key="month"
+                        :label="month"
+                        :value="month"
+                    ></el-option>
+                  </el-select>
+                  <span class="date-label">月</span>
+                  <el-select
+                      v-model="queryParams.endDay"
+                      placeholder=""
+                      class="date-select-small"
+                      @change="formatDate"
+                  >
+                    <el-option
+                        v-for="day in days"
+                        :key="day"
+                        :label="day"
+                        :value="day"
+                    ></el-option>
+                  </el-select>
+                  <span class="date-label">日</span>
+            </div>
+          </el-form-item>
+        </el-col>
+        <el-form-item>
+          <el-button icon="Search" type="primary" @click="handleQuery">検索</el-button>
+          <el-button icon="Refresh" @click="resetQuery">クリア</el-button>
+        </el-form-item>
+      </el-row>
+    </el-form>
+
+    <el-table v-show="initLoadingCompleted" v-loading="listLoading" :border="true" :data="certificationItemList">
+      <el-table-column
+          :show-overflow-tooltip="true"
+          align="center"
+          header-align="center"
+          label="アンケートコード"
+          min-width="50"
+          prop="surveyCode"
+      >
+      </el-table-column>
+      <el-table-column :show-overflow-tooltip="true" align="left" header-align="center" label="アンケートタイトル" min-width="60"
+                       prop="title"
+      >
+      </el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="left" header-align="center" label="FC会員名" min-width="70"
+                         prop="fcMemberName"
+                         width=""/>
+        <el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="回答状態" min-width="80"
+                         prop="answerStatus"
+                         width=""/>
+        <el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="回答日付" min-width="100"
+                         prop="answerTime"
+                         width=""/>
+        <el-table-column :show-overflow-tooltip="true" align="right" header-align="center" label="質問総数" min-width="50"
+                         prop="totalQuestions"
+                         width=""/>
+        <el-table-column :show-overflow-tooltip="true" align="right" header-align="center" label="回答数" min-width="30"
+                         prop="answeredQuestions"
+                         width=""/>
+      <el-table-column :show-overflow-tooltip="true" align="right" header-align="center" label="未回答数" min-width="30"
+                       prop="unansweredQuestions"
+                       width=""/>
+      <el-table-column
+          :show-overflow-tooltip="true"
+          align="center"
+          header-align="center"
+          label="回答完成率"
+          min-width="50"
+      >
+        <template #default="scope">
+          {{ scope.row.completionRate }}%
+        </template>
+      </el-table-column>
+      <el-table-column align="center" class-name="small-padding fixed-width" label="操作" min-width="180" width="180">
+        <template #default="scope">
+          <el-button icon="Edit" link type="primary" >回答詳細</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="listTotalCnt>0"
+                v-model:limit="queryParams.pageSize"
+                v-model:page="queryParams.pageNum"
+                :total="listTotalCnt"
+                @pagination="getSearchList"/>
+  </div>
+</template>
+
+<script name="SurveyResultsList" setup>
+
+import { listSurveyDetails} from "@/api/fcbi/survey.js"
+import { useRoute } from 'vue-router'
+import {download} from "@/utils/request.js";
+import {formatMsg} from "@/utils/yamada.js"
+import {ElMessage} from "element-plus";
+// 認証項目のテーブルデータを格納するリアクティブ変数
+const certificationItemList = ref([]);
+// データ読み込み中のローディング状態を管理するリアクティブ変数
+const listLoading = ref(false);
+// 検索結果の総件数を格納するリアクティブ変数
+const listTotalCnt = ref(0);
+// 初期ロードが完了したかどうかを示すリアクティブ変数
+const initLoadingCompleted = ref(false);
+const {proxy} = getCurrentInstance();
+const route= useRoute()
+const surveyCode = route.params.surveyCode
+
+/**
+ * コンポーネントの状態を管理するリアクティブオブジェクト
+ */
+const data = reactive({
+  form: {},
+  // 検索条件を格納するオブジェクト
+  queryParams: {
+    pageNum: 1,
+    pageSize: 100,
+    startYear: '',
+    startMonth: '',
+    startDay: '',
+    endYear: '',
+    endMonth: '',
+    endDay: '',
+    publicStartTime: '',
+    publicEndTime: '',
+    surveyCode: null,
+    fcMemberName: null,
+  },
+});
+
+// リアクティブオブジェクトからプロパティを参照可能なオブジェクトに変換
+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);
+const days = Array.from({ length: 31 }, (_, i) => i + 1);
+
+const formatDate = () => {
+    const publicStartTime = `${queryParams.value.startYear}-${String(queryParams.value.startMonth).padStart(2, '0')}-${String(queryParams.value.startDay).padStart(2, '0')}`;
+    const publicEndTime = `${queryParams.value.endYear}-${String(queryParams.value.endMonth).padStart(2, '0')}-${String(queryParams.value.endDay).padStart(2, '0')}`;
+    queryParams.publicStartTime = publicStartTime;
+    queryParams.publicEndTime = publicEndTime;
+};
+
+/**
+ * 日付範囲の完全性と有効性を検証します
+ * - 6つの値のうちいずれか1つに値が設定されている場合は、すべての値を入力する必要があります
+ * - すべての値が入力された場合、開始日は終了日よりも前でなければなりません
+ */
+const validateDateRange = () => {
+  const { startYear, startMonth, startDay, endYear, endMonth, endDay } = queryParams.value;
+
+  // すべての日付フィールドの値を収集します
+  const dateFields = [startYear, startMonth, startDay, endYear, endMonth, endDay];
+  // いずれかのフィールドに値が設定されているかどうかを判断します
+  const hasAnyValue = dateFields.some(value => value !== '');
+
+  // 値が設定されているフィールドが存在する場合、すべてのフィールドに値が設定されているかどうかを確認します
+  if (hasAnyValue) {
+    const isComplete = dateFields.every(value => value !== '');
+    if (!isComplete) {
+      ElMessage.error(formatMsg('Y0002'));
+      return false;
+    }
+
+    // すべてのフィールドに値が設定されている場合、開始日が終了日以前であることを検証します
+    const startDate = new Date(startYear, startMonth - 1, startDay);
+    const endDate = new Date(endYear, endMonth - 1, endDay);
+
+    if (startDate > endDate) {
+      ElMessage.error(formatMsg('Y0003'));
+      return false;
+    }
+  }
+
+  return true;
+};
+
+/**
+ * 検索ボタンのクリックイベントハンドラー
+ * 検索条件を初期ページに設定して検索を実行する
+ */
+function handleQuery() {
+  if (!validateDateRange()) return;
+  queryParams.value.pageNum = 1;
+  getSearchList();
+}
+
+function getSearchList() {
+  listLoading.value = true;
+  let params = {
+    pageNum: queryParams.value.pageNum,
+    pageSize: queryParams.value.pageSize,
+    surveyCode: queryParams.value.surveyCode,
+    fcMemberName: queryParams.value.fcMemberName,
+    publicStartTime: queryParams.publicStartTime,
+    publicEndTime: queryParams.publicEndTime,
+  };
+  listSurveyDetails(params).then(response => {
+    certificationItemList.value = response.rows;
+    listTotalCnt.value = response.total;
+    listLoading.value = false;
+    initLoadingCompleted.value = true;
+  });
+}
+
+function handleExport() {
+  // 检查アンケートコード是否为空
+  if (!queryParams.value.surveyCode) {
+    ElMessage.error(formatMsg('E0003', "アンケートコード"));
+    return;
+  }
+  const exportParams = {
+    pageNum: queryParams.value.pageNum,
+    pageSize: queryParams.value.pageSize,
+    surveyCode: queryParams.value.surveyCode,
+    fcMemberName: queryParams.value.fcMemberName,
+    publicStartTime: queryParams.publicStartTime,
+    publicEndTime: queryParams.publicEndTime,
+  };
+
+  download('system/survey/export', exportParams, `ymdf_survey${new Date().getTime()}.csv`);
+}
+/**
+ * リセット ボタンアクション
+ */
+function resetQuery() {
+  proxy.resetForm("queryRef");
+  queryParams.value.startYear = '';
+  queryParams.value.startMonth = '';
+  queryParams.value.startDay = '';
+  queryParams.value.endYear = '';
+  queryParams.value.endMonth = '';
+  queryParams.value.endDay = '';
+  queryParams.publicStartTime = '';
+  queryParams.publicEndTime = '';
+  handleQuery();
+}
+// コンポーネント初期化時に検索を実行
+onMounted(() => {
+  if (surveyCode) {
+    queryParams.value.surveyCode = surveyCode;
+  }
+  getSearchList();
+});
+</script>
+
+<style>
+.append-button{
+  background-color: black;
+  color: white;
+}
+
+.circle-checkbox .el-checkbox__inner {
+  border-radius: 50%;
+}
+
+.date-range-container {
+  display: flex;
+  align-items: center;
+  margin-left: 9%;
+  gap: 8px;
+}
+/* 年份选择器宽度 */
+.date-select {
+  min-width: 80px;
+}
+/* 月/日选择器宽度 */
+.date-select-small {
+  min-width: 60px;
+}
+/* 日期单位文本样式(年/月/日) */
+.date-label {
+  font-size: 14px;
+  color: #666;
+}
+</style>