Commit 46600559 authored by 王向前's avatar 王向前

添加部门信息、管理员设置、公司信息

parent f1a0bb95
......@@ -128,6 +128,34 @@ const routes = [
title: '修改密码',
},
},
{
path: '/pages/system/department/department',
name: 'department',
meta: {
title: '部门管理',
},
},
{
path: '/pages/system/txl/txl',
name: 'txl',
meta: {
title: '通讯录',
},
},
{
path: '/pages/system/glysetting/glysetting',
name: 'glysetting',
meta: {
title: '管理员设置',
},
},
{
path: '/pages/system/gsinfo/gsinfo',
name: 'gsinfo',
meta: {
title: '公司信息',
},
},
]
export default routes
/**
数据验证(表单验证)
来自 grace.hcoder.net
作者 hcoder 深海
版权声明 :
GraceUI 的版权约束是不能转售或者将 GraceUI 直接发布到公开渠道!
侵权必究,请遵守版权约定!
*/
module.exports = {
error:'',
check : function (data, rule){
for(var i = 0; i < rule.length; i++){
if (!rule[i].checkType){return true;}
if (!rule[i].name) {return true;}
if (!rule[i].errorMsg) {return true;}
if (!data[rule[i].name]) {this.error = rule[i].errorMsg; return false;}
switch (rule[i].checkType){
case 'string':
var reg = new RegExp('^.{' + rule[i].checkRule + '}$');
if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;}
break;
case 'int':
var reg = new RegExp('^(-[1-9]|[1-9])[0-9]{' + rule[i].checkRule + '}$');
if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;}
break;
break;
case 'between':
if (!this.isNumber(data[rule[i].name])){
this.error = rule[i].errorMsg;
return false;
}
var minMax = rule[i].checkRule.split(',');
minMax[0] = Number(minMax[0]);
minMax[1] = Number(minMax[1]);
if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'betweenD':
var reg = /^-?[1-9][0-9]?$/;
if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
var minMax = rule[i].checkRule.split(',');
minMax[0] = Number(minMax[0]);
minMax[1] = Number(minMax[1]);
if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'betweenF':
var reg = /^-?[0-9][0-9]?.+[0-9]+$/;
if (!reg.test(data[rule[i].name])){this.error = rule[i].errorMsg; return false;}
var minMax = rule[i].checkRule.split(',');
minMax[0] = Number(minMax[0]);
minMax[1] = Number(minMax[1]);
if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
this.error = rule[i].errorMsg;
return false;
}
break;
case 'same':
if (data[rule[i].name] != rule[i].checkRule) { this.error = rule[i].errorMsg; return false;}
break;
case 'notsame':
if (data[rule[i].name] == rule[i].checkRule) { this.error = rule[i].errorMsg; return false; }
break;
case 'email':
var reg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
break;
case 'phoneno':
var reg = /^1[0-9]{10,10}$/;
if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
break;
case 'zipcode':
var reg = /^[0-9]{6}$/;
if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
break;
case 'reg':
var reg = new RegExp(rule[i].checkRule);
if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
break;
case 'in':
if(rule[i].checkRule.indexOf(data[rule[i].name]) == -1){
this.error = rule[i].errorMsg; return false;
}
break;
case 'notnull':
if(data[rule[i].name] == null || data[rule[i].name].length < 1){this.error = rule[i].errorMsg; return false;}
break;
}
}
return true;
},
isNumber : function (checkVal){
checkVal = Number(checkVal);
if(checkVal == NaN){return false;}
return true;
}
}
\ No newline at end of file
<template>
<!-- 城市选择-->
<view class="city-select">
<scroll-view :scroll-top="scrollTop" scroll-y="true" class="city-select-main" id="city-select-main" :scroll-into-view="toView">
<!-- 预留搜索-->
<view class="city-serach" v-if="isSearch">
<!-- <input @input="keyInput" :placeholder="placeholder" class="city-serach-input" /> -->
<view class="cu-bar search bg-white ">
<view class="search-form round">
<text class="cuIcon-search" style="color: #007AFF;"></text>
<input style="color: #666;" :placeholder="placeholder" @input="keyInput" :adjust-position="true" type="text" confirm-type="search"></input>
</view>
</view>
</view>
<!-- 当前定位城市 -->
<view class="hot-title" v-if="activeCity && !serachCity">当前定位城市</view>
<view class="hot-city" v-if="activeCity && !serachCity">
<view class="hot-item" @click="cityTrigger(activeCity)">{{ activeCity[formatName] }}</view>
</view>
<!-- 热门城市 -->
<view class="hot-title" v-if="hotCity.length > 0 && !serachCity">热门城市</view>
<view class="hot-city" v-if="hotCity.length > 0 && !serachCity">
<template v-for="(item, index) in hotCity">
<view :key="index" @click="cityTrigger(item, 'hot')" class="hot-item">{{ item[formatName] }}</view>
</template>
</view>
<!-- 城市列表(搜索前) -->
<view class="citys" v-if="!serachCity">
<view v-for="(city, index) in sortItems" :key="index" v-show="city.isCity" class="citys-row">
<view class="citys-item-letter" :id="'city-letter-' + (city.name === '#' ? '0' : city.name)">{{ city.name }}</view>
<view class="citys-item" v-for="(item, inx) in city.citys" :key="inx" @click="cityTrigger(item)">{{ item.cityName }}-{{item.phone}}</view>
</view>
</view>
<!-- 城市列表(搜索后) -->
<view class="citys" v-if="serachCity">
<view v-for="(item, index) in searchDatas" :key="index" class="citys-row">
<view class="citys-item" :key="index" @click="cityTrigger(item)">{{ item.name }}-{{item.phone}}</view>
</view>
</view>
</scroll-view>
<!-- 城市选择索引-->
<view class="city-indexs-view" v-if="!serachCity">
<view class="city-indexs">
<view v-for="(cityIns, index) in handleCity" class="city-indexs-text" v-show="cityIns.isCity" :key="index" @click="cityindex(cityIns.forName)">
{{ cityIns.name }}
</view>
</view>
</view>
</view>
</template>
<script>
import citySelect from './citySelect.js';
export default {
props: {
//查询提示文字
placeholder: {
type: String,
default: '请输入城市名称'
},
//传入要排序的名称
formatName: {
type: String,
default: 'cityName'
},
//当前定位城市
activeCity: {
type: Object,
default: () => null
},
//热门城市
hotCity: {
type: Array,
default: () => []
},
//城市数据
obtainCitys: {
type: Array,
default: () => []
},
//是否有搜索
isSearch: {
type: Boolean,
default: true
}
},
data() {
return {
toView: 'city-letter-Find', //锚链接 初始值
scrollTop: 0, //scroll-view 滑动的距离
cityindexs: [], // 城市索引
activeCityIndex: '', // 当前所在的城市索引
handleCity: [], // 处理后的城市数据
serachCity: '', // 搜索的城市
cityData: []
};
},
computed: {
/**
* @desc 城市列表排序
* @return Array
*/
sortItems() {
for (let index = 0; index < this.handleCity.length; index++) {
if (this.handleCity[index].isCity) {
let cityArr = this.handleCity[index].citys;
cityArr = cityArr.sort(function(a, b) {
var value1 = a.unicode;
var value2 = b.unicode;
return value1 - value2;
});
}
}
return this.handleCity;
},
/**
* @desc 搜索后的城市列表
* @return Array
*/
searchDatas() {
var searchData = [];
for (let i = 0; i < this.cityData.length; i++) {
if (this.cityData[i][this.formatName].indexOf(this.serachCity) !== -1) {
searchData.push({
oldData: this.cityData[i],
name: this.cityData[i][this.formatName],
phone:this.cityData[i]["phone"]
});
}
}
return searchData;
}
},
created() {
// 初始化城市数据
this.cityData = this.obtainCitys;
this.initializationCity();
this.buildCityindexs();
},
watch: {
obtainCitys(newData) {
this.updateCitys(newData);
}
},
methods: {
/**
* @desc 初始化
*/
updateCitys(data) {
if (data && data.length) {
this.cityData = data;
this.initializationCity();
this.buildCityindexs();
}
},
/**
* @desc 监听输入框的值
*/
keyInput(event) {
this.serachCity = event.detail.value;
},
/**
* @desc 初始化城市数据
* @return undefind
*/
initializationCity() {
this.handleCity = [];
const cityLetterArr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#'];
for (let index = 0; index < cityLetterArr.length; index++) {
this.handleCity.push({
name: cityLetterArr[index],
isCity: false, // 用于区分是否含有当前字母开头的城市
citys: [], // 存放城市首字母含是此字母的数组
forName: 'city-letter-' + (cityLetterArr[index] == '#' ? '0' : cityLetterArr[index]) //label的绑定
});
}
},
/**
* @desc 得到城市的首字母
* @param str String
*/
getLetter(str) {
return citySelect.getFirstLetter(str[0]);
},
/**
* @desc 构建城市索引
* @return undefind
*/
buildCityindexs() {
this.cityindexs = [];
for (let i = 0; i < this.cityData.length; i++) {
// 获取首字母
const cityLetter = this.getLetter(this.cityData[i][this.formatName]).firstletter;
// 获取当前城市首字母的unicode,用作后续排序
const unicode = this.getLetter(this.cityData[i][this.formatName]).unicode;
const index = this.cityIndexPosition(cityLetter);
if (this.cityindexs.indexOf(cityLetter) === -1) {
this.handleCity[index].isCity = true;
this.cityindexs.push(cityLetter);
}
this.handleCity[index].citys.push({
cityName: this.cityData[i][this.formatName],
phone: this.cityData[i]["phone"],
unicode: unicode,
oldData: this.cityData[i]
});
}
},
/**
* @desc 滑动到城市索引所在的地方
* @param id String 城市索引
*/
cityindex(id) {
this.toView = id;
// //创建节点查询器
// const query = uni.createSelectorQuery().in(this)
// var that = this
// that.scrollTop = 0
// //滑动到指定位置(解决方法:重置到顶部,重新计算,影响:页面会闪一下)
// setTimeout(() => {
// query
// .select('#city-letter-' + (id === '#' ? '0' : id))
// .boundingClientRect(data => {
// // console.log("得到布局位置信息" + JSON.stringify(data));
// // console.log("节点离页面顶部的距离为" + data.top);
// data ? (that.scrollTop = data.top) : void 0
// })
// .exec()
// }, 0)
},
/**
* @desc 获取城市首字母的unicode
* @param letter String 城市索引
*/
cityIndexPosition(letter) {
if (!letter) {
return '';
}
const ACode = 65;
return letter === '#' ? 26 : letter.charCodeAt(0) - ACode;
},
/** @desc 城市列表点击事件
* @param Object
*/
cityTrigger(item) {
// 传值到父组件
this.$emit('cityClick', item.oldData ? item.oldData : item);
}
}
};
</script>
<style lang="scss">
//宽度转换vw
@function vww($number) {
@return ($number / 375) * 750 + rpx;
}
view {
box-sizing: border-box;
}
.city-serach {
width: 100%;
color: #4a4a4a;
padding: 0 vww(10);
&-input {
margin: vww(10) 0;
height: vww(40);
line-height: vww(40);
font-size: vww(14);
padding: 0 vww(5);
border: 1px solid #4d8cfd;
border-radius: 3px;
}
}
.city-select-main {
position: relative;
// overflow: scroll;
// -webkit-overflow-scrolling: touch;
width: 100%;
height: 100%;
background: #f6f5fa;
// overflow-y: auto;
}
.city-select {
position: relative;
width: 100vw;
height: 100vh;
background: #f6f5fa;
// 热门城市
.hot-title {
padding-left: vww(23);
width: 100vw;
font-size: 14px;
line-height: vww(40);
color: #9b9b9b;
}
.hot-city {
padding-left: vww(23);
padding-right: vww(20);
overflow: hidden;
width: 100vw;
.hot-item {
float: left;
padding: 0 vww(5);
margin-right: vww(16);
margin-bottom: vww(6);
overflow: hidden;
width: vww(100);
height: vww(31);
font-size: 14px;
text-align: center;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
line-height: vww(31);
color: #4a4a4a;
background: #fff;
border: 1px solid #ebebf0;
&:nth-child(3n) {
margin-right: 0;
}
}
.hot-hidden {
display: none;
margin-right: 0;
}
}
.citys {
.citys-row {
padding-left: vww(18);
width: 100%;
font-size: 14px;
background: #fff;
.citys-item-letter {
margin-left: vww(-18);
padding-left: vww(18);
margin-top: -1px;
width: 100vw;
line-height: vww(30);
color: #9b9b9b;
background: #f6f5fa;
border-top: none;
}
.citys-item {
width: 100%;
line-height: vww(50);
color: #4a4a4a;
border-bottom: 1px solid #ebebf0;
&:last-child {
border: none;
}
}
}
}
.city-indexs-view {
position: absolute;
right: 0;
top: 0;
z-index: 999;
display: flex;
width: vww(20);
height: 100%;
text-align: center;
.city-indexs {
width: vww(20);
text-align: center;
vertical-align: middle;
align-self: center;
.city-indexs-text {
margin-bottom: vww(10);
width: vww(20);
font-size: 12px;
color: #4d8cfd;
&:last-child {
margin-bottom: 0;
}
}
}
}
}
</style>
const citySelect = {
/* eslint-disable */
firstletter: 'YDYQSXMWZSSXJBYMGCCZQPSSQBYCDSCDQLDYLYBSSJGYZZJJFKCCLZDHWDWZJLJPFYYNWJJTMYHZWZHFLZPPQHGSCYYYNJQYXXGJHHSDSJNKKTMOMLCRXYPSNQSECCQZGGLLYJLMYZZSECYKYYHQWJSSGGYXYZYJWWKDJHYCHMYXJTLXJYQBYXZLDWRDJRWYSRLDZJPCBZJJBRCFTLECZSTZFXXZHTRQHYBDLYCZSSYMMRFMYQZPWWJJYFCRWFDFZQPYDDWYXKYJAWJFFXYPSFTZYHHYZYSWCJYXSCLCXXWZZXNBGNNXBXLZSZSBSGPYSYZDHMDZBQBZCWDZZYYTZHBTSYYFZGNTNXQYWQSKBPHHLXGYBFMJEBJHHGQTJCYSXSTKZHLYCKGLYSMZXYALMELDCCXGZYRJXSDLTYZCQKCNNJWHJTZZCQLJSTSTBNXBTYXCEQXGKWJYFLZQLYHYXSPSFXLMPBYSXXXYDJCZYLLLSJXFHJXPJBTFFYABYXBHZZBJYZLWLCZGGBTSSMDTJZXPTHYQTGLJSCQFZKJZJQNLZWLSLHDZBWJNCJZYZSQQYCQYRZCJJWYBRTWPYFTWEXCSKDZCTBZHYZZYYJXZCFFZZMJYXXSDZZOTTBZLQWFCKSZSXFYRLNYJMBDTHJXSQQCCSBXYYTSYFBXDZTGBCNSLCYZZPSAZYZZSCJCSHZQYDXLBPJLLMQXTYDZXSQJTZPXLCGLQTZWJBHCTSYJSFXYEJJTLBGXSXJMYJQQPFZASYJNTYDJXKJCDJSZCBARTDCLYJQMWNQNCLLLKBYBZZSYHQQLTWLCCXTXLLZNTYLNEWYZYXCZXXGRKRMTCNDNJTSYYSSDQDGHSDBJGHRWRQLYBGLXHLGTGXBQJDZPYJSJYJCTMRNYMGRZJCZGJMZMGXMPRYXKJNYMSGMZJYMKMFXMLDTGFBHCJHKYLPFMDXLQJJSMTQGZSJLQDLDGJYCALCMZCSDJLLNXDJFFFFJCZFMZFFPFKHKGDPSXKTACJDHHZDDCRRCFQYJKQCCWJDXHWJLYLLZGCFCQDSMLZPBJJPLSBCJGGDCKKDEZSQCCKJGCGKDJTJDLZYCXKLQSCGJCLTFPCQCZGWPJDQYZJJBYJHSJDZWGFSJGZKQCCZLLPSPKJGQJHZZLJPLGJGJJTHJJYJZCZMLZLYQBGJWMLJKXZDZNJQSYZMLJLLJKYWXMKJLHSKJGBMCLYYMKXJQLBMLLKMDXXKWYXYSLMLPSJQQJQXYXFJTJDXMXXLLCXQBSYJBGWYMBGGBCYXPJYGPEPFGDJGBHBNSQJYZJKJKHXQFGQZKFHYGKHDKLLSDJQXPQYKYBNQSXQNSZSWHBSXWHXWBZZXDMNSJBSBKBBZKLYLXGWXDRWYQZMYWSJQLCJXXJXKJEQXSCYETLZHLYYYSDZPAQYZCMTLSHTZCFYZYXYLJXDCJQAGYSLCQLYYYSHMRQQKLDXZSCSSSYDYCJYSFSJBFRSSZQSBXXPXJYSDRCKGJLGDKZJZBDKTCSYQPYHSTCLDJDHMXMCGXYZHJDDTMHLTXZXYLYMOHYJCLTYFBQQXPFBDFHHTKSQHZYYWCNXXCRWHOWGYJLEGWDQCWGFJYCSNTMYTOLBYGWQWESJPWNMLRYDZSZTXYQPZGCWXHNGPYXSHMYQJXZTDPPBFYHZHTJYFDZWKGKZBLDNTSXHQEEGZZYLZMMZYJZGXZXKHKSTXNXXWYLYAPSTHXDWHZYMPXAGKYDXBHNHXKDPJNMYHYLPMGOCSLNZHKXXLPZZLBMLSFBHHGYGYYGGBHSCYAQTYWLXTZQCEZYDQDQMMHTKLLSZHLSJZWFYHQSWSCWLQAZYNYTLSXTHAZNKZZSZZLAXXZWWCTGQQTDDYZTCCHYQZFLXPSLZYGPZSZNGLNDQTBDLXGTCTAJDKYWNSYZLJHHZZCWNYYZYWMHYCHHYXHJKZWSXHZYXLYSKQYSPSLYZWMYPPKBYGLKZHTYXAXQSYSHXASMCHKDSCRSWJPWXSGZJLWWSCHSJHSQNHCSEGNDAQTBAALZZMSSTDQJCJKTSCJAXPLGGXHHGXXZCXPDMMHLDGTYBYSJMXHMRCPXXJZCKZXSHMLQXXTTHXWZFKHCCZDYTCJYXQHLXDHYPJQXYLSYYDZOZJNYXQEZYSQYAYXWYPDGXDDXSPPYZNDLTWRHXYDXZZJHTCXMCZLHPYYYYMHZLLHNXMYLLLMDCPPXHMXDKYCYRDLTXJCHHZZXZLCCLYLNZSHZJZZLNNRLWHYQSNJHXYNTTTKYJPYCHHYEGKCTTWLGQRLGGTGTYGYHPYHYLQYQGCWYQKPYYYTTTTLHYHLLTYTTSPLKYZXGZWGPYDSSZZDQXSKCQNMJJZZBXYQMJRTFFBTKHZKBXLJJKDXJTLBWFZPPTKQTZTGPDGNTPJYFALQMKGXBDCLZFHZCLLLLADPMXDJHLCCLGYHDZFGYDDGCYYFGYDXKSSEBDHYKDKDKHNAXXYBPBYYHXZQGAFFQYJXDMLJCSQZLLPCHBSXGJYNDYBYQSPZWJLZKSDDTACTBXZDYZYPJZQSJNKKTKNJDJGYYPGTLFYQKASDNTCYHBLWDZHBBYDWJRYGKZYHEYYFJMSDTYFZJJHGCXPLXHLDWXXJKYTCYKSSSMTWCTTQZLPBSZDZWZXGZAGYKTYWXLHLSPBCLLOQMMZSSLCMBJCSZZKYDCZJGQQDSMCYTZQQLWZQZXSSFPTTFQMDDZDSHDTDWFHTDYZJYQJQKYPBDJYYXTLJHDRQXXXHAYDHRJLKLYTWHLLRLLRCXYLBWSRSZZSYMKZZHHKYHXKSMDSYDYCJPBZBSQLFCXXXNXKXWYWSDZYQOGGQMMYHCDZTTFJYYBGSTTTYBYKJDHKYXBELHTYPJQNFXFDYKZHQKZBYJTZBXHFDXKDASWTAWAJLDYJSFHBLDNNTNQJTJNCHXFJSRFWHZFMDRYJYJWZPDJKZYJYMPCYZNYNXFBYTFYFWYGDBNZZZDNYTXZEMMQBSQEHXFZMBMFLZZSRXYMJGSXWZJSPRYDJSJGXHJJGLJJYNZZJXHGXKYMLPYYYCXYTWQZSWHWLYRJLPXSLSXMFSWWKLCTNXNYNPSJSZHDZEPTXMYYWXYYSYWLXJQZQXZDCLEEELMCPJPCLWBXSQHFWWTFFJTNQJHJQDXHWLBYZNFJLALKYYJLDXHHYCSTYYWNRJYXYWTRMDRQHWQCMFJDYZMHMYYXJWMYZQZXTLMRSPWWCHAQBXYGZYPXYYRRCLMPYMGKSJSZYSRMYJSNXTPLNBAPPYPYLXYYZKYNLDZYJZCZNNLMZHHARQMPGWQTZMXXMLLHGDZXYHXKYXYCJMFFYYHJFSBSSQLXXNDYCANNMTCJCYPRRNYTYQNYYMBMSXNDLYLYSLJRLXYSXQMLLYZLZJJJKYZZCSFBZXXMSTBJGNXYZHLXNMCWSCYZYFZLXBRNNNYLBNRTGZQYSATSWRYHYJZMZDHZGZDWYBSSCSKXSYHYTXXGCQGXZZSHYXJSCRHMKKBXCZJYJYMKQHZJFNBHMQHYSNJNZYBKNQMCLGQHWLZNZSWXKHLJHYYBQLBFCDSXDLDSPFZPSKJYZWZXZDDXJSMMEGJSCSSMGCLXXKYYYLNYPWWWGYDKZJGGGZGGSYCKNJWNJPCXBJJTQTJWDSSPJXZXNZXUMELPXFSXTLLXCLJXJJLJZXCTPSWXLYDHLYQRWHSYCSQYYBYAYWJJJQFWQCQQCJQGXALDBZZYJGKGXPLTZYFXJLTPADKYQHPMATLCPDCKBMTXYBHKLENXDLEEGQDYMSAWHZMLJTWYGXLYQZLJEEYYBQQFFNLYXRDSCTGJGXYYNKLLYQKCCTLHJLQMKKZGCYYGLLLJDZGYDHZWXPYSJBZKDZGYZZHYWYFQYTYZSZYEZZLYMHJJHTSMQWYZLKYYWZCSRKQYTLTDXWCTYJKLWSQZWBDCQYNCJSRSZJLKCDCDTLZZZACQQZZDDXYPLXZBQJYLZLLLQDDZQJYJYJZYXNYYYNYJXKXDAZWYRDLJYYYRJLXLLDYXJCYWYWNQCCLDDNYYYNYCKCZHXXCCLGZQJGKWPPCQQJYSBZZXYJSQPXJPZBSBDSFNSFPZXHDWZTDWPPTFLZZBZDMYYPQJRSDZSQZSQXBDGCPZSWDWCSQZGMDHZXMWWFYBPDGPHTMJTHZSMMBGZMBZJCFZWFZBBZMQCFMBDMCJXLGPNJBBXGYHYYJGPTZGZMQBQTCGYXJXLWZKYDPDYMGCFTPFXYZTZXDZXTGKMTYBBCLBJASKYTSSQYYMSZXFJEWLXLLSZBQJJJAKLYLXLYCCTSXMCWFKKKBSXLLLLJYXTYLTJYYTDPJHNHNNKBYQNFQYYZBYYESSESSGDYHFHWTCJBSDZZTFDMXHCNJZYMQWSRYJDZJQPDQBBSTJGGFBKJBXTGQHNGWJXJGDLLTHZHHYYYYYYSXWTYYYCCBDBPYPZYCCZYJPZYWCBDLFWZCWJDXXHYHLHWZZXJTCZLCDPXUJCZZZLYXJJTXPHFXWPYWXZPTDZZBDZCYHJHMLXBQXSBYLRDTGJRRCTTTHYTCZWMXFYTWWZCWJWXJYWCSKYBZSCCTZQNHXNWXXKHKFHTSWOCCJYBCMPZZYKBNNZPBZHHZDLSYDDYTYFJPXYNGFXBYQXCBHXCPSXTYZDMKYSNXSXLHKMZXLYHDHKWHXXSSKQYHHCJYXGLHZXCSNHEKDTGZXQYPKDHEXTYKCNYMYYYPKQYYYKXZLTHJQTBYQHXBMYHSQCKWWYLLHCYYLNNEQXQWMCFBDCCMLJGGXDQKTLXKGNQCDGZJWYJJLYHHQTTTNWCHMXCXWHWSZJYDJCCDBQCDGDNYXZTHCQRXCBHZTQCBXWGQWYYBXHMBYMYQTYEXMQKYAQYRGYZSLFYKKQHYSSQYSHJGJCNXKZYCXSBXYXHYYLSTYCXQTHYSMGSCPMMGCCCCCMTZTASMGQZJHKLOSQYLSWTMXSYQKDZLJQQYPLSYCZTCQQPBBQJZCLPKHQZYYXXDTDDTSJCXFFLLCHQXMJLWCJCXTSPYCXNDTJSHJWXDQQJSKXYAMYLSJHMLALYKXCYYDMNMDQMXMCZNNCYBZKKYFLMCHCMLHXRCJJHSYLNMTJZGZGYWJXSRXCWJGJQHQZDQJDCJJZKJKGDZQGJJYJYLXZXXCDQHHHEYTMHLFSBDJSYYSHFYSTCZQLPBDRFRZTZYKYWHSZYQKWDQZRKMSYNBCRXQBJYFAZPZZEDZCJYWBCJWHYJBQSZYWRYSZPTDKZPFPBNZTKLQYHBBZPNPPTYZZYBQNYDCPJMMCYCQMCYFZZDCMNLFPBPLNGQJTBTTNJZPZBBZNJKLJQYLNBZQHKSJZNGGQSZZKYXSHPZSNBCGZKDDZQANZHJKDRTLZLSWJLJZLYWTJNDJZJHXYAYNCBGTZCSSQMNJPJYTYSWXZFKWJQTKHTZPLBHSNJZSYZBWZZZZLSYLSBJHDWWQPSLMMFBJDWAQYZTCJTBNNWZXQXCDSLQGDSDPDZHJTQQPSWLYYJZLGYXYZLCTCBJTKTYCZJTQKBSJLGMGZDMCSGPYNJZYQYYKNXRPWSZXMTNCSZZYXYBYHYZAXYWQCJTLLCKJJTJHGDXDXYQYZZBYWDLWQCGLZGJGQRQZCZSSBCRPCSKYDZNXJSQGXSSJMYDNSTZTPBDLTKZWXQWQTZEXNQCZGWEZKSSBYBRTSSSLCCGBPSZQSZLCCGLLLZXHZQTHCZMQGYZQZNMCOCSZJMMZSQPJYGQLJYJPPLDXRGZYXCCSXHSHGTZNLZWZKJCXTCFCJXLBMQBCZZWPQDNHXLJCTHYZLGYLNLSZZPCXDSCQQHJQKSXZPBAJYEMSMJTZDXLCJYRYYNWJBNGZZTMJXLTBSLYRZPYLSSCNXPHLLHYLLQQZQLXYMRSYCXZLMMCZLTZSDWTJJLLNZGGQXPFSKYGYGHBFZPDKMWGHCXMSGDXJMCJZDYCABXJDLNBCDQYGSKYDQTXDJJYXMSZQAZDZFSLQXYJSJZYLBTXXWXQQZBJZUFBBLYLWDSLJHXJYZJWTDJCZFQZQZZDZSXZZQLZCDZFJHYSPYMPQZMLPPLFFXJJNZZYLSJEYQZFPFZKSYWJJJHRDJZZXTXXGLGHYDXCSKYSWMMZCWYBAZBJKSHFHJCXMHFQHYXXYZFTSJYZFXYXPZLCHMZMBXHZZSXYFYMNCWDABAZLXKTCSHHXKXJJZJSTHYGXSXYYHHHJWXKZXSSBZZWHHHCWTZZZPJXSNXQQJGZYZYWLLCWXZFXXYXYHXMKYYSWSQMNLNAYCYSPMJKHWCQHYLAJJMZXHMMCNZHBHXCLXTJPLTXYJHDYYLTTXFSZHYXXSJBJYAYRSMXYPLCKDUYHLXRLNLLSTYZYYQYGYHHSCCSMZCTZQXKYQFPYYRPFFLKQUNTSZLLZMWWTCQQYZWTLLMLMPWMBZSSTZRBPDDTLQJJBXZCSRZQQYGWCSXFWZLXCCRSZDZMCYGGDZQSGTJSWLJMYMMZYHFBJDGYXCCPSHXNZCSBSJYJGJMPPWAFFYFNXHYZXZYLREMZGZCYZSSZDLLJCSQFNXZKPTXZGXJJGFMYYYSNBTYLBNLHPFZDCYFBMGQRRSSSZXYSGTZRNYDZZCDGPJAFJFZKNZBLCZSZPSGCYCJSZLMLRSZBZZLDLSLLYSXSQZQLYXZLSKKBRXBRBZCYCXZZZEEYFGKLZLYYHGZSGZLFJHGTGWKRAAJYZKZQTSSHJJXDCYZUYJLZYRZDQQHGJZXSSZBYKJPBFRTJXLLFQWJHYLQTYMBLPZDXTZYGBDHZZRBGXHWNJTJXLKSCFSMWLSDQYSJTXKZSCFWJLBXFTZLLJZLLQBLSQMQQCGCZFPBPHZCZJLPYYGGDTGWDCFCZQYYYQYSSCLXZSKLZZZGFFCQNWGLHQYZJJCZLQZZYJPJZZBPDCCMHJGXDQDGDLZQMFGPSYTSDYFWWDJZJYSXYYCZCYHZWPBYKXRYLYBHKJKSFXTZJMMCKHLLTNYYMSYXYZPYJQYCSYCWMTJJKQYRHLLQXPSGTLYYCLJSCPXJYZFNMLRGJJTYZBXYZMSJYJHHFZQMSYXRSZCWTLRTQZSSTKXGQKGSPTGCZNJSJCQCXHMXGGZTQYDJKZDLBZSXJLHYQGGGTHQSZPYHJHHGYYGKGGCWJZZYLCZLXQSFTGZSLLLMLJSKCTBLLZZSZMMNYTPZSXQHJCJYQXYZXZQZCPSHKZZYSXCDFGMWQRLLQXRFZTLYSTCTMJCXJJXHJNXTNRZTZFQYHQGLLGCXSZSJDJLJCYDSJTLNYXHSZXCGJZYQPYLFHDJSBPCCZHJJJQZJQDYBSSLLCMYTTMQTBHJQNNYGKYRQYQMZGCJKPDCGMYZHQLLSLLCLMHOLZGDYYFZSLJCQZLYLZQJESHNYLLJXGJXLYSYYYXNBZLJSSZCQQCJYLLZLTJYLLZLLBNYLGQCHXYYXOXCXQKYJXXXYKLXSXXYQXCYKQXQCSGYXXYQXYGYTQOHXHXPYXXXULCYEYCHZZCBWQBBWJQZSCSZSSLZYLKDESJZWMYMCYTSDSXXSCJPQQSQYLYYZYCMDJDZYWCBTJSYDJKCYDDJLBDJJSODZYSYXQQYXDHHGQQYQHDYXWGMMMAJDYBBBPPBCMUUPLJZSMTXERXJMHQNUTPJDCBSSMSSSTKJTSSMMTRCPLZSZMLQDSDMJMQPNQDXCFYNBFSDQXYXHYAYKQYDDLQYYYSSZBYDSLNTFQTZQPZMCHDHCZCWFDXTMYQSPHQYYXSRGJCWTJTZZQMGWJJTJHTQJBBHWZPXXHYQFXXQYWYYHYSCDYDHHQMNMTMWCPBSZPPZZGLMZFOLLCFWHMMSJZTTDHZZYFFYTZZGZYSKYJXQYJZQBHMBZZLYGHGFMSHPZFZSNCLPBQSNJXZSLXXFPMTYJYGBXLLDLXPZJYZJYHHZCYWHJYLSJEXFSZZYWXKZJLUYDTMLYMQJPWXYHXSKTQJEZRPXXZHHMHWQPWQLYJJQJJZSZCPHJLCHHNXJLQWZJHBMZYXBDHHYPZLHLHLGFWLCHYYTLHJXCJMSCPXSTKPNHQXSRTYXXTESYJCTLSSLSTDLLLWWYHDHRJZSFGXTSYCZYNYHTDHWJSLHTZDQDJZXXQHGYLTZPHCSQFCLNJTCLZPFSTPDYNYLGMJLLYCQHYSSHCHYLHQYQTMZYPBYWRFQYKQSYSLZDQJMPXYYSSRHZJNYWTQDFZBWWTWWRXCWHGYHXMKMYYYQMSMZHNGCEPMLQQMTCWCTMMPXJPJJHFXYYZSXZHTYBMSTSYJTTQQQYYLHYNPYQZLCYZHZWSMYLKFJXLWGXYPJYTYSYXYMZCKTTWLKSMZSYLMPWLZWXWQZSSAQSYXYRHSSNTSRAPXCPWCMGDXHXZDZYFJHGZTTSBJHGYZSZYSMYCLLLXBTYXHBBZJKSSDMALXHYCFYGMQYPJYCQXJLLLJGSLZGQLYCJCCZOTYXMTMTTLLWTGPXYMZMKLPSZZZXHKQYSXCTYJZYHXSHYXZKXLZWPSQPYHJWPJPWXQQYLXSDHMRSLZZYZWTTCYXYSZZSHBSCCSTPLWSSCJCHNLCGCHSSPHYLHFHHXJSXYLLNYLSZDHZXYLSXLWZYKCLDYAXZCMDDYSPJTQJZLNWQPSSSWCTSTSZLBLNXSMNYYMJQBQHRZWTYYDCHQLXKPZWBGQYBKFCMZWPZLLYYLSZYDWHXPSBCMLJBSCGBHXLQHYRLJXYSWXWXZSLDFHLSLYNJLZYFLYJYCDRJLFSYZFSLLCQYQFGJYHYXZLYLMSTDJCYHBZLLNWLXXYGYYHSMGDHXXHHLZZJZXCZZZCYQZFNGWPYLCPKPYYPMCLQKDGXZGGWQBDXZZKZFBXXLZXJTPJPTTBYTSZZDWSLCHZHSLTYXHQLHYXXXYYZYSWTXZKHLXZXZPYHGCHKCFSYHUTJRLXFJXPTZTWHPLYXFCRHXSHXKYXXYHZQDXQWULHYHMJTBFLKHTXCWHJFWJCFPQRYQXCYYYQYGRPYWSGSUNGWCHKZDXYFLXXHJJBYZWTSXXNCYJJYMSWZJQRMHXZWFQSYLZJZGBHYNSLBGTTCSYBYXXWXYHXYYXNSQYXMQYWRGYQLXBBZLJSYLPSYTJZYHYZAWLRORJMKSCZJXXXYXCHDYXRYXXJDTSQFXLYLTSFFYXLMTYJMJUYYYXLTZCSXQZQHZXLYYXZHDNBRXXXJCTYHLBRLMBRLLAXKYLLLJLYXXLYCRYLCJTGJCMTLZLLCYZZPZPCYAWHJJFYBDYYZSMPCKZDQYQPBPCJPDCYZMDPBCYYDYCNNPLMTMLRMFMMGWYZBSJGYGSMZQQQZTXMKQWGXLLPJGZBQCDJJJFPKJKCXBLJMSWMDTQJXLDLPPBXCWRCQFBFQJCZAHZGMYKPHYYHZYKNDKZMBPJYXPXYHLFPNYYGXJDBKXNXHJMZJXSTRSTLDXSKZYSYBZXJLXYSLBZYSLHXJPFXPQNBYLLJQKYGZMCYZZYMCCSLCLHZFWFWYXZMWSXTYNXJHPYYMCYSPMHYSMYDYSHQYZCHMJJMZCAAGCFJBBHPLYZYLXXSDJGXDHKXXTXXNBHRMLYJSLTXMRHNLXQJXYZLLYSWQGDLBJHDCGJYQYCMHWFMJYBMBYJYJWYMDPWHXQLDYGPDFXXBCGJSPCKRSSYZJMSLBZZJFLJJJLGXZGYXYXLSZQYXBEXYXHGCXBPLDYHWETTWWCJMBTXCHXYQXLLXFLYXLLJLSSFWDPZSMYJCLMWYTCZPCHQEKCQBWLCQYDPLQPPQZQFJQDJHYMMCXTXDRMJWRHXCJZYLQXDYYNHYYHRSLSRSYWWZJYMTLTLLGTQCJZYABTCKZCJYCCQLJZQXALMZYHYWLWDXZXQDLLQSHGPJFJLJHJABCQZDJGTKHSSTCYJLPSWZLXZXRWGLDLZRLZXTGSLLLLZLYXXWGDZYGBDPHZPBRLWSXQBPFDWOFMWHLYPCBJCCLDMBZPBZZLCYQXLDOMZBLZWPDWYYGDSTTHCSQSCCRSSSYSLFYBFNTYJSZDFNDPDHDZZMBBLSLCMYFFGTJJQWFTMTPJWFNLBZCMMJTGBDZLQLPYFHYYMJYLSDCHDZJWJCCTLJCLDTLJJCPDDSQDSSZYBNDBJLGGJZXSXNLYCYBJXQYCBYLZCFZPPGKCXZDZFZTJJFJSJXZBNZYJQTTYJYHTYCZHYMDJXTTMPXSPLZCDWSLSHXYPZGTFMLCJTYCBPMGDKWYCYZCDSZZYHFLYCTYGWHKJYYLSJCXGYWJCBLLCSNDDBTZBSCLYZCZZSSQDLLMQYYHFSLQLLXFTYHABXGWNYWYYPLLSDLDLLBJCYXJZMLHLJDXYYQYTDLLLBUGBFDFBBQJZZMDPJHGCLGMJJPGAEHHBWCQXAXHHHZCHXYPHJAXHLPHJPGPZJQCQZGJJZZUZDMQYYBZZPHYHYBWHAZYJHYKFGDPFQSDLZMLJXKXGALXZDAGLMDGXMWZQYXXDXXPFDMMSSYMPFMDMMKXKSYZYSHDZKXSYSMMZZZMSYDNZZCZXFPLSTMZDNMXCKJMZTYYMZMZZMSXHHDCZJEMXXKLJSTLWLSQLYJZLLZJSSDPPMHNLZJCZYHMXXHGZCJMDHXTKGRMXFWMCGMWKDTKSXQMMMFZZYDKMSCLCMPCGMHSPXQPZDSSLCXKYXTWLWJYAHZJGZQMCSNXYYMMPMLKJXMHLMLQMXCTKZMJQYSZJSYSZHSYJZJCDAJZYBSDQJZGWZQQXFKDMSDJLFWEHKZQKJPEYPZYSZCDWYJFFMZZYLTTDZZEFMZLBNPPLPLPEPSZALLTYLKCKQZKGENQLWAGYXYDPXLHSXQQWQCQXQCLHYXXMLYCCWLYMQYSKGCHLCJNSZKPYZKCQZQLJPDMDZHLASXLBYDWQLWDNBQCRYDDZTJYBKBWSZDXDTNPJDTCTQDFXQQMGNXECLTTBKPWSLCTYQLPWYZZKLPYGZCQQPLLKCCYLPQMZCZQCLJSLQZDJXLDDHPZQDLJJXZQDXYZQKZLJCYQDYJPPYPQYKJYRMPCBYMCXKLLZLLFQPYLLLMBSGLCYSSLRSYSQTMXYXZQZFDZUYSYZTFFMZZSMZQHZSSCCMLYXWTPZGXZJGZGSJSGKDDHTQGGZLLBJDZLCBCHYXYZHZFYWXYZYMSDBZZYJGTSMTFXQYXQSTDGSLNXDLRYZZLRYYLXQHTXSRTZNGZXBNQQZFMYKMZJBZYMKBPNLYZPBLMCNQYZZZSJZHJCTZKHYZZJRDYZHNPXGLFZTLKGJTCTSSYLLGZRZBBQZZKLPKLCZYSSUYXBJFPNJZZXCDWXZYJXZZDJJKGGRSRJKMSMZJLSJYWQSKYHQJSXPJZZZLSNSHRNYPZTWCHKLPSRZLZXYJQXQKYSJYCZTLQZYBBYBWZPQDWWYZCYTJCJXCKCWDKKZXSGKDZXWWYYJQYYTCYTDLLXWKCZKKLCCLZCQQDZLQLCSFQCHQHSFSMQZZLNBJJZBSJHTSZDYSJQJPDLZCDCWJKJZZLPYCGMZWDJJBSJQZSYZYHHXJPBJYDSSXDZNCGLQMBTSFSBPDZDLZNFGFJGFSMPXJQLMBLGQCYYXBQKDJJQYRFKZTJDHCZKLBSDZCFJTPLLJGXHYXZCSSZZXSTJYGKGCKGYOQXJPLZPBPGTGYJZGHZQZZLBJLSQFZGKQQJZGYCZBZQTLDXRJXBSXXPZXHYZYCLWDXJJHXMFDZPFZHQHQMQGKSLYHTYCGFRZGNQXCLPDLBZCSCZQLLJBLHBZCYPZZPPDYMZZSGYHCKCPZJGSLJLNSCDSLDLXBMSTLDDFJMKDJDHZLZXLSZQPQPGJLLYBDSZGQLBZLSLKYYHZTTNTJYQTZZPSZQZTLLJTYYLLQLLQYZQLBDZLSLYYZYMDFSZSNHLXZNCZQZPBWSKRFBSYZMTHBLGJPMCZZLSTLXSHTCSYZLZBLFEQHLXFLCJLYLJQCBZLZJHHSSTBRMHXZHJZCLXFNBGXGTQJCZTMSFZKJMSSNXLJKBHSJXNTNLZDNTLMSJXGZJYJCZXYJYJWRWWQNZTNFJSZPZSHZJFYRDJSFSZJZBJFZQZZHZLXFYSBZQLZSGYFTZDCSZXZJBQMSZKJRHYJZCKMJKHCHGTXKXQGLXPXFXTRTYLXJXHDTSJXHJZJXZWZLCQSBTXWXGXTXXHXFTSDKFJHZYJFJXRZSDLLLTQSQQZQWZXSYQTWGWBZCGZLLYZBCLMQQTZHZXZXLJFRMYZFLXYSQXXJKXRMQDZDMMYYBSQBHGZMWFWXGMXLZPYYTGZYCCDXYZXYWGSYJYZNBHPZJSQSYXSXRTFYZGRHZTXSZZTHCBFCLSYXZLZQMZLMPLMXZJXSFLBYZMYQHXJSXRXSQZZZSSLYFRCZJRCRXHHZXQYDYHXSJJHZCXZBTYNSYSXJBQLPXZQPYMLXZKYXLXCJLCYSXXZZLXDLLLJJYHZXGYJWKJRWYHCPSGNRZLFZWFZZNSXGXFLZSXZZZBFCSYJDBRJKRDHHGXJLJJTGXJXXSTJTJXLYXQFCSGSWMSBCTLQZZWLZZKXJMLTMJYHSDDBXGZHDLBMYJFRZFSGCLYJBPMLYSMSXLSZJQQHJZFXGFQFQBPXZGYYQXGZTCQWYLTLGWSGWHRLFSFGZJMGMGBGTJFSYZZGZYZAFLSSPMLPFLCWBJZCLJJMZLPJJLYMQDMYYYFBGYGYZMLYZDXQYXRQQQHSYYYQXYLJTYXFSFSLLGNQCYHYCWFHCCCFXPYLYPLLZYXXXXXKQHHXSHJZCFZSCZJXCPZWHHHHHAPYLQALPQAFYHXDYLUKMZQGGGDDESRNNZLTZGCHYPPYSQJJHCLLJTOLNJPZLJLHYMHEYDYDSQYCDDHGZUNDZCLZYZLLZNTNYZGSLHSLPJJBDGWXPCDUTJCKLKCLWKLLCASSTKZZDNQNTTLYYZSSYSSZZRYLJQKCQDHHCRXRZYDGRGCWCGZQFFFPPJFZYNAKRGYWYQPQXXFKJTSZZXSWZDDFBBXTBGTZKZNPZZPZXZPJSZBMQHKCYXYLDKLJNYPKYGHGDZJXXEAHPNZKZTZCMXCXMMJXNKSZQNMNLWBWWXJKYHCPSTMCSQTZJYXTPCTPDTNNPGLLLZSJLSPBLPLQHDTNJNLYYRSZFFJFQWDPHZDWMRZCCLODAXNSSNYZRESTYJWJYJDBCFXNMWTTBYLWSTSZGYBLJPXGLBOCLHPCBJLTMXZLJYLZXCLTPNCLCKXTPZJSWCYXSFYSZDKNTLBYJCYJLLSTGQCBXRYZXBXKLYLHZLQZLNZCXWJZLJZJNCJHXMNZZGJZZXTZJXYCYYCXXJYYXJJXSSSJSTSSTTPPGQTCSXWZDCSYFPTFBFHFBBLZJCLZZDBXGCXLQPXKFZFLSYLTUWBMQJHSZBMDDBCYSCCLDXYCDDQLYJJWMQLLCSGLJJSYFPYYCCYLTJANTJJPWYCMMGQYYSXDXQMZHSZXPFTWWZQSWQRFKJLZJQQYFBRXJHHFWJJZYQAZMYFRHCYYBYQWLPEXCCZSTYRLTTDMQLYKMBBGMYYJPRKZNPBSXYXBHYZDJDNGHPMFSGMWFZMFQMMBCMZZCJJLCNUXYQLMLRYGQZCYXZLWJGCJCGGMCJNFYZZJHYCPRRCMTZQZXHFQGTJXCCJEAQCRJYHPLQLSZDJRBCQHQDYRHYLYXJSYMHZYDWLDFRYHBPYDTSSCNWBXGLPZMLZZTQSSCPJMXXYCSJYTYCGHYCJWYRXXLFEMWJNMKLLSWTXHYYYNCMMCWJDQDJZGLLJWJRKHPZGGFLCCSCZMCBLTBHBQJXQDSPDJZZGKGLFQYWBZYZJLTSTDHQHCTCBCHFLQMPWDSHYYTQWCNZZJTLBYMBPDYYYXSQKXWYYFLXXNCWCXYPMAELYKKJMZZZBRXYYQJFLJPFHHHYTZZXSGQQMHSPGDZQWBWPJHZJDYSCQWZKTXXSQLZYYMYSDZGRXCKKUJLWPYSYSCSYZLRMLQSYLJXBCXTLWDQZPCYCYKPPPNSXFYZJJRCEMHSZMSXLXGLRWGCSTLRSXBZGBZGZTCPLUJLSLYLYMTXMTZPALZXPXJTJWTCYYZLBLXBZLQMYLXPGHDSLSSDMXMBDZZSXWHAMLCZCPJMCNHJYSNSYGCHSKQMZZQDLLKABLWJXSFMOCDXJRRLYQZKJMYBYQLYHETFJZFRFKSRYXFJTWDSXXSYSQJYSLYXWJHSNLXYYXHBHAWHHJZXWMYLJCSSLKYDZTXBZSYFDXGXZJKHSXXYBSSXDPYNZWRPTQZCZENYGCXQFJYKJBZMLJCMQQXUOXSLYXXLYLLJDZBTYMHPFSTTQQWLHOKYBLZZALZXQLHZWRRQHLSTMYPYXJJXMQSJFNBXYXYJXXYQYLTHYLQYFMLKLJTMLLHSZWKZHLJMLHLJKLJSTLQXYLMBHHLNLZXQJHXCFXXLHYHJJGBYZZKBXSCQDJQDSUJZYYHZHHMGSXCSYMXFEBCQWWRBPYYJQTYZCYQYQQZYHMWFFHGZFRJFCDPXNTQYZPDYKHJLFRZXPPXZDBBGZQSTLGDGYLCQMLCHHMFYWLZYXKJLYPQHSYWMQQGQZMLZJNSQXJQSYJYCBEHSXFSZPXZWFLLBCYYJDYTDTHWZSFJMQQYJLMQXXLLDTTKHHYBFPWTYYSQQWNQWLGWDEBZWCMYGCULKJXTMXMYJSXHYBRWFYMWFRXYQMXYSZTZZTFYKMLDHQDXWYYNLCRYJBLPSXCXYWLSPRRJWXHQYPHTYDNXHHMMYWYTZCSQMTSSCCDALWZTCPQPYJLLQZYJSWXMZZMMYLMXCLMXCZMXMZSQTZPPQQBLPGXQZHFLJJHYTJSRXWZXSCCDLXTYJDCQJXSLQYCLZXLZZXMXQRJMHRHZJBHMFLJLMLCLQNLDXZLLLPYPSYJYSXCQQDCMQJZZXHNPNXZMEKMXHYKYQLXSXTXJYYHWDCWDZHQYYBGYBCYSCFGPSJNZDYZZJZXRZRQJJYMCANYRJTLDPPYZBSTJKXXZYPFDWFGZZRPYMTNGXZQBYXNBUFNQKRJQZMJEGRZGYCLKXZDSKKNSXKCLJSPJYYZLQQJYBZSSQLLLKJXTBKTYLCCDDBLSPPFYLGYDTZJYQGGKQTTFZXBDKTYYHYBBFYTYYBCLPDYTGDHRYRNJSPTCSNYJQHKLLLZSLYDXXWBCJQSPXBPJZJCJDZFFXXBRMLAZHCSNDLBJDSZBLPRZTSWSBXBCLLXXLZDJZSJPYLYXXYFTFFFBHJJXGBYXJPMMMPSSJZJMTLYZJXSWXTYLEDQPJMYGQZJGDJLQJWJQLLSJGJGYGMSCLJJXDTYGJQJQJCJZCJGDZZSXQGSJGGCXHQXSNQLZZBXHSGZXCXYLJXYXYYDFQQJHJFXDHCTXJYRXYSQTJXYEFYYSSYYJXNCYZXFXMSYSZXYYSCHSHXZZZGZZZGFJDLTYLNPZGYJYZYYQZPBXQBDZTZCZYXXYHHSQXSHDHGQHJHGYWSZTMZMLHYXGEBTYLZKQWYTJZRCLEKYSTDBCYKQQSAYXCJXWWGSBHJYZYDHCSJKQCXSWXFLTYNYZPZCCZJQTZWJQDZZZQZLJJXLSBHPYXXPSXSHHEZTXFPTLQYZZXHYTXNCFZYYHXGNXMYWXTZSJPTHHGYMXMXQZXTSBCZYJYXXTYYZYPCQLMMSZMJZZLLZXGXZAAJZYXJMZXWDXZSXZDZXLEYJJZQBHZWZZZQTZPSXZTDSXJJJZNYAZPHXYYSRNQDTHZHYYKYJHDZXZLSWCLYBZYECWCYCRYLCXNHZYDZYDYJDFRJJHTRSQTXYXJRJHOJYNXELXSFSFJZGHPZSXZSZDZCQZBYYKLSGSJHCZSHDGQGXYZGXCHXZJWYQWGYHKSSEQZZNDZFKWYSSTCLZSTSYMCDHJXXYWEYXCZAYDMPXMDSXYBSQMJMZJMTZQLPJYQZCGQHXJHHLXXHLHDLDJQCLDWBSXFZZYYSCHTYTYYBHECXHYKGJPXHHYZJFXHWHBDZFYZBCAPNPGNYDMSXHMMMMAMYNBYJTMPXYYMCTHJBZYFCGTYHWPHFTWZZEZSBZEGPFMTSKFTYCMHFLLHGPZJXZJGZJYXZSBBQSCZZLZCCSTPGXMJSFTCCZJZDJXCYBZLFCJSYZFGSZLYBCWZZBYZDZYPSWYJZXZBDSYUXLZZBZFYGCZXBZHZFTPBGZGEJBSTGKDMFHYZZJHZLLZZGJQZLSFDJSSCBZGPDLFZFZSZYZYZSYGCXSNXXCHCZXTZZLJFZGQSQYXZJQDCCZTQCDXZJYQJQCHXZTDLGSCXZSYQJQTZWLQDQZTQCHQQJZYEZZZPBWKDJFCJPZTYPQYQTTYNLMBDKTJZPQZQZZFPZSBNJLGYJDXJDZZKZGQKXDLPZJTCJDQBXDJQJSTCKNXBXZMSLYJCQMTJQWWCJQNJNLLLHJCWQTBZQYDZCZPZZDZYDDCYZZZCCJTTJFZDPRRTZTJDCQTQZDTJNPLZBCLLCTZSXKJZQZPZLBZRBTJDCXFCZDBCCJJLTQQPLDCGZDBBZJCQDCJWYNLLZYZCCDWLLXWZLXRXNTQQCZXKQLSGDFQTDDGLRLAJJTKUYMKQLLTZYTDYYCZGJWYXDXFRSKSTQTENQMRKQZHHQKDLDAZFKYPBGGPZREBZZYKZZSPEGJXGYKQZZZSLYSYYYZWFQZYLZZLZHWCHKYPQGNPGBLPLRRJYXCCSYYHSFZFYBZYYTGZXYLXCZWXXZJZBLFFLGSKHYJZEYJHLPLLLLCZGXDRZELRHGKLZZYHZLYQSZZJZQLJZFLNBHGWLCZCFJYSPYXZLZLXGCCPZBLLCYBBBBUBBCBPCRNNZCZYRBFSRLDCGQYYQXYGMQZWTZYTYJXYFWTEHZZJYWLCCNTZYJJZDEDPZDZTSYQJHDYMBJNYJZLXTSSTPHNDJXXBYXQTZQDDTJTDYYTGWSCSZQFLSHLGLBCZPHDLYZJYCKWTYTYLBNYTSDSYCCTYSZYYEBHEXHQDTWNYGYCLXTSZYSTQMYGZAZCCSZZDSLZCLZRQXYYELJSBYMXSXZTEMBBLLYYLLYTDQYSHYMRQWKFKBFXNXSBYCHXBWJYHTQBPBSBWDZYLKGZSKYHXQZJXHXJXGNLJKZLYYCDXLFYFGHLJGJYBXQLYBXQPQGZTZPLNCYPXDJYQYDYMRBESJYYHKXXSTMXRCZZYWXYQYBMCLLYZHQYZWQXDBXBZWZMSLPDMYSKFMZKLZCYQYCZLQXFZZYDQZPZYGYJYZMZXDZFYFYTTQTZHGSPCZMLCCYTZXJCYTJMKSLPZHYSNZLLYTPZCTZZCKTXDHXXTQCYFKSMQCCYYAZHTJPCYLZLYJBJXTPNYLJYYNRXSYLMMNXJSMYBCSYSYLCYLXJJQYLDZLPQBFZZBLFNDXQKCZFYWHGQMRDSXYCYTXNQQJZYYPFZXDYZFPRXEJDGYQBXRCNFYYQPGHYJDYZXGRHTKYLNWDZNTSMPKLBTHBPYSZBZTJZSZZJTYYXZPHSSZZBZCZPTQFZMYFLYPYBBJQXZMXXDJMTSYSKKBJZXHJCKLPSMKYJZCXTMLJYXRZZQSLXXQPYZXMKYXXXJCLJPRMYYGADYSKQLSNDHYZKQXZYZTCGHZTLMLWZYBWSYCTBHJHJFCWZTXWYTKZLXQSHLYJZJXTMPLPYCGLTBZZTLZJCYJGDTCLKLPLLQPJMZPAPXYZLKKTKDZCZZBNZDYDYQZJYJGMCTXLTGXSZLMLHBGLKFWNWZHDXUHLFMKYSLGXDTWWFRJEJZTZHYDXYKSHWFZCQSHKTMQQHTZHYMJDJSKHXZJZBZZXYMPAGQMSTPXLSKLZYNWRTSQLSZBPSPSGZWYHTLKSSSWHZZLYYTNXJGMJSZSUFWNLSOZTXGXLSAMMLBWLDSZYLAKQCQCTMYCFJBSLXCLZZCLXXKSBZQCLHJPSQPLSXXCKSLNHPSFQQYTXYJZLQLDXZQJZDYYDJNZPTUZDSKJFSLJHYLZSQZLBTXYDGTQFDBYAZXDZHZJNHHQBYKNXJJQCZMLLJZKSPLDYCLBBLXKLELXJLBQYCXJXGCNLCQPLZLZYJTZLJGYZDZPLTQCSXFDMNYCXGBTJDCZNBGBQYQJWGKFHTNPYQZQGBKPBBYZMTJDYTBLSQMPSXTBNPDXKLEMYYCJYNZCTLDYKZZXDDXHQSHDGMZSJYCCTAYRZLPYLTLKXSLZCGGEXCLFXLKJRTLQJAQZNCMBYDKKCXGLCZJZXJHPTDJJMZQYKQSECQZDSHHADMLZFMMZBGNTJNNLGBYJBRBTMLBYJDZXLCJLPLDLPCQDHLXZLYCBLCXZZJADJLNCMMSSSMYBHBSQKBHRSXXJMXSDZNZPXLGBRHWGGFCXGMSKLLTSJYYCQLTSKYWYYHYWXBXQYWPYWYKQLSQPTNTKHQCWDQKTWPXXHCPTHTWUMSSYHBWCRWXHJMKMZNGWTMLKFGHKJYLSYYCXWHYECLQHKQHTTQKHFZLDXQWYZYYDESBPKYRZPJFYYZJCEQDZZDLATZBBFJLLCXDLMJSSXEGYGSJQXCWBXSSZPDYZCXDNYXPPZYDLYJCZPLTXLSXYZYRXCYYYDYLWWNZSAHJSYQYHGYWWAXTJZDAXYSRLTDPSSYYFNEJDXYZHLXLLLZQZSJNYQYQQXYJGHZGZCYJCHZLYCDSHWSHJZYJXCLLNXZJJYYXNFXMWFPYLCYLLABWDDHWDXJMCXZTZPMLQZHSFHZYNZTLLDYWLSLXHYMMYLMBWWKYXYADTXYLLDJPYBPWUXJMWMLLSAFDLLYFLBHHHBQQLTZJCQJLDJTFFKMMMBYTHYGDCQRDDWRQJXNBYSNWZDBYYTBJHPYBYTTJXAAHGQDQTMYSTQXKBTZPKJLZRBEQQSSMJJBDJOTGTBXPGBKTLHQXJJJCTHXQDWJLWRFWQGWSHCKRYSWGFTGYGBXSDWDWRFHWYTJJXXXJYZYSLPYYYPAYXHYDQKXSHXYXGSKQHYWFDDDPPLCJLQQEEWXKSYYKDYPLTJTHKJLTCYYHHJTTPLTZZCDLTHQKZXQYSTEEYWYYZYXXYYSTTJKLLPZMCYHQGXYHSRMBXPLLNQYDQHXSXXWGDQBSHYLLPJJJTHYJKYPPTHYYKTYEZYENMDSHLCRPQFDGFXZPSFTLJXXJBSWYYSKSFLXLPPLBBBLBSFXFYZBSJSSYLPBBFFFFSSCJDSTZSXZRYYSYFFSYZYZBJTBCTSBSDHRTJJBYTCXYJEYLXCBNEBJDSYXYKGSJZBXBYTFZWGENYHHTHZHHXFWGCSTBGXKLSXYWMTMBYXJSTZSCDYQRCYTWXZFHMYMCXLZNSDJTTTXRYCFYJSBSDYERXJLJXBBDEYNJGHXGCKGSCYMBLXJMSZNSKGXFBNBPTHFJAAFXYXFPXMYPQDTZCXZZPXRSYWZDLYBBKTYQPQJPZYPZJZNJPZJLZZFYSBTTSLMPTZRTDXQSJEHBZYLZDHLJSQMLHTXTJECXSLZZSPKTLZKQQYFSYGYWPCPQFHQHYTQXZKRSGTTSQCZLPTXCDYYZXSQZSLXLZMYCPCQBZYXHBSXLZDLTCDXTYLZJYYZPZYZLTXJSJXHLPMYTXCQRBLZSSFJZZTNJYTXMYJHLHPPLCYXQJQQKZZSCPZKSWALQSBLCCZJSXGWWWYGYKTJBBZTDKHXHKGTGPBKQYSLPXPJCKBMLLXDZSTBKLGGQKQLSBKKTFXRMDKBFTPZFRTBBRFERQGXYJPZSSTLBZTPSZQZSJDHLJQLZBPMSMMSXLQQNHKNBLRDDNXXDHDDJCYYGYLXGZLXSYGMQQGKHBPMXYXLYTQWLWGCPBMQXCYZYDRJBHTDJYHQSHTMJSBYPLWHLZFFNYPMHXXHPLTBQPFBJWQDBYGPNZTPFZJGSDDTQSHZEAWZZYLLTYYBWJKXXGHLFKXDJTMSZSQYNZGGSWQSPHTLSSKMCLZXYSZQZXNCJDQGZDLFNYKLJCJLLZLMZZNHYDSSHTHZZLZZBBHQZWWYCRZHLYQQJBEYFXXXWHSRXWQHWPSLMSSKZTTYGYQQWRSLALHMJTQJSMXQBJJZJXZYZKXBYQXBJXSHZTSFJLXMXZXFGHKZSZGGYLCLSARJYHSLLLMZXELGLXYDJYTLFBHBPNLYZFBBHPTGJKWETZHKJJXZXXGLLJLSTGSHJJYQLQZFKCGNNDJSSZFDBCTWWSEQFHQJBSAQTGYPQLBXBMMYWXGSLZHGLZGQYFLZBYFZJFRYSFMBYZHQGFWZSYFYJJPHZBYYZFFWODGRLMFTWLBZGYCQXCDJYGZYYYYTYTYDWEGAZYHXJLZYYHLRMGRXXZCLHNELJJTJTPWJYBJJBXJJTJTEEKHWSLJPLPSFYZPQQBDLQJJTYYQLYZKDKSQJYYQZLDQTGJQYZJSUCMRYQTHTEJMFCTYHYPKMHYZWJDQFHYYXWSHCTXRLJHQXHCCYYYJLTKTTYTMXGTCJTZAYYOCZLYLBSZYWJYTSJYHBYSHFJLYGJXXTMZYYLTXXYPZLXYJZYZYYPNHMYMDYYLBLHLSYYQQLLNJJYMSOYQBZGDLYXYLCQYXTSZEGXHZGLHWBLJHEYXTWQMAKBPQCGYSHHEGQCMWYYWLJYJHYYZLLJJYLHZYHMGSLJLJXCJJYCLYCJPCPZJZJMMYLCQLNQLJQJSXYJMLSZLJQLYCMMHCFMMFPQQMFYLQMCFFQMMMMHMZNFHHJGTTHHKHSLNCHHYQDXTMMQDCYZYXYQMYQYLTDCYYYZAZZCYMZYDLZFFFMMYCQZWZZMABTBYZTDMNZZGGDFTYPCGQYTTSSFFWFDTZQSSYSTWXJHXYTSXXYLBYQHWWKXHZXWZNNZZJZJJQJCCCHYYXBZXZCYZTLLCQXYNJYCYYCYNZZQYYYEWYCZDCJYCCHYJLBTZYYCQWMPWPYMLGKDLDLGKQQBGYCHJXY',
/**
* 获取汉字的拼音首字母
* @param str 汉字字符串,如果遇到非汉字则原样返回
* @return 返回对象 {unicode:NUmber,firstletter:String}
*/
getFirstLetter: (str) => {
if (!str || /^ +$/g.test(str)) {
return ''
}
// 使用首字母字典文件
if (citySelect.firstletter) {
const result = []
const unicode = str.charCodeAt(0)
let ch = str.charAt(0)
if (unicode >= 19968 && unicode <= 40869) {
ch = citySelect.firstletter.charAt(unicode - 19968)
} else if ((unicode >= 97 && unicode <= 122) || (unicode >= 65 && unicode <= 90)) {
ch = ch.toLocaleUpperCase()
} else {
ch = '#'
}
const obj = {
unicode: unicode,
firstletter: ch
}
return obj
}
return ''
}
}
export default citySelect
<template>
<view class="me-tabs">
<scroll-view :id="viewId" :scroll-left="scrollLeft" scroll-x scroll-with-animation :scroll-animation-duration="300">
<view class="tabs-item tabs-flex tabs-scroll">
<view class="tab-item" v-for="(tab, i) in tabs" :key="i" @click="tabClick(i)" :ref="'refTabItem' + i">{{ tab.name }}</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
tabs: [],
viewId:
'id_' +
Math.random()
.toString(36)
.substr(2, 16),
scrollLeft: 0,
warpWidth: 0
};
},
computed: {
isScroll() {
return this.tabWidth && this.tabs.length; // 指定了tabWidth的宽度,则支持水平滑动
},
tabHeightPx() {
return uni.upx2px(this.height);
},
tabHeightVal() {
return 48 + 'px';
},
tabWidthPx() {
return uni.upx2px(this.tabWidth);
},
tabWidthVal() {
return this.isScroll ? this.tabWidthPx + 'px' : '';
},
lineLeft() {
if (this.isScroll) {
return this.tabWidthPx * this.value + this.tabWidthPx / 2 + 'px'; // 需转为px (用rpx的话iOS真机显示有误差)
} else {
return (100 / this.tabs.length) * (this.value + 1) - 100 / (this.tabs.length * 2) + '%';
}
}
},
watch: {
value() {
this.scrollCenter(); // 水平滚动到中间
}
},
methods: {
tabClick(i) {
this.tabs.splice(i + 1, this.tabs.length);
this.$emit('change', this.tabs[i]);
},
async addTab(item) {
this.tabs.push(item);
setTimeout(() => {
let query = uni.createSelectorQuery();
query = query.in(this); // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
query
.select('.tab-item')
.boundingClientRect(data => {
this.warpWidth += data.width;
this.scrollLeft = this.warpWidth;
})
.exec();
}, 100);
},
async removeTab(index) {
this.tabs.splice(index);
},
async scrollCenter() {
if (!this.isScroll) return;
if (!this.warpWidth) {
// tabs容器的宽度
let rect = await this.initWarpRect();
this.warpWidth = rect ? rect.width : uni.getSystemInfoSync().windowWidth; // 某些情况下取不到宽度,暂时取屏幕宽度
}
let tabLeft = this.tabWidthPx * this.value + this.tabWidthPx / 2; // 当前tab中心点到左边的距离
let diff = tabLeft - this.warpWidth / 2; // 如果超过tabs容器的一半,则滚动差值
this.scrollLeft = diff;
// #ifdef MP-TOUTIAO
this.scrollTimer && clearTimeout(this.scrollTimer);
this.scrollTimer = setTimeout(() => {
// 字节跳动小程序,需延时再次设置scrollLeft,否则tab切换跨度较大时不生效
this.scrollLeft = Math.ceil(diff);
}, 400);
// #endif
},
initWarpRect() {
return new Promise(resolve => {
setTimeout(() => {
// 延时确保dom已渲染, 不使用$nextclick
let query = uni.createSelectorQuery();
// #ifndef MP-ALIPAY
query = query.in(this); // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
// #endif
query
.select('#' + this.viewId)
.boundingClientRect(data => {
resolve(data);
})
.exec();
}, 20);
});
}
},
mounted() {
this.scrollCenter(); // 滚动到当前下标
}
};
</script>
<style lang="scss">
.me-tabs {
position: relative;
font-size: 24rpx;
background-color: #fff;
box-sizing: border-box;
overflow-y: hidden;
height: 48px;
&.tabs-fixed {
z-index: 990;
position: fixed;
top: var(--window-top);
left: 0;
width: 100%;
}
.tabs-item {
position: relative;
white-space: nowrap;
box-sizing: border-box;
}
// 平分的方式显示item
.tabs-flex {
display: flex;
.tab-item {
color: #666;
font-size: 14px;
margin-right: 10px;
position: relative;
padding-right: 22px;
color: #3173ff;
position: relative;
text-align: center;
box-sizing: border-box;
height: 48px;
line-height: 48px;
&::after {
content: '';
background: url(../../static/icon/tab-link.png);
position: absolute;
height: 48px;
width: 18px;
right: 0px;
top: 0px;
}
&:last-child {
margin-right: 0px;
color: #999;
&::after {
content: none;
}
}
}
}
.tabs-scroll {
.tab-item {
display: inline-block;
}
}
}
</style>
......@@ -19,6 +19,7 @@ Vue.use(appcommon)
Vue.prototype.$store=store;
Vue.prototype.$tip=tip;
Vue.prototype.$config=configService;
Vue.prototype.$eventHub=new Vue();
import interfacepage from './pages/index/index.vue'
Vue.component('interfacepage',interfacepage)
import people from './pages/user/people.vue'
......
......@@ -67,6 +67,49 @@
}
}
},
{
"path": "pages/system/department/department",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"bounce": "none"
}
}
},
{
"path": "pages/system/gsinfo/gsinfo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"bounce": "none"
}
}
}, {
"path": "pages/system/glysetting/glysetting",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"bounce": "none"
}
}
}, {
"path": "pages/system/txl/txl",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"bounce": "none"
}
}
},
{
"path": "pages/kqgl/gzrb/index",
"style": {
......
<template>
<view class="bg-white">
<cu-custom :isBack="true" bgColor="bg-blue">
<block slot="content">
{{title}}
</block>
</cu-custom>
<view>
<view class="box-head">
<custom-mall-head ref="refUniMallHead" @change="tabChange"></custom-mall-head>
</view>
<scroll-view scroll-y="true">
<view class="box-list">
<template v-for="item in currentData">
<view class="box-list-item" :class="[
item.children && item.children.length ? 'box-list-item-department-icon' : '',
item.type === 'user' ? 'box-list-item-user' : 'box-list-item-department'
]" :key="item.id" @tap="handelClickItem(item)">
<!-- <view class="box-list-item-department-pic">
<label>
<checkbox class='round blue' :class="selectItem.id==item.id?'checked':''" :checked="selectItem.id==item.id?true:false" :value="item.id"/><text></text>
</label>
</view> -->
<view class="box-list-item-user-pic">
<image v-if="item.type === 'department'" :src="departmentPic"
style="width: 20px;height: 20px;"></image>
<text v-else>{{ getUserName(item.name) }} </text>
</view>
<view class="box-list-item-right">
<view class="box-list-item-text">{{ item.name }}<text class="text-red"
v-show="item.isFzr">(负责人)</text></view>
<view class="box-list-item-user-tag"
v-if="item.type === 'user' && item.tag && item.type.length > 0">
<text v-for="(tag, tagIndex) in item.tag">{{ tag }}</text>
</view>
</view>
<!-- <view v-show="item.children && item.children.length > 0 " @tap.stop="handelClickItem(item)">
<text class="cuIcon cuIcon-list"></text>下级
</view> -->
<view class="text-lg" style="width: 32px;height: 32px;"
v-show="item.type=='user'&&item.phone" @tap.stop="telphone(item.phone)">
<text class="cuIcon cuIcon-phone lg" style="font-size: 24px;"></text>
</view>
</view>
</template>
</view>
</scroll-view>
<view class="" style="height: 60px;width: 100%;">
</view>
<view class="cu-bar bg-white tabbar border shop fixed-bottom" v-show="isSuperuser">
<view class="cu-list grid col-3 no-border" style="width: 100%;">
<view class="cu-item" @tap="addPerson">
<text class="text-blue" style="color: #0081ff;">添加成员</text>
</view>
<view class="cu-item" @tap="addBm">
<text class="text-blue" style="color: #0081ff;">添加子部门</text>
</view>
<view class="cu-item" @tap="addSetting" v-show="isMoreSetting">
<text class="text-blue" style="color: #0081ff;">更多管理</text>
</view>
</view>
</view>
</view>
<!-- 添加子部门 -->
<view class="cu-modal" :class="modalName=='addDialogModal'?'show':''">
<view class="cu-dialog">
<view class="cu-bar bg-white justify-center">
<view class="content">添加子部门</view>
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view>
<view class="padding-xl">
<view v-if="errorMsg&&errorMsg!=''" class="text-red margin-bottom-xs">{{errorMsg}}</view>
<uni-forms ref="form" :modelValue="childrenBmData">
<!-- <uni-forms-item name="bmid" class="">
<uni-easyinput type="text" v-model="childrenBmData.bmid" placeholder="请输入部门编码" />
</uni-forms-item> -->
<uni-forms-item name="bmmc">
<uni-easyinput type="text" v-model="childrenBmData.bmmc" placeholder="请输入中文名称" />
</uni-forms-item>
</uni-forms>
</view>
<view class="cu-bar bg-white">
<view class="action margin-0 flex-sub text-blue solid-left" @tap="hideModal">取消</view>
<view class="action margin-0 flex-sub text-blue solid-left" @tap="addChildrenBm">添加</view>
</view>
</view>
</view>
<!-- 添加成员 -->
<view class="cu-modal" :class="modalName=='addPerson'?'show':''">
<view class="cu-dialog">
<view class="cu-bar bg-white justify-center">
<view class="content">添加成员</view>
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view>
<view class="padding-xl">
<view v-if="errorMsg&&errorMsg!=''" class="text-red margin-bottom-xs">{{errorMsg}}</view>
<uni-forms ref="formUser" :modelValue="userData">
<uni-forms-item name="username">
<uni-easyinput type="text" v-model="userData.username" placeholder="请输入用户名称" />
</uni-forms-item>
<uni-forms-item name="phone">
<uni-easyinput type="text" v-model="userData.phone" placeholder="请输入手机号码" />
</uni-forms-item>
<uni-forms-item name="email">
<uni-easyinput type="text" v-model="userData.email" placeholder="请输入邮箱" />
</uni-forms-item>
</uni-forms>
</view>
<view class="cu-bar bg-white">
<view class="action margin-0 flex-sub text-blue solid-left" @tap="hideModal">取消</view>
<view class="action margin-0 flex-sub text-blue solid-left" @tap="addChildrenPersonal">添加</view>
</view>
</view>
</view>
<!-- 更多管理 -->
<view class="cu-modal bottom-modal" :class="modalName=='addSetting'?'show':''">
<view class="cu-dialog">
<view class="padding-bottom-xs">
<view class="cu-list menu text-center">
<view class="cu-item" style="justify-content: center;" v-show="isDeleteBm" @tap="editBm">
<text>修改当前部门名称</text>
</view>
<view class="cu-item" style="justify-content: center;" @tap="setBmUser">
<view>设置部门负责人<text></text></view>
</view>
<view class="cu-item text-red" style="justify-content: center;" v-show="isDeleteBm"
@tap="deleteBm">
<text>删除部门</text>
</view>
</view>
</view>
<view class="cu-bar bg-white">
<view class="action margin-0 flex-sub solid-left" @tap="hideModal">取消</view>
</view>
</view>
</view>
<!-- 修改当前部门名称 -->
<view class="cu-modal" :class="modalName=='addEditModal'?'show':''">
<view class="cu-dialog">
<view class="cu-bar bg-white justify-center">
<view class="content">修改当前部门名称</view>
<view class="action" @tap="hideModal">
<text class="cuIcon-close text-red"></text>
</view>
</view>
<view class="padding-xl bg-white">
<view v-if="errorMsgBmmc&&errorMsgBmmc!=''" class="text-red margin-bottom-xs">{{errorMsgBmmc}}
</view>
<uni-forms ref="formEdditBm" :modelValue="editBmData">
<uni-forms-item name="bmmc">
<uni-easyinput type="text" v-model="editBmData.bmmc" placeholder="请输入中文名称" />
</uni-forms-item>
</uni-forms>
</view>
<view class="cu-bar ">
<view class="action margin-0 flex-sub text-blue solid-left" @tap="hideModal">取消</view>
<view class="action margin-0 flex-sub text-blue solid-left" @tap="updateBmmc">保存</view>
</view>
</view>
</view>
</view>
</template>
<script>
import customMallHead from '@/components/custom-mall-head/custom-mall-head.vue';
var graceChecker = require("@/common/util/graceChecker.js");
export default {
name: "department",
props: {},
components: {
customMallHead
},
computed: {
isSuperuser() {
return this.$store.state.isSuperuser
}
},
data() {
return {
title: "部门管理",
currentData: [],
defaultHeadList: {},
userList: [],
bmList: [],
dataList: [],
hackReset: true,
nodeIds: [],
moduleKey: 0,
type: null,
defaultHeadList: [],
dataList: [],
departmentPic: "/static/xmtb/folder-fill.png",
modalName: null,
isMoreSetting: true,
isDeleteBm: true,
selectItem: {
name: "",
id: "",
item: {}
},
childrenBmData: {
// bmid:"",
bmmc: ""
},
editBmData: {
bmmc: ""
},
userData: {
userid: "",
username: "",
phone: "",
email: ""
},
errorMsg: null,
errorMsgBmmc: null,
pid: "",
showElement: null,
currentBmData: {
username: "无"
}
};
},
mounted() {
this.getQyInfo();
},
methods: {
telphone(phone) {
// 获取设备平台
// #ifdef APP-PLUS
let platform = uni.getSystemInfoSync().platform
switch (platform) {
case 'android':
uni.showActionSheet({
itemList: [phone, '呼叫'],
success: function(res) {
console.log(res);
if (res.tapIndex == 1) {
plus.device.dial(phone, false)
}
},
complete: function(res) {
console.log("安卓失败", res)
}
})
break;
case 'ios':
// 使用uni-app提供的借口
uni.makePhoneCall({
phoneNumber: phone
})
break;
default:
// 调试器工具
}
// #endif
},
setBmUser() {
this.modalName = null;
this.$Router.push({
name: "selectBmuser",
params: this.currentBmData
})
},
addPerson() {
this.modalName = "addPerson"
},
addBm() {
this.modalName = "addDialogModal"
},
editBm() {
this.modalName = null;
this.editBmData.bmmc = this.getCurrentbm().name;
this.modalName = "addEditModal";
},
addSetting() {
let _this = this;
this.modalName = "addSetting";
if (this.currentBmData.id == this.defaultHeadList.id) {
this.isDeleteBm = false
} else {
this.isDeleteBm = true
}
this.$http.post(_this.API.bmApi, {
id: this.currentBmData.id
}).then(res => {
if (res.data.success) {
_this.currentBmData = res.data.data.records[0];
_this.$forceUpdate();
}
});
},
hideModal() {
this.modalName = null;
this.errorMsg = null;
},
init() {
if (Object.keys(this.defaultHeadList).length > 0) {
this.$refs.refUniMallHead.addTab(this.defaultHeadList);
}
this.currentData = this.dataList;
},
tabChange(obj) {
this.currentBmData = obj;
this.postBm(obj.id);
// this.getCurrentData(obj.id, this.dataList);
},
handelClickItem(item) {
if (item.type === 'department') {
this.$refs.refUniMallHead.addTab({
name: item.name,
id: item.id,
username: item.username || "无",
fzrid: item.fzrid || null
});
this.isMoreSetting = true;
if (item.children && item.children.length > 0) {
this.currentData = item.children;
this.change(item)
} else {
this.postBm(item.id)
}
this.currentBmData = item;
if (!item.username) {
this.currentBmData.username = "无"
}
} else {
this.$Router.push({
name: "updateuser",
params: {
userid: item.id
}
})
}
},
getUserName(name) {
if (name.length === 0) {
return '';
} else if (name.length <= 2) {
return name;
} else if (name.length >= 3) {
return name.substring(name.length - 2);
}
},
getCurrentData(id, data) {
if (id === this.defaultHeadList.id) {
this.currentData = this.dataList;
this.isMoreSetting = false;
} else {
if (data.length > 0) {
data.map(item => {
if (item.id === id) {
this.currentData = item.children;
this.change(item)
}
if (item.children && item.children.length > 0) {
this.getCurrentData(id, item.children);
}
});
}
}
},
admire() {
this.$emit("onSelectValue", this.selectItem);
},
updateView() {
this.$forceUpdate()
},
change(item) {
let _this = this;
let arr = item["children"];
if (arr) {
arr.forEach(user => {
if (arr.findIndex(u => u.type === "user") > -1) {
arr.splice(arr.findIndex(u => u.type === "user"), 1)
}
return arr
})
}
item["children"] = arr;
if (!item["children"]) {
item["children"] = []
}
_this.currentBmData = item;
_this.postUserListBybm(item);
},
postUserListBybm(value) {
let _that = this;
let nodes = [];
_that.userList = [];
_that.nodeIds = [];
this.$http.post("kzzx/user/query", {
choosebmid: value.id
}).then(res => {
if (res.data.success) {
let _res = res.data.data.records;
if (_res.length > 0) {
_that.parseTreeJson([value]);
nodes = [...new Set(_that.nodeIds)];
_res.forEach(item => {
item["name"] = item["username"]
item["type"] = "user";
if (_that.currentBmData.fzrid && _that.currentBmData.fzrid == item.id) {
item.isFzr = true;
} else {
item.isFzr = false
}
if (nodes.indexOf(item.bmid) < 0) {
_that.userList.push(item)
}
if (item.bmid == value.id) {
_that.userList.push(item)
}
value["children"].forEach(a => {
if (item.bmid == a.id) {
if (a["children"]) {
a["children"].push(item)
} else {
a["children"] = [item]
}
}
})
})
let children = value["children"].concat(_that.userList);
let newChildren = this.uniqueJsonArrByField(children, "id");
this.currentData = newChildren
} else {
}
} else {
_that.$tip.toast(res.data.message || "查询失败");
}
});
},
uniqueJsonArrByField(jsonArr, field) {
// 数组长度小于2 或 没有指定去重字段 或 不是json格式数据
if (jsonArr.length < 2 || !field || typeof jsonArr[0] !== "object") return jsonArr;
const res = new Map(); // 标识对象
const uniqueArr = jsonArr.filter((item) => !res.has(item[field]) && res.set(item[field], true));
return uniqueArr;
},
postUserList(value) {
let _that = this;
let nodes = [];
_that.userList = [];
_that.nodeIds = [];
this.$http.post("kzzx/user/query", {
choosebmid: value
}).then(res => {
if (res.data.success) {
let _res = res.data.data.records;
_that.parseTreeJson(_that.dataList);
nodes = [...new Set(_that.nodeIds)]
_res.forEach(item => {
item["name"] = item["username"]
item["type"] = "user"
if (nodes.indexOf(item.bmid) < 0) {
_that.userList.push(item);
}
if (_that.currentBmData.fzrid && _that.currentBmData.fzrid == item.id) {
item.isFzr = true;
} else {
item.isFzr = false
}
})
_that.dataList = _that.dataList.concat(_that.userList);
this.hackReset = false;
this.$nextTick(() => {
this.hackReset = true;
});
} else {
_that.$tip.toast(res.data.message || "查询失败");
}
this.currentData = this.dataList;
});
},
parseTreeJson(array) {
let _this = this;
for (let index = 0; index < array.length; index++) {
const element = array[index];
// 1.判断element.children是对象
if (element.children && typeof(element.children) == 'object') {
_this.parseTreeJson(element.children);
_this.nodeIds.push(element.id);
} else {
// 判断是否为子节点
if (element.type === "department") {
console.log(element.des + '的id:' + element.id)
// 获得符合的 nodeId
_this.nodeIds.push(element.id);
}
}
}
},
//获取企业信息
getQyInfo() {
let _that = this;
this.$http.post("kzzx/bm/query", {}).then(res => {
if (res.data.success) {
_that.bmList = res.data.data.records;
let _res = res.data.data;
let newBmdata = _that.transDataGUI(_that.bmList, "id", "pid", "children");
let headData = {};
if (_res) {
headData = {
name: _res.name,
id: _res.id
}
}
if (newBmdata.length > 0) {
newBmdata.forEach((item, index) => {
if (item.bmid == "root") {
headData = item;
}
})
}
_that.defaultHeadList = {
name: headData.bmmc,
id: headData.id,
fzrid: headData.fzrid
}
_that.currentBmData = headData;
if (Object.keys(this.defaultHeadList).length > 0) {
this.$refs.refUniMallHead.addTab(this.defaultHeadList);
}
_that.dataList = headData["children"] || []
_that.postUserList(this.defaultHeadList.id);
this.hackReset = false;
this.$nextTick(() => {
this.hackReset = true;
});
} else {
_that.$tip.toast(res.data.message || "查询失败");
}
});
},
postBm(pid) {
let _that = this;
this.$http.post("kzzx/bm/query", {
pid: pid
}).then(res => {
if (res.data.success) {
_that.bmList = res.data.data.records;
let _res = res.data.data;
let newBmdata = _that.transDataGUI(_that.bmList, "id", "pid", "children");
_that.dataList = newBmdata;
_that.postUserList(pid);
this.hackReset = false;
this.$nextTick(() => {
this.hackReset = true;
});
} else {
_that.$tip.toast(res.data.message || "查询失败");
}
});
},
transDataGUI(a, idStr, pidStr, chindrenStr) {
let r = [],
hash = {},
id = idStr,
pid = pidStr,
children = chindrenStr,
i = 0,
j = 0,
len = a.length;
for (; i < len; i++) {
a[i]["type"] = "department";
a[i]["name"] = a[i]["bmmc"];
hash[a[i][id]] = a[i];
}
for (; j < len; j++) {
let aVal = a[j],
hashVP = hash[aVal[pid]];
if (hashVP) {
!hashVP[children] && (hashVP[children] = []);
hashVP[children].push(aVal);
} else {
r.push(aVal);
}
}
return r;
},
resetBm() {
let _that = this;
_that.childrenBmData.bmmc = "";
// _that.childrenBmData.bmid="";
},
resetUser() {
let _that = this;
_that.userData.userid = "";
_that.userData.username = "";
_that.userData.phone = "";
_that.userData.email = "";
},
getCurrentbm() {
let pidElment = null;
if (this.$refs.refUniMallHead.tabs && this.$refs.refUniMallHead.tabs.length > 0) {
pidElment = this.$refs.refUniMallHead.tabs[this.$refs.refUniMallHead.tabs.length - 1]
}
return pidElment
},
/* 修改当前部门名称 */
updateBmmc() {
let _that = this;
let rule = [{
name: "bmmc",
checkType: "notnull",
checkRule: "",
errorMsg: "部门中文名称不能为空"
}, ]
var checkRes = graceChecker.check(this.editBmData, rule);
if (!checkRes) {
this.errorMsg = graceChecker.error;
} else {
this.errorMsg = null;
let formData = {
flag: "U",
bmmc: this.editBmData.bmmc,
id: this.getCurrentbm().id,
}
this.$http.post('/kzzx/bm/update',{
master: formData
}).then(res => {
if (res.data.success) {
_that.modalName = null;
_that.editBmData.bmmc = "";
_that.$tip.success("更新部门成功");
_that.$refs.refUniMallHead.tabs[_that.$refs.refUniMallHead.tabs.length - 1].name =
formData.bmmc;
_that.$forceUpdate();
} else {
_that.errorMsg = res.data.message || "添加失败"
_that.$tip.toast(res.data.message || "添加失败");
}
});
}
},
/* 选择部门负责人 */
selectBmfzr(value) {
let _that = this;
let formData = {
bmid: this.currentBmData.bmid,
id: this.currentBmData.id,
bmmc: this.currentBmData.bmmc
};
formData["flag"] = "U";
formData["fzrid"] = value;
this.$http.post('/kzzx/bm/update',{
master: formData
}).then(res => {
if (res.data.success) {
_that.$tip.success("操作成功");
_that.$forceUpdate();
} else {
_that.$tip.toast(res.data.message || "操作失败");
}
});
},
deleteBm() {
let _that = this;
let key = this.getCurrentbm().id;
_that.modalName = null;
this.$tip.confirm("是否删除该部门?").then(r => {
if (r.confirm) {
this.$http.post('/kzzx/bm/delete',{
id: key
}).then(res => {
if (res.data.success) {
_that.$tip.success("操作成功");
_that.postBm(_that.$refs.refUniMallHead.tabs[_that.$refs.refUniMallHead
.tabs.length - 2].id);
_that.$refs.refUniMallHead.removeTab(_that.$refs.refUniMallHead.tabs
.length - 1)
} else {
_that.$tip.toast(res.data.message || "操作失败");
}
_that.$forceUpdate()
});
}
})
},
/* 添加部门 */
addChildrenBm() {
let _that = this;
let pid = null;
if (this.$refs.refUniMallHead.tabs && this.$refs.refUniMallHead.tabs.length > 0) {
pid = this.$refs.refUniMallHead.tabs[this.$refs.refUniMallHead.tabs.length - 1].id
}
let rule = [{
name: "bmmc",
checkType: "notnull",
checkRule: "",
errorMsg: "部门中文名称不能为空"
}, ]
var checkRes = graceChecker.check(this.childrenBmData, rule);
if (!checkRes) {
this.errorMsg = graceChecker.error;
} else {
this.errorMsg = null;
let formData = {
pid: pid,
bmmc: this.childrenBmData.bmmc,
// bmid:this.childrenBmData.bmid,
}
this.$http.post('/kzzx/bm/add',{
master: formData
}).then(res => {
if (res.data.success) {
_that.modalName = null;
_that.resetBm();
_that.$tip.success("新建部门成功");
_that.postBm(pid)
} else {
_that.errorMsg = res.data.message || "添加失败"
_that.$tip.toast(res.data.message || "添加失败");
}
});
}
},
/* 添加成员 */
addChildrenPersonal() {
let _that = this;
let pid = null;
if (this.$refs.refUniMallHead.tabs && this.$refs.refUniMallHead.tabs.length > 0) {
pid = this.$refs.refUniMallHead.tabs[this.$refs.refUniMallHead.tabs.length - 1].id
}
let rule = [
// {name:"userid", checkType: "notnull", checkRule: "", errorMsg: "用户编码不能为空" },
{
name: "username",
checkType: "notnull",
checkRule: "",
errorMsg: "用户名称不能为空"
},
// {name:"phone", checkType: "phoneno", checkRule: "", errorMsg: "手机号码格式错误" },
// {name:"email", checkType: "email", checkRule: "", errorMsg: "邮箱格式错误" },
]
var checkRes = graceChecker.check(this.userData, rule);
if (!checkRes) {
this.errorMsg = graceChecker.error;
} else {
this.errorMsg = null;
let formData = {
bmid: pid,
userid: this.userData.phone,
username: this.userData.username,
phone: this.userData.phone,
email: this.userData.email
}
this.$http.post('/kzzx/user/add',{
master: formData
}).then(res => {
if (res.data.success) {
_that.modalName = null;
_that.resetUser();
_that.$tip.success("新建成员成功");
// _that.postUserList(_that.getCurrentbm().id)
let newData = {
bmid: pid,
userid: formData.userid,
username: formData.username,
phone: formData.phone,
email: formData.email,
id: res.data.id,
type: "user",
name: formData.username
};
_that.currentData.push(newData)
} else {
_that.errorMsg = res.data.message || "添加失败"
_that.$tip.toast(res.data.message || "添加失败");
}
});
}
},
},
onLoad() {
let _this = this;
this.$eventHub.$on("refreshbm", function(value) {
_this.postBm(_this.getCurrentbm().id)
_this.$forceUpdate();
});
this.$eventHub.$on("selectBmuser", function(value) {
_this.selectBmfzr(value.selectedItem.id)
// _this.postBm(_this.getCurrentbm().id)
_this.$forceUpdate();
})
},
onUnload() {
this.$eventHub.$off('selectBmuser');
this.$eventHub.$off('refreshbm');
}
};
</script>
<style lang="scss" scoped>
.box-head {
position: fixed;
left: 0px;
width: 100%;
height: 48px;
background: #ffffff;
padding-left: 17px;
box-sizing: border-box;
overflow-y: hidden;
z-index: 999;
}
.box-list {
padding-top: 52px;
.box-list-item {
position: relative;
height: 96rpx;
display: flex;
align-items: center;
padding: 0 20rpx;
box-sizing: border-box;
background: #ffffff;
margin-bottom: 1px;
border-bottom: 1px solid #f8f8f8;
&:active {
background: #f2f3f4;
}
&:last-child {
margin-bottom: 0px;
}
.box-list-item-department-pic {
width: 40px;
height: 40px;
// background: rgba(55, 127, 255, 0.1);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 14px;
overflow: hidden;
image {
width: 20px;
height: 20px;
}
}
.box-list-item-user-pic {
width: 40px;
height: 40px;
background: #377fff;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 14px;
overflow: hidden;
image {
width: 40px;
height: 40px;
}
text {
color: #ffffff;
font-size: 12px;
}
}
.box-list-item-right {
display: flex;
align-items: center;
justify-content: space-between;
flex: 1;
}
.box-list-item-text {
color: #333333;
font-size: 14px;
}
.box-list-item-user-tag {
text {
box-sizing: border-box;
border-radius: 8px;
padding: 4px 8px;
font-size: 10px;
margin-left: 5px;
&:first-child {
margin-left: 0px;
}
&:nth-child(1) {
background: rgba(49, 210, 144, 0.05);
border: 1px solid #31d290;
color: #31d290;
}
&:nth-child(2) {
background: rgba(55, 127, 255, 0.05);
border: 1px solid #377fff;
color: #377fff;
}
}
}
&.box-list-item-department-icon {
&::after {
content: '';
position: absolute;
width: 5px;
height: 10px;
top: 26px;
right: 15px;
// background: url(../../static/xmtb/more.png) no-repeat;
}
}
}
.box-list-item-department+.box-list-item-user {
margin-top: 10px;
}
}
.fixed-bottom {
position: fixed;
bottom: 0;
width: 100%;
border-top: 1px solid #eee;
}
/deep/ uni-checkbox .uni-checkbox-input {
width: 20px;
height: 20px;
}
/deep/ uni-radio::before,
uni-checkbox::before {
font-size: 14px;
}
/deep/ .flex-end {
justify-content: flex-end !important;
}
/deep/ .cu-dialog {
background-color: white;
}
</style>
\ No newline at end of file
<template>
<view class="">
<cu-header bgColor="bg-blue" isBack="true">
<block slot="content">
{{title}}</text>
</block>
<block slot="right">
</block>
</cu-header>
<view :style="[{top:CustomBar + 'px'}]">
<view class="cu-bar bg-white solid-bottom">
<view class="action">
<text class="font_weight">当前管理员</text>
</view>
</view>
<scroll-view scroll-y="true">
<view class="cu-list menu">
<view class="cu-item menu-avatar" v-for="(item,index) in myList" :key="index">
<view class="cu-avatar round lg"> <image style="border-radius: 50%;height: 36px;width: 36px;" src="../../../static/icon/tx.jpg"></image></view>
<view class="content text-overflow" style="display: block;padding: 0 10px;">
<view class="text-cut" style="color: black;font-size: 14px;"> {{item.username}}</view>
<!-- <view class="text-cut text-gray text-sm text-cut">{{item.userid}}</view> -->
</view>
<view class="action text-right" @tap="removeGlySetting(item)" v-show="isSuperuser">
<button class="bg-orange" size="mini">
移除
</button>
</view>
</view>
</view>
</scroll-view>
<view class="cu-list menu margin-top" v-show="isSuperuser">
<view class="cu-item menu-avatar" @tap="setNewGly">
<view class="cu-avatar " style="background-color: #eee;">
<uni-icons type="plusempty" size="24"></uni-icons>
</view>
<view class="content text-overflow" style="display: block;padding: 0 10px;">
<view class="text-cut" style="color: black;font-size: 14px;">添加新管理员</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title:"管理员设置",
myList:[],
modalName:null,
modalNameX:null,
API:'',
CustomBar:this.CustomBar,
usercx:"",
formData:{},
userinfo:{
bmid:""
}
}
},
computed:{
isSuperuser(){
return this.$store.state.isSuperuser
}
},
filters:{
formatTime: function(time) {
if(time!=null&&time!="")
{
var date = new Date(time);
return date.Format("yy-MM-dd hh:mm")
}else{
return "";
}
},
formatDate:function(time){
if(time!=null&&time!="")
{
var date = new Date(time);
return date.Format("yyyy-MM-dd")
}else{
return "";
}
},
},
methods: {
hideModal(){
this.modalNameX=null
},
queryUser(){
let _that=this;
_that.$http.post(_that.API.userApi,{superFlag:"Y"}).then(res => {
if (res.data.success) {
_that.myList=res.data.data.records;
_that.options.status="noMore"
} else {
_that.myList=[];
}
_that.$forceUpdate()
});
},
setNewGly(){
this.$Router.push({name:"userlist",params:{type:"gly"}})
},
agreeSp(item){
let _that=this;
_that.userinfo=item;
_that.modalNameX="bottomModal";
_that.$forceUpdate()
},
disagreeSp(item){
let _that=this;
this.$tip.confirm("是否确认不通过?").then(r=>{
if(r.confirm){
_that.$http.post(_that.API.userFailApi,{id:item.id}).then(res => {
if (res.data.success) {
_that.queryUser();
_that.$tip.success("操作成功");
} else {
_that.$tip.toast(res.data.message||"操作失败");
}
_that.modalName=null;
_that.$forceUpdate()
});
}
})
},
removeGlySetting(user){
let _that=this;
if(this.myList.length<2){
this.$tip.alert("当前管理员不允许删除");
return false
}
this.$tip.confirm("是否移除该管理员?").then(r=>{
if(r.confirm){
apiService.removeGlySetting({id:user.id}).then(res => {
if (res.data.success) {
_that.$tip.success("操作成功");
_that.queryUser();
} else {
_that.$tip.toast(res.data.message||"操作失败");
}
_that.$forceUpdate()
});
}
})
},
addGly(user){
let _that=this;
apiService.addGlySetting({id:user.userid}).then(res => {
if (res.data.success) {
_that.$tip.success("操作成功");
_that.queryUser();
} else {
_that.$tip.toast(res.data.message||"操作失败");
}
_that.$forceUpdate()
});
}
},
onLoad() {
let _this=this;
this.queryUser();
this.$eventHub.$on("updateGly",function(value){
_this.addGly(value)
_this.$forceUpdate();
})
},
onUnload() {
this.$eventHub.$off('updateGly');
}
}
</script>
<style scoped>
page{
/* background: white; */
width: 100%;
height: 100%;
}
/deep/ .cu-list.menu>.cu-item{
min-height: 54px;
}
/deep/ .cu-avatar{
background-color: transparent;
}
/deep/ .cu-list.menu>.cu-item .content{
text-align: left;
}
/deep/.font_weight{
font-weight: bolder;
font-size: 16px;
}
</style>
<template>
<view class="page">
<cu-header :isBack="true" bgColor="bg-blue">
<block slot="content">
{{taskTitle}}
</block>
</cu-header>
<view :style="[{top:CustomBar + 'px'}]">
<!-- <web-view class="webview" :src="imp"></web-view> -->
<view id="container">
<h1 class="font_weight">苏州格物信息有限公司</h1>
<p class="text_indent">苏州格物信息技术有限公司成立于2015年,是一家专注于智能制造整体解决方案和企业数字化产品研究、开发及销售的高科技企业,总部及研发基地设立于风景秀丽的江南水乡苏州,并在全国各地设有分支机构。</p>
<p class="text_indent">
公司技术和研发实力雄厚,是国家 863 项目的参与者,并被政府认定为“高新技术企业”、“民营高科技企业”。为了开发出真正适合企业需求的产品,特聘请各行业专家作为咨询顾问,使用户得到优质的服务和最好的投资回报。
</p>
<p class="text_indent">
公司自成立以来,始终坚持以人才为本、诚信立业的经营原则,荟萃业界精英,将国外先进的信息技术、管理方法及企业经验与国内企业的具体实际相结台,为企业提供全方位的数字化整体解决方案,帮助企业提高管理水平和竞争能力,使企业在激烈的市场竞争中始终保持竞争力,实现企业快速、稳定地发展。
</p>
<p class="text_indent">
最好的产品、最好的服务、最好的信誉,苏州格物信息技术有限公司已在装备制造、冶金、煤矿、化工、机械加工、电子、汽配、注塑、冲压、轨道交通等诸多行业取得辉煌成绩。
</p>
<p class="text_indent font_weight">
期待成为您最可信赖的长期合作伙伴。
</p>
<image src="../../../static/icon/gavel.png" mode="webix" style="width: 100%;height: 160px;"></image>
<p class="text_indent font_weight">
联系我们
</p>
<p class="text_indent">
<ul>
<li>联系方式:0512-6802 8813 / 159 9573 9622</li>
<li>电子邮件:sales@gavelinfo.com</li>
<li>官方网站:<a href="https://www.gavelinfo.com/">https://www.gavelinfo.com</a></li>
</ul>
</p>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
CustomBar:this.CustomBar,
taskTitle:"关于我们",
}
},
methods: {
}
}
</script>
<style scoped>
page {
background-color: #fff;
font: 13px/20px normal Helvetica, Arial, sans-serif;
color: #4F5155;
}
a {
color: #003399;
background-color: transparent;
font-weight: normal;
}
h1 {
color: #444;
background-color: transparent;
border-bottom: 1px solid #D0D0D0;
font-size: 16px;
font-weight: normal;
margin: 0 0 14px 0;
padding: 14px 15px 10px 15px;
}
.title {
font-size: 14px;
font-weight: bolder;
color:#007aff;
}
code {
font-family: Consolas, Monaco, Courier New, Courier, monospace;
font-size: 12px;
background-color: #f9f9f9;
border: 1px solid #D0D0D0;
color: #002166;
display: block;
margin: 14px 0 14px 0;
padding: 12px 10px 12px 10px;
}
#container {
margin: 0;
box-shadow: 0 0 8px #D0D0D0;
-webkit-box-shadow: 0 0 8px #D0D0D0;
-moz-box-shadow: 0 0 8px #D0D0D0;
padding: 10px;
}
p {
margin: 12px 15px 12px 15px;
}
.p_left {
margin: 0px;
}
.p_left_k {
margin-left: -13px;
}
.p_width {
font-weight: 800;
}
.p_mleft {
margin-left: -14px;
}
.text_indent {
text-indent: 1em;
margin-left: 0;
}
.font_weight {
font-weight: bold;
}
span.text_indent {
display: block;
}
ol {
display: block;
list-style-type: decimal;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 10px!important;
padding-left: 10px;
}
li {
display: list-item;
text-align: -webkit-match-parent;
}
#hadsky-tools {
display: none;
}
</style>
<template>
<view class="bg-white">
<cu-header bgColor="bg-blue" isBack="true">
<block slot="content">
{{title}}</text>
</block>
<block slot="right">
</block>
</cu-header>
<view class="bg-white">
<city-select
placeholder="搜索"
@cityClick="cityClick"
:formatName="formatName"
:obtainCitys="obtainCitys"
:isSearch="true"
></city-select>
</view>
</view>
</template>
<script>
import citySelect from '@/components/city-select/city-select.vue';
import apiService from '@/api/api.js';
export default {
data() {
return {
API:apiService.API,
title:"通讯录",
formatName: 'username',
//显示的城市数据
obtainCitys: []
}
},
components: {
citySelect
},
methods: {
queryUser(){
let _that=this;
_that.$http.post(_that.API.userApi,{}).then(res => {
if (res.data.success) {
_that.obtainCitys=res.data.data.records;
_that.options.status="noMore"
} else {
_that.obtainCitys=[];
}
_that.$forceUpdate()
});
},
cityClick(v){
if(v.phone){
this.telphone(v.phone)
}
},
telphone(phone){
// 获取设备平台
let platform = uni.getSystemInfoSync().platform
switch (platform) {
case 'android':
uni.showActionSheet({
itemList: [phone,'呼叫'],
success:function(res){
console.log(res);
if(res.tapIndex==1){
plus.device.dial(phone,false)
}
},
complete: function(res) {
console.log("安卓失败",res)
}
})
break;
case 'ios':
// 使用uni-app提供的借口
uni.makePhoneCall({
phoneNumber: phone
})
break;
default:
// 调试器工具
}
}
},
onLoad(){
this.queryUser();
}
}
</script>
<style scoped>
page{
background: white;
width: 100%;
height: 100%;
}
/deep/.city-select-main{
background-color: white;
}
</style>
## 1.1.19(2024-07-18)
- 修复 初始值传入 null 导致input报错的bug
## 1.1.18(2024-04-11)
- 修复 easyinput组件双向绑定问题
## 1.1.17(2024-03-28)
- 修复 在头条小程序下丢失事件绑定的问题
## 1.1.16(2024-03-20)
- 修复 在密码输入情况下 清除和小眼睛覆盖bug 在edge浏览器下显示双眼睛bug
## 1.1.15(2024-02-21)
- 新增 左侧插槽:left
## 1.1.14(2024-02-19)
- 修复 onBlur的emit传值错误
## 1.1.12(2024-01-29)
- 补充 adjust-position文档属性补充
## 1.1.11(2024-01-29)
- 补充 adjust-position属性传递值:(Boolean)当键盘弹起时,是否自动上推页面
## 1.1.10(2024-01-22)
- 去除 移除无用的log输出
## 1.1.9(2023-04-11)
- 修复 vue3 下 keyboardheightchange 事件报错的bug
## 1.1.8(2023-03-29)
- 优化 trim 属性默认值
## 1.1.7(2023-03-29)
- 新增 cursor-spacing 属性
## 1.1.6(2023-01-28)
- 新增 keyboardheightchange 事件,可监听键盘高度变化
## 1.1.5(2022-11-29)
- 优化 主题样式
## 1.1.4(2022-10-27)
- 修复 props 中背景颜色无默认值的bug
## 1.1.0(2022-06-30)
- 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容
- 新增 clear 事件,点击右侧叉号图标触发
- 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发
- 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等
## 1.0.5(2022-06-07)
- 优化 clearable 显示策略
## 1.0.4(2022-06-07)
- 优化 clearable 显示策略
## 1.0.3(2022-05-20)
- 修复 关闭图标某些情况下无法取消的 bug
## 1.0.2(2022-04-12)
- 修复 默认值不生效的 bug
## 1.0.1(2022-04-02)
- 修复 value 不能为 0 的 bug
## 1.0.0(2021-11-19)
- 优化 组件 UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
## 0.1.4(2021-08-20)
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.3(2021-08-11)
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.2(2021-07-30)
- 优化 vue3 下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.0(2021-07-13)
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.16(2021-06-29)
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.15(2021-06-21)
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.14(2021-06-18)
- 新增 passwordIcon 属性,当 type=password 时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.13(2021-06-04)
- 修复 disabled 状态可清出内容的 bug
## 0.0.12(2021-05-12)
- 新增 组件示例地址
## 0.0.11(2021-05-07)
- 修复 input-border 属性不生效的问题
## 0.0.10(2021-04-30)
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.9(2021-02-05)
- 调整为 uni_modules 目录规范
- 优化 兼容 nvue 页面
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行, false - 延迟执行
*/
export const debounce = function(func, wait = 1000, immediate = true) {
let timer;
return function() {
let context = this,
args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply(context, args);
}, wait)
}
}
}
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 使用表时间戳,在时间段开始的时候触发 2 使用表定时器,在时间段结束的时候触发
*/
export const throttle = (func, wait = 1000, type = 1) => {
let previous = 0;
let timeout;
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
<template>
<view class="uni-easyinput" :class="{ 'uni-easyinput-error': msg }" :style="boxStyle">
<view class="uni-easyinput__content" :class="inputContentClass" :style="inputContentStyle">
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')" size="22"></uni-icons>
<slot name="left">
</slot>
<!-- #ifdef MP-ALIPAY -->
<textarea :enableNative="enableNative" v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{ 'input-padding': inputBorder }" :name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class" :maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange"></textarea>
<input :enableNative="enableNative" v-else :type="type === 'password' ? 'text' : type" class="uni-easyinput__content-input" :style="inputStyle" :name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder" :placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @focus="_Focus" @blur="_Blur" @input="onInput" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange" />
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{ 'input-padding': inputBorder }" :name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class" :maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @input="onInput" @blur="_Blur" @focus="_Focus" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange"></textarea>
<input v-else :type="type === 'password' ? 'text' : type" class="uni-easyinput__content-input" :style="inputStyle" :name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder" :placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition" @focus="_Focus" @blur="_Blur" @input="onInput" @confirm="onConfirm" @keyboardheightchange="onkeyboardheightchange" />
<!-- #endif -->
<template v-if="type === 'password' && passwordIcon">
<!-- 开启密码时显示小眼睛 -->
<uni-icons v-if="isVal" class="content-clear-icon" :class="{ 'is-textarea-icon': type === 'textarea' }" :type="showPassword ? 'eye-slash-filled' : 'eye-filled'" :size="22" :color="focusShow ? primaryColor : '#c0c4cc'" @click="onEyes"></uni-icons>
</template>
<template v-if="suffixIcon">
<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')" size="22"></uni-icons>
</template>
<template v-else>
<uni-icons v-if="clearable && isVal && !disabled && type !== 'textarea'" class="content-clear-icon" :class="{ 'is-textarea-icon': type === 'textarea' }" type="clear" :size="clearSize" :color="msg ? '#dd524d' : focusShow ? primaryColor : '#c0c4cc'" @click="onClear"></uni-icons>
</template>
<slot name="right"></slot>
</view>
</view>
</template>
<script>
/**
* Easyinput 输入框
* @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
* @tutorial https://ext.dcloud.net.cn/plugin?id=3455
* @property {String} value 输入内容
* @property {String } type 输入框的类型(默认text) password/text/textarea/..
* @value text 文本输入键盘
* @value textarea 多行文本输入键盘
* @value password 密码输入键盘
* @value number 数字输入键盘,注意iOS上app-vue弹出的数字键盘并非9宫格方式
* @value idcard 身份证输入键盘,信、支付宝、百度、QQ小程序
* @value digit 带小数点的数字键盘 ,App的nvue页面、微信、支付宝、百度、头条、QQ小程序支持
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件,点击可清空输入框内容(默认true)
* @property {Boolean} autoHeight 是否自动增高输入区域,type为textarea时有效(默认true)
* @property {String } placeholder 输入框的提示文字
* @property {String } placeholderStyle placeholder的样式(内联样式,字符串),如"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点(默认false)
* @property {Boolean} disabled 是否禁用(默认false)
* @property {Number } maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140)
* @property {String } confirmType 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done)
* @property {Number } clearSize 清除图标的大小,单位px(默认15)
* @property {String} prefixIcon 输入框头部图标
* @property {String} suffixIcon 输入框尾部图标
* @property {String} primaryColor 设置主题色(默认#2979ff)
* @property {Boolean} trim 是否自动去除两端的空格
* @property {Boolean} cursorSpacing 指定光标与键盘的距离,单位 px
* @property {Boolean} ajust-position 当键盘弹起时,是否上推内容,默认值:true
* @value both 去除两端空格
* @value left 去除左侧空格
* @value right 去除右侧空格
* @value start 去除左侧空格
* @value end 去除右侧空格
* @value all 去除全部空格
* @value none 不去除空格
* @property {Boolean} inputBorder 是否显示input输入框的边框(默认true)
* @property {Boolean} passwordIcon type=password时是否显示小眼睛图标
* @property {Object} styles 自定义颜色
* @event {Function} input 输入框内容发生变化时触发
* @event {Function} focus 输入框获得焦点时触发
* @event {Function} blur 输入框失去焦点时触发
* @event {Function} confirm 点击完成按钮时触发
* @event {Function} iconClick 点击图标时触发
* @example <uni-easyinput v-model="mobile"></uni-easyinput>
*/
function obj2strClass(obj) {
let classess = '';
for (let key in obj) {
const val = obj[key];
if (val) {
classess += `${key} `;
}
}
return classess;
}
function obj2strStyle(obj) {
let style = '';
for (let key in obj) {
const val = obj[key];
style += `${key}:${val};`;
}
return style;
}
export default {
name: 'uni-easyinput',
emits: [
'click',
'iconClick',
'update:modelValue',
'input',
'focus',
'blur',
'confirm',
'clear',
'eyes',
'change',
'keyboardheightchange'
],
model: {
prop: 'modelValue',
event: 'update:modelValue'
},
options: {
// #ifdef MP-TOUTIAO
virtualHost: false,
// #endif
// #ifndef MP-TOUTIAO
virtualHost: true
// #endif
},
inject: {
form: {
from: 'uniForm',
default: null
},
formItem: {
from: 'uniFormItem',
default: null
}
},
props: {
name: String,
value: [Number, String],
modelValue: [Number, String],
type: {
type: String,
default: 'text'
},
clearable: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ' '
},
placeholderStyle: String,
focus: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
confirmType: {
type: String,
default: 'done'
},
clearSize: {
type: [Number, String],
default: 24
},
inputBorder: {
type: Boolean,
default: true
},
prefixIcon: {
type: String,
default: ''
},
suffixIcon: {
type: String,
default: ''
},
trim: {
type: [Boolean, String],
default: false
},
cursorSpacing: {
type: Number,
default: 0
},
passwordIcon: {
type: Boolean,
default: true
},
adjustPosition: {
type: Boolean,
default: true
},
primaryColor: {
type: String,
default: '#2979ff'
},
styles: {
type: Object,
default () {
return {
color: '#333',
backgroundColor: '#fff',
disableColor: '#F7F6F6',
borderColor: '#e5e5e5'
};
}
},
errorMessage: {
type: [String, Boolean],
default: ''
},
// #ifdef MP-ALIPAY
enableNative: {
type: Boolean,
default: false
}
// #endif
},
data() {
return {
focused: false,
val: '',
showMsg: '',
border: false,
isFirstBorder: false,
showClearIcon: false,
showPassword: false,
focusShow: false,
localMsg: '',
isEnter: false // 用于判断当前是否是使用回车操作
};
},
computed: {
// 输入框内是否有值
isVal() {
const val = this.val;
// fixed by mehaotian 处理值为0的情况,字符串0不在处理范围
if (val || val === 0) {
return true;
}
return false;
},
msg() {
// console.log('computed', this.form, this.formItem);
// if (this.form) {
// return this.errorMessage || this.formItem.errMsg;
// }
// TODO 处理头条 formItem 中 errMsg 不更新的问题
return this.localMsg || this.errorMessage;
},
// 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
},
// 处理外层样式的style
boxStyle() {
return `color:${
this.inputBorder && this.msg ? '#e43d33' : this.styles.color
};`;
},
// input 内容的类和样式处理
inputContentClass() {
return obj2strClass({
'is-input-border': this.inputBorder,
'is-input-error-border': this.inputBorder && this.msg,
'is-textarea': this.type === 'textarea',
'is-disabled': this.disabled,
'is-focused': this.focusShow
});
},
inputContentStyle() {
const focusColor = this.focusShow ?
this.primaryColor :
this.styles.borderColor;
const borderColor =
this.inputBorder && this.msg ? '#dd524d' : focusColor;
return obj2strStyle({
'border-color': borderColor || '#e5e5e5',
'background-color': this.disabled ?
this.styles.disableColor : this.styles.backgroundColor
});
},
// input右侧样式
inputStyle() {
const paddingRight =
this.type === 'password' || this.clearable || this.prefixIcon ?
'' :
'10px';
return obj2strStyle({
'padding-right': paddingRight,
'padding-left': this.prefixIcon ? '' : '10px'
});
}
},
watch: {
value(newVal) {
// fix by mehaotian 解决 值为null的情况下,input报错的bug
if (newVal === null) {
this.val = '';
return
}
this.val = newVal;
},
modelValue(newVal) {
if (newVal === null) {
this.val = '';
return
}
this.val = newVal;
},
focus(newVal) {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
}
},
created() {
this.init();
// TODO 处理头条vue3 computed 不监听 inject 更改的问题(formItem.errMsg)
if (this.form && this.formItem) {
this.$watch('formItem.errMsg', newVal => {
this.localMsg = newVal;
});
}
},
mounted() {
this.$nextTick(() => {
this.focused = this.focus;
this.focusShow = this.focus;
});
},
methods: {
/**
* 初始化变量值
*/
init() {
if (this.value || this.value === 0) {
this.val = this.value;
} else if (
this.modelValue ||
this.modelValue === 0 ||
this.modelValue === ''
) {
this.val = this.modelValue;
} else {
// fix by ht 如果初始值为null,则input报错,待框架修复
this.val = '';
}
},
/**
* 点击图标时触发
* @param {Object} type
*/
onClickIcon(type) {
this.$emit('iconClick', type);
},
/**
* 显示隐藏内容,密码框时生效
*/
onEyes() {
this.showPassword = !this.showPassword;
this.$emit('eyes', this.showPassword);
},
/**
* 输入时触发
* @param {Object} event
*/
onInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if (this.trim) {
if (typeof this.trim === 'boolean' && this.trim) {
value = this.trimStr(value);
}
if (typeof this.trim === 'string') {
value = this.trimStr(value, this.trim);
}
}
if (this.errMsg) this.errMsg = '';
this.val = value;
// TODO 兼容 vue2
this.$emit('input', value);
// TODO 兼容 vue3
this.$emit('update:modelValue', value);
},
/**
* 外部调用方法
* 获取焦点时触发
* @param {Object} event
*/
onFocus() {
this.$nextTick(() => {
this.focused = true;
});
this.$emit('focus', null);
},
_Focus(event) {
this.focusShow = true;
this.$emit('focus', event);
},
/**
* 外部调用方法
* 失去焦点时触发
* @param {Object} event
*/
onBlur() {
this.focused = false;
this.$emit('blur', null);
},
_Blur(event) {
let value = event.detail.value;
this.focusShow = false;
this.$emit('blur', event);
// 根据类型返回值,在event中获取的值理论上讲都是string
if (this.isEnter === false) {
this.$emit('change', this.val);
}
// 失去焦点时参与表单校验
if (this.form && this.formItem) {
const { validateTrigger } = this.form;
if (validateTrigger === 'blur') {
this.formItem.onFieldChange();
}
}
},
/**
* 按下键盘的发送键
* @param {Object} e
*/
onConfirm(e) {
this.$emit('confirm', this.val);
this.isEnter = true;
this.$emit('change', this.val);
this.$nextTick(() => {
this.isEnter = false;
});
},
/**
* 清理内容
* @param {Object} event
*/
onClear(event) {
this.val = '';
// TODO 兼容 vue2
this.$emit('input', '');
// TODO 兼容 vue2
// TODO 兼容 vue3
this.$emit('update:modelValue', '');
// 点击叉号触发
this.$emit('clear');
},
/**
* 键盘高度发生变化的时候触发此事件
* 兼容性:微信小程序2.7.0+、App 3.1.0+
* @param {Object} event
*/
onkeyboardheightchange(event) {
this.$emit('keyboardheightchange', event);
},
/**
* 去除空格
*/
trimStr(str, pos = 'both') {
if (pos === 'both') {
return str.trim();
} else if (pos === 'left') {
return str.trimLeft();
} else if (pos === 'right') {
return str.trimRight();
} else if (pos === 'start') {
return str.trimStart();
} else if (pos === 'end') {
return str.trimEnd();
} else if (pos === 'all') {
return str.replace(/\s+/g, '');
} else if (pos === 'none') {
return str;
}
return str;
}
}
};
</script>
<style lang="scss">
$uni-error: #e43d33;
$uni-border-1: #dcdfe6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
position: relative;
text-align: left;
color: #333;
font-size: 14px;
}
.uni-easyinput__content {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
// min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
// 处理border动画刚开始显示黑色的问题
border-color: #fff;
transition-property: border-color;
transition-duration: 0.3s;
}
.uni-easyinput__content-input {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
// min-height: 36px;
/*ifdef H5*/
& ::-ms-reveal {
display: none;
}
& ::-ms-clear {
display: none;
}
& ::-o-clear {
display: none;
}
/*endif*/
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
// font-weight: 200;
}
.is-textarea {
align-items: flex-start;
}
.is-textarea-icon {
margin-top: 5px;
}
.uni-easyinput__content-textarea {
position: relative;
overflow: hidden;
flex: 1;
line-height: 1.5;
font-size: 14px;
margin: 6px;
margin-left: 0;
height: 80px;
min-height: 80px;
/* #ifndef APP-NVUE */
min-height: 80px;
width: auto;
/* #endif */
}
.input-padding {
padding-left: 10px;
}
.content-clear-icon {
padding: 0 5px;
}
.label-icon {
margin-right: 5px;
margin-top: -1px;
}
// 显示边框
.is-input-border {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
border: 1px solid $uni-border-1;
border-radius: 4px;
/* #ifdef MP-ALIPAY */
overflow: hidden;
/* #endif */
}
.uni-error-message {
position: absolute;
bottom: -17px;
left: 0;
line-height: 12px;
color: $uni-error;
font-size: 12px;
text-align: left;
}
.uni-error-msg--boeder {
position: relative;
bottom: 0;
line-height: 22px;
}
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);
}
}
.uni-easyinput--border {
margin-bottom: 0;
padding: 10px 15px;
// padding-bottom: 0;
border-top: 1px #eee solid;
}
.uni-easyinput-error {
padding-bottom: 0;
}
.is-first-border {
/* #ifndef APP-NVUE */
border: none;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
.is-disabled {
background-color: #f7f6f6;
color: #d5d5d5;
.uni-easyinput__placeholder-class {
color: #d5d5d5;
font-size: 12px;
}
}
</style>
\ No newline at end of file
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.1.19",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
\ No newline at end of file
### Easyinput 增强输入框
> **组件名:uni-easyinput**
> 代码块: `uEasyinput`
easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
## 1.4.13(2024-10-08)
- 修复 校验规则在抖音开发者工具上不生效的bug,详见:[https://ask.dcloud.net.cn/question/191933](https://ask.dcloud.net.cn/question/191933)
## 1.4.12 (2024-9-21)
- 修复 form上次修改的问题
## 1.4.11 (2024-9-14)
- 修复 binddata的兼容性问题
## 1.4.10(2023-11-03)
- 优化 labelWidth 描述错误
## 1.4.9(2023-02-10)
- 修复 required 参数无法动态绑定
## 1.4.8(2022-08-23)
- 优化 根据 rules 自动添加 required 的问题
## 1.4.7(2022-08-22)
- 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540)
## 1.4.6(2022-07-13)
- 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug
## 1.4.5(2022-07-05)
- 新增 更多表单示例
- 优化 子表单组件过期提示的问题
- 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式
## 1.4.4(2022-07-04)
- 更新 删除组件日志
## 1.4.3(2022-07-04)
- 修复 由 1.4.0 引发的 label 插槽不生效的bug
## 1.4.2(2022-07-04)
- 修复 子组件找不到 setValue 报错的bug
## 1.4.1(2022-07-04)
- 修复 uni-data-picker 在 uni-forms-item 中报错的bug
- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug
## 1.4.0(2022-06-30)
- 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题
- 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力
- 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃
- 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效
- 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法
- 新增 子表单的 setRules 方法,配合自定义校验函数使用
- 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则
- 优化 动态表单校验方式,废弃拼接name的方式
## 1.3.3(2022-06-22)
- 修复 表单校验顺序无序问题
## 1.3.2(2021-12-09)
-
## 1.3.1(2021-11-19)
- 修复 label 插槽不生效的bug
## 1.3.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms)
## 1.2.7(2021-08-13)
- 修复 没有添加校验规则的字段依然报错的Bug
## 1.2.6(2021-08-11)
- 修复 重置表单错误信息无法清除的问题
## 1.2.5(2021-08-11)
- 优化 组件文档
## 1.2.4(2021-08-11)
- 修复 表单验证只生效一次的问题
## 1.2.3(2021-07-30)
- 优化 vue3下事件警告的问题
## 1.2.2(2021-07-26)
- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug
- 修复 1.2.1 引起的示例在小程序平台报错的Bug
## 1.2.1(2021-07-22)
- 修复 动态校验表单,默认值为空的情况下校验失效的Bug
- 修复 不指定name属性时,运行报错的Bug
- 优化 label默认宽度从65调整至70,使required为true且四字时不换行
- 优化 组件示例,新增动态校验示例代码
- 优化 组件文档,使用方式更清晰
## 1.2.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.2(2021-06-25)
- 修复 pattern 属性在微信小程序平台无效的问题
## 1.1.1(2021-06-22)
- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug
## 1.1.0(2021-06-22)
- 修复 只写setRules方法而导致校验不生效的Bug
- 修复 由上个办法引发的错误提示文字错位的Bug
## 1.0.48(2021-06-21)
- 修复 不设置 label 属性 ,无法设置label插槽的问题
## 1.0.47(2021-06-21)
- 修复 不设置label属性,label-width属性不生效的bug
- 修复 setRules 方法与rules属性冲突的问题
## 1.0.46(2021-06-04)
- 修复 动态删减数据导致报错的问题
## 1.0.45(2021-06-04)
- 新增 modelValue 属性 ,value 即将废弃
## 1.0.44(2021-06-02)
- 新增 uni-forms-item 可以设置单独的 rules
- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤
- 优化 submit 事件重命名为 validate
## 1.0.43(2021-05-12)
- 新增 组件示例地址
## 1.0.42(2021-04-30)
- 修复 自定义检验器失效的问题
## 1.0.41(2021-03-05)
- 更新 校验器
- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug
## 1.0.40(2021-03-04)
- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug
## 1.0.39(2021-02-05)
- 调整为uni_modules目录规范
- 修复 校验器传入 int 等类型 ,返回String类型的Bug
<template>
<view class="uni-forms-item"
:class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']">
<slot name="label">
<view class="uni-forms-item__label" :class="{'no-label':!label && !required}"
:style="{width:localLabelWidth,justifyContent: localLabelAlign}">
<text v-if="required" class="is-required">*</text>
<text>{{label}}</text>
</view>
</slot>
<!-- #ifndef APP-NVUE -->
<view class="uni-forms-item__content">
<slot></slot>
<view class="uni-forms-item__error" :class="{'msg--active':msg}">
<text>{{msg}}</text>
</view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="uni-forms-item__nuve-content">
<view class="uni-forms-item__content">
<slot></slot>
</view>
<view class="uni-forms-item__error" :class="{'msg--active':msg}">
<text class="error-text">{{msg}}</text>
</view>
</view>
<!-- #endif -->
</view>
</template>
<script>
/**
* uni-fomrs-item 表单子组件
* @description uni-fomrs-item 表单子组件,提供了基础布局已经校验能力
* @tutorial https://ext.dcloud.net.cn/plugin?id=2773
* @property {Boolean} required 是否必填,左边显示红色"*"号
* @property {String } label 输入框左边的文字提示
* @property {Number } labelWidth label的宽度,单位px(默认70)
* @property {String } labelAlign = [left|center|right] label的文字对齐方式(默认left)
* @value left label 左侧显示
* @value center label 居中
* @value right label 右侧对齐
* @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
* @property {String } name 表单域的属性名,在使用校验规则时必填
* @property {String } leftIcon 【1.4.0废弃】label左边的图标,限 uni-ui 的图标名称
* @property {String } iconColor 【1.4.0废弃】左边通过icon配置的图标的颜色(默认#606266)
* @property {String} validateTrigger = [bind|submit|blur] 【1.4.0废弃】校验触发器方式 默认 submit
* @value bind 发生变化时触发
* @value submit 提交时触发
* @value blur 失去焦点触发
* @property {String } labelPosition = [top|left] 【1.4.0废弃】label的文字的位置(默认left)
* @value top 顶部显示 label
* @value left 左侧显示 label
*/
export default {
name: 'uniFormsItem',
options: {
// #ifdef MP-TOUTIAO
virtualHost: false,
// #endif
// #ifndef MP-TOUTIAO
virtualHost: true
// #endif
},
provide() {
return {
uniFormItem: this
}
},
inject: {
form: {
from: 'uniForm',
default: null
},
},
props: {
// 表单校验规则
rules: {
type: Array,
default () {
return null;
}
},
// 表单域的属性名,在使用校验规则时必填
name: {
type: [String, Array],
default: ''
},
required: {
type: Boolean,
default: false
},
label: {
type: String,
default: ''
},
// label的宽度
labelWidth: {
type: [String, Number],
default: ''
},
// label 居中方式,默认 left 取值 left/center/right
labelAlign: {
type: String,
default: ''
},
// 强制显示错误信息
errorMessage: {
type: [String, Boolean],
default: ''
},
// 1.4.0 弃用,统一使用 form 的校验时机
// validateTrigger: {
// type: String,
// default: ''
// },
// 1.4.0 弃用,统一使用 form 的label 位置
// labelPosition: {
// type: String,
// default: ''
// },
// 1.4.0 以下属性已经废弃,请使用 #label 插槽代替
leftIcon: String,
iconColor: {
type: String,
default: '#606266'
},
},
data() {
return {
errMsg: '',
userRules: null,
localLabelAlign: 'left',
localLabelWidth: '70px',
localLabelPos: 'left',
border: false,
isFirstBorder: false,
};
},
computed: {
// 处理错误信息
msg() {
return this.errorMessage || this.errMsg;
}
},
watch: {
// 规则发生变化通知子组件更新
'form.formRules'(val) {
// TODO 处理头条vue3 watch不生效的问题
// #ifndef MP-TOUTIAO
this.init()
// #endif
},
'form.labelWidth'(val) {
// 宽度
this.localLabelWidth = this._labelWidthUnit(val)
},
'form.labelPosition'(val) {
// 标签位置
this.localLabelPos = this._labelPosition()
},
'form.labelAlign'(val) {
}
},
created() {
this.init(true)
if (this.name && this.form) {
// TODO 处理头条vue3 watch不生效的问题
// #ifdef MP-TOUTIAO
this.$watch('form.formRules', () => {
this.init()
})
// #endif
// 监听变化
this.$watch(
() => {
const val = this.form._getDataValue(this.name, this.form.localData)
return val
},
(value, oldVal) => {
const isEqual = this.form._isEqual(value, oldVal)
// 简单判断前后值的变化,只有发生变化才会发生校验
// TODO 如果 oldVal = undefined ,那么大概率是源数据里没有值导致 ,这个情况不哦校验 ,可能不严谨 ,需要在做观察
// fix by mehaotian 暂时取消 && oldVal !== undefined ,如果formData 中不存在,可能会不校验
if (!isEqual) {
const val = this.itemSetValue(value)
this.onFieldChange(val, false)
}
}, {
immediate: false
}
);
}
},
// #ifndef VUE3
destroyed() {
if (this.__isUnmounted) return
this.unInit()
},
// #endif
// #ifdef VUE3
unmounted() {
this.__isUnmounted = true
this.unInit()
},
// #endif
methods: {
/**
* 外部调用方法
* 设置规则 ,主要用于小程序自定义检验规则
* @param {Array} rules 规则源数据
*/
setRules(rules = null) {
this.userRules = rules
this.init(false)
},
// 兼容老版本表单组件
setValue() {
// console.log('setValue 方法已经弃用,请使用最新版本的 uni-forms 表单组件以及其他关联组件。');
},
/**
* 外部调用方法
* 校验数据
* @param {any} value 需要校验的数据
* @param {boolean} 是否立即校验
* @return {Array|null} 校验内容
*/
async onFieldChange(value, formtrigger = true) {
const {
formData,
localData,
errShowType,
validateCheck,
validateTrigger,
_isRequiredField,
_realName
} = this.form
const name = _realName(this.name)
if (!value) {
value = this.form.formData[name]
}
// fixd by mehaotian 不在校验前清空信息,解决闪屏的问题
// this.errMsg = '';
// fix by mehaotian 解决没有检验规则的情况下,抛出错误的问题
const ruleLen = this.itemRules.rules && this.itemRules.rules.length
if (!this.validator || !ruleLen || ruleLen === 0) return;
// 检验时机
// let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger);
const isRequiredField = _isRequiredField(this.itemRules.rules || []);
let result = null;
// 只有等于 bind 时 ,才能开启时实校验
if (validateTrigger === 'bind' || formtrigger) {
// 校验当前表单项
result = await this.validator.validateUpdate({
[name]: value
},
formData
);
// 判断是否必填,非必填,不填不校验,填写才校验 ,暂时只处理 undefined 和空的情况
if (!isRequiredField && (value === undefined || value === '')) {
result = null;
}
// 判断错误信息显示类型
if (result && result.errorMessage) {
if (errShowType === 'undertext') {
// 获取错误信息
this.errMsg = !result ? '' : result.errorMessage;
}
if (errShowType === 'toast') {
uni.showToast({
title: result.errorMessage || '校验错误',
icon: 'none'
});
}
if (errShowType === 'modal') {
uni.showModal({
title: '提示',
content: result.errorMessage || '校验错误'
});
}
} else {
this.errMsg = ''
}
// 通知 form 组件更新事件
validateCheck(result ? result : null)
} else {
this.errMsg = ''
}
return result ? result : null;
},
/**
* 初始组件数据
*/
init(type = false) {
const {
validator,
formRules,
childrens,
formData,
localData,
_realName,
labelWidth,
_getDataValue,
_setDataValue
} = this.form || {}
// 对齐方式
this.localLabelAlign = this._justifyContent()
// 宽度
this.localLabelWidth = this._labelWidthUnit(labelWidth)
// 标签位置
this.localLabelPos = this._labelPosition()
// 将需要校验的子组件加入form 队列
this.form && type && childrens.push(this)
if (!validator || !formRules) return
// 判断第一个 item
if (!this.form.isFirstBorder) {
this.form.isFirstBorder = true;
this.isFirstBorder = true;
}
// 判断 group 里的第一个 item
if (this.group) {
if (!this.group.isFirstBorder) {
this.group.isFirstBorder = true;
this.isFirstBorder = true;
}
}
this.border = this.form.border;
// 获取子域的真实名称
const name = _realName(this.name)
const itemRule = this.userRules || this.rules
if (typeof formRules === 'object' && itemRule) {
// 子规则替换父规则
formRules[name] = {
rules: itemRule
}
validator.updateSchema(formRules);
}
// 注册校验规则
const itemRules = formRules[name] || {}
this.itemRules = itemRules
// 注册校验函数
this.validator = validator
// 默认值赋予
this.itemSetValue(_getDataValue(this.name, localData))
},
unInit() {
if (this.form) {
const {
childrens,
formData,
_realName
} = this.form
childrens.forEach((item, index) => {
if (item === this) {
this.form.childrens.splice(index, 1)
delete formData[_realName(item.name)]
}
})
}
},
// 设置item 的值
itemSetValue(value) {
const name = this.form._realName(this.name)
const rules = this.itemRules.rules || []
const val = this.form._getValue(name, value, rules)
this.form._setDataValue(name, this.form.formData, val)
return val
},
/**
* 移除该表单项的校验结果
*/
clearValidate() {
this.errMsg = '';
},
// 是否显示星号
_isRequired() {
// TODO 不根据规则显示 星号,考虑后续兼容
// if (this.form) {
// if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) {
// return true
// }
// return false
// }
return this.required
},
// 处理对齐方式
_justifyContent() {
if (this.form) {
const {
labelAlign
} = this.form
let labelAli = this.labelAlign ? this.labelAlign : labelAlign;
if (labelAli === 'left') return 'flex-start';
if (labelAli === 'center') return 'center';
if (labelAli === 'right') return 'flex-end';
}
return 'flex-start';
},
// 处理 label宽度单位 ,继承父元素的值
_labelWidthUnit(labelWidth) {
// if (this.form) {
// const {
// labelWidth
// } = this.form
return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 70 : 'auto')))
// }
// return '70px'
},
// 处理 label 位置
_labelPosition() {
if (this.form) return this.form.labelPosition || 'left'
return 'left'
},
/**
* 触发时机
* @param {Object} rule 当前规则内时机
* @param {Object} itemRlue 当前组件时机
* @param {Object} parentRule 父组件时机
*/
isTrigger(rule, itemRlue, parentRule) {
// bind submit
if (rule === 'submit' || !rule) {
if (rule === undefined) {
if (itemRlue !== 'bind') {
if (!itemRlue) {
return parentRule === '' ? 'bind' : 'submit';
}
return 'submit';
}
return 'bind';
}
return 'submit';
}
return 'bind';
},
num2px(num) {
if (typeof num === 'number') {
return `${num}px`
}
return num
}
}
};
</script>
<style lang="scss">
.uni-forms-item {
position: relative;
display: flex;
/* #ifdef APP-NVUE */
// 在 nvue 中,使用 margin-bottom error 信息会被隐藏
padding-bottom: 22px;
/* #endif */
/* #ifndef APP-NVUE */
margin-bottom: 22px;
/* #endif */
flex-direction: row;
&__label {
display: flex;
flex-direction: row;
align-items: center;
text-align: left;
font-size: 14px;
color: #606266;
height: 36px;
padding: 0 12px 0 0;
/* #ifndef APP-NVUE */
vertical-align: middle;
flex-shrink: 0;
/* #endif */
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
&.no-label {
padding: 0;
}
}
&__content {
/* #ifndef MP-TOUTIAO */
// display: flex;
// align-items: center;
/* #endif */
position: relative;
font-size: 14px;
flex: 1;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
flex-direction: row;
/* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */
// TODO 因为小程序平台会多一层标签节点 ,所以需要在多余节点继承当前样式
&>uni-easyinput,
&>uni-data-picker {
width: 100%;
}
/* #endif */
}
& .uni-forms-item__nuve-content {
display: flex;
flex-direction: column;
flex: 1;
}
&__error {
color: #f56c6c;
font-size: 12px;
line-height: 1;
padding-top: 4px;
position: absolute;
/* #ifndef APP-NVUE */
top: 100%;
left: 0;
transition: transform 0.3s;
transform: translateY(-100%);
/* #endif */
/* #ifdef APP-NVUE */
bottom: 5px;
/* #endif */
opacity: 0;
.error-text {
// 只有 nvue 下这个样式才生效
color: #f56c6c;
font-size: 12px;
}
&.msg--active {
opacity: 1;
transform: translateY(0%);
}
}
// 位置修饰样式
&.is-direction-left {
flex-direction: row;
}
&.is-direction-top {
flex-direction: column;
.uni-forms-item__label {
padding: 0 0 8px;
line-height: 1.5715;
text-align: left;
/* #ifndef APP-NVUE */
white-space: initial;
/* #endif */
}
}
.is-required {
// color: $uni-color-error;
color: #dd524d;
font-weight: bold;
}
}
.uni-forms-item--border {
margin-bottom: 0;
padding: 10px 0;
// padding-bottom: 0;
border-top: 1px #eee solid;
/* #ifndef APP-NVUE */
.uni-forms-item__content {
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
.uni-forms-item__error {
position: relative;
top: 5px;
left: 0;
padding-top: 0;
}
}
/* #endif */
/* #ifdef APP-NVUE */
display: flex;
flex-direction: column;
.uni-forms-item__error {
position: relative;
top: 0px;
left: 0;
padding-top: 0;
margin-top: 5px;
}
/* #endif */
}
.is-first-border {
/* #ifndef APP-NVUE */
border: none;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
</style>
<template>
<view class="uni-forms">
<form>
<slot></slot>
</form>
</view>
</template>
<script>
import Validator from './validate.js';
import {
deepCopy,
getValue,
isRequiredField,
setDataValue,
getDataValue,
realName,
isRealName,
rawData,
isEqual
} from './utils.js'
// #ifndef VUE3
// 后续会慢慢废弃这个方法
import Vue from 'vue';
Vue.prototype.binddata = function(name, value, formName) {
if (formName) {
this.$refs[formName].setValue(name, value);
} else {
let formVm;
for (let i in this.$refs) {
const vm = this.$refs[i];
if (vm && vm.$options && vm.$options.name === 'uniForms') {
formVm = vm;
break;
}
}
if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性');
formVm.setValue(name, value);
}
};
// #endif
/**
* Forms 表单
* @description 由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据
* @tutorial https://ext.dcloud.net.cn/plugin?id=2773
* @property {Object} rules 表单校验规则
* @property {String} validateTrigger = [bind|submit|blur] 校验触发器方式 默认 submit
* @value bind 发生变化时触发
* @value submit 提交时触发
* @value blur 失去焦点时触发
* @property {String} labelPosition = [top|left] label 位置 默认 left
* @value top 顶部显示 label
* @value left 左侧显示 label
* @property {String} labelWidth label 宽度,默认 70px
* @property {String} labelAlign = [left|center|right] label 居中方式 默认 left
* @value left label 左侧显示
* @value center label 居中
* @value right label 右侧对齐
* @property {String} errShowType = [undertext|toast|modal] 校验错误信息提示方式
* @value undertext 错误信息在底部显示
* @value toast 错误信息toast显示
* @value modal 错误信息modal显示
* @event {Function} submit 提交时触发
* @event {Function} validate 校验结果发生变化触发
*/
export default {
name: 'uniForms',
emits: ['validate', 'submit'],
options: {
// #ifdef MP-TOUTIAO
virtualHost: false,
// #endif
// #ifndef MP-TOUTIAO
virtualHost: true
// #endif
},
props: {
// 即将弃用
value: {
type: Object,
default () {
return null;
}
},
// vue3 替换 value 属性
modelValue: {
type: Object,
default () {
return null;
}
},
// 1.4.0 开始将不支持 v-model ,且废弃 value 和 modelValue
model: {
type: Object,
default () {
return null;
}
},
// 表单校验规则
rules: {
type: Object,
default () {
return {};
}
},
//校验错误信息提示方式 默认 undertext 取值 [undertext|toast|modal]
errShowType: {
type: String,
default: 'undertext'
},
// 校验触发器方式 默认 bind 取值 [bind|submit]
validateTrigger: {
type: String,
default: 'submit'
},
// label 位置,默认 left 取值 top/left
labelPosition: {
type: String,
default: 'left'
},
// label 宽度
labelWidth: {
type: [String, Number],
default: ''
},
// label 居中方式,默认 left 取值 left/center/right
labelAlign: {
type: String,
default: 'left'
},
border: {
type: Boolean,
default: false
}
},
provide() {
return {
uniForm: this
}
},
data() {
return {
// 表单本地值的记录,不应该与传如的值进行关联
formData: {},
formRules: {}
};
},
computed: {
// 计算数据源变化的
localData() {
const localVal = this.model || this.modelValue || this.value
if (localVal) {
return deepCopy(localVal)
}
return {}
}
},
watch: {
// 监听数据变化 ,暂时不使用,需要单独赋值
// localData: {},
// 监听规则变化
rules: {
handler: function(val, oldVal) {
this.setRules(val)
},
deep: true,
immediate: true
}
},
created() {
// #ifdef VUE3
let getbinddata = getApp().$vm.$.appContext.config.globalProperties.binddata
if (!getbinddata) {
getApp().$vm.$.appContext.config.globalProperties.binddata = function(name, value, formName) {
if (formName) {
this.$refs[formName].setValue(name, value);
} else {
let formVm;
for (let i in this.$refs) {
const vm = this.$refs[i];
if (vm && vm.$options && vm.$options.name === 'uniForms') {
formVm = vm;
break;
}
}
if (!formVm) return console.error('当前 uni-froms 组件缺少 ref 属性');
if(formVm.model)formVm.model[name] = value
if(formVm.modelValue)formVm.modelValue[name] = value
if(formVm.value)formVm.value[name] = value
}
}
}
// #endif
// 子组件实例数组
this.childrens = []
// TODO 兼容旧版 uni-data-picker ,新版本中无效,只是避免报错
this.inputChildrens = []
this.setRules(this.rules)
},
methods: {
/**
* 外部调用方法
* 设置规则 ,主要用于小程序自定义检验规则
* @param {Array} rules 规则源数据
*/
setRules(rules) {
// TODO 有可能子组件合并规则的时机比这个要早,所以需要合并对象 ,而不是直接赋值,可能会被覆盖
this.formRules = Object.assign({}, this.formRules, rules)
// 初始化校验函数
this.validator = new Validator(rules);
},
/**
* 外部调用方法
* 设置数据,用于设置表单数据,公开给用户使用 , 不支持在动态表单中使用
* @param {Object} key
* @param {Object} value
*/
setValue(key, value) {
let example = this.childrens.find(child => child.name === key);
if (!example) return null;
this.formData[key] = getValue(key, value, (this.formRules[key] && this.formRules[key].rules) || [])
return example.onFieldChange(this.formData[key]);
},
/**
* 外部调用方法
* 手动提交校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
* @param {Array} keepitem 保留不参与校验的字段
* @param {type} callback 方法回调
*/
validate(keepitem, callback) {
return this.checkAll(this.formData, keepitem, callback);
},
/**
* 外部调用方法
* 部分表单校验
* @param {Array|String} props 需要校验的字段
* @param {Function} 回调函数
*/
validateField(props = [], callback) {
props = [].concat(props);
let invalidFields = {};
this.childrens.forEach(item => {
const name = realName(item.name)
if (props.indexOf(name) !== -1) {
invalidFields = Object.assign({}, invalidFields, {
[name]: this.formData[name]
});
}
});
return this.checkAll(invalidFields, [], callback);
},
/**
* 外部调用方法
* 移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果
* @param {Array|String} props 需要移除校验的字段 ,不填为所有
*/
clearValidate(props = []) {
props = [].concat(props);
this.childrens.forEach(item => {
if (props.length === 0) {
item.errMsg = '';
} else {
const name = realName(item.name)
if (props.indexOf(name) !== -1) {
item.errMsg = '';
}
}
});
},
/**
* 外部调用方法 ,即将废弃
* 手动提交校验表单
* 对整个表单进行校验的方法,参数为一个回调函数。
* @param {Array} keepitem 保留不参与校验的字段
* @param {type} callback 方法回调
*/
submit(keepitem, callback, type) {
for (let i in this.dataValue) {
const itemData = this.childrens.find(v => v.name === i);
if (itemData) {
if (this.formData[i] === undefined) {
this.formData[i] = this._getValue(i, this.dataValue[i]);
}
}
}
if (!type) {
console.warn('submit 方法即将废弃,请使用validate方法代替!');
}
return this.checkAll(this.formData, keepitem, callback, 'submit');
},
// 校验所有
async checkAll(invalidFields, keepitem, callback, type) {
// 不存在校验规则 ,则停止校验流程
if (!this.validator) return
let childrens = []
// 处理参与校验的item实例
for (let i in invalidFields) {
const item = this.childrens.find(v => realName(v.name) === i)
if (item) {
childrens.push(item)
}
}
// 如果validate第一个参数是funciont ,那就走回调
if (!callback && typeof keepitem === 'function') {
callback = keepitem;
}
let promise;
// 如果不存在回调,那么使用 Promise 方式返回
if (!callback && typeof callback !== 'function' && Promise) {
promise = new Promise((resolve, reject) => {
callback = function(valid, invalidFields) {
!valid ? resolve(invalidFields) : reject(valid);
};
});
}
let results = [];
// 避免引用错乱 ,建议拷贝对象处理
let tempFormData = JSON.parse(JSON.stringify(invalidFields))
// 所有子组件参与校验,使用 for 可以使用 awiat
for (let i in childrens) {
const child = childrens[i]
let name = realName(child.name);
const result = await child.onFieldChange(tempFormData[name]);
if (result) {
results.push(result);
// toast ,modal 只需要执行第一次就可以
if (this.errShowType === 'toast' || this.errShowType === 'modal') break;
}
}
if (Array.isArray(results)) {
if (results.length === 0) results = null;
}
if (Array.isArray(keepitem)) {
keepitem.forEach(v => {
let vName = realName(v);
let value = getDataValue(v, this.localData)
if (value !== undefined) {
tempFormData[vName] = value
}
});
}
// TODO submit 即将废弃
if (type === 'submit') {
this.$emit('submit', {
detail: {
value: tempFormData,
errors: results
}
});
} else {
this.$emit('validate', results);
}
// const resetFormData = rawData(tempFormData, this.localData, this.name)
let resetFormData = {}
resetFormData = rawData(tempFormData, this.name)
callback && typeof callback === 'function' && callback(results, resetFormData);
if (promise && callback) {
return promise;
} else {
return null;
}
},
/**
* 返回validate事件
* @param {Object} result
*/
validateCheck(result) {
this.$emit('validate', result);
},
_getValue: getValue,
_isRequiredField: isRequiredField,
_setDataValue: setDataValue,
_getDataValue: getDataValue,
_realName: realName,
_isRealName: isRealName,
_isEqual: isEqual
}
};
</script>
<style lang="scss">
.uni-forms {}
</style>
/**
* 简单处理对象拷贝
* @param {Obejct} 被拷贝对象
* @@return {Object} 拷贝对象
*/
export const deepCopy = (val) => {
return JSON.parse(JSON.stringify(val))
}
/**
* 过滤数字类型
* @param {String} format 数字类型
* @@return {Boolean} 返回是否为数字类型
*/
export const typeFilter = (format) => {
return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp';
}
/**
* 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined
* @param {String} key 字段名
* @param {any} value 字段值
* @param {Object} rules 表单校验规则
*/
export const getValue = (key, value, rules) => {
const isRuleNumType = rules.find(val => val.format && typeFilter(val.format));
const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool');
// 输入类型为 number
if (!!isRuleNumType) {
if (!value && value !== 0) {
value = null
} else {
value = isNumber(Number(value)) ? Number(value) : value
}
}
// 输入类型为 boolean
if (!!isRuleBoolType) {
value = isBoolean(value) ? value : false
}
return value;
}
/**
* 获取表单数据
* @param {String|Array} name 真实名称,需要使用 realName 获取
* @param {Object} data 原始数据
* @param {any} value 需要设置的值
*/
export const setDataValue = (field, formdata, value) => {
formdata[field] = value
return value || ''
}
/**
* 获取表单数据
* @param {String|Array} field 真实名称,需要使用 realName 获取
* @param {Object} data 原始数据
*/
export const getDataValue = (field, data) => {
return objGet(data, field)
}
/**
* 获取表单类型
* @param {String|Array} field 真实名称,需要使用 realName 获取
*/
export const getDataValueType = (field, data) => {
const value = getDataValue(field, data)
return {
type: type(value),
value
}
}
/**
* 获取表单可用的真实name
* @param {String|Array} name 表单name
* @@return {String} 表单可用的真实name
*/
export const realName = (name, data = {}) => {
const base_name = _basePath(name)
if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) {
const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_')
return realname
}
return base_name[0] || name
}
/**
* 判断是否表单可用的真实name
* @param {String|Array} name 表单name
* @@return {String} 表单可用的真实name
*/
export const isRealName = (name) => {
const reg = /^_formdata_#*/
return reg.test(name)
}
/**
* 获取表单数据的原始格式
* @@return {Object|Array} object 需要解析的数据
*/
export const rawData = (object = {}, name) => {
let newData = JSON.parse(JSON.stringify(object))
let formData = {}
for(let i in newData){
let path = name2arr(i)
objSet(formData,path,newData[i])
}
return formData
}
/**
* 真实name还原为 array
* @param {*} name
*/
export const name2arr = (name) => {
let field = name.replace('_formdata_#', '')
field = field.split('#').map(v => (isNumber(v) ? Number(v) : v))
return field
}
/**
* 对象中设置值
* @param {Object|Array} object 源数据
* @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c']
* @param {String} value 需要设置的值
*/
export const objSet = (object, path, value) => {
if (typeof object !== 'object') return object;
_basePath(path).reduce((o, k, i, _) => {
if (i === _.length - 1) {
// 若遍历结束直接赋值
o[k] = value
return null
} else if (k in o) {
// 若存在对应路径,则返回找到的对象,进行下一次遍历
return o[k]
} else {
// 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象
o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {}
return o[k]
}
}, object)
// 返回object
return object;
}
// 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用
function _basePath(path) {
// 若是数组,则直接返回
if (Array.isArray(path)) return path
// 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']'
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
}
/**
* 从对象中获取值
* @param {Object|Array} object 源数据
* @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c']
* @param {String} defaultVal 如果无法从调用链中获取值的默认值
*/
export const objGet = (object, path, defaultVal = 'undefined') => {
// 先将path处理成统一格式
let newPath = _basePath(path)
// 递归处理,返回最后结果
let val = newPath.reduce((o, k) => {
return (o || {})[k]
}, object);
return !val || val !== undefined ? val : defaultVal
}
/**
* 是否为 number 类型
* @param {any} num 需要判断的值
* @return {Boolean} 是否为 number
*/
export const isNumber = (num) => {
return !isNaN(Number(num))
}
/**
* 是否为 boolean 类型
* @param {any} bool 需要判断的值
* @return {Boolean} 是否为 boolean
*/
export const isBoolean = (bool) => {
return (typeof bool === 'boolean')
}
/**
* 是否有必填字段
* @param {Object} rules 规则
* @return {Boolean} 是否有必填字段
*/
export const isRequiredField = (rules) => {
let isNoField = false;
for (let i = 0; i < rules.length; i++) {
const ruleData = rules[i];
if (ruleData.required) {
isNoField = true;
break;
}
}
return isNoField;
}
/**
* 获取数据类型
* @param {Any} obj 需要获取数据类型的值
*/
export const type = (obj) => {
var class2type = {};
// 生成class2type映射
"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
class2type["[object " + item + "]"] = item.toLowerCase();
})
if (obj == null) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[Object.prototype.toString.call(obj)] || "object" :
typeof obj;
}
/**
* 判断两个值是否相等
* @param {any} a 值
* @param {any} b 值
* @return {Boolean} 是否相等
*/
export const isEqual = (a, b) => {
//如果a和b本来就全等
if (a === b) {
//判断是否为0和-0
return a !== 0 || 1 / a === 1 / b;
}
//判断是否为null和undefined
if (a == null || b == null) {
return a === b;
}
//接下来判断a和b的数据类型
var classNameA = toString.call(a),
classNameB = toString.call(b);
//如果数据类型不相等,则返回false
if (classNameA !== classNameB) {
return false;
}
//如果数据类型相等,再根据不同数据类型分别判断
switch (classNameA) {
case '[object RegExp]':
case '[object String]':
//进行字符串转换比较
return '' + a === '' + b;
case '[object Number]':
//进行数字转换比较,判断是否为NaN
if (+a !== +a) {
return +b !== +b;
}
//判断是否为0或-0
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
return +a === +b;
}
//如果是对象类型
if (classNameA == '[object Object]') {
//获取a和b的属性长度
var propsA = Object.getOwnPropertyNames(a),
propsB = Object.getOwnPropertyNames(b);
if (propsA.length != propsB.length) {
return false;
}
for (var i = 0; i < propsA.length; i++) {
var propName = propsA[i];
//如果对应属性对应值不相等,则返回false
if (a[propName] !== b[propName]) {
return false;
}
}
return true;
}
//如果是数组类型
if (classNameA == '[object Array]') {
if (a.toString() == b.toString()) {
return true;
}
return false;
}
}
var pattern = {
email: /^\S+?@\S+?\.\S+?$/,
idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
url: new RegExp(
"^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$",
'i')
};
const FORMAT_MAPPING = {
"int": 'integer',
"bool": 'boolean',
"double": 'number',
"long": 'number',
"password": 'string'
// "fileurls": 'array'
}
function formatMessage(args, resources = '') {
var defaultMessage = ['label']
defaultMessage.forEach((item) => {
if (args[item] === undefined) {
args[item] = ''
}
})
let str = resources
for (let key in args) {
let reg = new RegExp('{' + key + '}')
str = str.replace(reg, args[key])
}
return str
}
function isEmptyValue(value, type) {
if (value === undefined || value === null) {
return true;
}
if (typeof value === 'string' && !value) {
return true;
}
if (Array.isArray(value) && !value.length) {
return true;
}
if (type === 'object' && !Object.keys(value).length) {
return true;
}
return false;
}
const types = {
integer(value) {
return types.number(value) && parseInt(value, 10) === value;
},
string(value) {
return typeof value === 'string';
},
number(value) {
if (isNaN(value)) {
return false;
}
return typeof value === 'number';
},
"boolean": function(value) {
return typeof value === 'boolean';
},
"float": function(value) {
return types.number(value) && !types.integer(value);
},
array(value) {
return Array.isArray(value);
},
object(value) {
return typeof value === 'object' && !types.array(value);
},
date(value) {
return value instanceof Date;
},
timestamp(value) {
if (!this.integer(value) || Math.abs(value).toString().length > 16) {
return false
}
return true;
},
file(value) {
return typeof value.url === 'string';
},
email(value) {
return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255;
},
url(value) {
return typeof value === 'string' && !!value.match(pattern.url);
},
pattern(reg, value) {
try {
return new RegExp(reg).test(value);
} catch (e) {
return false;
}
},
method(value) {
return typeof value === 'function';
},
idcard(value) {
return typeof value === 'string' && !!value.match(pattern.idcard);
},
'url-https'(value) {
return this.url(value) && value.startsWith('https://');
},
'url-scheme'(value) {
return value.startsWith('://');
},
'url-web'(value) {
return false;
}
}
class RuleValidator {
constructor(message) {
this._message = message
}
async validateRule(fieldKey, fieldValue, value, data, allData) {
var result = null
let rules = fieldValue.rules
let hasRequired = rules.findIndex((item) => {
return item.required
})
if (hasRequired < 0) {
if (value === null || value === undefined) {
return result
}
if (typeof value === 'string' && !value.length) {
return result
}
}
var message = this._message
if (rules === undefined) {
return message['default']
}
for (var i = 0; i < rules.length; i++) {
let rule = rules[i]
let vt = this._getValidateType(rule)
Object.assign(rule, {
label: fieldValue.label || `["${fieldKey}"]`
})
if (RuleValidatorHelper[vt]) {
result = RuleValidatorHelper[vt](rule, value, message)
if (result != null) {
break
}
}
if (rule.validateExpr) {
let now = Date.now()
let resultExpr = rule.validateExpr(value, allData, now)
if (resultExpr === false) {
result = this._getMessage(rule, rule.errorMessage || this._message['default'])
break
}
}
if (rule.validateFunction) {
result = await this.validateFunction(rule, value, data, allData, vt)
if (result !== null) {
break
}
}
}
if (result !== null) {
result = message.TAG + result
}
return result
}
async validateFunction(rule, value, data, allData, vt) {
let result = null
try {
let callbackMessage = null
const res = await rule.validateFunction(rule, value, allData || data, (message) => {
callbackMessage = message
})
if (callbackMessage || (typeof res === 'string' && res) || res === false) {
result = this._getMessage(rule, callbackMessage || res, vt)
}
} catch (e) {
result = this._getMessage(rule, e.message, vt)
}
return result
}
_getMessage(rule, message, vt) {
return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default'])
}
_getValidateType(rule) {
var result = ''
if (rule.required) {
result = 'required'
} else if (rule.format) {
result = 'format'
} else if (rule.arrayType) {
result = 'arrayTypeFormat'
} else if (rule.range) {
result = 'range'
} else if (rule.maximum !== undefined || rule.minimum !== undefined) {
result = 'rangeNumber'
} else if (rule.maxLength !== undefined || rule.minLength !== undefined) {
result = 'rangeLength'
} else if (rule.pattern) {
result = 'pattern'
} else if (rule.validateFunction) {
result = 'validateFunction'
}
return result
}
}
const RuleValidatorHelper = {
required(rule, value, message) {
if (rule.required && isEmptyValue(value, rule.format || typeof value)) {
return formatMessage(rule, rule.errorMessage || message.required);
}
return null
},
range(rule, value, message) {
const {
range,
errorMessage
} = rule;
let list = new Array(range.length);
for (let i = 0; i < range.length; i++) {
const item = range[i];
if (types.object(item) && item.value !== undefined) {
list[i] = item.value;
} else {
list[i] = item;
}
}
let result = false
if (Array.isArray(value)) {
result = (new Set(value.concat(list)).size === list.length);
} else {
if (list.indexOf(value) > -1) {
result = true;
}
}
if (!result) {
return formatMessage(rule, errorMessage || message['enum']);
}
return null
},
rangeNumber(rule, value, message) {
if (!types.number(value)) {
return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
}
let {
minimum,
maximum,
exclusiveMinimum,
exclusiveMaximum
} = rule;
let min = exclusiveMinimum ? value <= minimum : value < minimum;
let max = exclusiveMaximum ? value >= maximum : value > maximum;
if (minimum !== undefined && min) {
return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ?
'exclusiveMinimum' : 'minimum'
])
} else if (maximum !== undefined && max) {
return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ?
'exclusiveMaximum' : 'maximum'
])
} else if (minimum !== undefined && maximum !== undefined && (min || max)) {
return formatMessage(rule, rule.errorMessage || message['number'].range)
}
return null
},
rangeLength(rule, value, message) {
if (!types.string(value) && !types.array(value)) {
return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
}
let min = rule.minLength;
let max = rule.maxLength;
let val = value.length;
if (min !== undefined && val < min) {
return formatMessage(rule, rule.errorMessage || message['length'].minLength)
} else if (max !== undefined && val > max) {
return formatMessage(rule, rule.errorMessage || message['length'].maxLength)
} else if (min !== undefined && max !== undefined && (val < min || val > max)) {
return formatMessage(rule, rule.errorMessage || message['length'].range)
}
return null
},
pattern(rule, value, message) {
if (!types['pattern'](rule.pattern, value)) {
return formatMessage(rule, rule.errorMessage || message.pattern.mismatch);
}
return null
},
format(rule, value, message) {
var customTypes = Object.keys(types);
var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType);
if (customTypes.indexOf(format) > -1) {
if (!types[format](value)) {
return formatMessage(rule, rule.errorMessage || message.typeError);
}
}
return null
},
arrayTypeFormat(rule, value, message) {
if (!Array.isArray(value)) {
return formatMessage(rule, rule.errorMessage || message.typeError);
}
for (let i = 0; i < value.length; i++) {
const element = value[i];
let formatResult = this.format(rule, element, message)
if (formatResult !== null) {
return formatResult
}
}
return null
}
}
class SchemaValidator extends RuleValidator {
constructor(schema, options) {
super(SchemaValidator.message);
this._schema = schema
this._options = options || null
}
updateSchema(schema) {
this._schema = schema
}
async validate(data, allData) {
let result = this._checkFieldInSchema(data)
if (!result) {
result = await this.invokeValidate(data, false, allData)
}
return result.length ? result[0] : null
}
async validateAll(data, allData) {
let result = this._checkFieldInSchema(data)
if (!result) {
result = await this.invokeValidate(data, true, allData)
}
return result
}
async validateUpdate(data, allData) {
let result = this._checkFieldInSchema(data)
if (!result) {
result = await this.invokeValidateUpdate(data, false, allData)
}
return result.length ? result[0] : null
}
async invokeValidate(data, all, allData) {
let result = []
let schema = this._schema
for (let key in schema) {
let value = schema[key]
let errorMessage = await this.validateRule(key, value, data[key], data, allData)
if (errorMessage != null) {
result.push({
key,
errorMessage
})
if (!all) break
}
}
return result
}
async invokeValidateUpdate(data, all, allData) {
let result = []
for (let key in data) {
let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData)
if (errorMessage != null) {
result.push({
key,
errorMessage
})
if (!all) break
}
}
return result
}
_checkFieldInSchema(data) {
var keys = Object.keys(data)
var keys2 = Object.keys(this._schema)
if (new Set(keys.concat(keys2)).size === keys2.length) {
return ''
}
var noExistFields = keys.filter((key) => {
return keys2.indexOf(key) < 0;
})
var errorMessage = formatMessage({
field: JSON.stringify(noExistFields)
}, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid'])
return [{
key: 'invalid',
errorMessage
}]
}
}
function Message() {
return {
TAG: "",
default: '验证错误',
defaultInvalid: '提交的字段{field}在数据库中并不存在',
validateFunction: '验证无效',
required: '{label}必填',
'enum': '{label}超出范围',
timestamp: '{label}格式无效',
whitespace: '{label}不能为空',
typeError: '{label}类型无效',
date: {
format: '{label}日期{value}格式无效',
parse: '{label}日期无法解析,{value}无效',
invalid: '{label}日期{value}无效'
},
length: {
minLength: '{label}长度不能少于{minLength}',
maxLength: '{label}长度不能超过{maxLength}',
range: '{label}必须介于{minLength}和{maxLength}之间'
},
number: {
minimum: '{label}不能小于{minimum}',
maximum: '{label}不能大于{maximum}',
exclusiveMinimum: '{label}不能小于等于{minimum}',
exclusiveMaximum: '{label}不能大于等于{maximum}',
range: '{label}必须介于{minimum}and{maximum}之间'
},
pattern: {
mismatch: '{label}格式不匹配'
}
};
}
SchemaValidator.message = new Message();
export default SchemaValidator
{
"id": "uni-forms",
"displayName": "uni-forms 表单",
"version": "1.4.13",
"description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据",
"keywords": [
"uni-ui",
"表单",
"校验",
"表单校验",
"表单验证"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
## Forms 表单
> **组件名:uni-forms**
> 代码块: `uForms`、`uni-forms-item`
> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。
uni-app的内置组件已经有了 `<form>`组件,用于提交表单内容。
然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 `<form>`组件封装了 `<uni-forms>`组件,内置了表单验证功能。
`<uni-forms>` 提供了 `rules`属性来描述校验规则、`<uni-forms-item>`子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。
每个要校验的表单项,不管input还是checkbox,都必须放在`<uni-forms-item>`组件中,且一个`<uni-forms-item>`组件只能放置一个表单项。
`<uni-forms-item>`组件内部预留了显示error message的区域,默认是在表单项的底部。
另外,`<uni-forms>`组件下面的各个表单项,可以通过`<uni-group>`包裹为不同的分组。同一`<uni-group>`下的不同表单项目将聚拢在一起,同其他group保持垂直间距。`<uni-group>`仅影响视觉效果。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-forms)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
\ No newline at end of file
<template>
<text class="uni-icons" :style="styleObj">
<slot>{{unicode}}</slot>
</text>
</template>
<script>
import { fontData, IconsDataItem } from './uniicons_file'
/**
* Icons 图标
* @description 用于展示 icon 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number,String} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: "uni-icons",
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {};
},
computed: {
unicode() : string {
let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
if (codes !== null) {
return codes.unicode
}
return ''
},
iconSize() : string {
const size = this.size
if (typeof size == 'string') {
const reg = /^[0-9]*$/g
return reg.test(size as string) ? '' + size + 'px' : '' + size;
// return '' + this.size
}
return this.getFontSize(size as number)
},
styleObj() : UTSJSONObject {
if (this.fontFamily !== '') {
return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
}
return { color: this.color, fontSize: this.iconSize }
}
},
created() { },
methods: {
/**
* 字体大小
*/
getFontSize(size : number) : string {
return size + 'px';
},
},
}
</script>
<style scoped>
@font-face {
font-family: UniIconsFontFamily;
src: url('./uniicons.ttf');
}
.uni-icons {
font-family: UniIconsFontFamily;
font-size: 18px;
font-style: normal;
color: #333;
}
<template>
<text class="uni-icons" :style="styleObj">
<slot>{{unicode}}</slot>
</text>
</template>
<script>
import { fontData, IconsDataItem } from './uniicons_file'
/**
* Icons 图标
* @description 用于展示 icon 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number,String} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: "uni-icons",
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {};
},
computed: {
unicode() : string {
let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
if (codes !== null) {
return codes.unicode
}
return ''
},
iconSize() : string {
const size = this.size
if (typeof size == 'string') {
const reg = /^[0-9]*$/g
return reg.test(size as string) ? '' + size + 'px' : '' + size;
// return '' + this.size
}
return this.getFontSize(size as number)
},
styleObj() : UTSJSONObject {
if (this.fontFamily !== '') {
return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
}
return { color: this.color, fontSize: this.iconSize }
}
},
created() { },
methods: {
/**
* 字体大小
*/
getFontSize(size : number) : string {
return size + 'px';
},
},
}
</script>
<style scoped>
@font-face {
font-family: UniIconsFontFamily;
src: url('./uniicons.ttf');
}
.uni-icons {
font-family: UniIconsFontFamily;
font-size: 18px;
font-style: normal;
color: #333;
}
</style>
<template>
<!-- #ifdef APP-NVUE -->
<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
<slot></slot>
</text>
<!-- #endif -->
</template>
<script>
import { fontData } from './uniicons_file_vue.js';
const getVal = (val) => {
const reg = /^[0-9]*$/g
return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
}
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
import iconUrl from './uniicons.ttf'
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('" + iconUrl + "')"
});
// #endif
/**
* Icons 图标
* @description 用于展示 icons 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: 'UniIcons',
emits: ['click'],
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
customPrefix: {
type: String,
default: ''
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {
icons: fontData
}
},
computed: {
unicode() {
let code = this.icons.find(v => v.font_class === this.type)
if (code) {
return code.unicode
}
return ''
},
iconSize() {
return getVal(this.size)
},
styleObj() {
if (this.fontFamily !== '') {
return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
}
return `color: ${this.color}; font-size: ${this.iconSize};`
}
},
methods: {
_onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss">
/* #ifndef APP-NVUE */
@import './uniicons.css';
@font-face {
font-family: uniicons;
src: url('./uniicons.ttf');
}
/* #endif */
.uni-icons {
font-family: uniicons;
text-decoration: none;
text-align: center;
}
<template>
<!-- #ifdef APP-NVUE -->
<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
<slot></slot>
</text>
<!-- #endif -->
</template>
<script>
import { fontData } from './uniicons_file_vue.js';
const getVal = (val) => {
const reg = /^[0-9]*$/g
return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
}
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
import iconUrl from './uniicons.ttf'
domModule.addRule('fontFace', {
'fontFamily': "uniicons",
'src': "url('" + iconUrl + "')"
});
// #endif
/**
* Icons 图标
* @description 用于展示 icons 图标
* @tutorial https://ext.dcloud.net.cn/plugin?id=28
* @property {Number} size 图标大小
* @property {String} type 图标图案,参考示例
* @property {String} color 图标颜色
* @property {String} customPrefix 自定义图标
* @event {Function} click 点击 Icon 触发事件
*/
export default {
name: 'UniIcons',
emits: ['click'],
props: {
type: {
type: String,
default: ''
},
color: {
type: String,
default: '#333333'
},
size: {
type: [Number, String],
default: 16
},
customPrefix: {
type: String,
default: ''
},
fontFamily: {
type: String,
default: ''
}
},
data() {
return {
icons: fontData
}
},
computed: {
unicode() {
let code = this.icons.find(v => v.font_class === this.type)
if (code) {
return code.unicode
}
return ''
},
iconSize() {
return getVal(this.size)
},
styleObj() {
if (this.fontFamily !== '') {
return `color: ${this.color}; font-size: ${this.iconSize}; font-family: ${this.fontFamily};`
}
return `color: ${this.color}; font-size: ${this.iconSize};`
}
},
methods: {
_onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss">
/* #ifndef APP-NVUE */
@import './uniicons.css';
@font-face {
font-family: uniicons;
src: url('./uniicons.ttf');
}
/* #endif */
.uni-icons {
font-family: uniicons;
text-decoration: none;
text-align: center;
}
</style>
export type IconsData = {
id : string
name : string
font_family : string
css_prefix_text : string
description : string
glyphs : Array<IconsDataItem>
}
export type IconsDataItem = {
font_class : string
unicode : string
}
export type IconsData = {
id : string
name : string
font_family : string
css_prefix_text : string
description : string
glyphs : Array<IconsDataItem>
}
export type IconsDataItem = {
font_class : string
unicode : string
}
export const fontData = [
{
"font_class": "arrow-down",
......@@ -659,6 +659,6 @@ export const fontData = [
"font_class": "weixin",
"unicode": "\ue691"
}
] as IconsDataItem[]
] as IconsDataItem[]
// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)
export const fontData = [
{
"font_class": "arrow-down",
......@@ -644,6 +644,6 @@ export const fontData = [
"font_class": "weixin",
"unicode": "\ue691"
}
]
]
// export const fontData = JSON.parse<IconsDataItem>(fontDataJson)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment