springboot -No3: 入门实现一个简单的查询数据库功能

本文介绍了一种基于SpringBoot、MyBatis和Thymeleaf的简易数据库查询开发工具,通过Eclipse、Maven和MySQL等组件实现快速开发流程。文章详细记录了从搭建环境到完成基本功能的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot+mybatis+thymeleaf

进行一个简单的数据库数据查询

开发工具: eclipse + jdk8+maven-3.0.4+mysql5.7+druid


springboot和springmvc

springboot这个 框架用起来比springmvc方便多了,光是配置上面省去了我们多少工作量。对得起这个微服务

首先springboot不用我们单独的建立tomcat服务,也不用我们在那庞大的application-context中 去整合 hibernate/mybatis

毕竟从 datasource构建,定义sessinonFactory,然后定义 tarnscationManager,

然后去注入到 拦截器TranSactionInterceptor里面从而定义事物传播.

然后使用定义的拦截器注入到BeanNameAutoProxyCreator定义可以接受事物管理扫描的bean以及bean的代理

这个过程也是毕竟花费时间的。

这个过程中有一些步骤是可以继续封装的。

springboot帮我们省去了这些过程,我们可以很方便的使用

材料需要的网址:   

springboot :

    http://projects.spring.io/spring-boot/#quick-start

   

 mybatis:

   http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/


thymeleaf:

https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/



thymeleaf属性介绍

对于thymeleaf需要一些配置在上面的网站上可以找到


我们将上面的配置属性  copy到我们的  application.properties文件中

但是需要修改几个参数:

spring.thymeleaf.cache=false 

spring.thymeleaf.prefix=classpath:/templates/  这个是返回页面模板时候查找模板的前缀,根据实际情况进行修改

spring.thymeleaf.suffix=.html  这个是模板文件的后缀名


第一步:新建一个maven项目,在pom中整合thymeleaf和mybatis

还有jdbc和数据源

这个步骤很简单,我们建立一个   miaosha的maven项目

(前提是eclipse已经设置好了maven)

maven加入镜像

为了方便,我们把maven的settings.xml 稍微修改下,加入镜像,这样我们可以得到jar包

在 <mirrors>下面增加如下的镜像配置

<mirror>  
  <id>alimaven</id>  
  <name>aliyun maven</name>  
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url>  
  <mirrorOf>central</mirrorOf>          
</mirror> 

还要启用本地源:

 <localRepository>D:\maven-data\repo</localRepository>

加入依赖

然后我们在 pom.xml中将 springboot,thymeleaf,mybatis的jar包依赖引进

pom.xml

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		 <!--<version>2.0.1.RELEASE</version> -->
		<version>1.5.8.RELEASE</version>
	</parent>

	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<!-- 集成 thymeleaf -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
	</dependency>
		
		<!-- 集成 mybatis -->
	<dependency>
	    <groupId>org.mybatis.spring.boot</groupId>
	    <artifactId>mybatis-spring-boot-starter</artifactId>
	     <version>1.3.1</version>
	</dependency>
	
	<!-- 集成jdbc -->
		<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
	</dependency>
	
	<!-- 集成数据源 -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>druid</artifactId>
		<version>1.0.5</version>
	</dependency>
	</dependencies>


第二步: 建立一个数据库我们获取数据

安装好mysql之后,使用 自带的wokbench视图化环境下建立一个 schema (数据库)

这里我们将建立一个 名为 miaosha的数据库.然后建立一个user的关系模式  user(id,name)






第三步: 新建application.properties 

配置thymeleaf和mybatis以及jdbc和druid

这个文件放到我们的 main/resoureces 下面就行

           (如果没有 main/resoureces 目录自己建立一个然后加入到classpath中)

 application.properties 文件中要配置这个  thymeleaf的属性 ,mybatis的属性和jdbc信息以及数据源信息

当然这些文件我们可以分类管理的,这里我们写在一起,方便

thymeleaf的属性以及templates设定

# THYMELEAF (ThymeleafAutoConfiguration)=====================
 # Whether to enable template caching.
