一、分页排序案例

后端负责提供接口(3000)

前端负责业务逻辑(8080)

接口地址:从8080跨域到3000拿数据

http://127.0.0.1:3000/shouji

http://127.0.0.1:8080/api/shouji

分页排序接口:
http://127.0.0.1:3000/shouji?page=1&pagesize=5&sortby=price&sortdirection=dao

代理跨域回来的数据接口地址:
http://127.0.0.1:8080/api/shouji?page=1&pagesize=5&sortby=price&sortdirection=dao

后端app.js

var express = require("express");
var url = require("url");
var app = express(); var arr = [
{"id" : 1 , "title" : "苹果A" , "price" : 1699},
{"id" : 2 , "title" : "苹果B" , "price" : 1999},
...
{"id" : 14 , "title" : "苹果N" , "price" : 8888}
]; app.get("/shouji" , function(req,res){
var obj = url.parse(req.url, true).query;
var page = obj.page; //页码
var pagesize = obj.pagesize; //每页显示的数量
var sortby = obj.sortby; //排序条件
var sortdirection = obj.sortdirection; //排序条件(正序或倒序) //按照id或价格排序
arr = arr.sort(function(a,b){
if(sortdirection == "zheng"){
return a[sortby] - b[sortby];
}else if(sortdirection == "dao"){
return b[sortby] - a[sortby];
}
})
//提供数据给前端
res.json({
"number" : arr.length , //商品总数量
"results": arr.slice((page - 1) * pagesize, page * pagesize) //显示多少条数据
})
});
app.listen(3000);

示例代码

前端main.js

import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import store from "./store";
Vue.use(Vuex); new Vue({
el : "#app",
store,
render : (h) => h(App)
})

示例代码

新建taobao文件夹存放三要素,然后在文件夹的index.js中引入三要素:

state.js、action.js、mutations.js三个文件:

export default {
...
}

store/index.js

import Vue from "vue";
import Vuex from "vuex";
import createLogger from "vuex/dist/logger"; import counterState from "./counter/state.js"
import counterMutations from "./counter/mutations.js"
import counterActions from "./counter/actions.js" import taobaoState from "./taobao/state.js"
import taobaoMutations from "./taobao/mutations.js"
import taobaoActions from "./taobao/actions.js" Vue.use(Vuex);
//全局数据
const store = new Vuex.Store({
state : {
counterState,
taobaoState
},
//同步的(commit)
mutations : {
...counterMutations,
...taobaoMutations
},
//异步的(dispatch)
actions : {
...counterActions,
...taobaoActions
},
plugins : [createLogger()]
}); export default store;

示例代码

state.js存储默认数据:

export default {
page : 1,
pagesize: 5,
sortby : "id",
sortdirection:"zheng",
number : 0,
results :[]
}

示例代码

App.vue

<template>
<div>
<table>
<tr>
<th>编号</th>
<th>商品</th>
<th>价格</th>
</tr>
</table>
</div>
</template>
<script>
export default {
created(){
//生命周期,当组件被创建时触发,发出一个异步请求接口数据
this.$store.dispatch("init");
}
}
</script>

示例代码

actions.js

export default {
async init({commit,state}){
var page = state.taobaoState.page;
var pagesize = state.taobaoState.pagesize;
var sortby = state.taobaoState.sortby;
var sortdirection = state.taobaoState.sortdirection;
//发出异步的get请求拿数据
var {results,number} = await fetch(`/api/shouji?page=${page}&pagesize=${pagesize}
&sortby=${sortby}&sortdirection=${sortdirection}`).then(data => data.json()); //数据从后端拿回来后,改变results和number
//改变state只能通过mutations
commit("changeResults", {results})
commit("changeNumber", {number})
}
}

示例代码

mutations.js,此时可以从控制台logger中查看请求数据成功,然后回到App.vue的结构显示数据。

export default {
changeResults(state , payload){
state.taobaoState.results = payload.results;
},
changeNumber(state, payload) {
state.taobaoState.number = payload.number;
}
}

示例代码

回到App.vue显示数据和换页:

