Browse Source

feat(问卷管理): 新增调查问卷管理功能模块

yz 2 months ago
parent
commit
e4a604ad80

+ 131 - 0
src/api/survey/survey.js

@@ -0,0 +1,131 @@
+import request from '@/router/axios';
+
+/**
+ * 调查问卷查询参数类型定义
+ * @typedef {Object} SurveyQueryParams
+ * @property {string} [surveyCode] - 问卷编码
+ * @property {string} [title] - 问卷标题
+ * @property {string} [description] - 问卷描述
+ * @property {number} [status] - 状态 0-草稿 1-已发布 2-已结束
+ * @property {number} [isTemplate] - 是否模板 0-否 1-是
+ * @property {string} [startTime] - 开始时间
+ * @property {string} [endTime] - 结束时间
+ * @property {string} [createTimeStart] - 创建时间开始
+ * @property {string} [createTimeEnd] - 创建时间结束
+ */
+
+/**
+ * 调查问卷表单数据类型定义
+ * @typedef {Object} SurveyForm
+ * @property {string|number} [id] - 问卷ID(修改时必填)
+ * @property {string} surveyCode - 问卷编码
+ * @property {string} title - 问卷标题
+ * @property {string} description - 问卷描述
+ * @property {string} startTime - 开始时间
+ * @property {string} endTime - 结束时间
+ * @property {number} status - 状态 0-草稿 1-已发布 2-已结束
+ * @property {number} isTemplate - 是否模板 0-否 1-是
+ */
+
+/**
+ * 调查问卷列表项类型定义
+ * @typedef {Object} SurveyItem
+ * @property {string} id - 问卷ID
+ * @property {string} createUser - 创建用户ID
+ * @property {string} createDept - 创建部门ID
+ * @property {string} createTime - 创建时间
+ * @property {string} updateUser - 更新用户ID
+ * @property {string} updateTime - 更新时间
+ * @property {number} status - 状态 0-草稿 1-已发布 2-已结束
+ * @property {number} isDeleted - 是否删除 0-未删除 1-已删除
+ * @property {string} surveyCode - 问卷编码
+ * @property {string} title - 问卷标题
+ * @property {string} description - 问卷描述
+ * @property {string} startTime - 开始时间
+ * @property {string} endTime - 结束时间
+ * @property {number} isTemplate - 是否模板 0-否 1-是
+ */
+
+/**
+ * API响应类型定义
+ * @template T
+ * @typedef {Object} ApiResponse
+ * @property {number} code - 响应码
+ * @property {boolean} success - 是否成功
+ * @property {T} data - 响应数据
+ * @property {string} msg - 响应消息
+ */
+
+/**
+ * 分页结果类型定义
+ * @template T
+ * @typedef {Object} PageResult
+ * @property {T[]} records - 数据列表
+ * @property {number} total - 总记录数
+ * @property {number} size - 每页大小
+ * @property {number} current - 当前页码
+ * @property {number} pages - 总页数
+ * @property {Array} orders - 排序信息
+ * @property {boolean} optimizeCountSql - 是否优化count查询
+ * @property {boolean} hitCount - 是否命中count缓存
+ * @property {string|null} countId - count查询ID
+ * @property {number|null} maxLimit - 最大限制
+ * @property {boolean} searchCount - 是否查询count
+ */
+
+/**
+ * 调查问卷分页查询
+ * @param {number} current - 当前页码
+ * @param {number} size - 每页大小
+ * @param {SurveyQueryParams} [params] - 查询参数
+ * @returns {Promise<ApiResponse<PageResult<SurveyItem>>>} 分页查询结果
+ */
+export const getList = (current, size, params) => {
+  return request({
+    url: '/api/blade-factory/api/factory/survey',
+    method: 'get',
+    params: {
+      ...params,
+      current,
+      size
+    }
+  })
+}
+
+/**
+ * 新增调查问卷
+ * @param {SurveyForm} row - 问卷表单数据
+ * @returns {Promise<ApiResponse<boolean>>} 新增结果
+ */
+export const add = (row) => {
+  return request({
+    url: '/api/blade-factory/api/factory/survey',
+    method: 'post',
+    data: row
+  })
+}
+
+/**
+ * 修改调查问卷
+ * @param {SurveyForm} row - 问卷表单数据
+ * @returns {Promise<ApiResponse<boolean>>} 修改结果
+ */
+export const update = (row) => {
+  return request({
+    url: '/api/blade-factory/api/factory/survey',
+    method: 'put',
+    data: row
+  })
+}
+
+/**
+ * 获取调查问卷详情
+ * @param {string|number} surveyId - 问卷ID
+ * @returns {Promise<ApiResponse<SurveyItem>>} 问卷详情
+ */
+export const getDetail = (surveyId) => {
+  return request({
+    url: `/api/blade-factory/api/factory/survey/${surveyId}`,
+    method: 'get'
+  })
+}

+ 4 - 1
src/constants/index.js

@@ -18,4 +18,7 @@ export * from './image-store-apply'
 export * from './lead'
 
 // 营销活动管理相关常量
-export * from './marketing-activity'
+export * from './marketing-activity'
+
+// 调查问卷管理相关常量
+export * from './survey'

+ 180 - 0
src/constants/survey.js

@@ -0,0 +1,180 @@
+/**
+ * 调查问卷管理相关常量定义
+ * @fileoverview 问卷状态、模板类型等枚举值和工具函数
+ */
+
+/**
+ * 问卷状态枚举
+ * @readonly
+ * @enum {number}
+ */
+export const SURVEY_STATUS = {
+  /** 草稿 */
+  DRAFT: 0,
+  /** 已发布 */
+  PUBLISHED: 1,
+  /** 已结束 */
+  ENDED: 2
+}
+
+/**
+ * 是否模板枚举
+ * @readonly
+ * @enum {number}
+ */
+export const SURVEY_TEMPLATE = {
+  /** 否 */
+  NO: 0,
+  /** 是(模板) */
+  YES: 1
+}
+
+/**
+ * 问卷状态配置映射
+ * @readonly
+ * @type {Record<number, {label: string, type: string, color: string}>}
+ */
+export const SURVEY_STATUS_CONFIG = {
+  [SURVEY_STATUS.DRAFT]: {
+    label: '草稿',
+    type: 'info',
+    color: '#909399'
+  },
+  [SURVEY_STATUS.PUBLISHED]: {
+    label: '已发布',
+    type: 'success',
+    color: '#67C23A'
+  },
+  [SURVEY_STATUS.ENDED]: {
+    label: '已结束',
+    type: 'danger',
+    color: '#F56C6C'
+  }
+}
+
+/**
+ * 是否模板配置映射
+ * @readonly
+ * @type {Record<number, {label: string, type: string, color: string}>}
+ */
+export const SURVEY_TEMPLATE_CONFIG = {
+  [SURVEY_TEMPLATE.NO]: {
+    label: '否',
+    type: 'info',
+    color: '#909399'
+  },
+  [SURVEY_TEMPLATE.YES]: {
+    label: '是(模板)',
+    type: 'warning',
+    color: '#E6A23C'
+  }
+}
+
+/**
+ * 问卷状态选项数组
+ * @readonly
+ * @type {Array<{label: string, value: number}>}
+ */
+export const SURVEY_STATUS_OPTIONS = [
+  { label: '草稿', value: SURVEY_STATUS.DRAFT },
+  { label: '已发布', value: SURVEY_STATUS.PUBLISHED },
+  { label: '已结束', value: SURVEY_STATUS.ENDED }
+]
+
+/**
+ * 是否模板选项数组
+ * @readonly
+ * @type {Array<{label: string, value: number}>}
+ */
+export const SURVEY_TEMPLATE_OPTIONS = [
+  { label: '否', value: SURVEY_TEMPLATE.NO },
+  { label: '是(模板)', value: SURVEY_TEMPLATE.YES }
+]
+
+/**
+ * 获取问卷状态标签
+ * @param {number} status - 状态值
+ * @returns {string} 状态标签
+ */
+export function getSurveyStatusLabel(status) {
+  return SURVEY_STATUS_CONFIG[status]?.label || '未知状态'
+}
+
+/**
+ * 获取问卷状态类型
+ * @param {number} status - 状态值
+ * @returns {string} 状态类型
+ */
+export function getSurveyStatusType(status) {
+  return SURVEY_STATUS_CONFIG[status]?.type || 'info'
+}
+
+/**
+ * 获取问卷状态颜色
+ * @param {number} status - 状态值
+ * @returns {string} 状态颜色
+ */
+export function getSurveyStatusColor(status) {
+  return SURVEY_STATUS_CONFIG[status]?.color || '#909399'
+}
+
+/**
+ * 获取是否模板标签
+ * @param {number} isTemplate - 模板值
+ * @returns {string} 模板标签
+ */
+export function getSurveyTemplateLabel(isTemplate) {
+  return SURVEY_TEMPLATE_CONFIG[isTemplate]?.label || '未知类型'
+}
+
+/**
+ * 获取是否模板类型
+ * @param {number} isTemplate - 模板值
+ * @returns {string} 模板类型
+ */
+export function getSurveyTemplateType(isTemplate) {
+  return SURVEY_TEMPLATE_CONFIG[isTemplate]?.type || 'info'
+}
+
+/**
+ * 获取是否模板颜色
+ * @param {number} isTemplate - 模板值
+ * @returns {string} 模板颜色
+ */
+export function getSurveyTemplateColor(isTemplate) {
+  return SURVEY_TEMPLATE_CONFIG[isTemplate]?.color || '#909399'
+}
+
+/**
+ * 验证问卷状态是否有效
+ * @param {number} status - 状态值
+ * @returns {boolean} 是否有效
+ */
+export function isValidSurveyStatus(status) {
+  return Object.values(SURVEY_STATUS).includes(status)
+}
+
+/**
+ * 验证是否模板值是否有效
+ * @param {number} isTemplate - 模板值
+ * @returns {boolean} 是否有效
+ */
+export function isValidSurveyTemplate(isTemplate) {
+  return Object.values(SURVEY_TEMPLATE).includes(isTemplate)
+}
+
+/**
+ * 获取所有问卷状态值
+ * @returns {Array<number>} 状态值数组
+ */
+export function getAllSurveyStatusValues() {
+  return Object.values(SURVEY_STATUS)
+}
+
+/**
+ * 获取所有是否模板值
+ * @returns {Array<number>} 模板值数组
+ */
+export function getAllSurveyTemplateValues() {
+  return Object.values(SURVEY_TEMPLATE)
+}