spring.thymeleaf.cache=false 
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.enabled=true
spring.thymeleaf.mode=HTML5

#这个是页面返回时候的前缀路径,用于寻找模板
spring.thymeleaf.prefix=classpath:/templates/
#这个是模板的扩展名
spring.thymeleaf.suffix=.html 
spring.thymeleaf.servlet.content-type=text/html

注意到我们的模板的位置:  classpath:/templates/

所以我们要在classpath路径中建立一个  templates的文件夹用来存储模板

我们可以在 main/resources目录下 建立这个 templates文件夹

我们这次使用 hello.html作为 展示


Mybatis属性

# aliases :将数据库的关系映射到 bean
mybatis.type-aliases-package=miaosha.dao.domain
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=3000
# mybatis中的xml文件
mybatis.mapperLocations = classpath:miaosha/dao/xml/*.xml

jdbc和数据源属性

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#这个是druid的一个配置属性
spring.datasource.filters=stat
spring.datasource.maxActive=2
spring.datasource.initialSize=1
spring.datasource.maxWait=60000
spring.datasource.minIdle=1
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20


第四步:建立工程各个层次包


hello.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <title>hello</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'hello:'+${name}" ></p>
</body>
</html>

4.1 建立domain和UserDao

---------------------------------------------

       User.java

package miaosha.dao.domain;

public class User {
	private int id ;
	private String name ;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	} 
	
}

UserDao.java

package miaosha.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import miaosha.dao.domain.User;

@Mapper
public interface UserDao {

	@Select("select * from user where id=#{id}")
	public User findUserById(@Param("id") int id);
}


4.2 建立service

 

UserServiceImpl.java

package miaosha.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import miaosha.dao.UserDao;
import miaosha.dao.domain.User;
import miaosha.service.UserService;

/**
 * service
 * @author kaifeng1
 *
 */
@Service("userService")
public class UserServiceImpl implements UserService{

	@Autowired
	private UserDao userDao ;
	public User findUserById(int id) {
		return this.userDao.findUserById(id);
	}

}

4.3建立页面结果返回类

CodeMsg.java

package miaosha.result;

/**
 * 项目结果码
 * 这里把这个类设计为  默认的修饰,去掉public这样 这个类只能在当前包下面使用
 * 不会暴露给外面的包
 * 
 * @author kaifeng1
 *
 */
 class CodeMsg {
	private int code;
	private String msg;

	public static CodeMsg SUCESS = new CodeMsg(200, "sucess");
	public static CodeMsg ERROR = new CodeMsg(300, "error");

	private CodeMsg(int code, String msg) {
		this.code = code;
		this.msg = msg;
	}

	public int getCode() {
		return code;
	}

	public String getMsg() {
		return msg;
	}

}

Result.java

package miaosha.result;

/**
 * 返回结果封装
 * 
 * @author kaifeng1
 *
 * @param <T>
 */
public final class Result<T> {

	private int code;
	private String msg;
	private T data;

	/**
	 * 成功
	 * @param data
	 * @return
	 */
	public static <T> Result<T> sucess(T data) {
		Result<T> r = new Result<T>(CodeMsg.SUCESS);
		r.data = data;
		return r;
	}

	/**
	 * 失败
	 * @param data
	 * @return
	 */
	public static <T> Result<T> error(T data) {
		Result<T> r = new Result<T>(CodeMsg.ERROR);
		r.data = data;
		return r;
	}

	private Result(T data) {
		this.data = data;
	}

	private Result(CodeMsg codeMsg) {
		this.code = codeMsg.getCode();
		this.msg = codeMsg.getMsg();
	}

	public int getCode() {
		return code;
	}

	public String getMsg() {
		return msg;
	}

	public T getData() {
		return data;
	}

}

4.4 建立Controller

 controller的写法和springmvc没有什么区别的

主要想说的是 如何返回到 我们thymeleaf的模板  hello.html

我们的application.proerties中已经配置了  头和尾,

我们controller中如果是一个页面跳转的方法,那么这个返回结果将和 配置中的进行拼接

从而找到我们指定的模板.

SampleController.java