<template>
<div>
<table>
<tr v-for="item in $store.state.taobaoState.results">
<td>{{item.id}}</td>
<td>{{item.title}}</td>
<td>{{item.price}}</td>
</tr>
</table>
<button v-for="i in allPage()" @click="changePage(i)">{{i}}</button>
</div>
</template>
<script>
export default {
created(){
//生命周期,当组件被创建的时候触发
this.$store.dispatch("init");
},
computed:{
allPage(){
//计算总页码数:总数量 / 每页数量,向上取整
var _state = this.$store.state.taobaoState;
return Math.ceil(_state.number / _state.pagesize)
}
},
methods : {
//分页页码跳转
changePage(page){
this.$store.dispatch("changePage", {page});
}
}
}
</script>

示例代码

actions.js

export default {
async init({commit,state}){
...
},
//其他都和init一样,只改名字即可
async changePage({commit,state},{page}){
//改变page
commit("changePage", {page})
//凑齐4个数据
var page = state.taobaoState.page;
var pagesize = state.taobaoState.pagesize;
var sortby = state.taobaoState.sortby;
var sortdirection = state.taobaoState.sortdirection; //发出请求和init方法的一样
var {results, number} = await fetch(`/api/shouji?page=${page}&pagesize=${pagesize}
&sortby=${sortby}&sortdirection=${sortdirection}`).then(data => data.json()); //改变state只能通过mutations
commit("changeResults", {results})
commit("changeNumber", {number})
}
}

示例代码

mutations.js

export default {
changeResult(state , payload){
state.taobaoState.results = payload.results;
},
changeNumber(state, payload) {
state.taobaoState.number = payload.number;
},
//页码跳转
changePage(state , payload){
state.taobaoState.page = payload.page;
}
}

示例代码

App.vue下面实现每页显示多少条:

<template>
<div>
<table>
<tr v-for="item in $store.state.taobaoState.results">
<td>{{item.id}}</td>
<td>{{item.title}}</td>
<td>{{item.price}}</td>
</tr>
</table>
<button v-for="i in allPage()" @click="changePage(i)">{{i}}</button> <select v-model="pagesize">
<option value="3">每页3条</option>
<option value="5">每页5条</option>
<option value="10">每页10条</option>
</select>
</div>
</template>
<script>
export default {
data(){
return {
pagesize : this.$store.state.taobaoState.pagesize
}
},
created(){
//生命周期,当组件被创建的时候触发
this.$store.dispatch("init");
},
methods : {
changePage(page){
this.$store.dispatch("changePage" , {page});
}
},
//v-mode的值不能加圆括号,所以不能直接计算,先通过data(){}中计算,再用watch监听变化
//Vue提供一种更通用的方式来观察和响应Vue实例上的数据变动:侦听属性。
//以V-model绑定数据时使用的数据变化监测
//使用watch允许我们执行异步操作
watch : {
pagesize(v){ //当pagesize改变,发出一个请求,去改变pagesize
this.$store.dispatch("changePageSize" , {pagesize : v});
}
}
}
</script>

示例代码

actions.js

export default {
async init({commit,state}){
...
},
async changePageSize({commit,state},{pagesize}){
//改变page
commit("changePageSize", {pagesize:pagesize})
//凑齐4个数据
var page = state.taobaoState.page;
var pagesize = state.taobaoState.pagesize;
var sortby = state.taobaoState.sortby;
var sortdirection = state.taobaoState.sortdirection; //发出请求
var {results,number} = await fetch(`/api/shouji?page=${page}&pagesize=${pagesize}
&sortby=${sortby}&sortdirection=${sortdirection}`).then(data => data.json()); //改变state只能通过mutations
commit("changeResults", {results})
commit("changeNumber", {number})
}
}

示例代码

mutations.js

export default {
//...省略
changePage(state , payload){
state.taobaoState.page = payload.page;
},
changePageSize(state, payload) {
state.taobaoState.pagesize = payload.pagesize;
}
}

示例代码

下面实现id和价格的排序

App.vue

<template>
<div>
<table>
<tr>
<th>
id:
<button @click="changeSort('id','dao')">↓</button>
<button @click="changeSort('id','zheng')">↑</button>
</th>
<th>商品:</th>
<th>
价格:
<button @click="changeSort('price','dao')">↓</button>
<button @click="changeSort('price','zheng')">↑</button>
</th>
</tr>
<tr v-for="item in $store.state.taobaoState.results">
<td>{{item.id}}</td>
<td>{{item.title}}</td>
<td>{{item.price}}</td>
</tr>
</table>
<button v-for="i in allPage()" @click="changePage(i)"
:class="{'cur':$store.state.taobaoState.page == i}">
{{i}}
</button>
</div>
</template>
<script>
export default {
methods : {
changePage(page){
this.$store.dispatch("changePage", {page});
},
changeSort(sortby , sortdirection){
this.$store.dispatch("changeSort", {sortby , sortdirection});
}
}
}
</script>
<style>
.cur{ background : orange;}
</style>

