Commit 0ce0284b by pangchong

feat: 全局组件封装

parent dc5e3ef8
......@@ -3,6 +3,4 @@
.local
/node_modules/**
**/*.svg
**/*.sh
/public/*
/src/assets/*
\ No newline at end of file
**/*.sh
\ No newline at end of file
@mixin global-button-primary {
color: #fff;
background-color: $uni-color-primary;
}
@mixin global-button-primary-active {
color: darken(#fff, 10%);
background-color: darken($uni-color-primary, 5%);
border-color: darken($uni-color-primary, 5%);
}
@mixin global-button-success {
color: #fff;
background-color: $uni-color-success;
}
@mixin global-button-success-active {
color: darken(#fff, 10%);
background-color: darken($uni-color-success, 5%);
border-color: darken($uni-color-success, 5%);
}
@mixin global-button-warning {
color: #fff;
background-color: $uni-color-warning;
}
@mixin global-button-warning-active {
color: darken(#fff, 10%);
background-color: darken($uni-color-warning, 5%);
border-color: darken($uni-color-warning, 5%);
}
@mixin global-button-danger {
color: #fff;
background-color: $uni-color-error;
}
@mixin global-button-danger-active {
color: darken(#fff, 10%);
background-color: darken($uni-color-error, 5%);
border-color: darken($uni-color-error, 5%);
}
.global-button {
padding: 0 20rpx;
margin: 0;
font-size: 28rpx;
border-radius: 52rpx;
&.primary {
@include global-button-primary;
&:active {
@include global-button-primary-active;
}
}
&.success {
@include global-button-success;
&:active {
@include global-button-success-active;
}
}
&.warning {
@include global-button-warning;
&:active {
@include global-button-warning-active;
}
}
&.danger {
@include global-button-danger;
&:active {
@include global-button-danger-active;
}
}
&.text {
color: $uni-color-primary;
background-color: transparent;
&::after {
border: none;
}
}
}
......@@ -15,10 +15,12 @@ const ps = defineProps({
type: Object,
default: () => {}
},
//default,primary,success,warning,danger,text
type: {
type: String,
default: 'default'
},
//default,small,medium,large
size: {
type: String,
default: 'default'
......@@ -58,35 +60,9 @@ const getSize = computed(() => {
})
</script>
<style lang="scss" scoped>
@import './global-button.scss';
.global-button {
padding: 0 20rpx;
margin: 0;
height: v-bind(getSize);
line-height: v-bind(getSize);
font-size: 28rpx;
border-radius: 52rpx;
&.primary {
background-color: $uni-color-primary;
color: #fff;
}
&.success {
background-color: $uni-color-success;
color: #fff;
}
&.warning {
background-color: $uni-color-warning;
color: #fff;
}
&.danger {
background-color: $uni-color-error;
color: #fff;
}
&.text {
color: $uni-color-primary;
background-color: transparent;
&::after {
border: none;
}
}
}
</style>
<template>
<view class="content">
<z-paging
:refresher-enabled="refresherEnabled"
:refresher-threshold="refresherThreshold"
:auto-show-back-to-top="autoShowBackToTop"
:loading-more-enabled="loadingMoreEnabled"
:back-to-top-bottom="backToTopBottom"
show-refresher-update-time
v-if="firstLoaded || isCurrentPage"
ref="paging"
v-model="dataList"
@query="queryList"
:fixed="false"
>
<template #empty v-if="!dataList.length && refresherEnabled">
<global-empty></global-empty>
</template>
<slot :dataList="dataList"></slot>
</z-paging>
</view>
</template>
<script setup>
import { ref, watch } from 'vue'
const paging = ref(null)
const dataList = ref([])
// 当前组件是否已经加载过了
const firstLoaded = ref(false)
// 是否滚动到当前页
const isCurrentPage = ref(false)
const props = defineProps({
tabIndex: {
type: Number,
default: 0
},
currentIndex: {
type: Number,
default: 0
},
//是否开启下拉刷新
refresherEnabled: {
type: Boolean,
default: false
},
refresherThreshold: {
type: [String, Boolean],
default: '150rpx'
},
//是否启用加载更多数据
loadingMoreEnabled: {
type: Boolean,
default: false
},
autoShowBackToTop: {
type: Boolean,
default: true
},
backToTopBottom: {
type: [Number, String],
default: '160rpx'
}
})
watch(
() => props.currentIndex,
(newVal) => {
if (newVal === props.tabIndex) {
// 懒加载,当滑动到当前的item时,才去加载
if (!firstLoaded.value) {
// 这里需要延迟渲染z-paging的原因是为了避免在一些平台上立即渲染可能引发的底层报错问题
setTimeout(() => {
isCurrentPage.value = true
}, 100)
}
}
},
{
immediate: true
}
)
const queryList = (pageNo, pageSize) => {
if (!dataList.value.length) {
paging.value?.complete(dataList.value)
}
setTimeout(() => {
paging.value?.complete([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
firstLoaded.value = true
}, 500)
}
</script>
<style>
/* 注意:父节点需要固定高度,z-paging的height:100%才会生效 */
.content {
height: 100%;
}
</style>
<template>
<z-paging-swiper>
<!-- 头部 -->
<template #top>
<view :style="{ height: safeAreaInsets?.top + 'px' }" v-if="custom"></view>
<!-- 自定义导航栏 -->
<global-navbar :title="title" v-if="showNavbar">
<template #left>
<uni-icons type="left" size="16" @tap="goBack"></uni-icons>
</template>
</global-navbar>
<!-- tab切换 -->
<z-tabs ref="tabs" :list="tabList" :current="current" @change="tabsChange"></z-tabs>
<slot name="top"></slot>
</template>
<!-- 滚动内容 -->
<swiper class="swiper" :current="current" @transition="swiperTransition" @animationfinish="swiperAnimationfinish">
<swiper-item v-for="(_, index) in tabList" :key="index">
<global-page-swiper-item
:tabIndex="index"
:currentIndex="current"
:refresher-enabled="refresherEnabled"
:refresher-threshold="refresherThreshold"
:auto-show-back-to-top="autoShowBackToTop"
:loading-more-enabled="loadingMoreEnabled"
:back-to-top-bottom="backToTopBottom"
>
<template #="{ dataList }">
<slot :dataList="dataList"></slot>
</template>
</global-page-swiper-item>
</swiper-item>
</swiper>
</z-paging-swiper>
</template>
<script setup>
import { ref } from 'vue'
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
const ps = defineProps({
//tab数据
tabList: {
type: Array,
default: () => []
},
//tab默认index
current: {
type: Number,
default: 0
},
//是否展示自定义导航栏
showNavbar: {
type: Boolean,
default: true
},
//页面标题
title: {
type: String,
default: ''
},
//是否关闭原生导航栏
custom: {
type: Boolean,
default: true
},
//是否开启下拉刷新
refresherEnabled: {
type: Boolean,
default: false
},
refresherThreshold: {
type: [String, Boolean],
default: '150rpx'
},
//是否启用加载更多数据
loadingMoreEnabled: {
type: Boolean,
default: false
},
autoShowBackToTop: {
type: Boolean,
default: true
},
backToTopBottom: {
type: [Number, String],
default: '160rpx'
}
})
const tabs = ref()
const current = ref(ps.current)
//返回
const goBack = () => {
uni.navigateBack()
}
// tabs通知swiper切换
const tabsChange = (index) => {
current.value = index
}
// swiper滑动中
const swiperTransition = (e) => {
tabs.value.setDx(e.detail.dx)
}
// swiper滑动结束
const swiperAnimationfinish = (e) => {
current.value = e.detail.current
tabs.value.unlockDx()
}
</script>
<style>
.swiper {
height: 100%;
}
</style>
......@@ -2,20 +2,23 @@
<z-paging
ref="paging"
v-model="dataList"
:auto="auto"
:refresher-enabled="refresherEnabled"
:refresher-threshold="refresherThreshold"
:auto-show-back-to-top="autoShowBackToTop"
:loading-more-enabled="refresherEnabled"
:loading-more-enabled="loadingMoreEnabled"
:back-to-top-bottom="backToTopBottom"
show-refresher-update-time
:hide-empty-view="!refresherEnabled"
:hide-empty-view="!isDataList"
@query="query"
>
<template #empty v-if="!dataList.length && refresherEnabled">
<template #empty v-if="refresherEnabled || loadingMoreEnabled">
<global-empty></global-empty>
</template>
<!-- 头部 -->
<template #top>
<view :style="{ height: safeAreaInsets?.top + 'px' }" v-if="custom"></view>
<!-- 自定义导航栏 -->
<global-navbar :title="title" v-if="showNavbar">
<template #left>
<uni-icons type="left" size="16" @tap="goBack"></uni-icons>
......@@ -23,36 +26,47 @@
</global-navbar>
<slot name="top"></slot>
</template>
<!-- 滚动内容 -->
<slot :dataList="dataList" v-if="isDataList"></slot>
<slot></slot>
<!-- 底部 -->
<template #bottom>
<slot name="bottom"></slot>
</template>
</z-paging>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ref } from 'vue'
const dataList = ref([])
const es = defineEmits(['update:modelValue', 'query'])
const es = defineEmits(['query'])
const paging = ref()
const ps = defineProps({
auto: {
type: Boolean,
default: false
},
//是否展示自定义导航栏
showNavbar: {
type: Boolean,
default: true
},
//页面标题
title: {
type: String,
default: ''
},
modelValue: {
type: Array,
default: () => []
},
//是否关闭原生导航栏
custom: {
type: Boolean,
default: true
},
// 是否开启下拉刷新 Boolean true
//是否展示列表
isDataList: {
type: Boolean,
default: false
},
//是否开启下拉刷新
refresherEnabled: {
type: Boolean,
default: false
......@@ -61,6 +75,11 @@ const ps = defineProps({
type: [String, Boolean],
default: '150rpx'
},
//是否启用加载更多数据
loadingMoreEnabled: {
type: Boolean,
default: false
},
autoShowBackToTop: {
type: Boolean,
default: true
......@@ -73,41 +92,23 @@ const ps = defineProps({
const complete = (data) => {
paging.value?.complete(data || [])
}
watch(
() => ps.modelValue,
(val) => {
dataList.value = val
},
{ immediate: true }
)
watch(
dataList,
(val) => {
es('update:modelValue', val)
},
{ deep: true }
)
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
//列表加载
const query = (pageIndex, pageSize) => {
if (ps.refresherEnabled) {
es('query', { pageIndex, pageSize })
}
if (!dataList.value.length) {
complete(dataList.value)
es('query', { pageIndex, pageSize })
if (ps.isDataList) {
if (!dataList.value.length) {
complete(dataList.value)
}
setTimeout(() => {
complete([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
}, 500)
}
}
//返回
const goBack = () => {
uni.navigateBack()
}
defineExpose({
complete,
reload: () => {
paging.value?.reload()
}
})
</script>
<style scoped></style>
export const hobby = [
{
label: '学习',
value: 1
},
{
label: '乒乓球',
value: 2
},
{
label: '羽毛球',
value: 3
}
]
\ No newline at end of file
<template>
<picker mode="selector" :range="getRange" @change="onChange">
<view class="picker placeholder" :class="{ placeholder: !!selectedValue }">
<text>{{ selectedValue }}</text>
<picker mode="selector" :range="getRange" @change="onChange" :disabled="disabled" :value="getPickerValue">
<view class="picker placeholder" :class="{ placeholder: !!getVal }">
<text>{{ getVal }}</text>
<uni-icons type="right" size="18" color="#86909C"></uni-icons>
</view>
</picker>
</template>
<script setup>
import { computed, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import * as dictData from './dictData'
import { cloneDeep } from 'lodash'
const es = defineEmits(['update:modelValue'])
const es = defineEmits(['update:modelValue', 'change'])
const ps = defineProps({
dictkey: {
type: String,
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
options: {
type: Array,
default: function () {
return []
}
default: () => []
},
labelField: {
type: String,
default: 'label'
},
valueField: {
type: String,
default: 'value'
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
}
})
const selectedValue = ref()
//获取options
const getOptions = computed(() => {
if (ps.dictkey) {
return cloneDeep(dictData[ps.dictkey])
} else {
return ps.options
}
})
const selectedValue = ref('请选择')
const getRange = computed(() => {
return ps.options.map((option) => option.label)
return getOptions.value.map((option) => option[ps.labelField])
})
const onChange = (event) => {
const index = event.detail.value
selectedValue.value = ps.options[index].label
es('update:modelValue', ps.options[index].value)
selectedValue.value = getOptions.value[index][ps.labelField]
es('update:modelValue', getOptions.value[index][ps.valueField])
es('change', getOptions.value[index][ps.valueField])
}
const getVal = computed(() => {
return selectedValue.value || ps.placeholder
})
//获取初始值
const init = () => {
const option = getOptions.value.find((option) => String(option[ps.valueField]) === String(ps.modelValue))
if (option) {
selectedValue.value = option[ps.labelField]
}
}
const getPickerValue = computed(() => {
const index = getOptions.value.findIndex((option) => String(option[ps.valueField]) === String(ps.modelValue))
return index > 0 ? index : 0
})
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>
.picker {
......
......@@ -9,6 +9,7 @@
import { computed } from 'vue'
const ps = defineProps({
//success,warning
type: {
type: String,
default: 'success'
......
......@@ -70,6 +70,12 @@
"navigationBarTitleText": "考核记录登记",
"navigationStyle": "custom"
}
},
{
"path": "pages/panel/assessment-records/add",
"style": {
"navigationBarTitleText": "添加公司值班信息"
}
}
],
"globalStyle": {
......
<template>
<global-page custom>
<global-page :showNavbar="false">
<view class="login-wrap">
<view class="login-header">
<image src="/static/image/login/Vector.png" />
......
<template>
<!-- <global-page-swiper>
<template v-slot="{ dataList }">
<view class="item" v-for="(item, index) in dataList" :key="index">
<view class="item-title">{{ index }}我是标题</view>
<view class="item-detail">{{ index }}我是详情</view>
<view class="item-line"></view>
</view>
</template>
</global-page-swiper> -->
</template>
<script setup>
import { ref, watch } from 'vue'
</script>
<style>
.item {
position: relative;
height: 150rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0rpx 30rpx;
}
.item-detail {
padding: 5rpx 15rpx;
border-radius: 10rpx;
font-size: 28rpx;
color: white;
background-color: #007aff;
}
.item-line {
position: absolute;
bottom: 0rpx;
left: 0rpx;
height: 1px;
width: 100%;
background-color: #eeeeee;
}
</style>
<template>
<global-page title="考核记录登记">
<global-page title="考核记录登记" refresherEnabled>
<view class="content">
<view class="form">
<view class="form-item">
......@@ -8,7 +8,7 @@
</view>
<view class="form-item">
<view class="form-item-label">基地/职能部门</view>
<global-picker></global-picker>
<global-picker v-model="select" dictkey="hobby"></global-picker>
</view>
<view class="form-item">
<view class="form-item-label">考核对象</view>
......@@ -69,6 +69,8 @@
<script setup>
import { ref } from 'vue'
import CardDetails from './components/card-details.vue'
const select = ref('1')
</script>
<style lang="scss" scoped>
.content {
......
<template>
<global-page title="考核记录" ref="pageRef" v-model="dataList" @query="getList" refresherEnabled>
<template #top>
<z-tabs :list="tabList" @change="tabsChange" bar-width="80rpx" bg-color="#f7f8fa" />
</template>
<view class="list">
<view class="item" v-for="(item, index) in dataList" :key="index" @tap="goTo">
<view class="item-title">
<view class="desc">
<view class="type">OPEN</view>
<view class="txt">190机型管控中心</view>
<view class="place">华北基地</view>
<global-page-swiper title="考核记录" refresherEnabled loadingMoreEnabled :tabList="tabList">
<template #="{ dataList }">
<view class="list">
<view class="item" v-for="(item, index) in dataList" :key="index" @tap="goTo">
<view class="item-title">
<view class="desc">
<view class="type">OPEN</view>
<view class="txt">190机型管控中心</view>
<view class="place">华北基地</view>
</view>
<score-details :type="index % 2 == 0 ? 'success' : 'warning'"></score-details>
</view>
<view class="item-content">
3月31日,天津航空6837/A320飞机执行天津=长春、天津=昆明航班,23:00落地,航后执行52A检工作,利用夜间8小时左右执行飞机检查和维修工
</view>
<score-details :type="index % 2 == 0 ? 'success' : 'warning'"></score-details>
</view>
<view class="item-content">
3月31日,天津航空6837/A320飞机执行天津=长春、天津=昆明航班,23:00落地,航后执行52A检工作,利用夜间8小时左右执行飞机检查和维修工
</view>
</view>
</view>
</global-page>
</template>
</global-page-swiper>
</template>
<script setup>
import { ref } from 'vue'
const tabIndex = ref(0)
const tabList = ref(['OPEN', 'CLOSE', '全部'])
//导航切换
const tabsChange = (index) => {
tabIndex.value = index
dataList.value = []
pageRef.value?.reload()
}
//加载
const pageRef = ref()
const dataList = ref([])
const getList = (opt) => {
setTimeout(() => {
pageRef.value?.complete([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
}, 500)
}
//跳转
const goTo = () => {
uni.navigateTo({ url: 'details' })
......
......@@ -3,5 +3,6 @@ export default {
'active-color': '#165DFF',//激活状态tab的颜色
'inactive-color': '#86909C',//未激活状态tab的颜色
'active-style': { color: '#1D2129' },//激活状态tab的样式
'bg-color': '#f7f8fa',//z-tabs背景色
'bar-width': '80rpx',//滑块宽度,单位rpx,支持传100、"100px"或"100rpx"
}
\ No newline at end of file
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