flat: 暂存

This commit is contained in:
史典卓
2025-05-13 11:10:38 +08:00
parent 582e432e6a
commit fd74b7d4df
109 changed files with 8644 additions and 5205 deletions

BIN
components/.DS_Store vendored

Binary file not shown.

View 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>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -3,6 +3,7 @@
</template>
<script setup>
// 匹配度
import { inject, computed } from 'vue';
const { job } = defineProps(['job']);
const { similarityJobs, throttle } = inject('globalFunction');

View File

@@ -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>

View 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>

View 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>

View 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">&nbsp;&nbsp;</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>

View 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">&nbsp;&nbsp;</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>

View 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>

View 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);
}
};