示例代码

actions.js封装成函数:

async function load(commit, state){
//凑齐4个
var page = state.taobaoState.page;
var pagesize = state.taobaoState.pagesize;
var sortby = state.taobaoState.sortby;
var sortdirection = state.taobaoState.sortdirection; //发出请求
var {results,number} = await fetch(`/api/shouji?page=${page}&pagesize=${pagesize}&sortby=${sortby}&sortdirection=${sortdirection}`).then(data => data.json()); //数据从后端拿回来后,改变results和number
//改变state只能通过mutations
commit("changeResults", { results})
commit("changeNumber", { number })
} export default {
async init({commit , state}){
await load(commit , state);
},
async changePage({ commit, state } , {page}) {
//改变page
commit("changePage", { page: page})
await load(commit, state);
},
async changePageSize({ commit, state }, { pagesize }) {
//改变pagesize
commit("changePageSize", { pagesize: pagesize })
//页码归1
commit("changePage", { page: 1 })
await load(commit, state);
},
//排序
async changeSort({commit, state}, {sortby, sortdirection}){
//改变pagesize
commit("changeSort", { sortby, sortdirection})
//页码归1
commit("changePage", { page: 1 })
await load(commit, state);
},
}

示例代码

mutations.js

export default {
//...省略
changePageSize(state, payload) {
state.taobaoState.pagesize = payload.pagesize;
},
changeSort(state , payload){
state.taobaoState.sortby = payload.sortby;
state.taobaoState.sortdirection = payload.sortdirection;
}
}

示例代码


二、Vue-cli

2.1 Vue-cli的安装

Vue 提供一个官方命令行工具(CLI),可用于快速搭建大型单页应用。该工具为现代化的前端开发工作流提供了开箱即用的构建配置。只需几分钟即可创建并启动一个带热重载、保存时静态检查以及可用于生产环境的构建配置的项目。

Vue-cli是Vue的快速起步工具(脚手架工具),再也不用手动配webpack。

Vue-loader的官网:

https://cn.vuejs.org/v2/guide/installation.html

https://vue-loader.vuejs.org/zh-cn/start/setup.html

在全局安装vue-cli:

npm install -g vue-cli

创建一个基于webpack模板的新项目文件夹,并初始化配置:

vue init webpack hello-vue
vue init webpack-simple hello-vue

vue init webpack-simple项目默认打包后只有一个html和js文件(适合小项目)

vue init webpack项目默认打包完之后,会有很标准的目录(适合中大型项目)

两种方式初始化Vue-cli项目的目录差别很大,你会发现vue init webpack的方式初始化项目,默认提供了很多webpack的配置,也更加方便你对代理(跨域)、最终打包资源放到服务器什么目录、以及js、css、img和项目在打包过程等优化的配置等。

vue init webpack

vue init webpack-simple

 

安装依赖:

npm install

启动项目:

npm run dev

2.2 Vue-cli的配置讲解

"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},

cross-env NODE_ENV=development 将环境变量设置成开发模式

cross-env NODE_ENV=production  将环境变量设置成生产模式

--open 自动开启浏览器

--hot 开启热更新, 热更新就是保存后进行局部刷新

打开项目以后 vue-cli给我们配置了很多东西。

.editorconfig对编辑器的统一配置,是让大家的代码有一个规范、代码缩进形式的统一,当大家提交代码后使用不同的编辑器打开时,显示的代码格式是一样的

root = true
[*]
charset = utf-8
indent_style = space //空格缩进
indent_size = 4 //统一缩进为4个
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

关于生产模式的配置

开发过程不需要优化配置,只有在生产模式下,才需要优化、css压缩打包到一个文件,js合并压缩,关于性能优化,小图片会转成base64 减少http请求。

if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

将vue-cli提供的src文件夹中的assets图片文件夹,移动到根目录外面去,就是为了不让图片webpack编译打包。

