一、任务要求
- 对多张表增删改查 (数据表字段类型包括字符、数值、时间)
- Controller、Dao、 Service 分层
- 代码及命名规范
- 页面展示使用多种形式(radio 、checkbox 、下拉框、日期选择等)
- 多种条件查询( 文本框、radio 、checkbox 、下拉框、日期选择等 ),多种匹配模式(精确匹配、模糊匹配、范围匹配等)
- 查询分页
- 前台JS 控制
- 前、后台校验
- 查询页面,包括显示列的字典翻译、URL、查询条件的字典、字典级联。
- 表单页面,包括字段的字典、字典级联、字典过滤、自动带值、操作校验等。
二、页面搭建
Ant Design Vue【任务要求使用】
三、曲折过程
1、ant-design-vue的button失效
问题所在:float:left
解决:加个 z-index: 99;
<style>
.editable-add-btn {
margin-bottom: 8px;
float: left;
z-index: 99;
}
</style>
2、跨域问题
解决方案:
1、前端vue设置全局参数withCredentials : true
2、后端相关Controller添加跨域注解@CrossOrigin
3、后端新建CorsConfig类,继承WebMvcConfigurerAdapter,并重写addCorsMappings方法
@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
public void addCorsMappings(CorsRegistry registry) {
//设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
.allowedOrigins("*")
//这里:是否允许证书 不再默认开启
.allowCredentials(true)
//设置允许的方法
.allowedMethods("*")
//跨域允许时间
.maxAge(3600);
}
}
3、字典翻译
<!-- 字典翻译:性别 -->
<span slot="switchStuSex" slot-scope="stuSex">
<span v-if="stuSex === 1">男</span>
<span v-else>女</span>
</span>
<!-- 关联上面的 slot -->
{
title: '性别',
dataIndex: 'stuSex',
key: 'stuSex',
align:'center',
fixed:'left',
width: 60,
scopedSlots: { customRender: 'switchStuSex' }, // 关联那个 <a slot='switchStuSex'>
},
4、获取表格当前行的数据
<!-- text:文本数据,record:行对象数据 -->
<a slot="name" slot-scope="text,record" @click="fetch(record.id)">{{text}}</a>
5、弹窗信息信息
通过控制 visible 的值进行控制弹窗
<a-modal v-model="visible" title="Basic Modal" @ok="handleOk">
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</a-modal>
6、表格标签自闭和导致插槽失效
结果就是所有的 slot 的字典翻译都失效了,我看了所有的逻辑代码,没有一点毛病。。。我还以为遇上什么诡异的bug了,后来才发现,我不小心把第一个 a-table
标签自闭和了!因为最后我把去官网重新拷了一份表格代码,重新改了一次。。。。靠!好气啊!
但是 axios 查询出来的数据还能遍历显示。。。因为这个情况,导致我一直没想到和标签闭合有关。
7、限制输入文本长度在 1-6 个字符
使用ui表单自带的校验api
<a-form-item label="姓名">
<a-input
v-decorator="['note', { rules: [{ required: true, message: '姓名为1-6个汉字!', min: 1, max: 6 }] }]"
/>
</a-form-item>
8、限制选择今天之后的日期
前提:ui(antd vue )
<template>
<a-form-item label="入学日期" has-feedback >
<a-date-picker format="YYYY-MM-DD"
:disabled-date="disabledDate"
style="width: 100%" />
</a-form-item>
</template>
<script>
import moment from 'moment';
export default {
name: 'StuAdd',
data() {},
methods: {
moment,
disabledDate(current) {
// 不能选择今天之后作为入学时间
return current > moment().endOf('day');
},
},
}
</script>
9、表单项的 name
对前端不是很熟悉,当时提交表单时,发现请求体中各表单项都没有 name 属性!!!最后一顿百度,发现没有结果,可能是因为这个问题太简单了。。。然后没办法,对着ui库的文档死磕,最后发现了v-decorator
里面的值,就是name属性,我...@!#$%&*(!@#$%^&*()!!!
例如:stuName
<a-input v-decorator="['stuName']" />
10、自定义表单项校验,姓名在 1-6个汉字
例如:stuName
<a-form-item label="姓名">
<a-input
v-decorator="['stuName', {
rules: [
{ required: true},
{ validator: this.checkStuName }
]
}]"
/>
</a-form-item>
methods:
<script>
methods: {
// 检查名字
checkStuName(rule, value, callback){
const pwdRegex = new RegExp('^[\\u4E00-\\u9FA5]{1,6}$');
if (!pwdRegex.test(value)) {
callback(new Error('名字应为1-6个汉字!'))
}
callback()
},
}
</script>
11、给表单项赋默认值为一个变量
错误做法:
<a-form-item label="必修技能">
<a-input disabled v-bind:value="stuSkill" />
</a-form-item>
会报错:
warning.js?d96e:34 Warning: `getFieldDecorator` will override `value`, so please don't set `value and v-model` directly and use `setFieldsValue` to set it.
原因:v-bind 和 v-decorator 不能同时使用!
正确做法:
<a-form-item label="必修技能">
<a-input disabled v-decorator="['stuSkill']" />
</a-form-item>
在级联下拉框的 onChange 方法里:
// 级联选择
onChange(value, selectedOptions) {
// alert('院系——'+value[0]);
// alert('专业——'+value[1]);
if(value[0] == 1){
this.form.setFieldsValue({stuSkill:'C语言'});
}else if(value[0] == 2){
this.form.setFieldsValue({stuSkill:'基础急救'});
}else {
this.form.setFieldsValue({stuSkill:''});
}
},
12、可输入值的级联选择框的name(7-17: 好看不会用...已换~)
这个不能通过 v-decorator 定义 name,好像很冷门,百度了好久,试了好多,才发现是 v-decorator="['depMajName']" !
<a-form-item label="院系及专业">
<a-cascader
:options="options"
:show-search="{ filter }"
placeholder="请选择"
@change="onChange"
v-decorator="['depMajName']"
/>
</a-form-item>
13、成功往数据库添加的数据中,日期比传递的日期时间提前一天
时区问题,解决:在连接库url地址后面加上 ?serverTimezone=GMT%2B8
14、对话框和表格同时使用的bug
把对话框的代码放到表格外面即可!
15、表单数据回显:setFieldsValue
1.radio 数据回显
ant design vue 推荐使用 this.form.setFieldsValue
进行不赋值,而不是 v-bind
和 v-model
。
而且必须 a-radio
中的value
,必须要是 :value='1'
,注意要有冒号:,就无法设置值了。。。
<a-form-item label="性别">
<a-radio-group v-decorator="['stuSex']" @change="onRadioChange">
<a-radio :value='1'>
男
</a-radio>
<a-radio :value='0'>
女
</a-radio>
</a-radio-group>
</a-form-item>
2、select 数据回显(静态option)
例如:因为年级选项 “大一”的 value 是数字 1,但是显示的是字符串“大一”。故 setFieldsValue 的时候,需要把 value的值 用 toString() 转为 String。
例如:
this.form.setFieldsValue({
stuGrade: this.stu.stuGrade.toString(),
});
3、checkbox 数据回显
直接 setFieldsValue 即可。(日期控件
例如:this.stu.stuLoves = "2,3,4"
setTimeout(() => {
this.form.setFieldsValue({
stuLoves: this.stu.stuLoves,
});
}, 300);
4、select 数据回显(动态option)
这是第二种情况,动态option:
:value ,产生动态 value。
<a-form-item label="院系">
<a-select
show-search
placeholder="请选择"
:filter-option="filterOption"
@change="handleChange"
v-decorator="['depName']"
>
<a-select-option v-for="dep in departments" :key="dep.depId" :value="dep.depName">
{{dep.depName == 1 ? '计算机系' : ''}}
{{dep.depName == 2 ? '医学系' : ''}}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="专业">
<a-select
show-search
placeholder="请选择"
:filter-option="filterOption2"
@change="handleChange2"
v-decorator="['majName']"
>
<a-select-option v-for="major in majors" :key="major.majId" :value="major.majName">
{{major.majName == 1 ? '软工' : ''}}
{{major.majName == 2 ? '计科' : ''}}
{{major.majName == 3 ? '网工' : ''}}
{{major.majName == 4 ? '信管' : ''}}
{{major.majName == 5 ? '医学检验' : ''}}
{{major.majName == 6 ? '临床医学' : ''}}
{{major.majName == 7 ? '法医' : ''}}
</a-select-option>
</a-select>
</a-form-item>
然后赋值:
setTimeout(() => {
this.form.setFieldsValue({
// stuGrade: stuGradeStr,
stuGrade: this.stu.stuGrade.toString(),
depName: this.stu.depName.toString(),
majName: this.stu.majName.toString(),
});
}, 0);
却发现不可以,于是把 toString 给去掉,就可以了!应该是因为下拉框的value是整数,而非字符串,显示的字符是我们的字典翻译!!!
setTimeout(() => {
this.form.setFieldsValue({
// stuGrade: stuGradeStr,
stuGrade: this.stu.stuGrade.toString(),
depName: this.stu.depName,
majName: this.stu.majName,
});
}, 0);
16、条件查询整合分页查询
// 翻页
onChange(pagination) {
console.log('Page: ', pagination.current);
/* 带过滤条件的分页查询 */
// 在这个方法里,可以直接获取表单的属性
this.form.validateFields((err, values) => {
const _this = this;
this.axios.post("/list/"+ pagination.current +'/'+ pagination.pageSize,
{
stuName: values.stuName,
stuLoves: values.stuLoves.toString(),
stuGrade: values.stuGrade,
startDate:values.startDate,
endDate: values.endDate,
}).then(resp => {
_this.data = resp.data.records;
_this.pagination.total = resp.data.total;
_this.pagination.ipages = resp.data.pages;
});
});
},
17、checkbox 提交数据的格式问题
解决:转字符串
问题:提交不上去,如图 ,这样的数据,后端对接不上!
报错信息如下:
2020-07-17 19:34:12.080 WARN 18856 --- [io-8081-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token
at [Source: (PushbackInputStream); line: 1, column: 26] (through reference chain: com.feng.pojo.QueryCondition["stuLoves"])]
加上了 toString(),转为字符串后成功解决:
18、删除后的条件分页查询(根据情况,修改当前页)
此处如果不对查询参数 当前页 进行处理,则会出现一个不友好的情况:当最后一页只有一条数据时,删除后,该页面就为空白了!
<script>
// 删除学生信息
delStu(record){
const _this = this;
this.$confirm({
title: '删除提示',
content: '您确定要删除 “'+record.stuName+'” 吗?',
// content: 'Some descriptions',
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk() {
_this.axios.delete('/delete/'+record.stuId).then(resp => {
if(resp.data.flag){
// 再查询一次
/* 带过滤条件的分页查询 */
// 在这个方法里,可以直接获取表单的属性
_this.form.validateFields((err, values) => {
let stuLovesStr = values.stuLoves+'';
// 如果删除时,本页只有一条数据,name当前页减一
let refreshPage = _this.pagination.current;
if(_this.pagination.total % _this.pagination.pageSize == 1){
refreshPage -= 1;
}
// 删除后的条件分页查询
_this.axios.post("/list/"+ refreshPage +'/'+ _this.pagination.pageSize,
{
stuName: values.stuName,
stuLoves: stuLovesStr,
stuGrade: values.stuGrade,
startDate:values.startDate,
endDate: values.endDate,
}).then(resp => {
_this.data = resp.data.records;
_this.pagination.total = resp.data.total;
_this.pagination.ipages = resp.data.pages;
_this.pagination.current = resp.data.current;
});
});
}
});
console.log('OK');
},
onCancel() {
console.log('Cancel');
}
});
},
</script>