flat: 暂存
This commit is contained in:
BIN
components/.DS_Store
vendored
BIN
components/.DS_Store
vendored
Binary file not shown.
146
components/AppLayout/AppLayout.vue
Normal file
146
components/AppLayout/AppLayout.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<view class="app-custom-root">
|
||||
<view
|
||||
class="app-container"
|
||||
:style="{ backgroundColor: backGorundColor, backgroundImage: showBgImage && `url(${img})` }"
|
||||
>
|
||||
<!-- 顶部头部区域 -->
|
||||
<view
|
||||
class="container-header"
|
||||
:style="border ? { borderBottom: `2rpx solid ${borderColor}` } : { borderBottom: 'none' }"
|
||||
>
|
||||
<view class="header-btnLf">
|
||||
<slot name="headerleft"></slot>
|
||||
</view>
|
||||
<view class="header-title">
|
||||
<view>{{ title }}</view>
|
||||
<view v-show="subTitle" class="subtitle-text">{{ subTitle }}</view>
|
||||
</view>
|
||||
<view class="header-btnRi">
|
||||
<slot name="headerright"></slot>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体不可滚动 headContent -->
|
||||
<view class="container-headContent">
|
||||
<slot name="headContent"></slot>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容区域 -->
|
||||
<view class="container-main">
|
||||
<scroll-view v-if="useScrollView" scroll-y class="main-scroll" @scrolltolower="handleScrollToLower">
|
||||
<slot></slot>
|
||||
</scroll-view>
|
||||
<view class="main-scroll" v-else><slot></slot></view>
|
||||
</view>
|
||||
|
||||
<!-- 底部 footer -->
|
||||
<view class="container-footer">
|
||||
<slot name="footer"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import img from '@/static/icon/background2.png';
|
||||
const emit = defineEmits(['onScrollBottom']);
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题',
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '#F4F4F4',
|
||||
},
|
||||
subTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
backGorundColor: {
|
||||
type: String,
|
||||
default: '#ffffff',
|
||||
},
|
||||
useScrollView: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showBgImage: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const handleScrollToLower = () => {
|
||||
emit('onScrollBottom');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-custom-root {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
width: 100vw;
|
||||
height: calc(100% - var(--window-bottom));
|
||||
overflow: hidden;
|
||||
}
|
||||
.app-container {
|
||||
// background-image: url('@/static/icon/background2.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 0;
|
||||
background-size: 100% 728rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.container-header {
|
||||
min-height: calc(88rpx - 14rpx);
|
||||
text-align: center;
|
||||
line-height: calc(88rpx - 14rpx);
|
||||
font-size: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 7rpx 3rpx;
|
||||
.header-title {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
.subtitle-text {
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
line-height: 45rpx;
|
||||
margin-top: -16rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
}
|
||||
.header-btnLf,
|
||||
.header-btnRi {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: calc(60rpx * 3);
|
||||
}
|
||||
.header-btnRi {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-scroll {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@@ -1,67 +0,0 @@
|
||||
.bing-progress {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.bp-marea {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: space-around;
|
||||
background-color: rgba(0,0,0,0);
|
||||
z-index: 6;
|
||||
}
|
||||
.bp-mview,
|
||||
.bp-handle {
|
||||
position: absolute;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
z-index: 5;
|
||||
}
|
||||
.bp-handle-text {
|
||||
text-align: center;
|
||||
z-index: 5;
|
||||
}
|
||||
.bp-bar_max {
|
||||
position: absolute;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bp-bar_active {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
.bp-bar_sub_active {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
.bp-value {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
z-index: 4;
|
||||
}
|
||||
.bp-handle-widget {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
}
|
@@ -1,868 +0,0 @@
|
||||
<template>
|
||||
<view class="bing-progress" :style="{width:bpWidth,height:bpHeight,borderRadius:borderRadius,
|
||||
backgroundColor:backgroundColor,flexDirection:direction!='vertical'?'row':'column'}">
|
||||
<!-- 进度 -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<div class="bp-bar_max"
|
||||
:style="{width:barMaxWidth,height:barMaxHeight,backgroundColor:noActiveColor,
|
||||
flexDirection:direction!='vertical'?'row':'column',left:barMaxLeft,borderRadius:barBorderRadius}">
|
||||
<div class="bp-bar_sub_active"
|
||||
:style="{width:barSubActiveWidth,height:barSubActiveHeight,backgroundColor:subActiveColor,
|
||||
top:subActiveTop,bottom:subActiveBottom,left:subActiveLeft,right:subActiveRight,borderRadius:isActiveCircular?barBorderRadius:0}"></div>
|
||||
<div class="bp-bar_active"
|
||||
:style="{width:barActiveWidth,height:barActiveHeight,backgroundColor:activeColor,
|
||||
top:activeTop,bottom:activeBottom,left:activeLeft,right:activeRight,borderRadius:isActiveCircular?barBorderRadius:0}"></div>
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view class="bp-bar_max"
|
||||
:style="{width:barMaxWidth,height:barMaxHeight,backgroundColor:noActiveColor,borderRadius:barBorderRadius,
|
||||
flexDirection:direction!='vertical'?'row':'column',left:barMaxLeft}">
|
||||
<view class="bp-bar_sub_active"
|
||||
:style="{width:barSubActiveWidth,height:barSubActiveHeight,backgroundColor:subActiveColor,
|
||||
top:subActiveTop,bottom:subActiveBottom,left:subActiveLeft,right:subActiveRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view>
|
||||
<view class="bp-bar_active"
|
||||
:style="{width:barActiveWidth,height:barActiveHeight,backgroundColor:activeColor,
|
||||
top:activeTop,bottom:activeBottom,left:activeLeft,right:activeRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<movable-area id="bp-marea" class="bp-marea" @touchmove.stop.prevent="touchmove" @touchstart.stop.prevent="touchstart" @touchcancel="touchend" @touchend="touchend"
|
||||
:style="{width:mareaWidth,height:mareaHeight,left:mareaLeft}">
|
||||
<!-- 拖柄 -->
|
||||
<movable-view id="bp-mview" class="bp-mview" :direction="direction=='vertical'?'vertical':'horizontal'" :animation="false"
|
||||
:disabled="true" :x="handleX" :y="handleY" friction="10" damping="100"
|
||||
:style="{width:mhandleWidth,height:mhandleHeight,backgroundColor:handleColor,
|
||||
borderRadius:handleBorderRadius,fontSize:infoFontSize,top:mhandleTop}">
|
||||
<view id="bp-handle" class="bp-handle" :style="{fontSize:infoFontSize,width:mhandleWidth,height:mhandleHeight,borderRadius:handleBorderRadius}">
|
||||
<image class="bp-handle-img" :src="handleImgUrl" v-if="handleImgUrl"
|
||||
:style="{fontSize:infoFontSize,width:mhandleWidth,height:mhandleHeight,borderRadius:handleBorderRadius}"></image>
|
||||
<!-- 进度值 -->
|
||||
<text class="bp-handle-text" v-if="handleImgUrl=='' && infoAlign=='handle' && showInfo"
|
||||
:style="{fontSize:infoFontSize,color:infoColor,width:mhandleWidth,height:textHeight,borderRadius:'20px'}">{{ infoContent=='subValue'?msubValue:showValue }}{{ infoEndText }}</text>
|
||||
<!-- 挂件 -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<!-- 图片挂件 -->
|
||||
<image v-if="widgetPos=='top' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius, bottom: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image>
|
||||
<image v-if="widgetPos=='right' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,left: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image>
|
||||
<image v-if="widgetPos=='bottom' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius,top: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image>
|
||||
<image v-if="widgetPos=='left' && widgetUrl" class="bp-handle-widget" :src="widgetUrl" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,right: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}"></image>
|
||||
<!-- 自定义元素挂件 -->
|
||||
<view v-if="widgetPos=='top' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius,bottom: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}">
|
||||
<slot/>
|
||||
</view>
|
||||
<view v-if="widgetPos=='right' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,left: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}">
|
||||
<slot/>
|
||||
</view>
|
||||
<view v-if="widgetPos=='bottom' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'column',borderRadius:mwidgetBorderRadius,top: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}">
|
||||
<slot/>
|
||||
</view>
|
||||
<view v-if="widgetPos=='left' && widgetUrl==''" class="bp-handle-widget" :style="{flexDirection: 'row',borderRadius:mwidgetBorderRadius,right: moffset,width:mwidgetWidth,height:mwidgetHeight,opacity:widgetOpacity,transform: mwidgetAngle}">
|
||||
<slot/>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
<!-- 进度值 -->
|
||||
<text class="bp-value" v-if="showValueState() || (infoAlign=='center'&&direction!='vertical' && showInfo)"
|
||||
:style="{color:infoColor,fontSize:infoFontSize,left:valueLeft,width:valueWidth()+'px'}">{{ infoContent=='subValue'?msubValue:showValue }}{{ infoEndText }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 进度条,副进度条
|
||||
*/
|
||||
export default {
|
||||
created() {
|
||||
/**
|
||||
* 获取系统屏幕信息,用于后续单位换算
|
||||
*/
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
this.px2rpx = 750 / systemInfo.screenWidth
|
||||
this.screenWidth = systemInfo.screenWidth
|
||||
this.screenHeight = systemInfo.screenHeight
|
||||
},
|
||||
mounted() {
|
||||
// #ifndef APP-NVUE
|
||||
/**
|
||||
* 非NVUE movable-area 滑动事件获取到的位置是相对于文档的,获取组件位置,用于计算滑块位置
|
||||
*/
|
||||
this.updateRect()
|
||||
// #endif
|
||||
this.mmax = this.valueFormat(this.max,false)
|
||||
this.percent = Math.abs((this.valueFormat(this.value) - this.min) / (this.mmax - this.min))
|
||||
this.subPercent = Math.abs((this.valueFormat(this.subValue,true) - this.min) / (this.mmax - this.min))
|
||||
if(this.reverse) {
|
||||
if(this.direction!='vertical') {
|
||||
this.handleX = (1 - this.percent) * this.barMaxLength
|
||||
}
|
||||
else {
|
||||
this.handleY = this.percent * this.barMaxLength
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(this.direction!='vertical') {
|
||||
this.handleX = this.percent * this.barMaxLength
|
||||
}
|
||||
else {
|
||||
this.handleY = (1 - this.percent) * this.barMaxLength
|
||||
}
|
||||
}
|
||||
if(this.bpname=='test') {
|
||||
console.log(this.mainInfo)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* sub表示副进度条属性
|
||||
*/
|
||||
props: {
|
||||
// 组件名字
|
||||
bpname: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: String,
|
||||
default: '30px'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: 'rgba(0,0,0,0)'
|
||||
},
|
||||
noActiveColor: {
|
||||
type: String,
|
||||
default: "#00ffff"
|
||||
},
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: "#0000ff"
|
||||
},
|
||||
subActiveColor: {
|
||||
type: String,
|
||||
default: "#ffaaaa"
|
||||
},
|
||||
handleColor: {
|
||||
type: String,
|
||||
default: "#ffff00"
|
||||
},
|
||||
infoColor: {
|
||||
type: String,
|
||||
default: "#000000"
|
||||
},
|
||||
// 整个进度条的外边界圆角半径
|
||||
borderRadius: {
|
||||
type: String,
|
||||
default: '5px'
|
||||
},
|
||||
// 进度条内部滑轨圆角半径
|
||||
barBorderRadius: {
|
||||
type: String,
|
||||
default: '5px'
|
||||
},
|
||||
// active and subActive 是否显示圆角 NVUE默认true,其他默认false
|
||||
// #ifdef APP-NVUE
|
||||
isActiveCircular: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
isActiveCircular: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// #endif
|
||||
handleWidth: {
|
||||
type: String,
|
||||
default: '50px'
|
||||
},
|
||||
handleHeight: {
|
||||
type: String,
|
||||
default: '40px'
|
||||
},
|
||||
handleBorderRadius: {
|
||||
type: String,
|
||||
default: '5px'
|
||||
},
|
||||
handleImgUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'horizontal'
|
||||
},
|
||||
infoEndText: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
infoFontSize: {
|
||||
type: String,
|
||||
default: '18px'
|
||||
},
|
||||
showInfo: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 进度值显示value还是subValue
|
||||
infoContent: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
// 进度值显示位置 left, right, center, handle
|
||||
infoAlign: {
|
||||
type: String,
|
||||
default: 'right'
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
subValue: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 副进度条步长
|
||||
subStep: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// true连续滑动,false步进,即以step的间隔变化
|
||||
continuous: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 副进度条continuous
|
||||
subContinuous: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
reverse: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 挂件位置 top, right, bottom, left
|
||||
widgetPos: {
|
||||
type: String,
|
||||
default: "top"
|
||||
},
|
||||
widgetHeight: {
|
||||
type: [String,Number],
|
||||
default: '40px'
|
||||
},
|
||||
widgetWidth: {
|
||||
type: [String,Number],
|
||||
default: '50px'
|
||||
},
|
||||
widgetBorderRadius: {
|
||||
type: [String,Number],
|
||||
default: '5px'
|
||||
},
|
||||
// 挂件不透明度 0完全透明 1不透明
|
||||
widgetOpacity: {
|
||||
type: [String,Number],
|
||||
default: 1
|
||||
},
|
||||
// 挂件距离组件的偏移量,正数原理组件,负数靠近组件
|
||||
widgetOffset: {
|
||||
type: [String,Number],
|
||||
default: '0px'
|
||||
},
|
||||
// 挂件图片
|
||||
widgetUrl: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 挂件旋转角度
|
||||
widgetAngle: {
|
||||
type: [String,Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
handleX: 50,
|
||||
handleY: 0,
|
||||
px2rpx: 1,
|
||||
percent: 0, // 0-1
|
||||
subPercent: 0, // 0-1
|
||||
mainInfo: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0
|
||||
},
|
||||
touchState: false,
|
||||
screenHeight: 0,
|
||||
screenWidth: 0,
|
||||
msubValue: 0,
|
||||
moveable: true,
|
||||
lastTouchTime: 0,
|
||||
mmax: 100
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
/**
|
||||
* @param {Object} newValue
|
||||
* @param {Object} oldValue
|
||||
*/
|
||||
value(newValue, oldValue) {
|
||||
if(!this.touchState) {
|
||||
newValue = this.valueSetBoundary(newValue)
|
||||
this.percent = Math.abs((newValue - this.min) / (this.mmax - this.min))
|
||||
}
|
||||
},
|
||||
showValue(newValue, oldValue) {
|
||||
// 步进
|
||||
if(!this.continuous) {
|
||||
let percent
|
||||
if(this.reverse) {
|
||||
if(this.direction!='vertical') {
|
||||
percent = Math.abs(1 - (newValue - this.min) / (this.mmax - this.min))
|
||||
this.handleX = percent * this.barMaxLength
|
||||
}
|
||||
else {
|
||||
percent = Math.abs((newValue - this.min) / (this.mmax - this.min))
|
||||
this.handleY = percent * this.barMaxLength
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(this.direction!='vertical') {
|
||||
percent = Math.abs((newValue - this.min) / (this.mmax - this.min))
|
||||
this.handleX = percent * this.barMaxLength
|
||||
}
|
||||
else {
|
||||
percent = (1 - Math.abs((newValue - this.min) / (this.mmax - this.min)))
|
||||
this.handleY = percent * this.barMaxLength
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this.$emit("change", {bpname: this.bpname,type: 'change',value:this.showValue,subValue:this.msubValue})
|
||||
this.$emit("valuechange", {bpname: this.bpname,type: 'valuechange',value:this.showValue,subValue:this.msubValue})
|
||||
},
|
||||
percent(newValue, oldValue) {
|
||||
// 连续
|
||||
if(this.continuous) {
|
||||
if(this.reverse) {
|
||||
if(this.direction!='vertical') {
|
||||
this.handleX = (1 - newValue) * this.barMaxLength
|
||||
}
|
||||
else {
|
||||
this.handleY = newValue * this.barMaxLength
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(this.direction!='vertical') {
|
||||
this.handleX = newValue * this.barMaxLength
|
||||
}
|
||||
else {
|
||||
this.handleY = (1 - newValue) * this.barMaxLength
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
subValue(newValue, oldValue) {
|
||||
newValue = this.valueSetBoundary(newValue)
|
||||
|
||||
if(this.subContinuous) {
|
||||
this.msubValue = newValue
|
||||
}
|
||||
else {
|
||||
this.msubValue = this.valueFormat(newValue, true)
|
||||
}
|
||||
this.subPercent = Math.abs((newValue - this.min) / (this.mmax - this.min))
|
||||
this.$emit("change", {bpname: this.bpname,type: 'change',value:this.showValue,subValue:this.msubValue})
|
||||
this.$emit("subvaluechange", {bpname: this.bpname,type: 'subvaluechange',value:this.showValue,subValue:this.msubValue})
|
||||
|
||||
},
|
||||
max(newValue,oldValue) {
|
||||
this.mmax = this.valueFormat(newValue,false)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bpWidth() {
|
||||
if(this.direction=="vertical") {
|
||||
return this.maxHeight()[2]
|
||||
}
|
||||
return this.sizeDeal(this.width)[2]
|
||||
},
|
||||
bpHeight() {
|
||||
if(this.direction=="vertical") {
|
||||
return this.sizeDeal(this.width)[2]
|
||||
}
|
||||
return this.maxHeight()[2]
|
||||
},
|
||||
mareaWidth() {
|
||||
if(this.direction=="vertical") {
|
||||
return this.maxHeight()[2]
|
||||
}
|
||||
let width = this.sizeDeal(this.width)[0]
|
||||
return (width - this.textWidth()) + 'px'
|
||||
},
|
||||
mareaHeight() {
|
||||
if(this.direction=="vertical") {
|
||||
let width = this.sizeDeal(this.width)[0]
|
||||
return (width - this.textWidth()) + 'px'
|
||||
}
|
||||
return this.maxHeight()[2]
|
||||
},
|
||||
mareaLeft() {
|
||||
if(this.showValueState()) {
|
||||
if(this.infoAlign == 'left') {
|
||||
return this.textWidth() + 'px'
|
||||
}
|
||||
}
|
||||
return 0
|
||||
},
|
||||
barMaxHeight() {
|
||||
if(this.direction=="vertical") {
|
||||
let width = this.sizeDeal(this.width)[0]
|
||||
let handleWidth = this.sizeDeal(this.handleWidth)
|
||||
return (width - this.textWidth() - handleWidth[0]) + 'px'
|
||||
}
|
||||
return this.sizeDeal(this.strokeWidth)[2]
|
||||
},
|
||||
barMaxWidth() {
|
||||
if(this.direction=="vertical") {
|
||||
return this.sizeDeal(this.strokeWidth)[2]
|
||||
}
|
||||
let width = this.sizeDeal(this.width)[0]
|
||||
let handleWidth = this.sizeDeal(this.handleWidth)
|
||||
return (width - this.textWidth() - handleWidth[0]) + 'px'
|
||||
},
|
||||
barMaxLeft() {
|
||||
if(this.showValueState()) {
|
||||
if(this.infoAlign == 'left') {
|
||||
return this.textWidth() + this.sizeDeal(this.handleWidth)[0] / 2 + 'px'
|
||||
}
|
||||
}
|
||||
if(this.direction != 'vertical') {
|
||||
return this.sizeDeal(this.handleWidth)[0] / 2 + 'px'
|
||||
}
|
||||
// vertical
|
||||
return (this.maxHeight()[0] - this.sizeDeal(this.strokeWidth)[0]) / 2 + 'px'
|
||||
|
||||
},
|
||||
activeRight() {
|
||||
if(this.reverse) {
|
||||
return 0
|
||||
}
|
||||
return 'unset'
|
||||
},
|
||||
activeLeft() {
|
||||
if(this.reverse) {
|
||||
return 'unset'
|
||||
}
|
||||
return 0
|
||||
},
|
||||
activeTop() {
|
||||
if(this.reverse) {
|
||||
return 0
|
||||
}
|
||||
return 'unset'
|
||||
},
|
||||
activeBottom() {
|
||||
if(this.reverse) {
|
||||
return 'unset'
|
||||
}
|
||||
return 0
|
||||
},
|
||||
barActiveWidth() {
|
||||
if(this.direction=="vertical") {
|
||||
return this.sizeDeal(this.strokeWidth)[2]
|
||||
}
|
||||
let percent
|
||||
if(this.continuous) {
|
||||
percent = this.percent
|
||||
}
|
||||
else {
|
||||
percent = Math.abs((this.showValue - this.min) / (this.mmax - this.min))
|
||||
}
|
||||
return this.barMaxLength * percent + 'px'
|
||||
},
|
||||
barActiveHeight() {
|
||||
if(this.direction=="vertical") {
|
||||
let percent
|
||||
if(this.continuous) {
|
||||
percent = this.percent
|
||||
}
|
||||
else {
|
||||
percent = Math.abs((this.showValue - this.min) / (this.mmax - this.min))
|
||||
}
|
||||
return this.barMaxLength * percent + 'px'
|
||||
}
|
||||
return this.sizeDeal(this.strokeWidth)[2]
|
||||
},
|
||||
subActiveTop() {
|
||||
if(this.reverse) {
|
||||
return 0
|
||||
}
|
||||
return 'unset'
|
||||
},
|
||||
subActiveBottom() {
|
||||
if(this.reverse) {
|
||||
return 'unset'
|
||||
}
|
||||
return 0
|
||||
},
|
||||
subActiveRight() {
|
||||
if(this.reverse) {
|
||||
return 0
|
||||
}
|
||||
return 'unset'
|
||||
},
|
||||
subActiveLeft() {
|
||||
if(this.reverse) {
|
||||
return 'unset'
|
||||
}
|
||||
return 0
|
||||
},
|
||||
barSubActiveWidth() {
|
||||
if(this.direction == "vertical") {
|
||||
return this.sizeDeal(this.strokeWidth)[2]
|
||||
}
|
||||
if(this.subContinuous) {
|
||||
return this.barMaxLength * this.subPercent + 'px'
|
||||
}
|
||||
else {
|
||||
return this.barMaxLength * Math.abs((this.msubValue - this.min) / (this.mmax - this.min)) + 'px'
|
||||
}
|
||||
|
||||
},
|
||||
barSubActiveHeight() {
|
||||
if(this.direction == "vertical") {
|
||||
if(this.subContinuous) {
|
||||
return this.barMaxLength * this.subPercent + 'px'
|
||||
}
|
||||
else {
|
||||
this.barMaxLength * Math.abs((this.msubValue - this.min) / (this.mmax - this.min)) + 'px'
|
||||
}
|
||||
|
||||
}
|
||||
return this.sizeDeal(this.strokeWidth)[2]
|
||||
},
|
||||
mhandleWidth() {
|
||||
if(this.direction == "vertical") {
|
||||
return this.sizeDeal(this.handleHeight)[2]
|
||||
}
|
||||
return this.sizeDeal(this.handleWidth)[2]
|
||||
},
|
||||
mhandleHeight() {
|
||||
if(this.direction == "vertical") {
|
||||
return this.sizeDeal(this.handleWidth)[2]
|
||||
}
|
||||
return this.sizeDeal(this.handleHeight)[2]
|
||||
},
|
||||
mhandleTop() {
|
||||
if(this.direction == 'vertical') {
|
||||
return 0
|
||||
}
|
||||
else {
|
||||
// 拖柄垂直居中
|
||||
let handle = this.sizeDeal(this.handleHeight)[0]
|
||||
let top = this.maxHeight()[0] / 2 - handle / 2 + 'px'
|
||||
return top
|
||||
}
|
||||
},
|
||||
showValue() {
|
||||
return this.valueFormat(this.percent * (this.mmax - this.min) + this.min)
|
||||
},
|
||||
textHeight() {
|
||||
let infoSize = this.sizeDeal(this.infoFontSize)
|
||||
return infoSize[0]*1.2 + infoSize[1]
|
||||
},
|
||||
valueLeft() {
|
||||
if(this.infoAlign=='left') {
|
||||
return 0
|
||||
}
|
||||
else if(this.infoAlign == 'center') {
|
||||
let width = this.sizeDeal(this.width)
|
||||
return width[0]/2 - this.valueWidth()/2 + 'px'
|
||||
}
|
||||
else if(this.infoAlign=='right'){
|
||||
let width = this.sizeDeal(this.width)
|
||||
return width[0] - this.textWidth() + 'px'
|
||||
}
|
||||
return 0
|
||||
},
|
||||
barMaxLength() {
|
||||
let width = this.sizeDeal(this.width)[0]
|
||||
let handleWidth = this.sizeDeal(this.handleWidth)
|
||||
return width - this.textWidth() - handleWidth[0]
|
||||
},
|
||||
mwidgetWidth() {
|
||||
return this.sizeDeal(this.widgetWidth)[2];
|
||||
},
|
||||
mwidgetHeight() {
|
||||
return this.sizeDeal(this.widgetHeight)[2];
|
||||
},
|
||||
moffset() {
|
||||
let off = this.sizeDeal(this.widgetOffset);
|
||||
// console.log(off)
|
||||
switch(this.widgetPos) {
|
||||
case 'top':
|
||||
return this.sizeDeal(this.mhandleHeight)[0] + off[0] + 'px'
|
||||
case 'right':
|
||||
return this.sizeDeal(this.mhandleWidth)[0] + off[0] + 'px'
|
||||
case 'bottom':
|
||||
return this.sizeDeal(this.mhandleHeight)[0] + off[0] + 'px'
|
||||
case 'left':
|
||||
return this.sizeDeal(this.mhandleWidth)[0] + off[0] + 'px'
|
||||
}
|
||||
return 0
|
||||
},
|
||||
mwidgetBorderRadius() {
|
||||
return this.sizeDeal(this.widgetBorderRadius)[2];
|
||||
},
|
||||
mwidgetAngle() {
|
||||
return "rotate("+Number(this.widgetAngle)+"deg)"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
prevent(e) {
|
||||
console.log(1)
|
||||
},
|
||||
updateRect() {
|
||||
// #ifndef APP-NVUE
|
||||
/**
|
||||
* 非NVUE movable-area 滑动事件获取到的位置是相对于文档的,获取组件位置,用于计算滑块位置
|
||||
*/
|
||||
let query = uni.createSelectorQuery().in(this)
|
||||
query.select('.bing-progress').boundingClientRect(data => {
|
||||
this.mainInfo.top = data.top
|
||||
this.mainInfo.left = data.left
|
||||
this.mainInfo.bottom = data.bottom
|
||||
this.mainInfo.right = data.right
|
||||
}).exec()
|
||||
// #endif
|
||||
},
|
||||
touchstart(e) {
|
||||
if(!this.disabled) {
|
||||
// #ifdef APP-NVUE
|
||||
e.stopPropagation()
|
||||
e.target.attr.preventGesture = true
|
||||
if(this.direction == 'vertical' && e.target.attr.id != 'bp-mview' && (e.timestamp - this.lastTouchTime > 100)) {
|
||||
this.moveable = false
|
||||
}
|
||||
this.lastTouchTime = e.timestamp
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
/**
|
||||
* 防止组件在文档流中的位置被修改,导致组件进度值异常
|
||||
*/
|
||||
this.updateRect()
|
||||
// #endif
|
||||
// 阻止组件信息异常情况下的进度值修改
|
||||
if(this.mainInfo.top > this.screenHeight) {
|
||||
this.$emit("dragstart", {bpname: this.bpname,type: 'dragstart',value:this.showValue,subValue:this.msubValue})
|
||||
return
|
||||
}
|
||||
this.touchState = true
|
||||
let detail = e.changedTouches[0]
|
||||
this.handleMove(detail)
|
||||
this.$emit("dragstart", {bpname: this.bpname,type: 'dragstart',value:this.showValue,subValue:this.msubValue})
|
||||
}
|
||||
},
|
||||
touchmove(e) {
|
||||
if(!this.disabled) {
|
||||
let detail = e.changedTouches[0]
|
||||
this.handleMove(detail)
|
||||
this.$emit("dragging", {bpname: this.bpname,type: 'dragging',value:this.showValue,subValue:this.msubValue})
|
||||
}
|
||||
},
|
||||
touchend(e) {
|
||||
if(!this.disabled) {
|
||||
// #ifdef APP-NVUE
|
||||
if(!this.moveable) {
|
||||
this.moveable = true
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
let detail = e.changedTouches[0]
|
||||
this.handleMove(detail)
|
||||
this.touchState = false
|
||||
this.$emit("dragend", {bpname: this.bpname,type: 'dragend',value:this.showValue,subValue:this.msubValue})
|
||||
}
|
||||
},
|
||||
handleMove(detail) {
|
||||
let width = this.sizeDeal(this.width)[0]
|
||||
let handleWidth = this.sizeDeal(this.handleWidth)
|
||||
let percent
|
||||
if(this.direction!='vertical') {
|
||||
if(this.infoAlign=='left') {
|
||||
// #ifndef APP-NVUE
|
||||
percent = (detail.pageX - this.mainInfo.left - this.textWidth() - handleWidth[0]/2)/ this.barMaxLength
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
percent = (detail.pageX - handleWidth[0]/2)/ this.barMaxLength
|
||||
// #endif
|
||||
}
|
||||
else {
|
||||
// #ifndef APP-NVUE
|
||||
percent = (detail.pageX - this.mainInfo.left - handleWidth[0]/2)/ this.barMaxLength
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
percent = (detail.pageX - handleWidth[0]/2)/ this.barMaxLength
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
// #ifdef APP-NVUE
|
||||
percent = 1 - (detail.pageY - handleWidth[0]/2- 1) / this.barMaxLength
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
percent = 1 - (detail.clientY - this.mainInfo.top - handleWidth[0]/2)/ this.barMaxLength
|
||||
// #endif
|
||||
}
|
||||
percent = percent > 0 ? percent : 0
|
||||
percent = percent < 1 ? percent : 1
|
||||
if(this.reverse) {
|
||||
this.percent = 1 - percent
|
||||
}
|
||||
else {
|
||||
this.percent = percent
|
||||
}
|
||||
},
|
||||
showValueState() {
|
||||
if(this.direction != 'vertical' && this.showInfo && (this.infoAlign=='left' || this.infoAlign=='right')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
valueSetBoundary(value) {
|
||||
// 控制value在合法范围内
|
||||
if(this.mmax > this.min) {
|
||||
value = value < this.mmax ? value : this.mmax
|
||||
value = value > this.min ? value : this.min
|
||||
}
|
||||
else {
|
||||
value = value > this.mmax ? value : this.mmax
|
||||
value = value < this.min ? value : this.min
|
||||
}
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* @param {Object} v
|
||||
* @param {Object} isSub 是否是副副进度条
|
||||
*/
|
||||
valueFormat (v,isSub){
|
||||
// set step
|
||||
v = this.valueSetBoundary(v)
|
||||
let stepInfo = this.stepInfo(isSub)
|
||||
v = Number(v - this.min).toFixed(stepInfo[1])
|
||||
let step = stepInfo[0] * 10 ** stepInfo[1]
|
||||
let valueE = v * 10 ** stepInfo[1]
|
||||
let remainder = valueE % step
|
||||
let remainderInt = Math.floor(remainder)
|
||||
// 对余数四舍五入0-1
|
||||
let sub = Math.round(remainder / step)
|
||||
let value = (Math.floor(valueE) - remainderInt + sub*step) / (10 ** stepInfo[1])
|
||||
value = Number((value + this.min).toFixed(stepInfo[1]))
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* @param {Object} v
|
||||
* @param {Object} isSub 是否是副副进度条
|
||||
*/
|
||||
stepInfo(isSub) {
|
||||
// return step, decimal位数
|
||||
let step
|
||||
if(isSub) {
|
||||
step = Number(this.subStep)
|
||||
}
|
||||
else {
|
||||
step = Number(this.step)
|
||||
}
|
||||
|
||||
if (step <= 0 || !step){
|
||||
return [1, 0]
|
||||
}
|
||||
else{
|
||||
let steps = step.toString().split('.')
|
||||
if (steps.length == 1){
|
||||
return [step,0]
|
||||
}
|
||||
else {
|
||||
return [step,steps[1].length]
|
||||
}
|
||||
}
|
||||
},
|
||||
textWidth() {
|
||||
if(this.showValueState()) {
|
||||
let numWidth = this.mmax.toString().length> this.min.toString().length? this.mmax.toString().length: this.min.toString().length
|
||||
let textWidth = ((numWidth + this.stepInfo()[1]) * 0.7 + this.infoEndText.length) * this.sizeDeal(this.infoFontSize)[0]
|
||||
return Number(textWidth.toFixed(2))
|
||||
}
|
||||
return 0
|
||||
},
|
||||
valueWidth() {
|
||||
let numWidth = this.mmax.toString().length> this.min.toString().length? this.mmax.toString().length: this.min.toString().length
|
||||
let textWidth = ((numWidth + this.stepInfo()[1]) * 0.7 + this.infoEndText.length) * this.sizeDeal(this.infoFontSize)[0]
|
||||
return Number(textWidth.toFixed(2))
|
||||
},
|
||||
maxHeight() {
|
||||
let h = []
|
||||
if (this.direction!='vertical'){ // direction 为 vertical 时不显示info
|
||||
let subt = this.infoEndText.match(/[^\x00-\xff]/g)
|
||||
if (subt){
|
||||
h.push(this.sizeDeal(this.infoFontSize)[0] * 1.1)
|
||||
}
|
||||
else{
|
||||
h.push(this.sizeDeal(this.infoFontSize)[0])
|
||||
}
|
||||
}
|
||||
h.push(this.sizeDeal(this.strokeWidth)[0])
|
||||
h.push(this.sizeDeal(this.handleHeight)[0])
|
||||
h.sort(function(a, b) {
|
||||
return b - a
|
||||
}) // 降序
|
||||
return [h[0], 'px', h[0] + 'px']
|
||||
},
|
||||
sizeDeal(size) {
|
||||
// 分离字体大小和单位,rpx 转 px
|
||||
let s = Number.isNaN(parseFloat(size)) ? 0 : parseFloat(size)
|
||||
let u = size.toString().replace(/[0-9\.]/g, '')
|
||||
if (u == 'rpx') {
|
||||
s /= this.px2rpx
|
||||
u = 'px'
|
||||
}else if (u == 'vw') {
|
||||
u = 'px'
|
||||
s = s / 100 * this.screenWidth
|
||||
} else if(u == 'vh') {
|
||||
u = 'px'
|
||||
s = s / 100 * this.screenHeight
|
||||
} else{
|
||||
u = 'px'
|
||||
}
|
||||
|
||||
return [s, u, s + u]
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import "bing-progress.css"
|
||||
</style>
|
@@ -1,15 +1,17 @@
|
||||
<template>
|
||||
<span style="padding-left: 16rpx">{{ tofixedAndKmM(distance) }}</span>
|
||||
<span style="padding-left: 16rpx">{{ distance }}</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
import { inject, computed, watch } from 'vue';
|
||||
const { haversine, getDistanceFromLatLonInKm } = inject('globalFunction');
|
||||
const { alat, along, blat, blong } = defineProps(['alat', 'along', 'blat', 'blong']);
|
||||
const distance = getDistanceFromLatLonInKm(alat, along, blat, blong);
|
||||
function tofixedAndKmM(data) {
|
||||
const { km, m } = data;
|
||||
if (!alat && !along) {
|
||||
const props = defineProps(['alat', 'along', 'blat', 'blong']);
|
||||
|
||||
const distance = computed(() => {
|
||||
const distance2 = getDistanceFromLatLonInKm(props.alat, props.along, props.blat, props.blong);
|
||||
// console.log(distance2, props.alat, props.along, props.blat, props.blong);
|
||||
const { km, m } = distance2;
|
||||
if (!props.alat && !props.along) {
|
||||
return '--km';
|
||||
}
|
||||
if (km > 1) {
|
||||
@@ -17,7 +19,8 @@ function tofixedAndKmM(data) {
|
||||
} else {
|
||||
return m.toFixed(2) + 'm';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<view
|
||||
v-if="visible"
|
||||
class="tianditu-popop"
|
||||
:style="{ height: winHeight + 'px', width: winWidth + 'px', top: winTop + 'px' }"
|
||||
>
|
||||
<view v-if="header" class="popup-header" @click="close">
|
||||
<slot name="header"></slot>
|
||||
</view>
|
||||
<view :style="{ minHeight: contentHeight + 'vh' }" class="popup-content fadeInUp animated">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'custom-popup',
|
||||
data() {
|
||||
return {
|
||||
winWidth: 0,
|
||||
winHeight: 0,
|
||||
winTop: 0,
|
||||
contentHeight: 30,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
require: true,
|
||||
default: false,
|
||||
},
|
||||
hide: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
header: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
contentH: {
|
||||
type: Number,
|
||||
default: 30,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
var that = this;
|
||||
if (this.contentH) {
|
||||
this.contentHeight = this.contentH;
|
||||
}
|
||||
uni.getSystemInfo({
|
||||
success: function (res) {
|
||||
if (that.hide === 0) {
|
||||
that.winWidth = res.screenWidth;
|
||||
that.winHeight = res.screenHeight;
|
||||
that.winTop = 0;
|
||||
} else {
|
||||
that.winWidth = res.windowWidth;
|
||||
that.winHeight = res.windowHeight;
|
||||
that.winTop = res.windowTop;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
close(e) {
|
||||
this.$emit('onClose');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tianditu-popop {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
background-color: #ffffff;
|
||||
min-height: 300px;
|
||||
width: 100%;
|
||||
/* position: absolute;
|
||||
bottom: 0;
|
||||
left: 0; */
|
||||
}
|
||||
|
||||
/*base code*/
|
||||
.animated {
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animated.infinite {
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.animated.hinge {
|
||||
-webkit-animation-duration: 2s;
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
-ms-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeInUp {
|
||||
-webkit-animation-name: fadeInUp;
|
||||
animation-name: fadeInUp;
|
||||
}
|
||||
</style>
|
84
components/empty/empty.vue
Normal file
84
components/empty/empty.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<view class="empty" :style="{ background: bgcolor, marginTop: mrTop + 'rpx' }">
|
||||
<view class="ty_content" :style="{ paddingTop: pdTop + 'rpx' }">
|
||||
<view class="content_top btn-shaky">
|
||||
<image v-if="pictrue" :src="pictrue" mode=""></image>
|
||||
<image v-else src="@/static/icon/empty.png" mode=""></image>
|
||||
</view>
|
||||
<view class="content_c">{{ content }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '暂时没有结果,下一秒也许就有惊喜',
|
||||
},
|
||||
bgcolor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'transparent',
|
||||
},
|
||||
pdTop: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '80',
|
||||
},
|
||||
mrTop: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '20',
|
||||
},
|
||||
pictrue: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.empty {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
.ty_content {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
transform: translate(-50%, 0);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.content_top {
|
||||
width: 450rpx;
|
||||
height: 322rpx;
|
||||
}
|
||||
.content_c {
|
||||
margin-top: 32rpx;
|
||||
color: #6a707c;
|
||||
font-size: 28rpx;
|
||||
width: 512rpx;
|
||||
height: 44rpx;
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -14,12 +14,18 @@
|
||||
@click="changeStationLog(item)"
|
||||
>
|
||||
{{ item.label }}
|
||||
<view class="positionNum" v-show="item.checkednumber">
|
||||
<!-- <view class="positionNum" v-show="item.checkednumber">
|
||||
{{ item.checkednumber }}
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view :show-scrollbar="false" :scroll-y="true" class="sex-content-right">
|
||||
<scroll-view
|
||||
:show-scrollbar="false"
|
||||
:scroll-top="scrollTop"
|
||||
@scroll="scrollTopBack"
|
||||
:scroll-y="true"
|
||||
class="sex-content-right"
|
||||
>
|
||||
<view v-for="item in rightValue" :key="item.id">
|
||||
<view class="secondary-title">{{ item.label }}</view>
|
||||
<view class="grid-sex">
|
||||
@@ -50,6 +56,7 @@ export default {
|
||||
rightValue: [],
|
||||
stationCateLog: 0,
|
||||
copyTree: [],
|
||||
scrollTop: 0,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
@@ -86,9 +93,14 @@ export default {
|
||||
changeStationLog(item) {
|
||||
this.leftValue = item;
|
||||
this.rightValue = item.children;
|
||||
this.scrollTop = 0;
|
||||
},
|
||||
scrollTopBack(e) {
|
||||
this.scrollTop = e.detail.scrollTop;
|
||||
},
|
||||
addItem(item) {
|
||||
let titiles = [];
|
||||
let labels = [];
|
||||
let count = 0;
|
||||
|
||||
// 先统计已选中的职位数量
|
||||
@@ -121,13 +133,18 @@ export default {
|
||||
// 统计被选中的第三层节点
|
||||
if (thirdLayer.checked) {
|
||||
titiles.push(`${thirdLayer.id}`);
|
||||
labels.push(`${thirdLayer.label}`);
|
||||
firstLayer.checkednumber++; // 累加计数器
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
titiles = titiles.join(',');
|
||||
this.$emit('onChange', titiles);
|
||||
labels = labels.join(',');
|
||||
this.$emit('onChange', {
|
||||
ids: titiles,
|
||||
labels,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -135,7 +152,8 @@ export default {
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.secondary-title{
|
||||
font-weight: bold;
|
||||
color: #333333
|
||||
font-size: 28rpx
|
||||
padding: 40rpx 0 10rpx 30rpx;
|
||||
}
|
||||
.expected-station{
|
||||
@@ -143,6 +161,7 @@ export default {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%
|
||||
}
|
||||
.sex-search
|
||||
width: calc(100% - 28rpx - 28rpx);
|
||||
@@ -160,15 +179,16 @@ export default {
|
||||
padding: 10rpx 0 10rpx 58rpx;
|
||||
.sex-content
|
||||
background: #FFFFFF;
|
||||
border-radius: 20rpx;
|
||||
// border-radius: 20rpx;
|
||||
width: 100%;
|
||||
margin-top: 20rpx;
|
||||
display: flex;
|
||||
border-bottom: 2px solid #D9D9D9;
|
||||
overflow: hidden;
|
||||
height: 100%
|
||||
height: 100%;
|
||||
.sex-content-left
|
||||
width: 250rpx;
|
||||
width: 198rpx;
|
||||
padding: 20rpx 0 0 0;
|
||||
.left-list-btn
|
||||
padding: 0 40rpx 0 24rpx;
|
||||
display: grid;
|
||||
@@ -178,49 +198,57 @@ export default {
|
||||
color: #606060;
|
||||
font-size: 28rpx;
|
||||
position: relative
|
||||
.positionNum
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 50%;
|
||||
transform: translate(0, -50%)
|
||||
color: #FFFFFF
|
||||
background: #4778EC
|
||||
border-radius: 50%
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-top: 60rpx
|
||||
.left-list-btn:first-child
|
||||
margin-top: 0
|
||||
// .positionNum
|
||||
// position: absolute
|
||||
// right: 0
|
||||
// top: 50%;
|
||||
// transform: translate(0, -50%)
|
||||
// color: #FFFFFF
|
||||
// background: #4778EC
|
||||
// border-radius: 50%
|
||||
// width: 36rpx;
|
||||
// height: 36rpx;
|
||||
.left-list-btned
|
||||
color: #4778EC;
|
||||
position: relative;
|
||||
.left-list-btned::after
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
content: '';
|
||||
width: 7rpx;
|
||||
height: 38rpx;
|
||||
background: #4778EC;
|
||||
border-radius: 0rpx 0rpx 0rpx 0rpx;
|
||||
// .left-list-btned::after
|
||||
// position: absolute;
|
||||
// left: 20rpx;
|
||||
// content: '';
|
||||
// width: 7rpx;
|
||||
// height: 38rpx;
|
||||
// background: #4778EC;
|
||||
// border-radius: 0rpx 0rpx 0rpx 0rpx;
|
||||
|
||||
.sex-content-right
|
||||
border-left: 2px solid #D9D9D9;
|
||||
// border-left: 2px solid #D9D9D9;
|
||||
background: #F6F6F6;
|
||||
flex: 1;
|
||||
.grid-sex
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
place-items: center;
|
||||
padding: 0 0 40rpx 0;
|
||||
padding: 0 20rpx 40rpx 20rpx;
|
||||
.sex-right-btn
|
||||
width: 211rpx;
|
||||
height: 84rpx;
|
||||
font-size: 32rpx;
|
||||
width: 228rpx;
|
||||
height: 80rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 41rpx;
|
||||
text-align: center;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #D9D9D9;
|
||||
border-radius: 20rpx;
|
||||
margin-top:30rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-top: 30rpx;
|
||||
background: #E8EAEE;
|
||||
color: #606060;
|
||||
.sex-right-btned
|
||||
color: #FFFFFF;
|
||||
background: #4778EC;
|
||||
font-weight: 500
|
||||
width: 224rpx;
|
||||
height: 76rpx;
|
||||
background: rgba(37,107,250,0.06);
|
||||
border: 2rpx solid #256BFA;
|
||||
color: #256BFA
|
||||
</style>
|
||||
|
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<picker range-key="text" @change="changeLatestHotestStatus" :value="rangeVal" :range="rangeOptions">
|
||||
<view class="uni-input">{{ rangeOptions[rangeVal].text }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted, getCurrentInstance } from 'vue';
|
||||
const rangeVal = ref(0);
|
||||
const emit = defineEmits(['confirm', 'close']);
|
||||
const rangeOptions = ref([
|
||||
{ value: 0, text: '推荐' },
|
||||
{ value: 1, text: '最热' },
|
||||
{ value: 2, text: '最新发布' },
|
||||
]);
|
||||
|
||||
function changeLatestHotestStatus(e) {
|
||||
const id = e.detail.value;
|
||||
rangeVal.value = id;
|
||||
const obj = rangeOptions.value.filter((item) => item.value === id)[0];
|
||||
emit('confirm', obj);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus"></style>
|
@@ -3,6 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 匹配度
|
||||
import { inject, computed } from 'vue';
|
||||
const { job } = defineProps(['job']);
|
||||
const { similarityJobs, throttle } = inject('globalFunction');
|
||||
|
@@ -1,323 +0,0 @@
|
||||
<template>
|
||||
<view v-if="show" class="popup-container">
|
||||
<view class="popup-content">
|
||||
<!-- 标题 -->
|
||||
<view class="title">岗位推荐</view>
|
||||
|
||||
<!-- 圆环 -->
|
||||
<view class="circle-content" :style="{ height: contentHeight * 2 + 'rpx' }">
|
||||
<!-- 渲染岗位标签 -->
|
||||
<view class="tabs">
|
||||
<!-- 动画 -->
|
||||
<view
|
||||
class="circle"
|
||||
:style="{ height: circleDiameter * 2 + 'rpx', width: circleDiameter * 2 + 'rpx' }"
|
||||
@click="serchforIt"
|
||||
>
|
||||
搜一搜
|
||||
</view>
|
||||
<view
|
||||
v-for="(item, index) in jobList"
|
||||
:key="index"
|
||||
class="tab"
|
||||
:style="getLabelStyle(index)"
|
||||
@click="selectTab(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<button class="close-btn" @click="closePopup">完成</button>
|
||||
</view>
|
||||
<!-- piker -->
|
||||
<custom-popup :content-h="100" :visible="state.visible" :header="false">
|
||||
<view class="popContent">
|
||||
<view class="s-header">
|
||||
<view class="heade-lf" @click="state.visible = false">取消</view>
|
||||
<view class="heade-ri" @click="confimPopup">确认</view>
|
||||
</view>
|
||||
<view class="sex-content fl_1">
|
||||
<expected-station
|
||||
:search="false"
|
||||
@onChange="changeJobTitleId"
|
||||
:station="state.stations"
|
||||
:max="5"
|
||||
></expected-station>
|
||||
</view>
|
||||
</view>
|
||||
</custom-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, inject, computed, onMounted, defineProps, defineEmits, reactive } from 'vue';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
const { $api, navTo, setCheckedNodes } = inject('globalFunction');
|
||||
const { getUserResume } = useUserStore();
|
||||
const props = defineProps({
|
||||
show: Boolean, // 是否显示弹窗
|
||||
jobList: Array, // 职位列表
|
||||
});
|
||||
const contentHeight = ref(373);
|
||||
const circleDiameter = ref(113);
|
||||
const screenWidth = ref(375); // 默认值,避免初始化报错
|
||||
const screenHeight = ref(667);
|
||||
const centerX = ref(187.5); // 圆心X
|
||||
const centerY = ref(333.5); // 圆心Y
|
||||
const radius = ref(120); // 圆半径
|
||||
const tabPositions = ref([]); // 存储计算好的随机坐标
|
||||
const emit = defineEmits(['update:show']);
|
||||
const userInfo = ref({});
|
||||
|
||||
const state = reactive({
|
||||
jobTitleId: '',
|
||||
stations: [],
|
||||
visible: false,
|
||||
});
|
||||
|
||||
const closePopup = () => {
|
||||
emit('update:show', false);
|
||||
};
|
||||
|
||||
const updateScreenSize = () => {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
screenWidth.value = systemInfo.windowWidth;
|
||||
screenHeight.value = systemInfo.windowHeight;
|
||||
centerX.value = screenWidth.value / 2;
|
||||
centerY.value = screenHeight.value / 2 - contentHeight.value / 2; // 让圆心稍微上移
|
||||
};
|
||||
|
||||
function serchforIt() {
|
||||
if (state.stations.length) {
|
||||
state.visible = true;
|
||||
return;
|
||||
}
|
||||
$api.createRequest('/app/common/jobTitle/treeselect', {}, 'GET').then((resData) => {
|
||||
if (userInfo.value.jobTitleId) {
|
||||
const ids = userInfo.value.jobTitleId.split(',').map((id) => Number(id));
|
||||
setCheckedNodes(resData.data, ids);
|
||||
}
|
||||
state.jobTitleId = userInfo.value.jobTitleId;
|
||||
state.stations = resData.data;
|
||||
state.visible = true;
|
||||
});
|
||||
}
|
||||
|
||||
function confimPopup() {
|
||||
$api.createRequest('/app/user/resume', { jobTitleId: state.jobTitleId }, 'post').then((resData) => {
|
||||
$api.msg('完成');
|
||||
state.visible = false;
|
||||
getUserResume().then(() => {
|
||||
initload();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function selectTab(item) {
|
||||
console.log(item);
|
||||
}
|
||||
|
||||
function changeJobTitleId(ids) {
|
||||
state.jobTitleId = ids;
|
||||
}
|
||||
|
||||
function getLabelStyle(index) {
|
||||
// 基础半径(根据标签数量动态调整)
|
||||
const baseRadius = Math.min(Math.max(props.jobList.length * 15, 130), screenWidth.value * 0.4);
|
||||
|
||||
// 基础角度间隔
|
||||
const angleStep = 360 / props.jobList.length;
|
||||
|
||||
// 随机扰动参数
|
||||
const randomRadius = baseRadius + Math.random() * 60 - 50;
|
||||
const randomAngle = angleStep * index + Math.random() * 20 - 10;
|
||||
|
||||
// 极坐标转笛卡尔坐标
|
||||
const radians = (randomAngle * Math.PI) / 180;
|
||||
const x = Math.cos(radians) * randomRadius;
|
||||
const y = Math.sin(radians) * randomRadius;
|
||||
|
||||
return {
|
||||
left: `calc(50% + ${x}px)`,
|
||||
top: `calc(50% + ${y}px)`,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
userInfo.value = useUserStore().userInfo;
|
||||
updateScreenSize();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 全屏弹窗 */
|
||||
.popup-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* 弹窗内容 */
|
||||
.popup-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, #007aff, #005bbb);
|
||||
text-align: center;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-left: 20rpx;
|
||||
width: calc(100% - 20rpx);
|
||||
height: 68rpx;
|
||||
font-family: Inter, Inter;
|
||||
font-weight: 400;
|
||||
font-size: 56rpx;
|
||||
color: #ffffff;
|
||||
line-height: 65rpx;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.circle-content {
|
||||
width: 731rpx;
|
||||
height: 747rpx;
|
||||
position: relative;
|
||||
}
|
||||
.circle {
|
||||
width: 225rpx;
|
||||
height: 225rpx;
|
||||
background: linear-gradient(145deg, #13c57c 0%, #8dc5ae 100%);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-weight: 400;
|
||||
font-size: 35rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
line-height: 225rpx;
|
||||
}
|
||||
.circle::before,
|
||||
.circle::after {
|
||||
width: 225rpx;
|
||||
height: 225rpx;
|
||||
background: linear-gradient(145deg, #13c57c 0%, #8dc5ae 100%);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
}
|
||||
|
||||
@keyframes larger2 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(2.5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.circle::after {
|
||||
animation: larger1 2s infinite;
|
||||
}
|
||||
@keyframes larger1 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(3.5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.circle::before {
|
||||
animation: larger2 2s infinite;
|
||||
}
|
||||
|
||||
/* 岗位标签 */
|
||||
.tabs {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.tab {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
padding: 8rpx 16rpx;
|
||||
background: #fff;
|
||||
border-radius: 40rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
/* 关闭按钮 */
|
||||
.close-btn {
|
||||
width: 549rpx;
|
||||
line-height: 85rpx;
|
||||
height: 85rpx;
|
||||
background: #2ecc71;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* popup */
|
||||
.popContent {
|
||||
padding: 24rpx;
|
||||
background: #4778ec;
|
||||
height: calc(100% - 49rpx);
|
||||
.sex-content {
|
||||
border-radius: 20rpx;
|
||||
width: 100%;
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 40rpx;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: calc(100% - 100rpx);
|
||||
border: 1px solid #4778ec;
|
||||
}
|
||||
.s-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
.heade-lf {
|
||||
line-height: 30px;
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #666666;
|
||||
color: #666666;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.heade-ri {
|
||||
line-height: 30px;
|
||||
width: 50px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #1b66ff;
|
||||
background-color: #1b66ff;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
124
components/renderCompanys/renderCompanys.vue
Normal file
124
components/renderCompanys/renderCompanys.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<view v-for="job in listData" :key="job.id">
|
||||
<view class="cards" @click="nextDetail(job)">
|
||||
<view class="card-company">
|
||||
<text class="company line_1">{{ job.name }}</text>
|
||||
</view>
|
||||
<view class="card-bottom">
|
||||
<view class="fl_box fs_14">
|
||||
<dict-tree-Label class="mar_ri10" dictType="industry" :value="job.industry"></dict-tree-Label>
|
||||
<dict-Label dictType="scale" :value="job.scale"></dict-Label>
|
||||
</view>
|
||||
<view>
|
||||
<text class="color_256BFA fs_14">在招职位·{{ job.totalRecruitment || '-' }}个</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-tags">
|
||||
<view class="tag" v-if="job.nature">
|
||||
<dict-Label dictType="nature" :value="job.nature"></dict-Label>
|
||||
</view>
|
||||
<view class="tag">
|
||||
{{ vacanciesTo(job.vacancies) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, computed, toRaw } from 'vue';
|
||||
const { insertSortData, navTo, vacanciesTo } = inject('globalFunction');
|
||||
import { useRecommedIndexedDBStore } from '@/stores/useRecommedIndexedDBStore.js';
|
||||
const recommedIndexDb = useRecommedIndexedDBStore();
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default: '标题',
|
||||
},
|
||||
longitude: {
|
||||
type: Number,
|
||||
default: 120.382665,
|
||||
},
|
||||
latitude: {
|
||||
type: Number,
|
||||
default: 36.066938,
|
||||
},
|
||||
seeDate: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const listData = computed(() => {
|
||||
return props.list;
|
||||
});
|
||||
|
||||
function nextDetail(company) {
|
||||
navTo(`/packageA/pages/UnitDetails/UnitDetails?companyId=${company.companyId}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.date-jobTitle{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #495265;
|
||||
padding: 28rpx 0 0 20rpx
|
||||
}
|
||||
.cards{
|
||||
padding: 32rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0,0,0,0.04);
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
margin-top: 22rpx;
|
||||
.card-company{
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: flex-start
|
||||
.company{
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.salary{
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #4C6EFB;
|
||||
white-space: nowrap
|
||||
line-height: 48rpx
|
||||
}
|
||||
}
|
||||
.card-companyName{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
.card-tags{
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
.tag{
|
||||
width: fit-content;
|
||||
height: 30rpx;
|
||||
background: #F4F4F4;
|
||||
border-radius: 4rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
line-height: 30rpx;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #6C7282;
|
||||
text-align: center;
|
||||
margin-top: 14rpx;
|
||||
white-space: nowrap
|
||||
margin-right: 20rpx
|
||||
}
|
||||
}
|
||||
.card-bottom{
|
||||
margin-top: 4rpx
|
||||
margin-bottom: 10rpx
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
}
|
||||
</style>
|
148
components/renderJobs/renderJobs.vue
Normal file
148
components/renderJobs/renderJobs.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<view v-for="job in listData" :key="job.id">
|
||||
<view v-if="!job.isTitle" class="cards" @click="nextDetail(job)">
|
||||
<view class="card-company">
|
||||
<text class="company">{{ job.jobTitle }}</text>
|
||||
<view class="salary">
|
||||
<Salary-Expectation :max-salary="job.maxSalary" :min-salary="job.minSalary"></Salary-Expectation>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-companyName">{{ job.companyName }}</view>
|
||||
<view class="card-tags">
|
||||
<view class="tag">
|
||||
<dict-Label dictType="education" :value="job.education"></dict-Label>
|
||||
</view>
|
||||
<view class="tag">
|
||||
<dict-Label dictType="experience" :value="job.experience"></dict-Label>
|
||||
</view>
|
||||
<view class="tag">
|
||||
{{ vacanciesTo(job.vacancies) }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-bottom">
|
||||
<view>{{ job.postingDate }}</view>
|
||||
<view>
|
||||
<convert-distance
|
||||
:alat="job.latitude"
|
||||
:along="job.longitude"
|
||||
:blat="latitude"
|
||||
:blong="longitude"
|
||||
></convert-distance>
|
||||
<dict-Label class="mar_le10" dictType="area" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="date-jobTitle" v-else>
|
||||
{{ job.title }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, computed, toRaw } from 'vue';
|
||||
const { insertSortData, navTo, vacanciesTo } = inject('globalFunction');
|
||||
import { useRecommedIndexedDBStore } from '@/stores/useRecommedIndexedDBStore.js';
|
||||
const recommedIndexDb = useRecommedIndexedDBStore();
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default: '标题',
|
||||
},
|
||||
longitude: {
|
||||
type: Number,
|
||||
default: 120.382665,
|
||||
},
|
||||
latitude: {
|
||||
type: Number,
|
||||
default: 36.066938,
|
||||
},
|
||||
seeDate: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const listData = computed(() => {
|
||||
if (props.seeDate && props.list.length) {
|
||||
const ulist = toRaw(props.list);
|
||||
const [reslist, lastDate] = insertSortData(ulist, props.seeDate);
|
||||
return reslist;
|
||||
}
|
||||
console.log(props.list);
|
||||
|
||||
return props.list;
|
||||
});
|
||||
|
||||
function nextDetail(job) {
|
||||
// 记录岗位类型,用作数据分析
|
||||
if (job.jobCategory) {
|
||||
const recordData = recommedIndexDb.JobParameter(job);
|
||||
recommedIndexDb.addRecord(recordData);
|
||||
}
|
||||
navTo(`/packageA/pages/post/post?jobId=${btoa(job.jobId)}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.date-jobTitle{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #495265;
|
||||
padding: 28rpx 0 0 20rpx
|
||||
}
|
||||
.cards{
|
||||
padding: 32rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0,0,0,0.04);
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
margin-top: 22rpx;
|
||||
.card-company{
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: flex-start
|
||||
.company{
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.salary{
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #4C6EFB;
|
||||
white-space: nowrap
|
||||
line-height: 48rpx
|
||||
}
|
||||
}
|
||||
.card-companyName{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
.card-tags{
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
.tag{
|
||||
width: fit-content;
|
||||
height: 30rpx;
|
||||
background: #F4F4F4;
|
||||
border-radius: 4rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
line-height: 30rpx;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #6C7282;
|
||||
text-align: center;
|
||||
margin-top: 14rpx;
|
||||
white-space: nowrap
|
||||
margin-right: 20rpx
|
||||
}
|
||||
}
|
||||
.card-bottom{
|
||||
margin-top: 32rpx
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
}
|
||||
</style>
|
360
components/selectFilter/selectFilter.vue
Normal file
360
components/selectFilter/selectFilter.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<uni-popup
|
||||
ref="popup"
|
||||
type="bottom"
|
||||
borderRadius="10px 10px 0 0"
|
||||
background-color="#FFFFFF"
|
||||
@maskClick="maskClickFn"
|
||||
:mask-click="maskClick"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<view class="btn-cancel" @click="cancel">取消</view>
|
||||
<view class="title">
|
||||
<text>{{ title }}</text>
|
||||
<text style="color: #256bfa">·{{ count }}</text>
|
||||
</view>
|
||||
<view class="btn-confirm" @click="confirm"> </view>
|
||||
</view>
|
||||
<view class="popup-list">
|
||||
<view class="content-wrapper">
|
||||
<!-- 左侧筛选类别 -->
|
||||
<scroll-view class="filter-nav" scroll-y>
|
||||
<view
|
||||
v-for="(item, index) in filterOptions"
|
||||
:key="index"
|
||||
class="nav-item button-click"
|
||||
:class="{ active: activeTab === item.key }"
|
||||
@click="scrollTo(item.key)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 右侧筛选内容 -->
|
||||
<scroll-view class="filter-content" :scroll-into-view="activeTab" scroll-y>
|
||||
<template v-for="(item, index) in filterOptions" :key="index">
|
||||
<view class="content-item">
|
||||
<view class="item-title" :id="item.key">{{ item.label }}</view>
|
||||
<checkbox-group class="check-content" @change="(e) => handleSelect(item.key, e)">
|
||||
<label
|
||||
v-for="option in item.options"
|
||||
:key="option.value"
|
||||
class="checkbox-item button-click"
|
||||
:class="{
|
||||
checkedstyle: selectedValues[item.key]?.includes(String(option.value)),
|
||||
}"
|
||||
>
|
||||
<checkbox
|
||||
style="display: none"
|
||||
:value="String(option.value)"
|
||||
:checked="selectedValues[item.key]?.includes(String(option.value))"
|
||||
/>
|
||||
<text class="option-label">{{ option.label }}</text>
|
||||
</label>
|
||||
</checkbox-group>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-bottom">
|
||||
<view class="btn-cancel btn-feel" @click="cleanup">清除</view>
|
||||
<view class="btn-confirm btn-feel" @click="confirm">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, onBeforeMount } from 'vue';
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
const { getTransformChildren } = useDictStore();
|
||||
|
||||
const area = ref(true);
|
||||
const maskClick = ref(false);
|
||||
const maskClickFn = ref(null);
|
||||
|
||||
const title = ref('标题');
|
||||
const confirmCallback = ref(null);
|
||||
const cancelCallback = ref(null);
|
||||
const changeCallback = ref(null);
|
||||
const popup = ref(null);
|
||||
const selectedValues = reactive({});
|
||||
const count = ref(0);
|
||||
// 当前激活的筛选类别
|
||||
const activeTab = ref('');
|
||||
const filterOptions = ref([]);
|
||||
|
||||
const open = (newConfig = {}) => {
|
||||
const { title: configTitle, success, cancel, change, data, maskClick: configMaskClick = false } = newConfig;
|
||||
|
||||
// reset();
|
||||
|
||||
if (configTitle) title.value = configTitle;
|
||||
if (typeof success === 'function') confirmCallback.value = success;
|
||||
if (typeof cancel === 'function') cancelCallback.value = cancel;
|
||||
if (typeof change === 'function') changeCallback.value = change;
|
||||
|
||||
if (configMaskClick) {
|
||||
maskClick.value = configMaskClick;
|
||||
maskClickFn.value = cancel;
|
||||
}
|
||||
|
||||
getoptions();
|
||||
|
||||
nextTick(() => {
|
||||
popup.value?.open();
|
||||
});
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
popup.value?.close();
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
handleClick(cancelCallback.value);
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
handleClick(confirmCallback.value);
|
||||
};
|
||||
|
||||
const handleClick = async (callback) => {
|
||||
if (typeof callback !== 'function') {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await callback(selectedValues);
|
||||
if (result !== false) close();
|
||||
} catch (error) {
|
||||
console.error('confirmCallback 执行出错:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理选项选择
|
||||
const handleSelect = (key, e) => {
|
||||
selectedValues[key] = e.detail.value.map(String);
|
||||
let va = 0;
|
||||
for (const [key, value] of Object.entries(selectedValues)) {
|
||||
va += value.length;
|
||||
}
|
||||
count.value = va;
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
Object.keys(selectedValues).forEach((key) => {
|
||||
delete selectedValues[key];
|
||||
});
|
||||
};
|
||||
|
||||
const scrollTo = (key) => {
|
||||
activeTab.value = key;
|
||||
};
|
||||
|
||||
function getoptions() {
|
||||
const arr = [
|
||||
getTransformChildren('education', '学历要求'),
|
||||
getTransformChildren('experience', '工作经验'),
|
||||
getTransformChildren('scale', '公司规模'),
|
||||
];
|
||||
if (area.value) {
|
||||
arr.push(getTransformChildren('area', '区域'));
|
||||
}
|
||||
filterOptions.value = arr;
|
||||
activeTab.value = 'education';
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
maskClick.value = false;
|
||||
confirmCallback.value = null;
|
||||
cancelCallback.value = null;
|
||||
changeCallback.value = null;
|
||||
Object.keys(selectedValues).forEach((key) => delete selectedValues[key]);
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
color: #000000;
|
||||
height: 80vh;
|
||||
}
|
||||
.popup-bottom {
|
||||
padding: 40rpx 28rpx 20rpx 28rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.btn-cancel {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #666d7f;
|
||||
line-height: 90rpx;
|
||||
width: 33%;
|
||||
min-width: 222rpx;
|
||||
height: 90rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
width: 67%;
|
||||
height: 90rpx;
|
||||
margin-left: 28rpx;
|
||||
line-height: 90rpx;
|
||||
background: #256bfa;
|
||||
min-width: 444rpx;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
}
|
||||
}
|
||||
.popup-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
height: calc(80vh - 100rpx - 150rpx);
|
||||
overflow: hidden;
|
||||
.picker-view {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
margin-top: 20rpx;
|
||||
.uni-picker-view-mask {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.item {
|
||||
line-height: 84rpx;
|
||||
height: 84rpx;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #cccccc;
|
||||
}
|
||||
.item-active {
|
||||
color: #333333;
|
||||
}
|
||||
.uni-picker-view-indicator:after {
|
||||
border-color: #e3e3e3;
|
||||
}
|
||||
.uni-picker-view-indicator:before {
|
||||
border-color: #e3e3e3;
|
||||
}
|
||||
}
|
||||
// .list {
|
||||
// .row {
|
||||
// font-weight: 400;
|
||||
// font-size: 32rpx;
|
||||
// color: #333333;
|
||||
// line-height: 84rpx;
|
||||
// text-align: center;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 40rpx 40rpx 10rpx 40rpx;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-cancel {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #666d7f;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.filter-nav {
|
||||
width: 200rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
.nav-item {
|
||||
height: 100rpx;
|
||||
line-height: 100rpx;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #666d7f;
|
||||
&.active {
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-content {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
.content-item {
|
||||
margin-top: 40rpx;
|
||||
.item-title {
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
.content-item:first-child {
|
||||
margin-top: 0rpx;
|
||||
}
|
||||
|
||||
.check-content {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
place-items: center;
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 20rpx 20rpx 0 0;
|
||||
text-align: center;
|
||||
background-color: #d9d9d9;
|
||||
width: 228rpx;
|
||||
height: 80rpx;
|
||||
background: #e8eaee;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
|
||||
.option-label {
|
||||
font-size: 28rpx;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.checkedstyle {
|
||||
width: 224rpx;
|
||||
height: 76rpx;
|
||||
background: rgba(37, 107, 250, 0.06);
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
border: 2rpx solid #256bfa;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
287
components/selectJobs/selectJobs.vue
Normal file
287
components/selectJobs/selectJobs.vue
Normal file
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<uni-popup
|
||||
ref="popup"
|
||||
type="bottom"
|
||||
borderRadius="10px 10px 0 0"
|
||||
background-color="#FFFFFF"
|
||||
:mask-click="maskClick"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<view class="btn-cancel" @click="cancel">取消</view>
|
||||
<view class="title">
|
||||
<text>{{ title }}</text>
|
||||
<text style="color: #256bfa">·{{ count }}</text>
|
||||
</view>
|
||||
<view class="btn-confirm" @click="confirm"> </view>
|
||||
</view>
|
||||
<view class="popup-list">
|
||||
<expected-station
|
||||
:search="false"
|
||||
@onChange="changeJobTitleId"
|
||||
:station="state.stations"
|
||||
:max="5"
|
||||
></expected-station>
|
||||
</view>
|
||||
<view class="popup-bottom">
|
||||
<view class="btn-cancel" @click="cleanup">清除</view>
|
||||
<view class="btn-confirm" @click="confirm">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, inject, nextTick, defineExpose, onMounted } from 'vue';
|
||||
const { $api, navTo, setCheckedNodes, cloneDeep } = inject('globalFunction');
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import { storeToRefs } from 'pinia';
|
||||
const { userInfo } = storeToRefs(useUserStore());
|
||||
const maskClick = ref(false);
|
||||
const title = ref('标题');
|
||||
const confirmCallback = ref(null);
|
||||
const cancelCallback = ref(null);
|
||||
const changeCallback = ref(null);
|
||||
const listData = ref([]);
|
||||
const selectedIndex = ref([0, 0, 0]);
|
||||
const rowLabel = ref('label');
|
||||
const rowKey = ref('value');
|
||||
const selectedItems = ref([]);
|
||||
const popup = ref(null);
|
||||
const count = ref(0);
|
||||
const JobsIdsValue = ref('');
|
||||
const JobsLabelValue = ref('');
|
||||
|
||||
const state = reactive({
|
||||
jobTitleId: '',
|
||||
stations: [],
|
||||
visible: false,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
serchforIt();
|
||||
});
|
||||
|
||||
// 统一处理二维数组格式
|
||||
const processedListData = computed(() => {
|
||||
return listData.value.map((column) => {
|
||||
if (!Array.isArray(column)) return [];
|
||||
return column.map((item) => {
|
||||
return typeof item === 'object' ? item : { [rowLabel.value]: item, [rowKey.value]: item };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const open = (newConfig = {}) => {
|
||||
const {
|
||||
title: configTitle,
|
||||
success,
|
||||
cancel,
|
||||
change,
|
||||
data,
|
||||
rowLabel: configRowLabel = 'label',
|
||||
rowKey: configRowKey = 'value',
|
||||
maskClick: configMaskClick = false,
|
||||
defaultIndex = [],
|
||||
} = newConfig;
|
||||
|
||||
reset();
|
||||
serchforIt();
|
||||
|
||||
if (configTitle) title.value = configTitle;
|
||||
if (typeof success === 'function') confirmCallback.value = success;
|
||||
if (typeof cancel === 'function') cancelCallback.value = cancel;
|
||||
if (typeof change === 'function') changeCallback.value = change;
|
||||
if (Array.isArray(data)) listData.value = data;
|
||||
|
||||
rowLabel.value = configRowLabel;
|
||||
rowKey.value = configRowKey;
|
||||
maskClick.value = configMaskClick;
|
||||
nextTick(() => {
|
||||
popup.value?.open();
|
||||
});
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
popup.value?.close();
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
handleClick(cancelCallback.value);
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
if (JobsIdsValue.value) {
|
||||
handleClick(confirmCallback.value);
|
||||
} else {
|
||||
$api.msg('请选择期望岗位');
|
||||
}
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
setCheckedNodes(state.stations, []);
|
||||
count.value = 0;
|
||||
reset();
|
||||
};
|
||||
|
||||
const changeJobTitleId = (e) => {
|
||||
const ids = e.ids.split(',').map((id) => Number(id));
|
||||
count.value = ids.length;
|
||||
JobsIdsValue.value = e.ids;
|
||||
JobsLabelValue.value = e.labels;
|
||||
};
|
||||
const handleClick = async (callback) => {
|
||||
if (typeof callback !== 'function') {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await callback(JobsIdsValue.value, JobsLabelValue.value);
|
||||
if (result !== false) close();
|
||||
} catch (error) {
|
||||
console.error('confirmCallback 执行出错:', error);
|
||||
}
|
||||
};
|
||||
function serchforIt() {
|
||||
if (state.stations.length) {
|
||||
const ids = userInfo.value.jobTitleId.split(',').map((id) => Number(id));
|
||||
count.value = ids.length;
|
||||
state.jobTitleId = userInfo.value.jobTitleId;
|
||||
setCheckedNodes(state.stations, ids);
|
||||
state.visible = true;
|
||||
return;
|
||||
}
|
||||
$api.createRequest('/app/common/jobTitle/treeselect', {}, 'GET').then((resData) => {
|
||||
if (userInfo.value.jobTitleId) {
|
||||
const ids = userInfo.value.jobTitleId.split(',').map((id) => Number(id));
|
||||
count.value = ids.length;
|
||||
setCheckedNodes(resData.data, ids);
|
||||
}
|
||||
state.jobTitleId = userInfo.value.jobTitleId;
|
||||
state.stations = resData.data;
|
||||
state.visible = true;
|
||||
});
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
maskClick.value = false;
|
||||
confirmCallback.value = null;
|
||||
cancelCallback.value = null;
|
||||
changeCallback.value = null;
|
||||
listData.value = [];
|
||||
selectedIndex.value = [0, 0, 0];
|
||||
rowLabel.value = 'label';
|
||||
rowKey.value = 'value';
|
||||
selectedItems.value = [];
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
color: #000000;
|
||||
height: 80vh;
|
||||
}
|
||||
.popup-bottom {
|
||||
padding: 40rpx 28rpx 20rpx 28rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.btn-cancel {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #666d7f;
|
||||
line-height: 90rpx;
|
||||
width: 33%;
|
||||
min-width: 222rpx;
|
||||
height: 90rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
width: 67%;
|
||||
height: 90rpx;
|
||||
margin-left: 28rpx;
|
||||
line-height: 90rpx;
|
||||
background: #256bfa;
|
||||
min-width: 444rpx;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
}
|
||||
}
|
||||
.popup-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
height: calc(80vh - 100rpx - 150rpx);
|
||||
overflow: hidden;
|
||||
.picker-view {
|
||||
width: 100%;
|
||||
height: 500rpx;
|
||||
margin-top: 20rpx;
|
||||
.uni-picker-view-mask {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.item {
|
||||
line-height: 84rpx;
|
||||
height: 84rpx;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #cccccc;
|
||||
}
|
||||
.item-active {
|
||||
color: #333333;
|
||||
}
|
||||
.uni-picker-view-indicator:after {
|
||||
border-color: #e3e3e3;
|
||||
}
|
||||
.uni-picker-view-indicator:before {
|
||||
border-color: #e3e3e3;
|
||||
}
|
||||
}
|
||||
// .list {
|
||||
// .row {
|
||||
// font-weight: 400;
|
||||
// font-size: 32rpx;
|
||||
// color: #333333;
|
||||
// line-height: 84rpx;
|
||||
// text-align: center;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 40rpx 40rpx 10rpx 40rpx;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-cancel {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #666d7f;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
</style>
|
233
components/selectPopup/selectPopup.vue
Normal file
233
components/selectPopup/selectPopup.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<uni-popup
|
||||
ref="popup"
|
||||
type="bottom"
|
||||
borderRadius="10px 10px 0 0"
|
||||
background-color="#FFFFFF"
|
||||
:mask-click="maskClick"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<view class="btn-cancel" @click="cancel">取消</view>
|
||||
<view class="title">{{ title }}</view>
|
||||
<view class="btn-confirm" @click="confirm">确认</view>
|
||||
</view>
|
||||
<view class="popup-list">
|
||||
<picker-view
|
||||
indicator-style="height: 84rpx;"
|
||||
:value="selectedIndex"
|
||||
@change="bindChange"
|
||||
class="picker-view"
|
||||
>
|
||||
<template v-for="(list, lsIndex) in processedListData" :key="lsIndex">
|
||||
<picker-view-column>
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
class="item"
|
||||
:class="{ 'item-active': selectedIndex[lsIndex] === index }"
|
||||
>
|
||||
<text>{{ getLabel(item) }}</text>
|
||||
<text>{{ unit }}</text>
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</template>
|
||||
</picker-view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'selectPopup',
|
||||
data() {
|
||||
return {
|
||||
maskClick: false,
|
||||
title: '标题',
|
||||
confirmCallback: null,
|
||||
cancelCallback: null,
|
||||
changeCallback: null,
|
||||
listData: [],
|
||||
selectedIndex: [0, 0, 0],
|
||||
rowLabel: 'label',
|
||||
rowKey: 'value',
|
||||
selectedItems: [],
|
||||
unit: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 统一处理二维数组格式
|
||||
processedListData() {
|
||||
return this.listData.map((column) => {
|
||||
if (!Array.isArray(column)) return [];
|
||||
return column.map((item) => {
|
||||
return typeof item === 'object' ? item : { [this.rowLabel]: item, [this.rowKey]: item };
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open(newConfig = {}) {
|
||||
const {
|
||||
title,
|
||||
success,
|
||||
cancel,
|
||||
change,
|
||||
data,
|
||||
unit = '',
|
||||
rowLabel = 'label',
|
||||
rowKey = 'value',
|
||||
maskClick = false,
|
||||
defaultIndex = [],
|
||||
} = newConfig;
|
||||
this.reset();
|
||||
if (title) this.title = title;
|
||||
if (typeof success === 'function') this.confirmCallback = success;
|
||||
if (typeof cancel === 'function') this.cancelCallback = cancel;
|
||||
if (typeof change === 'function') this.changeCallback = change;
|
||||
if (Array.isArray(data)) this.listData = data;
|
||||
|
||||
this.rowLabel = rowLabel;
|
||||
this.rowKey = rowKey;
|
||||
this.maskClick = maskClick;
|
||||
this.unit = unit;
|
||||
|
||||
this.selectedIndex =
|
||||
defaultIndex.length === this.listData.length ? defaultIndex : new Array(this.listData.length).fill(0);
|
||||
this.selectedItems = this.selectedIndex.map((val, index) => this.processedListData[index][val]);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.popup.open();
|
||||
});
|
||||
},
|
||||
close() {
|
||||
this.$refs.popup.close();
|
||||
},
|
||||
bindChange(e) {
|
||||
this.selectedIndex = e.detail.value;
|
||||
this.selectedItems = this.selectedIndex.map((val, index) => this.processedListData[index][val]);
|
||||
this.changeCallback && this.changeCallback(e, this.selectedIndex, this.selectedItems);
|
||||
},
|
||||
cancel() {
|
||||
this.clickCallback(this.cancelCallback);
|
||||
},
|
||||
confirm() {
|
||||
this.clickCallback(this.confirmCallback);
|
||||
},
|
||||
getLabel(item) {
|
||||
return item?.[this.rowLabel] ?? '';
|
||||
},
|
||||
setColunm(index, list) {
|
||||
if (index > this.listData.length) {
|
||||
return console.warn('最长' + this.listData.length);
|
||||
}
|
||||
if (!list.length) {
|
||||
return console.warn(list + '不能为空');
|
||||
}
|
||||
this.listData[index] = list;
|
||||
this.selectedIndex[index] = 0;
|
||||
this.selectedItems = this.selectedIndex.map((val, index) => this.processedListData[index][val]);
|
||||
},
|
||||
async clickCallback(callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
this.$refs.popup.close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await callback(this.selectedIndex, this.selectedItems); // 无论是 async 还是返回 Promise 的函数都可以 await
|
||||
if (result !== false) {
|
||||
this.$refs.popup.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('confirmCallback 执行出错:', error);
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.maskClick = false;
|
||||
this.confirmCallback = null;
|
||||
this.cancelCallback = null;
|
||||
this.changeCallback = null;
|
||||
this.listData = [];
|
||||
this.selectedIndex = [0, 0, 0];
|
||||
this.rowLabel = 'label';
|
||||
this.rowKey = 'value';
|
||||
this.selectedItems = [];
|
||||
this.unit = '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
color: #000000;
|
||||
height: 50vh;
|
||||
}
|
||||
.popup-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.picker-view {
|
||||
width: 100%;
|
||||
height: calc(50vh - 100rpx);
|
||||
margin-top: 20rpx;
|
||||
.uni-picker-view-mask {
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.item {
|
||||
line-height: 84rpx;
|
||||
height: 84rpx;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #cccccc;
|
||||
}
|
||||
.item-active {
|
||||
color: #333333;
|
||||
}
|
||||
.uni-picker-view-indicator:after {
|
||||
border-color: #e3e3e3;
|
||||
}
|
||||
.uni-picker-view-indicator:before {
|
||||
border-color: #e3e3e3;
|
||||
}
|
||||
}
|
||||
// .list {
|
||||
// .row {
|
||||
// font-weight: 400;
|
||||
// font-size: 32rpx;
|
||||
// color: #333333;
|
||||
// line-height: 84rpx;
|
||||
// text-align: center;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 40rpx 40rpx 10rpx 40rpx;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-cancel {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #666d7f;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
</style>
|
21
components/selectPopup/selectPopupPlugin.js
Normal file
21
components/selectPopup/selectPopupPlugin.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// plugins/selectPopup.js
|
||||
import {
|
||||
createApp
|
||||
} from 'vue';
|
||||
import SelectPopup from './selectPopup.vue';
|
||||
|
||||
export default {
|
||||
install(app) {
|
||||
const popupApp = createApp(SelectPopup);
|
||||
const popupInstance = popupApp.mount(document.createElement('div'));
|
||||
document.body.appendChild(popupInstance.$el);
|
||||
|
||||
// 提供 open 方法
|
||||
const openPopup = (config) => {
|
||||
popupInstance.open(config);
|
||||
};
|
||||
|
||||
// 提供给所有组件使用
|
||||
app.provide('openSelectPopup', openPopup);
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user