vue中使用图片的两种方式:

第一种:传统方式

<img src="../assets/logo.png">

第二种:使用vue的v-bind指令来使用data数据中的图片:

<img :src="imgSrc">
<script>
export default {
data () {
return {
imgSrc : "../assets/logo.png"
}
}
}
</script>

webpack将png的图片变成base64,使用url-loader

{
test: /\.(png|jpg|gif|svg)$/,
loader: 'url-loader',
options: {
limit: 8192
}
}

limit是设置一个图片大小的临界点,值小于8192字节的图片就转成base64图片源码,好处就是能减少一个HTTP请求。


2.3项目打包上线

如果把自己的项目放到服务器运行,就需要使用npm run build将自己的项目打包出来。

然后在dist文件夹下面,就有打包、优化好的项目文件、打包好的项目文件可以放到服务器中运行。

注意:项目启动一定要在服务器环境下运行,在webpack服务器、node、phpnow服务器都可以。


三、酷表单项目

main.js

import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue"; Vue.use(Vuex);
// 创建一个全局仓库
const store = new Vuex.Store({
state : { }
}) new Vue({
el : "#app",
store,
render : (h) => h(App)
})

示例代码

第一步:写静态页面组件,App.vue

<template>
<div>
<div class="warp">
<div class="leftPart">左侧部分</div>
<div class="centerPart">
<div class="outerBox onedit">
<div class="cbox">
<div class="qtitle"><em>*</em> 我是新的多选题目,请编辑</div>
<div class="qoption">
<label><input type="checkbox" />新的项目A</label>
<label><input type="checkbox" />新的项目B</label>
</div>
</div>
<span class="edit"></span>
<span class="up"></span>
<span class="down"></span>
</div>
<div class="outerBox">
<div class="cbox">
<div class="qtitle"><em>*</em> 我是新的单选题目,请编辑</div>
<div class="qoption">
<label><input type="radio" />新的项目A</label>
<label><input type="radio" />新的项目B</label>
</div>
</div>
<span class="edit"></span>
<span class="up"></span>
<span class="down"></span>
</div>
<div class="outerBox">
<div class="cbox">
<div class="qtitle"><em>*</em> 我是新的下拉选项题目,请编辑</div>
<div class="qoption">
<select>
<option>新的项目A</option>
<option>新的项目B</option>
</select>
</div>
</div>
<span class="edit"></span>
<span class="up"></span>
<span class="down"></span>
</div>
</div>
<div class="rightPart">右侧部分</div>
</div>
</div>
</template>
<style lang='stylus'>
.warp{
width:1300px;min-height:500px; margin:50px auto;overflow:hidden;
.leftPart,.rightPart{
float:left; width:350px;min-height:500px;background-color:#ccc;
}
.centerPart{
float:left; width:600px;min-height:500px;padding:20px;
overflow:hidden;box-sizing:border-box;background: #fff;
.outerBox{
width: 500px;position: relative;
.cbox{
width:500px; padding:10px 0px;
border-bottom: 1px solid #eee;position: relative;
.qtitle{
font-size:18px;font-weight:bold;margin-bottom:10px;
}
label{margin-right: 10px;cursor: pointer;}
input[type=checkbox], input[type=radio]{margin-right: 5px;}
select{
width:300px;height:30px;
border: 1px solid #bdbdbd;border-radius:6px;
}
}
.edit{
position:absolute;right:20px;top:16px;
width:20px;height:20px;
background:url(/images/bianji.svg);
background-size:cover; display:none;
}
.down{
position:absolute;right:50px;top:18px;
width:16px;height:16px;
background:url(/images/down.svg);
background-size:cover;display:none;
}
.up{
position:absolute;right:80px;top:18px;
width:16px;height:16px;
background:url(/images/up.svg);
background-size:cover;display:none;
}
&:hover .edit, &:hover .up, &:hover .down{display:block;}
&.onedit{animation:donghua .5s linear infinite alternate;}
@-webkit-keyframes donghua{
0%{box-shadow:0px 0px 0px red;}
100%{box-shadow:0px 0px 20px red;}
}
}
}
}
</style>

示例代码


第二步:拆分组件App.vue

