iView
A high quality UI Toolkit built on Vue.js.
Features
- Dozens of useful and beautiful components.
- Friendly API. It's made for people with any skill level.
- Extensive documentation and demos.
- It is quite beautiful.
- Supports both Vue.js 2 and Vue.js 1.
Install
We provide an iView plugin for Vue CLI 3, which you can use to quickly build an iView-based project.
We also provide a starter kit iview-project for you.
Install iView
Using npm:
npm install iview-admin-package --save
Using alias:
config.resolve.alias.set('iview', 'iview-admin-package')
Using a script tag for global use:
<script type="text/javascript" src="iview.min.js"></script>
<link rel="stylesheet" href="dist/styles/iview.css">
Usage
搜索表格
- SearchForm
- 整合所有可编辑表格控件+分页控件+筛选区表单控件+tab切换控件+操作区控件
没有分页,没有data.list,可编辑
<!--
editIncell - 可编辑表格
@editCell - 编辑操作 this.$emit('editCell',list, index, key);
hasSearch - 是否有搜索按钮
hasPage - 是否有分页
loading - 默认:false, 用于操作区异步时的加载状态
-->
<search-from
ref="searchFrom" editIncell
:hasSearch="false"
:hasPage="false"
:listUrl="listUrl"
:query="from_query"
:columns="columns"
:loading="loading"
:tabs="from_tabs"
:tabData="from_tabData"
@editCell="table_edit">
<template slot-scope="{row}" slot='nameId' >
<div style="text-align:left;">{{row.categoryName}}</div>
</template>
</search-from>
template
<!-- 起止时间 width: 145 || 260 -->
<template slot-scope="{row}" slot='rangeTime' >
<div style="text-align:left;">{{row.startTime}} ~ {{row.endTime}}</div>
</template>
<!-- 操作区 -->
<template slot-scope="{row}" slot='optionId' >
<div class="action_table">
<a href="javascript:void(0)" class="barTable-btna" style="margin: 10px 5px 0 5px;">编辑</a>
<Poptip title="你确定要删除吗" confirm transfer style="text-align:left;" @on-ok="click_deleteButton(row)">
<a href="javascript:void(0)" class="barTable-btna" style="margin: 10px 5px 0 5px;">删除</a>
</Poptip>
</div>
</template>
<!-- userInfo + 图片 -->
<template slot-scope="{row}" slot='userId' >
<div class="cell-box cell-box-info">
<div class="cell-imgbox imgbox inline"><img class="imgbox-img" :src="row.portrait" /></div>
<div class="inline cell-count">
<p>{{row.userId}}</p>
<p>{{row.nickname}}</p>
</div>
</div>
</template>
columns
columns: [
//选择控件
{type: 'selection',width: 50, align:'center'},
//可编辑
{key: 'sort', title: '排序', minWidth: 120, align:'center', editable: true},
//操作
{title: '操作', align:'center', slot:'optionId',className:'action_table',width:140},
],
权限
//columns 格式变动
let columns_edit = _deepCopy(columns);
columns_edit.splice(1,0,{title: 'SKU', width: 80, align:'center', render: barTable, className:'action_table'})
SearchForm - from_query
//筛选数据区
* @param {Array} query 筛选数据区
* { type: "input", label: "主题", tips: "输入主题", key: "recommendName", value: "" },
// select选择列表
// selectList: 默认 [value:'',label:'']
* { type: "select", label: "商品分类", key: "categoryId", selectList: [], "item-value": "data", "item-label": "value" },
// datetime 时间控件
* { type: "datetime", dateType: "datetimerange", label: "起止时间", tips: "选择日期", start:'activityStartTime', end:'activityEndTime', value:["2019-10-03 00:00:00","2019-10-05 00:00:00"], className: "dateTime", format: "yyyy-MM-dd HH:mm:ss"},
// 按钮区
// show - 是否显示
* { type: "button", label: "新增", icon: "ios-search", is: "error", show: true, render(event){} },
SearchForm - 其他
// 判断表格的单选和复选 - highlight
* if(this.highlight) newArr.unshift({key: 'goodsId', title: ' ', width: 50, align:'center', editRadio:true})
else newArr.unshift({type: 'selection',width: 50, align:'center'});
render - 参考
const barTable = (h, {column, index, row}) => {
let arr = [];
let placement = (index<3)?"bottom":'top';
if(that.isEditButton && row.activityStatus==1){
arr.push(h('A', {
class: 'barTable-btna',
style: {
margin: '10px 5px 0 5px',
},
on: {
click(){
that.click_editUserButton(row);
}
}
},'编辑'));
};
if(that.isPauseButton && (row.activityStatus==2||row.activityStatus==4)){
let btntxt = (row.activityStatus==4)?'启用':'暂停'
arr.push(h('Poptip', {
props: {
confirm: true,
title: `你确定要${btntxt}吗?`,
transfer:true,
placement
},
style: {
textAlign: "left",
},
on: {
'on-ok'(){
return that.click_pauseButton(row);
}
}
}, [
h('A', { class: 'barTable-btna',style: {margin: '10px 5px 0 5px'}}, btntxt)
]));
};
return h('div', {
style: {
textAlign: "center",
//paddingBottom:'10px'
}
}, arr);
};
// 格式
const columns = [
{key: 'goodsJoinModeName', title: '参加活动商品总数', width: 130, align:'center',
render(h, {row}){
let isA = row.goodsJoinMode==2;
return h(isA?'A':'div',{
class:{
'barTable-btna barTable-btna-zw': isA
},
on:{
click(){
that.go_goodsJoinMode(row);
}
}
},`${row.goodsJoinModeName}${isA?'('+row.goodsJoinTotal+')':''}`)
}
},
{key: 'levelTotal', title: '优惠阶梯', width: 100, align:'center',
render: (h, {row})=> h('A',{class:'barTable-btna',on:{click(){that.go_levelTotal(row);}}},`${row.levelTotal||0}`)},
{title: '状态', width: 60, align:'center', render(h, {row}){
const stateList = ['','未开始','进行中','已结束','已暂停']
return <div><p class={row.activityStatus==2?`red`:``}>{stateList[row.activityStatus]}</p></div>
}},
{title: '起止时间', width: 145, align:'center', render(h, {row}){ //145 || 260
return <div><p style="text-align:left">{row.startTime} ~ {row.endTime}</p></div>
}},
{key: 'createTime', title: '创建时间', width: 140, align:'center'},
{title: '操作', align:'center',width: 100, render: barTable, className:'action_table'}
];
选择弹窗
- choice-popup
<choice-popup
class="ShoperExclusiveLink"
listUrl="memberLinkShopkeeperList"
:listParames="ChoicePopup_params"
title="选择店主"
listKey="userId"
:width="860"
:visible.sync="ChoicePopupVisible"
:columns="ChoicePopupColumns"
@ok="ChoicePopup_ok">
<template slot-scope="{row}" slot='userId' >
<div class="cell-box cell-box-info">
<div class="cell-imgbox imgbox inline"><img class="imgbox-img" :src="row.portrait" /></div>
<div class="inline cell-count">
<p>{{row.userId}}</p>
<p>{{row.nickname}}</p>
</div>
</div>
</template>
<template slot-scope="{row}" slot='groupName' >
<p style="text-align:left">{{ row.groupName }}</p>
</template>
</choice-popup>
data() {
return {
selectList: [],
//弹窗
ChoicePopupVisible: false,
ChoicePopupColumns: [
{type: 'selection',width: 50, align:'center'},
{key: 'id', title: '店主信息', minWidth: 160, align:'center', slot:'userId'},
],
isDiscern: true,
}
},
ChoicePopup_ok(selectList,ids){
if(ids && ids.length){
this.selectList = selectList;
this.ChoicePopupVisible = false;
}else return this.$tipsMessage("请选择店主");
},
微信文案表情
<FormItem label="文案">
<Dropdown trigger="custom" placement="bottom-start" :visible="faceVisible">
<Button type="primary" class="inline" @click="faceVisible=!faceVisible">插入表情</Button>
<div slot="list" class="faceList">
<div class="inline face-item" v-for="(item,index) in faceList" @click="click_face(index)">{{item}}</div>
<!-- <li v-for="(item,index) in faceList" :key="index" @click="getBrow(index)">{{item}}</li> -->
</div>
</Dropdown>
<div class="face-red">* 因PC端中微信表情代码不全,部分表情在PC中有显示问题,手机端微信不存在此问题,或者可以使用文字<span>[微笑]</span>这种方式添加微信表情</div>
<div style="margin-top:10px;">
<Input
type="textarea" show-word-limit
:autosize="{minRows: 4,maxRows: 8}"
v-model="formInfo.shareContent"
placeholder="文案 (200字内)"
class="fromInput" :maxlength="200"
></Input>
</div>
</FormItem>
<style scoped lang="less">
/deep/ .faceList{
font-size: 26px;
height: 500px;
overflow: auto;
.face-item{
cursor: pointer;
margin:4px 6px;
}
}
/deep/ .face-red{
color: red;
line-height: 1.5;
margin-top: 5px;
span{
background-color: aqua;
display: inline-block;
margin: 0 2px;
}
}
</style>
import emojis from '_a/json/emojis.json'; //微信表情数据
data() {
return {
faceVisible: false,
get faceList(){
return emojis.map((el,ix) => {
return el.char;
});
}
}
},
util
$cloneObj
let requedata = this.$cloneObj(this.formInfo);
Modal_ok()
//表单提交
async Modal_ok() {
let requedata = this.$cloneObj(this.formInfo);
let msg = "";
if((!requedata.goodsId)){
msg = "商品id不为空"
}
if(msg) return this.$tipsMessage(msg);
let requeurl = "memberLinkAdd";
this.save_loading = true
let res = await this.$post(requeurl,requedata);
this.save_loading = false
if(res) {
this.isShow = false;
this.$tipsMessage('保存成功',1);
this.$emit('modal-ok')
}
},
async Modal_ok() {
this.$refs['formValidate'].validate(async (valid) => {
if (valid) {
this.save_loading = true
let requedata = this.$cloneObj(this.formInfo);
let res = await this.$post('promoteRewardSetup',requedata);
this.save_loading = false
if(res) {
this.$tipsMessage('保存成功',1);
this.Modal_cencel();
}
} else {
this.$tipsMessage('提交失败');
}
})
},
Modal_cencel()
Modal_cencel(){
this.isShow = false;
this.$nextTick(this.$refs['formValidate'].resetFields);
},
动态路由
{
path: 'morePurchLevel/:id',
name: 'morePurchase_levelDetail',
parent: 'marketingManagement',
meta: {
title: route => `换购阶梯`,
hideInMenu: true,
isSpecial: true,
specialCode: 'morePurchase'
},
component: () =>
import ('@/view/detail-page')
},
注入
import details from './details-config'
export default (Vue) => {
for (let name in details) {
const value = details[name]
// const app = resolve => require([`./${value}.vue`], resolve)
Vue.component(`${name}`, () => import(`./${value}.vue`) )
}
}
使用
//监听返回
//1. bus中央事件总线
created(){
this.bus.$on('refresh', (data) => {
if(data==="EquipageCategory_listDetail"){
console.log('刷新');
return this.GetList()
};
})
},
//2. 监听路由
watch:{
$route(newVal, oldVal) {
if (newVal.name == "EquipageCategory" && (oldVal.name == 'EquipageCategory_listDetail')) {
console.log('刷新');
return this.GetList()
}
},
}
//跳转
//return that.$go({name:"news_detail",replace:false,params:{searchId: row.id},query:{n:row.popupTitle}}, that.$router);
//return that.$go('back', that.$router); == go(-1)
//return that.$go('back'); == http
go_levelTotal(row){
//跳转清除 - 防止参数被改动
//场景:detail提前打开,list中改变状态后某一个参数被改变,再次跳转detail,需要关闭前一个
this.$go({
name:"morePurchase_levelDetail",replace:false,
params:{id: row.id},
query:{state:row.activityStatus}
}, this.$router,1);
}
//关闭
this.closeNoTag({
name: 'morePurchase_levelDetail',
params: { id: row.id },
query: { state:row.activityStatus }
});
关闭路由页面
import { mapState, mapActions, mapMutations } from 'vuex'
...mapMutations([
'closeTag','closeNoTag'
]),
//关闭其他tab
this.closeNoTag({
name: 'morePurchase_levelDetail',
params: { id: row.id },
query: { state:row.activityStatus }
});
//关闭当前tab
this.closeTag(this.$route)
其他
$Modal
click_deleteButton(){
let msg = "";
if(!this.selectList.length) msg = "请选择信息";
if(msg) return this.$tipsMessage(msg);
const _this = this;
this.$Modal.confirm({
title: '提示',
content: '是否确认删除吗',
loading: true,
async onOk(){
let ids = _this.selectList.map((el,ix) => {
return el.id; //id
})
let res = await _this.$get('memberLinkDelete',{
ids: ids.join(',')
// idList: ids.join(',')
});
this.buttonLoading = false;
if(res){
_this.selectList = [];
_this.$Modal.remove();
_this.$tipsMessage('删除成功',1);
_this.GetList();
};
}
});
},
<Modal v-model="category_visble" :width="360" :title="category_edit?'编辑分类':'新增分类'">
<div class="model-wraper" @keydown.enter="category_ok">
<Form>
<FormItem label="分类名称" style="margin-bottom:0">
<div class="form-cont inline" style="width:70%;">
<Input v-model="category_input" placeholder="输入分类名称" class="fromInput" maxlength="5" clearable></Input>
</div>
</FormItem>
</Form>
</div>
<div slot="footer">
<Button size="large" @click="category_visble=false">取消</Button>
<Button :loading="save_loading" type="primary" size="large" @click="category_ok">确定</Button>
</div>
</Modal>
导出
click_Export(){
let searchForm = this.$refs.searchFrom.searchForm;
let startTime = searchForm.userStartTime;
let endTime = searchForm.userEndTime;
let msg = "";
if(!(searchForm.userId || searchForm.orderId || searchForm.couponId || searchForm.createName) && !(startTime && endTime)) msg = "时间必填";
else if(startTime && endTime) {
let diffTime = new Date(endTime) - new Date(startTime)
let maxDay = 86400000 * 30;
if(diffTime>maxDay) msg = "时间跨度最大为一个月";
};
if(msg) {
this.$Message.warning(msg);
return false;
};
exportExcel(this,'couponUserLogExecl',searchForm);
},
form
<Modal v-model="isShow" class="modalwarp" :width="420" title="设置推广奖励和条件" @on-cancel="Modal_cencel">
<div class="model-wraper" > <!-- @keydown.enter.prevent="Modal_ok" -->
<Form ref="formValidate" :rules="ruleInfo" :model="formInfo" :label-width="80">
<FormItem label="推广奖励" prop="reward">
<div class="form-cont inline" style="width:70%;">
<Input type="number" v-model="formInfo.reward" placeholder="小数点后两位,不能超过100.00" class="fromInput" clearable></Input>
</div>
<div class="inline" style="margin-left:12px;">装币</div>
</FormItem>
<FormItem label="素材数量" prop="materialNum">
<div class="form-cont inline" style="width:70%;">
<Input type="number" v-model="formInfo.materialNum" placeholder="正整数,最小5" class="fromInput" clearable></Input>
</div>
</FormItem>
<FormItem label="引流uv" prop="uv">
<div class="form-cont inline" style="width:70%;">
<Input type="number" v-model="formInfo.uv" placeholder="正整数,最小5" class="fromInput" clearable></Input>
</div>
</FormItem>
</Form>
</div>
<div slot="footer">
<Button size="large" @click="Modal_cencel">取消</Button>
<Button :loading="save_loading" type="primary" size="large" @click="Modal_ok">确定</Button>
</div>
</Modal>
return {
save_loading: false,
formInfo: {
reward: '',
materialNum: '',
uv: '',
},
ruleInfo: {
fill: [{ required: true, type: 'number', message: '必填信息不为空' }],
reward: [
{ required: true, message: '推广奖励不为空'},
{ validator: changeRewardCheck }
],
materialNum:[
{ required: true, message: '素材数量不为空'},
{ validator: changeNumCheck }
],
uv:[
{ required: true, message: '引流uv不为空'},
{ validator: changeNumCheck }
]
}
}