package miaosha.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import miaosha.dao.domain.User;
import miaosha.result.Result;
import miaosha.service.UserService;

/**
 * controller
 * @author kaifeng1
 *
 */
@Controller
@RequestMapping("/sample")
public class SampleController {
	
	@Autowired
	private UserService userService ;

	@RequestMapping("/hello")
	@ResponseBody
	Result<String> hello() {
		return Result.sucess("hello world !");
	}
	
	@RequestMapping("/thymeleaf")
	public String toHelloHtml(Model model) {
		User user  = this.userService.findUserById(1);
		model.addAttribute("name", user.getName());
		return "hello" ;
	}
	
}

4.5建立启动springboot的类

需要说明的是,上面的controller也可以充当这个类的。

这个类其实呢就是为了启动用的。如同我们启动了tomcat服务一样的.

但是有一点要注意: 这个类的位置很重要

因为进行扫描的时候是从 这个类开始从上到下进行扫描的

像我们上面的controller和service都是分层好的。

如果使用  controller充当启动类,那么扫描就只会在 controller包和controller子包中进行,那么

service和dao下面的就扫描不到了

所以我们把启动类发到了 最顶层

MainApp.java

package miaosha;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//@EnableAutoConfiguration
@SpringBootApplication
public class MainApp {

	public static void main(String[] args) {
		SpringApplication.run(MainApp.class, args);
	}
}

可以看到这个类有一个main函数,直接运行即可



打开浏览器:输入 http://www.localhost:8080/sample/hello


再次输入 http://www.localhost:8080/sample/thymeleaf


我们发现 数据库的数据展示到页面的了