<template>
<div>
<div class="warp">
<div class="leftPart">
<typeTestArea></typeTestArea>
</div>
<div class="centerPart">
<div class="outerBox">
<singleOption></singleOption>
<span class="edit"></span>
<span class="up"></span>
<span class="down"></span>
</div>
<div class="outerBox">
...
</div>
</div>
<div class="rightPart">
<setArea></setArea>
</div>
</div>
</div>
</template>
<script>
import singleOption from "./components/singleOption.vue";
import multipleOption from "./components/multipleOption.vue";
import menuOption from "./components/menuOption.vue";
import setArea from "./components/setArea.vue";
import typeTestArea from "./components/typeTestArea.vue"; export default{
components:{
singleOption,
multipleOption,
menuOption,
setArea,
typeTestArea
}
}
</script>

示例代码

把单选、多选、下拉分别拆分到singleOption.vue、multipleOption.vue、menuOption.vue中。

组件中的.cbox类名可以不用写了,后面会在App.vue中添加。


第三步:设置题目默认数据和显示到视图(App.vue)

<template>
<div>
<div class="warp">
<div class="leftPart">
<typeTestArea></typeTestArea>
</div>
<div class="centerPart" >
<div class="outerBox" v-for="(item,index) in q">
<!-- <singleOption></singleOption> -->
<!-- :is="item.type" 表示要显示的选项类型 -->
<div :is="item.type" :item="item" :index="index" class="cbox"></div>
<span class="edit" :data-index="index"></span>
<span class="up" :data-index="index"></span>
<span class="down" :data-index="index"></span>
</div>
</div>
<div class="rightPart">
<setArea></setArea>
</div>
</div>
</div>
</template>
<script>
export default{
data(){
return {
q:[
{
"title":"你觉得下面哪个学历最牛叉?",
"type":"singleOption",
"option":[
{"v":"家里蹲大学"},
{"v":"英国贱桥大学"},
{"v":"美国麻绳礼工"},
{"v":"蓝翔技工学校"}
],
"required":false //是否为必填
},
{
"title":"你喜欢吃的食物? ",
"type":"multipleOption",
"option":[
{"v":"榴莲"},
{"v":"香蕉"},
{"v":"葡萄"},
{"v":"梨子"}
],
"required":false
},
{
"title":"治疗失眠最有效的方法是?",
"type":"menuOption",
"option":[
{"v":"吃安眠药"},
{"v":"看国产电视剧"},
{"v":"催眠术"},
{"v":"用大锤打晕"}
],
"required":false
}
]
}
}
}
</script>

示例代码

下面把数据显示在视图

单选组件singleoption.vue:

<template>
<div>
<div class="qtitle">
<em v-show="item.required"> * </em> {{index+1}}、{{item.title}}
</div>
<div class="qoption">
<label v-for="option in item.option">
<input type="radio" :name="'q'+(index+1)" /> {{option.v}}
</label>
</div>
</div>
</template>
<script>
export default{
props:["item","index"]
}
</script>

示例代码

多选组件multipleoption.vue:

<template>
<div>
<div class="qtitle">
<em v-show="item.required"> * </em> {{index+1}}、{{item.title}}
</div>
<div class="qoption">
<label v-for="option in item.option">
<input type="checkbox" :name="'q'+(index+1)" /> {{option.v}}
</label>
</div>
</div>
</template>
<script>
export default{
props:["item","index"]
}
</script>

示例代码

下拉组件menuoption.vue:

<template>
<div>
<div class="qtitle">
<em v-show="item.required">*</em> {{index+1}}、{{item.title}}
</div>
<div class="qoption">
<select>
<option v-for="option in item.option" :value="option.v">
{{option.v}}
</option>
</select>
</div>
</div>
</template>
<script>
export default{
props:["item","index"]
}
</script>

示例代码


第四步:拖拽

App.vue

