移动端V1.0
This commit is contained in:
137
components/DateTimePicker.vue
Normal file
137
components/DateTimePicker.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<view>
|
||||
<picker mode="multiSelector" :disabled="disabled" :value="multiIndex" :range="multiRange"
|
||||
@change="onMultiChange" @tap="onPickerTap">
|
||||
<view class="picker">
|
||||
<input type="text" :value="formattedDateTime" readonly />
|
||||
<text v-if="!disabled" class="picker-label">选择时间</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
defaultTime: {
|
||||
type: String,
|
||||
default: () => {
|
||||
const now = new Date();
|
||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const now = new Date();
|
||||
return {
|
||||
multiIndex: [],
|
||||
multiRange: this.getMultiRange()
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formattedDateTime() {
|
||||
if (this.multiIndex.length === 0) return ''; // 未选择时不显示
|
||||
const year = this.multiRange[0][this.multiIndex[0]];
|
||||
const month = String(this.multiRange[1][this.multiIndex[1]]).padStart(2, '0');
|
||||
const day = String(this.multiRange[2][this.multiIndex[2]]).padStart(2, '0');
|
||||
const hour = String(this.multiRange[3][this.multiIndex[3]]).padStart(2, '0');
|
||||
const minute = String(this.multiRange[4][this.multiIndex[4]]).padStart(2, '0');
|
||||
const second = String(this.multiRange[5][this.multiIndex[5]]).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMultiRange() {
|
||||
let yearRange = [];
|
||||
let monthRange = [];
|
||||
let dayRange = [];
|
||||
let hourRange = [];
|
||||
let minuteRange = [];
|
||||
let secondRange = [];
|
||||
|
||||
for (let i = 1900; i <= 2100; i++) yearRange.push(i);
|
||||
for (let i = 1; i <= 12; i++) monthRange.push(i);
|
||||
for (let i = 1; i <= 31; i++) dayRange.push(i);
|
||||
for (let i = 0; i <= 23; i++) hourRange.push(i);
|
||||
for (let i = 0; i <= 59; i++) minuteRange.push(i);
|
||||
for (let i = 0; i <= 59; i++) secondRange.push(i);
|
||||
|
||||
return [yearRange, monthRange, dayRange, hourRange, minuteRange, secondRange];
|
||||
},
|
||||
onMultiChange(e) {
|
||||
this.multiIndex = e.detail.value;
|
||||
this.$emit('input', this.formattedDateTime);
|
||||
},
|
||||
onPickerTap() {
|
||||
// 如果还未选择,则用当前时间作为默认
|
||||
if (this.multiIndex.length === 0) {
|
||||
const now = new Date();
|
||||
this.multiIndex = [
|
||||
now.getFullYear() - 1900,
|
||||
now.getMonth(),
|
||||
now.getDate() - 1,
|
||||
now.getHours(),
|
||||
now.getMinutes(),
|
||||
now.getSeconds()
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true, // 组件挂载时立即执行
|
||||
handler(newValue) {
|
||||
if (!newValue || typeof newValue !== 'string') return;
|
||||
|
||||
const dateParts = newValue.split(/[- :]/);
|
||||
if (dateParts.length === 6) {
|
||||
this.multiIndex = [
|
||||
parseInt(dateParts[0]) - 1900,
|
||||
parseInt(dateParts[1]) - 1,
|
||||
parseInt(dateParts[2]) - 1,
|
||||
parseInt(dateParts[3]),
|
||||
parseInt(dateParts[4]),
|
||||
parseInt(dateParts[5])
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
/* 使其看起来像可点击的按钮 */
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.picker-label {
|
||||
margin-left: 10px;
|
||||
color: #007BFF;
|
||||
/* 可选:设置文字颜色 */
|
||||
}
|
||||
</style>
|
11535
components/complex-picker-region/area.js
Normal file
11535
components/complex-picker-region/area.js
Normal file
File diff suppressed because it is too large
Load Diff
211
components/complex-picker-region/complex-picker-region.vue
Normal file
211
components/complex-picker-region/complex-picker-region.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<view style="background-color: white;padding: 5rpx;">
|
||||
<view class="center">
|
||||
<view style="margin-right: 20rpx;">
|
||||
{{label}}:
|
||||
</view>
|
||||
<uni-easyinput style="padding: 5rpx;" @focus="open" trim="all" v-model="selctValue"
|
||||
placeholder="请选择"></uni-easyinput>
|
||||
</view>
|
||||
<uni-popup ref="popup" type="bottom">
|
||||
<view style="height: 60vh;background-color: white;padding: 20rpx;padding-top: 40rpx;">
|
||||
<uni-row>
|
||||
<uni-col :span="4" :offset="0">
|
||||
<view class="demo-uni-col center">
|
||||
<span @click.stop="cancle" style="color: rgba(69, 90, 100, 0.6);">
|
||||
取 消
|
||||
</span>
|
||||
</view>
|
||||
</uni-col>
|
||||
<uni-col :span="14" :offset="1">
|
||||
<view class="center" v-if="filterable">
|
||||
<uni-easyinput v-model="search" focus placeholder="输入关键字过滤"
|
||||
@input="inputChange"></uni-easyinput>
|
||||
</view>
|
||||
|
||||
</uni-col>
|
||||
<uni-col :span="4" :offset="filterable ?1:15">
|
||||
<view class="demo-uni-col center" @click="confirm" style="color: #576b95;">确 认</view>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
<uni-row>
|
||||
<uni-col :span="8">
|
||||
<view class="">
|
||||
<picker-view @change="bindChange" class="picker-view">
|
||||
<!-- 省 -->
|
||||
<picker-view-column>
|
||||
<view class="center" v-for="(item,index) in provinces" :key="Math.random()">
|
||||
{{item.value}}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
<!-- 市 -->
|
||||
<picker-view-column>
|
||||
<view class="center" v-for="(item,index) in cities" :key="Math.random()">
|
||||
{{item.value}}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
区县
|
||||
<picker-view-column>
|
||||
<view class="center" v-for="(item,index) in counties" :key="Math.random()">
|
||||
{{item.value}}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
</view>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import area from './area.js'
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
default: '标签'
|
||||
},
|
||||
filterable: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
treeArr: {
|
||||
default: function() {
|
||||
return area
|
||||
},
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//---
|
||||
selctValue: '',
|
||||
//
|
||||
popupVisible: true,
|
||||
title: 'picker-view',
|
||||
filterpickeArr: [],
|
||||
visible: true,
|
||||
//搜索框---
|
||||
search: '',
|
||||
//滚动框的值
|
||||
pickeValue: '',
|
||||
// 省市县数据
|
||||
provinces: [],
|
||||
cities: [],
|
||||
counties: [],
|
||||
selectProvince: '',
|
||||
selectCity: '',
|
||||
selectCounty: '',
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.filterpickeArr = [...this.treeArr]
|
||||
this.resetArr()
|
||||
},
|
||||
methods: {
|
||||
//恢复 下拉数据/搜索数据/选中数据
|
||||
resetArr() {
|
||||
this.provinces = [...this.treeArr]
|
||||
this.cities = [...this.provinces[0].children]
|
||||
this.counties = [...this.cities[0].children]
|
||||
this.selectProvince = this.provinces[0]
|
||||
this.selectCity = this.cities[0]
|
||||
this.selectCounty = this.counties[0]
|
||||
//搜索项
|
||||
this.search = ''
|
||||
},
|
||||
//表单项
|
||||
open() {
|
||||
// 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
|
||||
this.$refs.popup.open('bottom')
|
||||
this.resetArr()
|
||||
},
|
||||
//弹出框---
|
||||
// 输入框
|
||||
inputChange(val) {
|
||||
this.filter(val)
|
||||
},
|
||||
//过滤
|
||||
filter(val) {
|
||||
const filteArr = this.treeArr.filter((item, index) => {
|
||||
return item.indexOf(val) !== -1;
|
||||
})
|
||||
this.filterpickeArr = [...filteArr]
|
||||
this.pickeValue = this.filterpickeArr[0]
|
||||
},
|
||||
//取消
|
||||
cancle() {
|
||||
this.$refs.popup.close()
|
||||
},
|
||||
//确认
|
||||
confirm() {
|
||||
this.selctValue = this.pickeValue
|
||||
this.$refs.popup.close()
|
||||
const selectData = {
|
||||
selectProvince: this.selectProvince,
|
||||
selectCity: this.selectCity,
|
||||
selectCounty: this.selectCounty
|
||||
}
|
||||
let showValue = this.selectProvince.value + '/' + this.selectCity.value + '/' + this.selectCounty.value
|
||||
//回显
|
||||
this.selctValue = showValue
|
||||
this.$emit('confirm', selectData)
|
||||
},
|
||||
// 滑动
|
||||
bindChange: function(e) {
|
||||
const val = e.detail.value
|
||||
//联动-实现
|
||||
this.linkArr(val)
|
||||
},
|
||||
|
||||
//联动实现
|
||||
linkArr(val = []) {
|
||||
let provinceIndex = val[0]
|
||||
let cityIndex = val[1]
|
||||
let countyIndex = val[2]
|
||||
if (provinceIndex || provinceIndex === 0) {
|
||||
//说明第一列 改变了------
|
||||
this.cities = (this.provinces[provinceIndex] || {}).children
|
||||
this.counties = (this.cities[0] || {}).children
|
||||
//默认选中的数据
|
||||
this.selectProvince = this.provinces[provinceIndex]
|
||||
this.selectCity = this.cities[0]
|
||||
this.selectCounty = this.counties[0]
|
||||
}
|
||||
if (cityIndex || cityIndex === 0) {
|
||||
//说明第二列 改变了------
|
||||
this.counties = (this.cities[cityIndex] || {}).children
|
||||
//默认选中的数据
|
||||
this.selectCity = this.cities[cityIndex]
|
||||
this.selectCounty = this.counties[0]
|
||||
}
|
||||
if (countyIndex || countyIndex === 0) {
|
||||
//说明第三列 改变了-----
|
||||
//默认选中的数据
|
||||
this.selectCounty = this.counties[countyIndex]
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo-uni-col {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.picker-view {
|
||||
height: 50vh;
|
||||
width: 100vw;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
67
components/complex-picker-region/readme.md
Normal file
67
components/complex-picker-region/readme.md
Normal file
@@ -0,0 +1,67 @@
|
||||
## 下拉选择器
|
||||
|
||||
> **组件名:complex-picker**
|
||||
|
||||
下拉选择器 ,可搜索( 快速过滤下拉选项 ) 可清空
|
||||
|
||||
## 属性
|
||||
|
||||
##### label --- 字符串类型 , 选择器的标签
|
||||
|
||||
##### treeArr --- 数组类型 , 省市区树形数据, 默认已经集成了全国数据, 可以直接使用
|
||||
|
||||
```javascript
|
||||
#例如
|
||||
[
|
||||
{
|
||||
"code": "110000",
|
||||
"value": "北京市",
|
||||
"children":[]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## 事件
|
||||
|
||||
##### @confirm --- 弹出框中点击 确定 触发, 参数为选中的省市区对象
|
||||
|
||||
## 示例代码
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="container">
|
||||
<complex-picker-region label="年份" @confirm='confirm' />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import 配置 测试用 实际引入遵循easycom, 直接导入 即可使用组件
|
||||
// import complexpickerregion from '@/uni_modules/complex-plugin/complex-picker-region/components/complex-picker-region/complex-picker-region.vue'
|
||||
export default {
|
||||
// components: {
|
||||
// complexpickerregion
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm(selectData) {
|
||||
console.log('selectData', selectData);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
|
102
components/comprehensive-dict-type/dict-type.vue
Normal file
102
components/comprehensive-dict-type/dict-type.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<view class="types">
|
||||
<block v-for="(item,index) in types" :key="index">
|
||||
<block v-if="values.includes(item.dictValue)">
|
||||
<view :class="getStatusClass(item.dictValue)">
|
||||
<text class="status-text">{{item.dictLabel}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "dict-type",
|
||||
props: ['types', 'type'],
|
||||
computed: {
|
||||
values() {
|
||||
if (this.type !== null && typeof this.type !== 'undefined') {
|
||||
return Array.isArray(this.type) ? this.type : [String(this.type)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getStatusClass(dictValue) {
|
||||
switch (dictValue) {
|
||||
case '1':
|
||||
return 'status primary';
|
||||
case '0':
|
||||
case '3':
|
||||
case '6':
|
||||
case '2':
|
||||
return 'status success';
|
||||
case '10':
|
||||
case '13':
|
||||
return 'status warn';
|
||||
case '14':
|
||||
case '12':
|
||||
return 'status gray';
|
||||
default:
|
||||
return 'status refuse';
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.status {
|
||||
position: relative;
|
||||
padding:5px 10px;
|
||||
border-radius: 0 8px 0 8px;
|
||||
&.primary {
|
||||
position: absolute;
|
||||
top:0px;
|
||||
right:0px;
|
||||
background-color: #E8F4FF;
|
||||
color: #1890FF;
|
||||
border: 1px solid #1890FF;
|
||||
}
|
||||
|
||||
&.success {
|
||||
position: absolute;
|
||||
top:0px;
|
||||
right:0px;
|
||||
background-color: #E7FAF0;
|
||||
color: #71E2A3;
|
||||
border: 1px solid #71E2A3;
|
||||
}
|
||||
&.gray {
|
||||
position: absolute;
|
||||
top:0px;
|
||||
right:0px;
|
||||
background-color: #f4f4f5;
|
||||
color: #909399;
|
||||
border: 1px solid #e9e9eb;
|
||||
}
|
||||
&.warn {
|
||||
position: absolute;
|
||||
top:0px;
|
||||
right:0px;
|
||||
background-color: #FFF8E6;
|
||||
color: #FFBA00;
|
||||
border: 1px solid #FFBA00;
|
||||
}
|
||||
&.refuse {
|
||||
position: absolute;
|
||||
top:0px;
|
||||
right:0px;
|
||||
background-color: #FFEDED;
|
||||
color: #FF9292;
|
||||
border: 1px solid #FFDBDB;
|
||||
}
|
||||
}
|
||||
</style>
|
34
components/dict-status/dict-status.vue
Normal file
34
components/dict-status/dict-status.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<view>
|
||||
<block v-for="(item,index) in status" :key="index">
|
||||
<text v-if="values.includes(item.dictValue)">{{item.dictLabel}}</text>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "dict-status",
|
||||
props: ['status', 'value'],
|
||||
computed: {
|
||||
values() {
|
||||
if (this.value !== null && typeof this.value !== 'undefined') {
|
||||
return Array.isArray(this.value) ? this.value : [String(this.value)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
text {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
91
components/dict-type/dict-type.vue
Normal file
91
components/dict-type/dict-type.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<view class="types">
|
||||
<block v-for="(item,index) in types" :key="index">
|
||||
<block v-if="values.includes(item.dictValue)">
|
||||
<view :class="getStatusClass(item.dictValue)">
|
||||
<text class="status-text">{{item.dictLabel}}</text>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "dict-type",
|
||||
props: ['types', 'type'],
|
||||
computed: {
|
||||
values() {
|
||||
if (this.type !== null && typeof this.type !== 'undefined') {
|
||||
return Array.isArray(this.type) ? this.type : [String(this.type)];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getStatusClass(dictValue) {
|
||||
switch (dictValue) {
|
||||
case '0':
|
||||
return 'status primary';
|
||||
case '1':
|
||||
return 'status success';
|
||||
case '6':
|
||||
return 'status success';
|
||||
case '11':
|
||||
return 'status refuse';
|
||||
case '14':
|
||||
return 'status gray';
|
||||
default:
|
||||
return 'status warn';
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.status {
|
||||
position: relative;
|
||||
padding: 5px 10px;
|
||||
border-radius: 0 8px 0 8px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
|
||||
&.primary {
|
||||
background-color: #E8F4FF;
|
||||
color: #1890FF;
|
||||
border: 1px solid #1890FF;
|
||||
}
|
||||
|
||||
&.success {
|
||||
background-color: #E7FAF0;
|
||||
color: #71E2A3;
|
||||
border: 1px solid #71E2A3;
|
||||
}
|
||||
|
||||
&.warn {
|
||||
background-color: #FFF8E6;
|
||||
color: #FFBA00;
|
||||
border: 1px solid #FFBA00;
|
||||
}
|
||||
|
||||
&.refuse {
|
||||
background-color: #FFEDED;
|
||||
color: #FF9292;
|
||||
border: 1px solid #FFDBDB;
|
||||
}
|
||||
|
||||
&.gray {
|
||||
background-color: #f4f4f5;
|
||||
border: 1px solid #e9e9eb;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
</style>
|
76
components/flow-step/flow-step.vue
Normal file
76
components/flow-step/flow-step.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<view class="steps">
|
||||
<view class="step" v-for="step in stepList" :key="step.taskId">
|
||||
<image v-if="step.finishTime" class="icon" src="../../static/success.png" mode="widthFix"></image>
|
||||
<image style="width:34rpx;" v-else class="icon" src="../../static/wating.png" mode="widthFix">
|
||||
</image>
|
||||
<image class="line" src="../../static/step-line.png" mode="widthFix"></image>
|
||||
<view class="right">
|
||||
<text>{{step.taskName}}:</text>
|
||||
<text>{{step.assigneeName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="loading-more-top" v-if="stepList.length==0">
|
||||
<uni-load-more status="loading" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
flowRecord
|
||||
} from "@/api/flowRecord/flowRecord.js";
|
||||
export default {
|
||||
name: "flow-step",
|
||||
props:["procInsId","deployId"],
|
||||
data() {
|
||||
return {
|
||||
stepList: [], //流程进度列表
|
||||
};
|
||||
},
|
||||
created() {
|
||||
console.log(this.procInsId);
|
||||
console.log(this.deployId);
|
||||
this.getFlowRecord();
|
||||
},
|
||||
methods:{
|
||||
async getFlowRecord() {
|
||||
let res = await flowRecord({
|
||||
procInsId: this.procInsId,
|
||||
deployId: this.deployId
|
||||
});
|
||||
this.stepList = res.data.flowList.reverse();
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.steps {
|
||||
margin-top: 20px;
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 50rpx;
|
||||
|
||||
.icon {
|
||||
width: 35rpx;
|
||||
}
|
||||
|
||||
.line {
|
||||
flex: 0.7;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1.4;
|
||||
|
||||
text:first-child {
|
||||
color: #9E9E9E;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
55
components/navs/navs.vue
Normal file
55
components/navs/navs.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<view class="navs">
|
||||
<text @tap="navChange(item.val)" :class="current==item.val?'active':''" v-for="(item,index) in navs" :key="index">{{item.text}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props:['navs','loading'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
current:"",
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
navChange(val){
|
||||
if(this.loading==undefined){
|
||||
this.current=val;
|
||||
this.$emit("change",val);
|
||||
}else{
|
||||
if(!this.loading){
|
||||
this.current=val;
|
||||
this.$emit("change",val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.navs{
|
||||
background-color: white;
|
||||
padding:26rpx 40rpx;
|
||||
position: fixed;
|
||||
top: 44px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
box-shadow:0 0 5px #dadada;
|
||||
text{
|
||||
color: #202020;
|
||||
opacity: 0.5;
|
||||
margin-right: 40rpx;
|
||||
padding-bottom:3rpx;
|
||||
&.active{
|
||||
opacity: 1;
|
||||
border-bottom:3px solid #1890FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
176
components/u-waterfall/u-waterfall.vue
Normal file
176
components/u-waterfall/u-waterfall.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<view class="u-waterfall">
|
||||
<view id="u-left-column" class="u-column"><slot name="left" :leftList="leftList"></slot></view>
|
||||
<view id="u-right-column" class="u-column"><slot name="right" :rightList="rightList"></slot></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* waterfall 瀑布流
|
||||
* @description 这是一个瀑布流形式的组件,内容分为左右两列,结合uView的懒加载组件效果更佳。相较于某些只是奇偶数左右分别,或者没有利用vue作用域插槽的做法,uView的瀑布流实现了真正的 组件化,搭配LazyLoad 懒加载和loadMore 加载更多组件,让您开箱即用,眼前一亮。
|
||||
* @tutorial https://www.uviewui.com/components/waterfall.html
|
||||
* @property {Array} flow-list 用于渲染的数据
|
||||
* @property {String Number} add-time 单条数据添加到队列的时间间隔,单位ms,见上方注意事项说明(默认200)
|
||||
* @example <u-waterfall :flowList="flowList"></u-waterfall>
|
||||
*/
|
||||
export default {
|
||||
name: "u-waterfall",
|
||||
props: {
|
||||
value: {
|
||||
// 瀑布流数据
|
||||
type: Array,
|
||||
required: true,
|
||||
default: function() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
|
||||
// 单位ms
|
||||
addTime: {
|
||||
type: [Number, String],
|
||||
default: 200
|
||||
},
|
||||
// id值,用于清除某一条数据时,根据此idKey名称找到并移除,如数据为{idx: 22, name: 'lisa'}
|
||||
// 那么该把idKey设置为idx
|
||||
idKey: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
leftList: [],
|
||||
rightList: [],
|
||||
tempList: [],
|
||||
children: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
copyFlowList(nVal, oVal) {
|
||||
// 取差值,即这一次数组变化新增的部分
|
||||
let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
|
||||
// 拼接上原有数据
|
||||
this.tempList = this.tempList.concat(this.cloneData(nVal.slice(startIndex)));
|
||||
this.splitData();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.tempList = this.cloneData(this.copyFlowList);
|
||||
this.splitData();
|
||||
},
|
||||
computed: {
|
||||
// 破坏flowList变量的引用,否则watch的结果新旧值是一样的
|
||||
copyFlowList() {
|
||||
return this.cloneData(this.value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async splitData() {
|
||||
if (!this.tempList.length) return;
|
||||
let leftRect = await this.$uGetRect('#u-left-column');
|
||||
let rightRect = await this.$uGetRect('#u-right-column');
|
||||
// 如果左边小于或等于右边,就添加到左边,否则添加到右边
|
||||
let item = this.tempList[0];
|
||||
// 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
|
||||
// 数组可能变成[],导致此item值可能为undefined
|
||||
if(!item) return ;
|
||||
if (leftRect.height < rightRect.height) {
|
||||
this.leftList.push(item);
|
||||
} else if (leftRect.height > rightRect.height) {
|
||||
this.rightList.push(item);
|
||||
} else {
|
||||
// 这里是为了保证第一和第二张添加时,左右都能有内容
|
||||
// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
|
||||
if (this.leftList.length <= this.rightList.length) {
|
||||
this.leftList.push(item);
|
||||
} else {
|
||||
this.rightList.push(item);
|
||||
}
|
||||
}
|
||||
// 移除临时列表的第一项
|
||||
this.tempList.splice(0, 1);
|
||||
// 如果临时数组还有数据,继续循环
|
||||
if (this.tempList.length) {
|
||||
setTimeout(() => {
|
||||
this.splitData();
|
||||
}, this.addTime)
|
||||
}
|
||||
},
|
||||
// 复制而不是引用对象和数组
|
||||
cloneData(data) {
|
||||
return JSON.parse(JSON.stringify(data));
|
||||
},
|
||||
// 清空数据列表
|
||||
clear() {
|
||||
this.leftList = [];
|
||||
this.rightList = [];
|
||||
// 同时清除父组件列表中的数据
|
||||
this.$emit('input', []);
|
||||
this.tempList = [];
|
||||
},
|
||||
// 清除某一条指定的数据,根据id实现
|
||||
remove(id) {
|
||||
// 如果findIndex找不到合适的条件,就会返回-1
|
||||
let index = -1;
|
||||
index = this.leftList.findIndex(val => val[this.idKey] == id);
|
||||
if(index != -1) {
|
||||
// 如果index不等于-1,说明已经找到了要找的id,根据index索引删除这一条数据
|
||||
this.leftList.splice(index, 1);
|
||||
} else {
|
||||
// 同理于上方面的方法
|
||||
index = this.rightList.findIndex(val => val[this.idKey] == id);
|
||||
if(index != -1) this.rightList.splice(index, 1);
|
||||
}
|
||||
// 同时清除父组件的数据中的对应id的条目
|
||||
index = this.value.findIndex(val => val[this.idKey] == id);
|
||||
if(index != -1) this.$emit('input', this.value.splice(index, 1));
|
||||
},
|
||||
// 修改某条数据的某个属性
|
||||
modify(id, key, value) {
|
||||
// 如果findIndex找不到合适的条件,就会返回-1
|
||||
let index = -1;
|
||||
index = this.leftList.findIndex(val => val[this.idKey] == id);
|
||||
if(index != -1) {
|
||||
// 如果index不等于-1,说明已经找到了要找的id,修改对应key的值
|
||||
this.leftList[index][key] = value;
|
||||
} else {
|
||||
// 同理于上方面的方法
|
||||
index = this.rightList.findIndex(val => val[this.idKey] == id);
|
||||
if(index != -1) this.rightList[index][key] = value;
|
||||
}
|
||||
// 修改父组件的数据中的对应id的条目
|
||||
index = this.value.findIndex(val => val[this.idKey] == id);
|
||||
if(index != -1) {
|
||||
// 首先复制一份value的数据
|
||||
let data = this.cloneData(this.value);
|
||||
// 修改对应索引的key属性的值为value
|
||||
data[index][key] = value;
|
||||
// 修改父组件通过v-model绑定的变量的值
|
||||
this.$emit('input', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../libs/css/style.components.scss";
|
||||
|
||||
.u-waterfall {
|
||||
@include vue-flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.u-column {
|
||||
@include vue-flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.u-image {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
0
components/uview-v1/uview-v1.vue
Normal file
0
components/uview-v1/uview-v1.vue
Normal file
89
components/zwy-popup/zwy-popup.vue
Normal file
89
components/zwy-popup/zwy-popup.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<view v-show="ishide" @touchmove.stop.prevent>
|
||||
<!-- 遮罩 -->
|
||||
<view class="mask" :style="maskStyle"></view>
|
||||
<!-- 内容 -->
|
||||
<view class="container" :style="containerStyle">
|
||||
<view class="tip" :style="tipStyle">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
// 控制弹窗显隐
|
||||
ishide: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
// 设置弹窗层级
|
||||
zindex: {
|
||||
type: Number,
|
||||
default: 99
|
||||
},
|
||||
// 设置遮罩透明度
|
||||
opacity: {
|
||||
type: Number,
|
||||
default: 0.6
|
||||
},
|
||||
// 设置内容区宽度
|
||||
width: {
|
||||
type: String,
|
||||
default: '650rpx'
|
||||
},
|
||||
// 设置内容区高度
|
||||
height: {
|
||||
type: String,
|
||||
default: '950rpx'
|
||||
},
|
||||
// 设置内容区圆角
|
||||
radius: {
|
||||
type: String
|
||||
},
|
||||
// 设置内容区底色
|
||||
bgcolor: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 遮罩样式
|
||||
maskStyle() {
|
||||
return `z-index:${this.zindex}; background:rgba(0,0,0,${this.opacity});`
|
||||
},
|
||||
// 内容样式
|
||||
tipStyle() {
|
||||
return `
|
||||
width:${this.width};
|
||||
height:${this.height};
|
||||
border-radius:${this.radius};
|
||||
background-color:${this.bgcolor};
|
||||
`
|
||||
},
|
||||
// 内容区域样式
|
||||
containerStyle() {
|
||||
return `position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index:${this.zindex + 1};`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mask {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.container {
|
||||
position: fixed;
|
||||
}
|
||||
.tip {
|
||||
margin-bottom: 30px;
|
||||
/* 留出关闭按钮的空间 */
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user