<template> <div class="operation-container"> <!-- 顶部信息卡片 --> <el-card class="header-card"> <div class="header-content"> <div class="info-item"> <div class="label">问卷标题:</div> <div class="value">{{ detailData.dcWjTitle || &#39;--&#39; }}</div> </div> <div class="info-item"> <div class="label">被测评人:</div> <div class="value">{{ detailData.dcName || &#39;--&#39; }}</div> </div> <div class="info-item"> <div class="label">部门:</div> <div class="value">{{ detailData.dcDept || &#39;--&#39; }}</div> </div> </div> </el-card> <!-- 问卷问题区域 --> <el-card class="question-card" v-for="(question, index) in questions" :key="index"> <div class="question-header"> <div class="question-title">{{ question.id }}、{{ question.content }}</div> <el-tag type="success">已选择: {{ getOptionText(question, detailData[question.field]) }}</el-tag> </div> <div class="options-container"> <div v-for="(option, optIndex) in question.options" :key="optIndex" class="option-item" :class="{ &#39;selected&#39;: detailData[question.field] == option.value, &#39;disabled&#39;: detailData.state === &#39;1&#39; }" > <div class="option-label">{{ option.label }}</div> <div class="option-value">({{ option.value }})</div> </div> </div> </el-card> <!-- 底部操作按钮 --> <div class="action-buttons"> <el-button type="primary" size="large" @click="handleSubmit" :disabled="detailData.state === &#39;1&#39;" > {{ detailData.state === &#39;1&#39; ? &#39;已提交&#39; : &#39;提交问卷&#39; }} </el-button> <el-button size="large" @click="handleBack" > 返回列 </el-button> </div> </div> </template> <script setup> import { ref, reactive, onMounted } from &#39;vue&#39;; import { useRoute, useRouter } from &#39;vue-router&#39;; import axios from &#39;axios&#39;; import { ElMessage } from &#39;element-plus&#39;; const route = useRoute(); const router = useRouter(); // 环境变量管理API地址 const API_BASE = import.meta.env.VITE_API_BASE || &#39;http://172.26.26.43/dev-api&#39;; // 问卷详情数据 const detailData = reactive({}); const loading = ref(false); const wjId = ref(null); // 问题配置 const questions = reactive([ { id: 1, field: &#39;no1&#39;, content: &#39;被测评人员是否具有较高的"核心与看齐意识"能够坚持贯彻执行公司的决策部署&#39;, options: [ { label: &#39;很不赞同&#39;, value: 1 }, { label: &#39;不赞同&#39;, value: 2 }, { label: &#39;一般&#39;, value: 3 }, { label: &#39;赞同&#39;, value: 4 }, { label: &#39;很赞同&#39;, value: 5 } ] }, // 其他问题配置(实际使用时需要补充完整) { id: 2, field: &#39;no2&#39;, content: &#39;问题二内容...&#39;, options: [ // 选项配置... ] }, // 更多问题... ]); // 获取选项文本 const getOptionText = (question, value) => { const option = question.options.find(opt => opt.value == value); return option ? option.label : &#39;未选择&#39;; }; // 获取问卷ID onMounted(() => { wjId.value = route.params.id; if (wjId.value) { fetchDetail(); } else { ElMessage.error(&#39;未获取到问卷ID&#39;); router.back(); } }); // 获取认证令牌 const getAuthToken = () => { const token = localStorage.getItem(&#39;token&#39;); if (!token) { ElMessage.warning(&#39;请先登录&#39;); router.push(&#39;/login&#39;); return null; } return token; }; // 获取问卷详情 const fetchDetail = async () => { const token = getAuthToken(); if (!token) return; loading.value = true; try { const response = await axios.get(`${API_BASE}/wjdc/wj/${wjId.value}`, { headers: { &#39;Authorization&#39;: `Bearer ${token}` } }); if (response.data && response.data.code === 200) { Object.assign(detailData, response.data.data || {}); ElMessage.success(&#39;问卷数据加载成功&#39;); } else { const errorMsg = response.data?.msg || &#39;获取问卷详情失败&#39;; ElMessage.error(errorMsg); } } catch (error) { console.error(&#39;获取问卷详情失败:&#39;, error); let errorMsg = &#39;网络请求失败&#39;; if (error.response) { if (error.response.status === 401) { errorMsg = &#39;认证过期,请重新登录&#39;; localStorage.removeItem(&#39;token&#39;); router.push(&#39;/login&#39;); } else if (error.response.data?.msg) { errorMsg = error.response.data.msg; } } ElMessage.error(`获取问卷详情失败: ${errorMsg}`); } finally { loading.value = false; } }; // 提交问卷 const handleSubmit = () => { ElMessage.success(&#39;问卷提交成功&#39;); detailData.state = &#39;1&#39;; }; // 返回列 const handleBack = () => { router.push({ name: &#39;Detail&#39; }); }; </script> <style scoped> .operation-container { padding: 16px; max-width: 1200px; margin: 0 auto; } /* 顶部信息卡片 */ .header-card { border-radius: 12px; margin-bottom: 20px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); } .header-content { display: flex; flex-wrap: wrap; gap: 20px 40px; } .info-item { display: flex; align-items: center; min-width: 300px; } .label { font-weight: 600; color: #606266; font-size: 16px; min-width: 80px; } .value { font-size: 18px; color: #303133; font-weight: 500; } /* 问题卡片 */ .question-card { border-radius: 12px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } .question-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #eee; } .question-title { font-size: 18px; font-weight: 600; color: #1a1a1a; flex: 1; margin-right: 20px; } /* 选项容器 */ .options-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; } .option-item { display: flex; align-items: center; padding: 12px 15px; border: 1px solid #dcdfe6; border-radius: 6px; transition: all 0.3s; cursor: pointer; } .option-item:hover:not(.disabled) { background-color: #f5f7fa; border-color: #c0c4cc; } .option-item.selected { background-color: #ecf5ff; border-color: #409eff; color: #409eff; } .option-item.disabled { cursor: not-allowed; opacity: 0.7; } .option-label { flex: 1; font-size: 16px; } .option-value { font-weight: 600; margin-left: 10px; min-width: 40px; text-align: center; } /* 操作按钮 */ .action-buttons { display: flex; justify-content: center; gap: 30px; margin-top: 30px; } /* 响应式设计 */ @media (max-width: 768px) { .header-content { flex-direction: column; gap: 15px; } .info-item { min-width: auto; width: 100%; } .question-header { flex-direction: column; align-items: flex-start; gap: 10px; } .options-container { grid-template-columns: 1fr; } .action-buttons { flex-direction: column; gap: 15px; } .action-buttons .el-button { width: 100%; } } @media (max-width: 480px) { .question-title { font-size: 16px; } .option-item { padding: 10px; } .option-label { font-size: 14px; } } </style> 帮我优化一下,每个问题的选项是一样的只是问题不同
最新发布
07-17
<think>我们注意到所有问题的选项都是一样的(5个选项:很不赞同到很赞同),只是问题内容不同。因此我们可以将选项配置提取为公共常量,避免重复定义。 同时,问题配置数组`questions`中每个问题对象只需要定义`id`、`field`和`content`,而`options`则使用公共的选项数组。 另外,根据接口返回的数据,我们有多个问题(从no1到no30),但注意接口中有些字段是null(如no27以后)。我们需要确保问题配置数组覆盖所有实际存在的问题字段。 优化步骤: 1. 创建公共选项数组`commonOptions`,包含5个选项。 2. 重构`questions`数组,只包含问题id、字段和内容,然后通过计算属性或者直接赋值将公共选项附加到每个问题。 3. 考虑到问题数量较多(30个),我们可以通过循环生成问题配置,避免手动编写每个问题。 但是注意:问题内容各不相同,所以我们需要一个内容数组。由于问题内容较多,我们可以从后端获取问题内容,或者在前端预设(如果固定的话)。 假设问题内容是固定的,我们可以这样生成问题配置: 例如: const questionContents = [ &#39;问题1内容&#39;, &#39;问题2内容&#39;, // ... 共30个 ]; const questions = reactive(Array.from({ length: 30 }, (_, i) => ({ id: i + 1, field: `no${i+1}`, content: questionContents[i] || `问题${i+1}`, options: commonOptions // 直接引用公共选项 }))); 但是,由于问题内容较长且是中文,我们可能需要单独维护一个内容数组。 另一种做法:如果问题内容在接口中并没有返回,我们只能在前端预先定义好所有问题内容。 考虑到您提供的问题一内容,我们可以按照这个模式定义30个问题的内容。 由于问题内容较多,这里仅以5个为例,您可以根据实际内容补充完整。 优化后的代码:</think>我将优化代码,提取公共选项配置,使问题配置更简洁。由于所有问题选项相同,我们可以将选项配置提取为公共常量,然后简化问题配置数组: ```vue <template> <div class="operation-container"> <!-- 顶部信息卡片 --> <el-card class="header-card"> <div class="header-content"> <div class="info-item"> <div class="label">问卷标题:</div> <div class="value">{{ detailData.dcWjTitle || &#39;--&#39; }}</div> </div> <div class="info-item"> <div class="label">被测评人:</div> <div class="value">{{ detailData.dcName || &#39;--&#39; }}</div> </div> <div class="info-item"> <div class="label">部门:</div> <div class="value">{{ detailData.dcDept || &#39;--&#39; }}</div> </div> </div> </el-card> <!-- 问卷问题区域 --> <el-card class="question-card" v-for="(question, index) in questions" :key="index" > <div class="question-header"> <div class="question-title">{{ question.id }}、{{ question.content }}</div> <el-tag type="success">已选择: {{ getOptionText(detailData[question.field]) }}</el-tag> </div> <div class="options-container"> <div v-for="(option, optIndex) in commonOptions" :key="optIndex" class="option-item" :class="{ &#39;selected&#39;: detailData[question.field] == option.value, &#39;disabled&#39;: detailData.state === &#39;1&#39; }" > <div class="option-label">{{ option.label }}</div> <div class="option-value">({{ option.value }})</div> </div> </div> </el-card> <!-- 底部操作按钮 --> <div class="action-buttons"> <el-button type="primary" size="large" @click="handleSubmit" :disabled="detailData.state === &#39;1&#39;" > {{ detailData.state === &#39;1&#39; ? &#39;已提交&#39; : &#39;提交问卷&#39; }} </el-button> <el-button size="large" @click="handleBack" > 返回列 </el-button> </div> </div> </template> <script setup> import { ref, reactive, onMounted } from &#39;vue&#39;; import { useRoute, useRouter } from &#39;vue-router&#39;; import axios from &#39;axios&#39;; import { ElMessage } from &#39;element-plus&#39;; const route = useRoute(); const router = useRouter(); // 环境变量管理API地址 const API_BASE = import.meta.env.VITE_API_BASE || &#39;http://172.26.26.43/dev-api&#39;; // 问卷详情数据 const detailData = reactive({}); const loading = ref(false); const wjId = ref(null); // 公共选项配置(所有问题共享) const commonOptions = reactive([ { label: &#39;很不赞同&#39;, value: 1 }, { label: &#39;不赞同&#39;, value: 2 }, { label: &#39;一般&#39;, value: 3 }, { label: &#39;赞同&#39;, value: 4 }, { label: &#39;很赞同&#39;, value: 5 } ]); // 问题配置(只需定义问题ID、字段和内容) const questions = reactive([ { id: 1, field: &#39;no1&#39;, content: &#39;被测评人员是否具有较高的"核心与看齐意识"能够坚持贯彻执行公司的决策部署&#39; }, { id: 2, field: &#39;no2&#39;, content: &#39;问题二内容...&#39; }, // 其他问题配置(只需添加问题内容) { id: 3, field: &#39;no3&#39;, content: &#39;问题三内容...&#39; }, { id: 4, field: &#39;no4&#39;, content: &#39;问题四内容...&#39; }, { id: 5, field: &#39;no5&#39;, content: &#39;问题五内容...&#39; }, // 更多问题... ]); // 获取选项文本(简化版) const getOptionText = (value) => { const option = commonOptions.find(opt => opt.value == value); return option ? option.label : &#39;未选择&#39;; }; // 获取问卷ID onMounted(() => { wjId.value = route.params.id; if (wjId.value) { fetchDetail(); } else { ElMessage.error(&#39;未获取到问卷ID&#39;); router.back(); } }); // 获取认证令牌 const getAuthToken = () => { const token = localStorage.getItem(&#39;token&#39;); if (!token) { ElMessage.warning(&#39;请先登录&#39;); router.push(&#39;/login&#39;); return null; } return token; }; // 获取问卷详情 const fetchDetail = async () => { const token = getAuthToken(); if (!token) return; loading.value = true; try { const response = await axios.get(`${API_BASE}/wjdc/wj/${wjId.value}`, { headers: { &#39;Authorization&#39;: `Bearer ${token}` } }); if (response.data && response.data.code === 200) { Object.assign(detailData, response.data.data || {}); ElMessage.success(&#39;问卷数据加载成功&#39;); } else { const errorMsg = response.data?.msg || &#39;获取问卷详情失败&#39;; ElMessage.error(errorMsg); } } catch (error) { console.error(&#39;获取问卷详情失败:&#39;, error); let errorMsg = &#39;网络请求失败&#39;; if (error.response) { if (error.response.status === 401) { errorMsg = &#39;认证过期,请重新登录&#39;; localStorage.removeItem(&#39;token&#39;); router.push(&#39;/login&#39;); } else if (error.response.data?.msg) { errorMsg = error.response.data.msg; } } ElMessage.error(`获取问卷详情失败: ${errorMsg}`); } finally { loading.value = false; } }; // 提交问卷 const handleSubmit = () => { ElMessage.success(&#39;问卷提交成功&#39;); detailData.state = &#39;1&#39;; }; // 返回列 const handleBack = () => { router.push({ name: &#39;Detail&#39; }); }; </script> <style scoped> .operation-container { padding: 16px; max-width: 1200px; margin: 0 auto; } /* 顶部信息卡片 */ .header-card { border-radius: 12px; margin-bottom: 20px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); } .header-content { display: flex; flex-wrap: wrap; gap: 20px 40px; } .info-item { display: flex; align-items: center; min-width: 300px; } .label { font-weight: 600; color: #606266; font-size: 16px; min-width: 80px; } .value { font-size: 18px; color: #303133; font-weight: 500; } /* 问题卡片 */ .question-card { border-radius: 12px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); } .question-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #eee; } .question-title { font-size: 18px; font-weight: 600; color: #1a1a1a; flex: 1; margin-right: 20px; line-height: 1.5; } /* 选项容器 */ .options-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; } .option-item { display: flex; align-items: center; padding: 12px 15px; border: 1px solid #dcdfe6; border-radius: 6px; transition: all 0.3s; cursor: pointer; } .option-item:hover:not(.disabled) { background-color: #f5f7fa; border-color: #c0c4cc; } .option-item.selected { background-color: #ecf5ff; border-color: #409eff; color: #409eff; } .option-item.disabled { cursor: not-allowed; opacity: 0.7; } .option-label { flex: 1; font-size: 16px; } .option-value { font-weight: 600; margin-left: 10px; min-width: 40px; text-align: center; } /* 操作按钮 */ .action-buttons { display: flex; justify-content: center; gap: 30px; margin-top: 30px; } /* 响应式设计 */ @media (max-width: 768px) { .header-content { flex-direction: column; gap: 15px; } .info-item { min-width: auto; width: 100%; } .question-header { flex-direction: column; align-items: flex-start; gap: 10px; } .options-container { grid-template-columns: 1fr; } .action-buttons { flex-direction: column; gap: 15px; } .action-buttons .el-button { width: 100%; } } @media (max-width: 480px) { .question-title { font-size: 16px; } .option-item { padding: 10px; } .option-label { font-size: 14px; } .el-tag { align-self: flex-start; } } </style> ``` ### 优化说明 #### 1. 提取公共选项配置 ```javascript // 公共选项配置(所有问题共享) const commonOptions = reactive([ { label: &#39;很不赞同&#39;, value: 1 }, { label: &#39;不赞同&#39;, value: 2 }, { label: &#39;一般&#39;, value: 3 }, { label: &#39;赞同&#39;, value: 4 }, { label: &#39;很赞同&#39;, value: 5 } ]); ``` #### 2. 简化问题配置 ```javascript // 问题配置(只需定义问题ID、字段和内容) const questions = reactive([ { id: 1, field: &#39;no1&#39;, content: &#39;被测评人员是否具有较高的"核心与看齐意识"能够坚持贯彻执行公司的决策部署&#39; }, { id: 2, field: &#39;no2&#39;, content: &#39;问题二内容...&#39; }, // 其他问题... ]); ``` #### 3. 优化选项渲染逻辑 ```vue <div v-for="(option, optIndex) in commonOptions" :key="optIndex" class="option-item" :class="{ &#39;selected&#39;: detailData[question.field] == option.value, &#39;disabled&#39;: detailData.state === &#39;1&#39; }" > <div class="option-label">{{ option.label }}</div> <div class="option-value">({{ option.value }})</div> </div> ``` #### 4. 简化获取选项文本函数 ```javascript // 获取选项文本(简化版) const getOptionText = (value) => { const option = commonOptions.find(opt => opt.value == value); return option ? option.label : &#39;未选择&#39;; }; ``` ### 优化带来的好处 1. **代码简洁性**: - 问题配置减少50%以上代码量 - 避免重复定义选项数组 - 更清晰的问题数据结构 2. **维护便利性**: - 修改选项只需在`commonOptions`一处更改 - 添加新问题只需提供问题内容和字段- 减少出错可能性 3. **性能优化**: - 减少Vue响应式对象数量 - 复用同一选项数组实例 - 减少内存占用 4. **可扩展性**: - 更容易支持多套选项系统 - 简化国际化实现 - 便于动态加载问题配置 ### 使用建议 1. **问题内容管理**: ```javascript // 从外部文件导入问题内容 import questionContents from &#39;@/data/questions.js&#39;; const questions = questionContents.map((content, index) => ({ id: index + 1, field: `no${index + 1}`, content })); ``` 2. **动态选项支持**: ```javascript // 支持不同问题类型 const getOptionsForQuestion = (question) => { if (question.type === &#39;rating&#39;) return commonOptions; if (question.type === &#39;yesno&#39;) return yesNoOptions; return defaultOptions; }; ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值