<script>
export default{
data(){
return {
...
},
components:{
...
},
//组件上树之后的生命周期
mounted:function(){
var self = this;
//draggable(拖拽)和sortable(拖拽排序)结合使用
//拖拽
$('.typeTestBox li').draggable({
connectToSortable:".centerPart", //可拖拽到什么位置
helper:"clone", //克隆拖拽
revert: "invalid",//拖拽停止时,归位的动画
});
//拖拽排序
$('.centerPart').sortable({
cancel:".cbox,span", //禁止从匹配的元素上拖拽排序。
//当排序停止时触发该事件。
stop:function(event,ui){
//获取拖拽后的排序编号和data-titletype属性值
var index = $(ui.item[0]).index();
var titleType = $(ui.item[0]).data("titletype");
//拖拽后题目名称消失
$(ui.item[0]).remove();
//然后从index开始,不删除,添加新项
self.q.splice(index,0,{
"title":"一个新的题目,请编辑",
"type":titleType,
"option":[
{"v":"新选项A"},
..
{"v":"新选项D"}
],
"required":false
});
}
})
//事件委托,上箭头、下箭头
//向上排序交互位置
$(".centerPart").on('click','.up', function(event){
var index = $(this).data("index"); //获取题目编号
if(index > 0){//如果大于0即可交换位置
//尾删头插
//temp是要添加的新项,即删除的那项(即当前点击的项)
var temp = self.q.splice(index,1)[0];
//从当前的上一题开始,删除0项,从后面添加新项
self.q.splice(index-1,0,temp);
};
});
$(".centerPart").on('click','.down', function(event){
var index = $(this).data("index");
var temp = self.q.splice(index,1)[0];
self.q.splice(index+1,0,temp)
});
}
}
</script>

示例代码


第五步:题目编辑

main.js

import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue"; Vue.use(Vuex);
// 创建一个全局仓库
const store = new Vuex.Store({
state : {
nowedit : 1 //当前编辑的题号
},
mutations: {
// 修改全局的nowedit
changeNowEdit(state,{nowedit}){
state.nowedit = nowedit
}
}
})

示例代码

App.vue:

<template>
<div>
<div class="wrap">
<div class="leftPart">
<typeTestArea></typeTestArea>
</div>
<div class="centerPart">
<div class="outerBox" v-for="(item,index) in q">
<div :is="item.type" :item="item" :index="index" class="cbox"></div>
<span class="edit" :data-index="index"></span>
<span class="up" :data-index="index"></span>
<span class="down" :data-index="index"></span>
</div>
</div>
<div class="rightPart">
<setArea v-if="$store.state.nowedit != 0" :item="q[$store.state.nowedit - 1]">
</setArea>
</div>
</div>
</div>
</template>

示例代码

setarea.vue右侧组件布局:

<template>
<div class="typeTestArea">
<h3>设置题目</h3>
<div class="con">
标题:<input type="text" v-model="item.title" />
</div>
<div class="con">
是否必填:<input type="checkbox" v-model="item.required">
</div>
<div class="con">
题型:
<input type="radio" value="singleoption" v-model="item.type" />单选
<input type="radio" value="multipleoption" v-model="item.type" />多选
<input type="radio" value="menuoption" v-model="item.type" />下拉
</div>
<div class="con">
<!-- 题目选项们(更改之后,鼠标离开后双向修改) -->
<div class="options">
<p v-for="(option,index) in item.option" :key="option.v">
<input type="text" v-model="option.v">
<span class="del"></span>
<span class="changeOrder"></span>
</p>
</div>
<p class="addoption" >添加新的选项</p>
</div>
</div>
</template>
<script>
export default{
props:["item"],
}
</script>
<style scoped lang='stylus'>
.typeTestArea{
padding:20px;
.con{
line-height:150%;padding:10px 0;
}
input[type="text"]{
width:230px;height:30px;color: #495060;
border-radius:4px; border: 1px solid #dddee1;padding-left:5px;
}
.addoption{
width:230px;height:35px;background: #2db7f5;border-radius:5px;
}
.addoption:hover{background:#18b566;}
.options input{ margin-bottom:10px; }
.del,.changeOrder{
display:inline-block;width: 16px;height:16px;padding:2px;
background:url(/images/del.svg);background-size:cover;
position:relative;top:6px;left:5px;border-radius:5px;
}
.changeOrder{
background:url(/images/order.svg);cursor:move;
}
.del:hover,.changeOrder:hover{animation:donghua 0.3s linear 0s alternate;}
@-webkit-keyframes donghua{
0%{transform:rotate(0deg) scale(1);}
50%{transform:rotate(180deg) scale(1.3);}
100%{transform:rotate(360deg) scale(1);}
}
}
</style>

示例代码

setarea.vue右侧组件功能实现:

<template>
<div class="typeTestArea">
<h3>设置题目</h3>
<div class="con">
标题:<input type="text" v-model="item.title" />
</div>
<div class="con">
是否必填:<input type="checkbox" v-model="item.required">
</div>
<div class="con">
题型:
<input type="radio" value="singleoption" v-model="item.type" />单选
<input type="radio" value="multipleoption" v-model="item.type" />多选
<input type="radio" value="menuoption" v-model="item.type" />下拉
</div>
<div class="con">
<!-- 题目选项们(更改之后,鼠标离开后双向修改) -->
<div class="options" ref="option">
<p v-for="(option,index) in item.option" :key="option.v">
<input type="text" v-model.lazy="option.v">
<span class="del" @click="delBtn(index)"></span>
<span class="changeOrder"></span>
</p>
</div>
<p class="addoption" @click="addoption">添加新的选项</p>
</div>
</div>
</template>
<script>
export default{
props:["item"],
methods:{
addoption(){
this.item.option.push({"v":""});
},
delBtn(index){
this.item.option.splice(index,1);
}
},
mounted:function(){
var startIndex = 0; //全局变量
var self =this;
$(this.$refs.option).sortable({
handle:".changeOrder", //限制拖拽的对象
//拖拽开始
start:function(e,ui){
//获取当前拖拽的编号
startIndex = $(ui.item).index();
console.log(startIndex)
},
//拖拽结束后
stop:function(e,ui){
//拖拽结束后的编号
var endIndex = $(ui.item).index();
//视图中题目的选项也要跟着变化(前删后插)
//从startIndex删除1项
var delOption = self.item.option.splice(startIndex,1)[0];
//从endIndex的位置添加之前删除的项
self.item.option.splice(endIndex,0,delOption);
}
})
}
}
</script>

示例代码

App.vue 编辑题目按钮:

<template>
<div>
<div class="warp">
<div class="leftPart">
<typetestarea></typetestarea>
</div>
<div class="centerPart" >
<div :class="{'outerBox':true,'onedit':$store.state.nowedit==index+1}" v-for="(item,index) in q">
<div :is="item.type" :item="item" :index="index" class="cbox"></div>
<span class="edit" :data-index="index"
@click="$store.commit('changeNowEdit',{'nowedit':index+1})">
</span>
<span class="up" :data-index="index"
@click="$store.commit('changeNowEdit',{'nowedit':index+1})">
</span>
<span class="down" :data-index="index"
@click="$store.commit('changeNowEdit',{'nowedit':index+1})">
</span>
</div>
</div>
<div class="rightPart">
<setarea v-if="$store.state.nowedit != 0"
:item="q[$store.state.nowedit-1]">
</setarea>
</div>
</div>
</div>
</template>
<script>
export default{
data(){
return {
...
}
},
mounted:function(){
var self = this;
//拖拽排序
$('.centerPart').sortable({
cancel:".cbox,span",
// 当排序停止时触发该事件。
stop:function(event,ui){
...
self.q.splice(index,0,{
...
});
//拖拽添加题目完成后,让新的题目变成当前编辑状态
self.$store.commit('changeNowEdit',{'nowedit':index+1});
}
});
}
}
</script>

示例代码


v-model的问题:

1)我们的数据放在全局,全局的数据理论上讲只能有commit()来更改!

2)vue中视图更新的原理和React完全不同,vue使用数据劫持,只要数据变化,视图一定变化。