+ 405 - 0
src/mixins/survey/surveyIndex.js

@@ -0,0 +1,405 @@
+/**
+ * 调查问卷管理页面 Mixin
+ * @fileoverview 提供调查问卷列表、新增、编辑、查看等功能的混入
+ */
+
+import { 
+  getList, 
+  add, 
+  update, 
+  getDetail 
+} from '@/api/survey/survey'
+import { 
+  SURVEY_STATUS_OPTIONS, 
+  SURVEY_TEMPLATE_OPTIONS,
+  getSurveyStatusLabel,
+  getSurveyStatusType,
+  getSurveyTemplateLabel,
+  getSurveyTemplateType
+} from '@/constants/survey'
+
+/**
+ * 调查问卷管理mixin
+ * 提供问卷列表查询、新增、修改、查看等功能
+ */
+export default {
+  data() {
+    return {
+      // 表格数据
+      tableData: [],
+      loading: false,
+      
+      // 分页信息
+      pagination: {
+        currentPage: 1,
+        pageSize: 20,
+        total: 0
+      },
+      
+      // 搜索表单
+      searchForm: {
+        surveyCode: '',
+        title: '',
+        status: null,
+        isTemplate: null,
+        startTime: '',
+        endTime: ''
+      },
+      
+      // 问卷表单
+      surveyForm: {
+        id: null,
+        surveyCode: '',
+        title: '',
+        description: '',
+        startTime: '',
+        endTime: '',
+        status: 0,
+        isTemplate: 0
+      },
+      
+      // 对话框控制
+      dialogVisible: false,
+      dialogMode: 'add', // add, edit, view
+      submitLoading: false,
+      
+      // 状态选项
+      statusOptions: [
+        { label: '草稿', value: 0 },
+        { label: '已发布', value: 1 },
+        { label: '已结束', value: 2 }
+      ],
+      
+      // 模板选项
+      templateOptions: [
+        { label: '否', value: 0 },
+        { label: '是', value: 1 }
+      ],
+      
+      // 表单验证规则
+      formRules: {
+        surveyCode: [
+          { required: true, message: '请输入问卷编码', trigger: 'blur' },
+          { min: 3, max: 50, message: '长度在 3 到 50 个字符', trigger: 'blur' }
+        ],
+        title: [
+          { required: true, message: '请输入问卷标题', trigger: 'blur' },
+          { min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
+        ],
+        description: [
+          { required: true, message: '请输入问卷描述', trigger: 'blur' },
+          { min: 5, max: 500, message: '长度在 5 到 500 个字符', trigger: 'blur' }
+        ],
+        startTime: [
+          { required: true, message: '请选择开始时间', trigger: 'change' }
+        ],
+        endTime: [
+          { required: true, message: '请选择结束时间', trigger: 'change' }
+        ],
+        status: [
+          { required: true, message: '请选择状态', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  
+  computed: {
+    /**
+     * 对话框标题
+     * @returns {string} 标题文本
+     */
+    dialogTitle() {
+      const titleMap = {
+        add: '新增调查问卷',
+        edit: '编辑调查问卷',
+        view: '查看调查问卷'
+      }
+      return titleMap[this.dialogMode] || '调查问卷'
+    },
+    
+    /**
+     * 是否为新增模式
+     * @returns {boolean} 是否新增模式
+     */
+    isAddMode() {
+      return this.dialogMode === 'add'
+    },
+    
+    /**
+     * 是否为编辑模式
+     * @returns {boolean} 是否编辑模式
+     */
+    isEditMode() {
+      return this.dialogMode === 'edit'
+    },
+    
+    /**
+     * 是否为查看模式
+     * @returns {boolean} 是否查看模式
+     */
+    isViewMode() {
+      return this.dialogMode === 'view'
+    }
+  },
+  
+  mounted() {
+    this.loadTableData()
+  },
+  
+  methods: {
+    /**
+     * 加载表格数据
+     * @returns {Promise<void>}
+     */
+    async loadTableData() {
+      try {
+        this.loading = true
+        const response = await getList(
+          this.pagination.currentPage,
+          this.pagination.pageSize,
+          this.searchForm
+        )
+        
+        if (response.data && response.data.success) {
+          const { records, total } = response.data.data
+          this.tableData = records || []
+          this.pagination.total = total || 0
+        } else {
+          this.$message.error('获取数据失败')
+        }
+      } catch (error) {
+        console.error('加载表格数据失败:', error)
+        this.$message.error('加载数据失败,请稍后重试')
+      } finally {
+        this.loading = false
+      }
+    },
+    
+    /**
+     * 搜索处理
+     * @returns {void}
+     */
+    handleSearch() {
+      this.pagination.currentPage = 1
+      this.loadTableData()
+    },
+    
+    /**
+     * 重置搜索
+     * @returns {void}
+     */
+    handleResetSearch() {
+      this.$refs.searchForm.resetFields()
+      this.pagination.currentPage = 1
+      this.loadTableData()
+    },
+    
+    /**
+     * 分页大小变化
+     * @param {number} size - 新的分页大小
+     * @returns {void}
+     */
+    handleSizeChange(size) {
+      this.pagination.pageSize = size
+      this.pagination.currentPage = 1
+      this.loadTableData()
+    },
+    
+    /**
+     * 当前页变化
+     * @param {number} page - 新的页码
+     * @returns {void}
+     */
+    handleCurrentChange(page) {
+      this.pagination.currentPage = page
+      this.loadTableData()
+    },
+    
+    /**
+     * 新增问卷处理
+     * @returns {void}
+     */
+    handleAdd() {
+      this.resetForm()
+      this.dialogMode = 'add'
+      this.dialogVisible = true
+    },
+    
+    /**
+     * 编辑问卷处理
+     * @param {SurveyItem} row - 问卷数据
+     * @returns {Promise<void>}
+     */
+    async handleEdit(row) {
+      await this.loadSurveyDetail(row.id)
+      this.dialogMode = 'edit'
+      this.dialogVisible = true
+    },
+    
+    /**
+     * 查看问卷处理
+     * @param {SurveyItem} row - 问卷数据
+     * @returns {Promise<void>}
+     */
+    async handleView(row) {
+      await this.loadSurveyDetail(row.id)
+      this.dialogMode = 'view'
+      this.dialogVisible = true
+    },
+    
+    /**
+     * 加载问卷详情
+     * @param {string|number} surveyId - 问卷ID
+     * @returns {Promise<void>}
+     */
+    async loadSurveyDetail(surveyId) {
+      try {
+        const response = await getDetail(surveyId)
+        if (response.data && response.data.success) {
+          const surveyData = response.data.data
+          this.setSurveyForm(surveyData)
+        } else {
+          this.$message.error('获取问卷详情失败')
+        }
+      } catch (error) {
+        console.error('加载问卷详情失败:', error)
+        this.$message.error('加载问卷详情失败,请稍后重试')
+      }
+    },
+    
+    /**
+     * 设置问卷表单数据
+     * @param {SurveyItem} data - 问卷数据
+     * @returns {void}
+     */
+    setSurveyForm(data) {
+      this.surveyForm = {
+        id: data.id,
+        surveyCode: data.surveyCode || '',
+        title: data.title || '',
+        description: data.description || '',
+        startTime: data.startTime || '',
+        endTime: data.endTime || '',
+        status: data.status || 0,
+        isTemplate: data.isTemplate || 0
+      }
+    },
+    
+    /**
+     * 重置表单
+     * @returns {void}
+     */
+    resetForm() {
+      this.surveyForm = {
+        id: null,
+        surveyCode: '',
+        title: '',
+        description: '',
+        startTime: '',
+        endTime: '',
+        status: 0,
+        isTemplate: 0
+      }
+      
+      if (this.$refs.surveyForm) {
+        this.$refs.surveyForm.clearValidate()
+      }
+    },
+    
+    /**
+     * 表单提交处理
+     * @returns {Promise<void>}
+     */
+    async handleSubmit() {
+      try {
+        // 表单验证
+        const isValid = await this.validateForm()
+        if (!isValid) {
+          return
+        }
+        
+        // 时间验证
+        if (new Date(this.surveyForm.startTime) >= new Date(this.surveyForm.endTime)) {
+          this.$message.error('开始时间必须小于结束时间')
+          return
+        }
+        
+        this.submitLoading = true
+        
+        if (this.isAddMode) {
+          await add(this.surveyForm)
+          this.$message.success('新增问卷成功')
+        } else if (this.isEditMode) {
+          await update(this.surveyForm)
+          this.$message.success('更新问卷成功')
+        }
+        
+        this.dialogVisible = false
+        this.loadTableData()
+      } catch (error) {
+        console.error('提交问卷失败:', error)
+        this.$message.error('操作失败,请稍后重试')
+      } finally {
+        this.submitLoading = false
+      }
+    },
+    
+    /**
+     * 表单验证
+     * @returns {Promise<boolean>} 验证结果
+     */
+    async validateForm() {
+      try {
+        await this.$refs.surveyForm.validate()
+        return true
+      } catch (error) {
+        return false
+      }
+    },
+    
+    /**
+     * 对话框关闭处理
+     * @returns {void}
+     */
+    handleDialogClose() {
+      this.resetForm()
+    },
+    
+    /**
+     * 获取状态标签
+     * @param {number} status - 状态值
+     * @returns {string} 状态标签
+     */
+    getStatusLabel(status) {
+      return getSurveyStatusLabel(status)
+    },
+    
+    /**
+     * 获取状态类型
+     * @param {number} status - 状态值
+     * @returns {string} 状态类型
+     */
+    getStatusType(status) {
+      return getSurveyStatusType(status)
+    },
+    
+    /**
+     * 获取模板标签
+     * @param {number} isTemplate - 模板值
+     * @returns {string} 模板标签
+     */
+    getTemplateLabel(isTemplate) {
+      return getSurveyTemplateLabel(isTemplate)
+    },
+    
+    /**
+     * 获取模板类型
+     * @param {number} isTemplate - 模板值
+     * @returns {string} 模板类型
+     */
+    getTemplateType(isTemplate) {
+      return getSurveyTemplateType(isTemplate)
+    }
+    
+  }
+}

+ 22 - 0
src/router/views/index.js

@@ -103,6 +103,28 @@ export default [
         ]
     },
     {
+        path: "/survey",
+        component: Layout,
+        redirect: "/survey/index",
+        meta: {
+            icon: "el-icon-document-checked",
+            title: "调查问卷管理",
+            keepAlive: true
+        },
+        children: [
+            {
+                path: "index",
+                name: "调查问卷管理",
+                component: () => import("@/views/survey/index"),
+                meta: {
+                    keepAlive: true,
+                    isAuth: true,
+                    title: "调查问卷管理"
+                }
+            }
+        ]
+    },
+    {
         path: "/lead",
         component: Layout,
         redirect: "/lead/index",

+ 189 - 0
src/views/survey/index.scss

@@ -0,0 +1,189 @@
+.search-container {
+  background: #fff;
+//   padding: 20px;
+  margin-bottom: 20px;
+//   border-radius: 4px;
+//   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  
+  .search-form {
+    .el-form-item {
+      margin-bottom: 16px;
+      
+      .el-form-item__label {
+        font-weight: 500;
+        color: #606266;
+      }
+    }
+  }
+}
+
+.toolbar {
+//   background: #fff;
+//   padding: 16px 20px;
+  margin-bottom: 20px;
+//   border-radius: 4px;
+//   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  
+  .el-button {
+    margin-right: 12px;
+    
+    &:last-child {
+      margin-right: 0;
+    }
+  }
+}
+
+.pagination-container {
+  background: #fff;
+  padding: 20px;
+  margin-top: 20px;
+  border-radius: 4px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  text-align: right;
+}
+
+// 对话框样式
+.survey-form-dialog {
+  .el-dialog__header {
+    background: #f5f7fa;
+    padding: 20px 24px;
+    border-bottom: 1px solid #e4e7ed;
+    
+    .el-dialog__title {
+      font-size: 16px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+  
+  .el-dialog__body {
+    padding: 24px;
+  }
+  
+  .el-dialog__footer {
+    padding: 16px 24px;
+    background: #f5f7fa;
+    border-top: 1px solid #e4e7ed;
+    text-align: right;
+  }
+}
+
+.survey-form {
+  .el-form-item {
+    margin-bottom: 20px;
+    
+    .el-form-item__label {
+      font-weight: 500;
+      color: #606266;
+    }
+    
+    .el-input,
+    .el-select,
+    .el-date-picker {
+      width: 100%;
+    }
+    
+    .el-textarea {
+      .el-textarea__inner {
+        resize: vertical;
+        min-height: 80px;
+      }
+    }
+  }
+}
+
+// 表格样式优化
+.el-table {
+  background: #fff;
+  border-radius: 4px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  
+  .el-table__header {
+    th {
+      background: #f5f7fa;
+      color: #606266;
+      font-weight: 600;
+    }
+  }
+  
+  .el-table__body {
+    tr {
+      &:hover {
+        background: #f5f7fa;
+      }
+    }
+  }
+  
+  .el-button--text {
+    padding: 0;
+    margin-right: 12px;
+    
+    &:last-child {
+      margin-right: 0;
+    }
+    
+    &:hover {
+      color: #409eff;
+    }
+  }
+}
+
+// 标签样式
+.el-tag {
+  border: none;
+  font-weight: 500;
+  
+  &.el-tag--info {
+    background: #f4f4f5;
+    color: #909399;
+  }
+  
+  &.el-tag--success {
+    background: #f0f9ff;
+    color: #67c23a;
+  }
+  
+  &.el-tag--warning {
+    background: #fdf6ec;
+    color: #e6a23c;
+  }
+}
+
+// 响应式设计
+@media (max-width: 768px) {
+  .search-container {
+    .search-form {
+      .el-form-item {
+        width: 100%;
+        margin-bottom: 12px;
+        
+        .el-input,
+        .el-select,
+        .el-date-picker {
+          width: 100%;
+        }
+      }
+    }
+  }
+  
+  .survey-form-dialog {
+    width: 95% !important;
+    
+    .el-dialog__body {
+      padding: 16px;
+    }
+  }
+}
+
+// 加载状态
+.el-loading-mask {
+  background: rgba(255, 255, 255, 0.9);
+}
+
+// 空数据状态
+.el-table__empty-block {
+  .el-table__empty-text {
+    color: #909399;
+    font-size: 14px;
+  }
+}

+ 313 - 0
src/views/survey/index.vue

@@ -0,0 +1,313 @@
+<template>
+  <basic-container>
+    <!-- 搜索区域 -->
+    <div class="search-container">
+      <el-form :model="searchForm" ref="searchForm" :inline="true" class="search-form">
+        <el-form-item label="问卷编码" prop="surveyCode">
+          <el-input
+            v-model="searchForm.surveyCode"
+            placeholder="请输入问卷编码"
+            clearable
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item label="问卷标题" prop="title">
+          <el-input
+            v-model="searchForm.title"
+            placeholder="请输入问卷标题"
+            clearable
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-select
+            v-model="searchForm.status"
+            placeholder="请选择状态"
+            clearable
+            style="width: 150px"
+          >
+            <el-option
+              v-for="item in statusOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="是否模板" prop="isTemplate">
+          <el-select
+            v-model="searchForm.isTemplate"
+            placeholder="请选择是否模板"
+            clearable
+            style="width: 150px"
+          >
+            <el-option
+              v-for="item in templateOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="开始时间" prop="startTime">
+          <el-date-picker
+            v-model="searchForm.startTime"
+            type="datetime"
+            placeholder="选择开始时间"
+            format="yyyy-MM-dd HH:mm:ss"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item label="结束时间" prop="endTime">
+          <el-date-picker
+            v-model="searchForm.endTime"
+            type="datetime"
+            placeholder="选择结束时间"
+            format="yyyy-MM-dd HH:mm:ss"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch" :loading="loading">
+            <i class="el-icon-search"></i> 搜索
+          </el-button>
+          <el-button @click="handleResetSearch">
+            <i class="el-icon-refresh"></i> 重置
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 操作按钮区域 -->
+    <div class="toolbar">
+      <el-button
+        type="primary"
+        size="small"
+        icon="el-icon-plus"
+        @click="handleAdd"
+      >
+        新增问卷
+      </el-button>
+    </div>
+
+    <!-- 表格区域 -->
+    <el-table
+      :data="tableData"
+      v-loading="loading"
+      border
+      stripe
+      style="width: 100%"
+    >
+      <el-table-column type="index" label="序号" width="60" align="center" />
+      
+      <el-table-column prop="surveyCode" label="问卷编码" width="150" show-overflow-tooltip />
+      <el-table-column prop="title" label="问卷标题" width="200" show-overflow-tooltip />
+      <el-table-column prop="description" label="问卷描述" width="250" show-overflow-tooltip />
+      
+      <el-table-column prop="status" label="状态" width="100" align="center">
+        <template slot-scope="{row}">
+          <el-tag :type="getStatusType(row.status)">
+            {{ getStatusLabel(row.status) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      
+      <el-table-column prop="isTemplate" label="是否模板" width="100" align="center">
+        <template slot-scope="{row}">
+          <el-tag :type="getTemplateType(row.isTemplate)">
+            {{ getTemplateLabel(row.isTemplate) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      
+      <el-table-column prop="startTime" label="开始时间" width="160" show-overflow-tooltip />
+      <el-table-column prop="endTime" label="结束时间" width="160" show-overflow-tooltip />
+      <el-table-column prop="createTime" label="创建时间" width="160" show-overflow-tooltip />
+      
+      <el-table-column label="操作"  align="center" fixed="right">
+        <template slot-scope="{row}">
+          <el-button
+            type="text"
+            size="small"
+            icon="el-icon-view"
+            @click="handleView(row)"
+          >
+            查看
+          </el-button>
+          <el-button
+            type="text"
+            size="small"
+            icon="el-icon-edit"
+            @click="handleEdit(row)"
+          >
+            编辑
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页组件 -->
+    <div class="pagination-container">
+      <el-pagination
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="pagination.currentPage"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="pagination.pageSize"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="pagination.total"
+      />
+    </div>
+
+    <!-- 问卷表单弹窗 -->
+    <el-dialog
+      :title="dialogTitle"
+      :visible.sync="dialogVisible"
+      width="800px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :modal="true"
+      :modal-append-to-body="true"
+      :append-to-body="true"
+      custom-class="survey-form-dialog"
+      @close="handleDialogClose"
+    >
+      <el-form
+        :model="surveyForm"
+        :rules="formRules"
+        ref="surveyForm"
+        label-width="120px"
+        class="survey-form"
+      >
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="问卷编码" prop="surveyCode">
+              <el-input
+                v-model="surveyForm.surveyCode"
+                placeholder="请输入问卷编码"
+                :disabled="isViewMode"
+              />
+            </el-form-item>
+          </el-col>
+          
+          <el-col :span="12">
+            <el-form-item label="状态" prop="status">
+              <el-select
+                v-model="surveyForm.status"
+                placeholder="请选择状态"
+                :disabled="isViewMode"
+                style="width: 100%"
+              >
+                <el-option
+                  v-for="item in statusOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          
+          <el-col :span="24">
+            <el-form-item label="问卷标题" prop="title">
+              <el-input
+                v-model="surveyForm.title"
+                placeholder="请输入问卷标题"
+                :disabled="isViewMode"
+              />
+            </el-form-item>
+          </el-col>
+          
+          <el-col :span="24">
+            <el-form-item label="问卷描述" prop="description">
+              <el-input
+                v-model="surveyForm.description"
+                type="textarea"
+                :rows="4"
+                placeholder="请输入问卷描述"
+                :disabled="isViewMode"
+              />
+            </el-form-item>
+          </el-col>
+          
+          <el-col :span="12">
+            <el-form-item label="开始时间" prop="startTime">
+              <el-date-picker
+                v-model="surveyForm.startTime"
+                type="datetime"
+                placeholder="选择开始时间"
+                format="yyyy-MM-dd HH:mm:ss"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                :disabled="isViewMode"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          
+          <el-col :span="12">
+            <el-form-item label="结束时间" prop="endTime">
+              <el-date-picker
+                v-model="surveyForm.endTime"
+                type="datetime"
+                placeholder="选择结束时间"
+                format="yyyy-MM-dd HH:mm:ss"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                :disabled="isViewMode"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+          
+          <el-col :span="12">
+            <el-form-item label="是否模板" prop="isTemplate">
+              <el-select
+                v-model="surveyForm.isTemplate"
+                placeholder="请选择是否模板"
+                :disabled="isViewMode"
+                style="width: 100%"
+              >
+                <el-option
+                  v-for="item in templateOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      
+      <div slot="footer" class="dialog-footer" v-if="!isViewMode">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button
+          type="primary"
+          @click="handleSubmit"
+          :loading="submitLoading"
+        >
+          确定
+        </el-button>
+      </div>
+      
+      <div slot="footer" class="dialog-footer" v-else>
+        <el-button @click="dialogVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
+  </basic-container>
+</template>
+
+<script>
+import surveyIndexMixin from '@/mixins/survey/surveyIndex'
+
+export default {
+  name: 'SurveyManagement',
+  
+  mixins: [surveyIndexMixin]
+}
+</script>
+
+<style lang="scss" scoped>
+@import './index.scss';
+</style>