v-model可以直接和全局的store中的数据进行双向绑定!但是绑定了,就违背了commit()更改数据的原则,管他呢!!

我们可以在computed中写set()和get()方法,让get从store中取值,set发出commit命令。

官网:https://vuex.vuejs.org/zh-cn/forms.html


前端笔记之Vue(六)分页排序|酷表单实战&Vue-cli的更多相关文章

  1. vue教程1-01 v-model 一般表单元素(input) 双向数据绑定

    vue教程1-01   v-model 一般表单元素(input) 双向数据绑定el:'#box',//这里放的是选择器.不然会不生效 <!DOCTYPE html> <html l ...

  2. Vue基础-自定义事件的表单输入组件、自定义组件的 v-model

    Vue 测试版本:Vue.js v2.5.13 学习 Vue 的自定义事件的表单输入组件,觉得文档讲的不太细致,所以这里再细化一下: 如果不用 v-model,代码应该是这样: <myinput ...

  3. amazeui学习笔记--css(HTML元素3)--表单Form

    amazeui学习笔记--css(HTML元素3)--表单Form 一.总结 1.form样式使用:在容器上添加 .am-form class,容器里的子元素才会应用 Amaze UI 定义的样式. ...

  4. 前端MVC Vue2学习总结(五)——表单输入绑定、组件

    一.表单输入绑定 1.1.基础用法 你可以用 v-model 指令在表单控件元素上创建双向数据绑定.它会根据控件类型自动选取正确的方法来更新元素.尽管有些神奇,但 v-model 本质上不过是语法糖, ...

  5. MVC + Vue.js 初体验(实现表单操作)

    Vuejs http://cn.vuejs.org/ Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的 ...

  6. [原创]java WEB学习笔记62:Struts2学习之路--表单标签:form,表单标签的属性,textfield, password, hidden,submit ,textarea ,checkbox ,list, listKey 和 listValue 属性,select ,optiongroup ,checkboxlist

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  7. angularjs学习第四天笔记(第一篇:简单的表单验证)

    您好,我是一名后端开发工程师,由于工作需要,现在系统的从0开始学习前端js框架之angular,每天把学习的一些心得分享出来,如果有什么说的不对的地方,请多多指正,多多包涵我这个前端菜鸟,欢迎大家的点 ...

  8. vue指令:v-model绑定表单控件;v-model与v-bind结合使用

    一.v-model绑定表单控件 v-model 双向数据绑定:一般用于表单元素,会忽略表单元素的value.checked.selected的初始值,且将Vue实例的数据作为数据来源. 1. 单行文本 ...

  9. 六、Django之表单和类视图-Part 4

    一.表单form 为了接收用户的投票选择,我们需要在前端页面显示一个投票界面.让我们重写先前的polls/detail.html文件,代码如下: <h1>{{ question.quest ...

随机推荐

  1. 18c & 19c Physical Standby Switchover Best Practices using SQL*Plus (Doc ID 2485237.1)

    18c & 19c Physical Standby Switchover Best Practices using SQL*Plus (Doc ID 2485237.1) APPLIES T ...

  2. LVS的工作模式介绍和NAT模式&DR模式实验步骤

    一:LVS介绍 二.LVS的NAT和DR模式的实验及配置步骤 一.LVS的简单介绍 linux virtual server 简单来讲lvs是一段内核代码 类似于netfilter本身是一框架但不提供 ...

  3. Appium(三):安装appium client、adb命令

    1. 安装appium client appium client是对webdriver原生api的一些扩展和封装.它可以帮助我们更容易的写出用例,写出更好的用例. appium client是配合原生 ...

  4. Appium+Java 自动化测试系列二:Maven+Testng

    新建Maven项目作为测试项目分为3个步骤: 1.Eclipse安装Testng 插件 2.新建Maven项目 3.引入Testng 一.Eclipse安装Testng插件 TestNG安装可选择在线 ...

  5. go语言的json

    简介 json 中提供的处理 json 的标准包是 encoding/json,主要使用的是以下两个方法: // 序列化 func Marshal(v interface{}) ([]byte, er ...

  6. [Spring cloud 一步步实现广告系统] 1. 业务架构分析

    什么是广告系统? 主要包含: 广告主投放广告的<广告投放系统> 媒体方(广告展示媒介-)检索广告用的<广告检索系统> 广告计费系统(按次,曝光量等等) 报表系统 Etc. 使用 ...

  7. C# -- is和as运算符

    一.is运算符用于判断一个对象是否可以转换为指定的类型,不会抛出异常,返回bool值用来表示是否转换成功: if (myObj is MyClass) { //do... } 当满足以下任意一种情况时 ...

  8. Python:有参装饰器与多个装饰器装饰一个函数

    有参装饰器 def timmerout(flag1): #flag1 =flag def timmer(f): def inner(*args,**kwargs): if flag1: start_t ...

  9. python 对字典分别按照key值、value值进行排序

    1.sorted函数首先介绍sorted函数,sorted(iterable,key,reverse),sorted一共有iterable,key,reverse这三个参数. 其中iterable表示 ...

  10. 《跟我学shiro》

    张开涛<跟我学shiro>博客系列: Shiro目录 第一章  Shiro简介 第二章  身份验证 第三章  授权 第四章  INI配置 第五章  编码/加密 第六章  Realm及相关对 ...