合并 智慧就业第一版
This commit is contained in:
@@ -83,7 +83,7 @@
|
||||
<empty v-else pdTop="200"></empty>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<Tabbar :currentpage="1"></Tabbar>
|
||||
<!-- 统一使用系统tabBar -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -91,7 +91,6 @@
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import Tabbar from '@/components/tabbar/midell-box.vue';
|
||||
import useLocationStore from '@/stores/useLocationStore';
|
||||
import { storeToRefs } from 'pinia';
|
||||
const { longitudeVal, latitudeVal } = storeToRefs(useLocationStore());
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
</view>
|
||||
<!-- 自定义tabbar -->
|
||||
<view class="chatmain-footer" v-show="!isDrawerOpen">
|
||||
<Tabbar :currentpage="2"></Tabbar>
|
||||
<!-- 统一使用系统tabBar -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -74,7 +74,6 @@
|
||||
import { ref, inject, nextTick, computed } from 'vue';
|
||||
const { $api, navTo, insertSortData, config } = inject('globalFunction');
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app';
|
||||
import Tabbar from '@/components/tabbar/midell-box.vue';
|
||||
import useChatGroupDBStore from '@/stores/userChatGroupStore';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import aiPaging from './components/ai-paging.vue';
|
||||
@@ -102,7 +101,7 @@ onLoad(() => {
|
||||
|
||||
onShow(() => {
|
||||
nextTick(() => {
|
||||
paging.value?.colseFile();
|
||||
paging.value?.closeFile();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -249,14 +249,13 @@ import {
|
||||
ref,
|
||||
inject,
|
||||
nextTick,
|
||||
defineProps,
|
||||
defineEmits,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
toRaw,
|
||||
reactive,
|
||||
computed,
|
||||
watch,
|
||||
getCurrentInstance,
|
||||
} from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
// import config from '@/config.js';
|
||||
@@ -289,6 +288,9 @@ const {
|
||||
|
||||
const { speak, pause, resume, isSpeaking, isPaused, cancelAudio } = useTTSPlayer(config.speechSynthesis);
|
||||
|
||||
// 获取组件实例(用于小程序 SelectorQuery)
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
// state
|
||||
const queries = ref([]);
|
||||
const guessList = ref([]);
|
||||
@@ -338,18 +340,46 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
const requestMicPermission = async () => {
|
||||
// #ifdef H5
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
console.log('✅ 麦克风权限已授权');
|
||||
if (typeof navigator !== 'undefined' && navigator.mediaDevices) {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
console.log('✅ 麦克风权限已授权');
|
||||
|
||||
// 立刻停止所有音轨,释放麦克风
|
||||
stream.getTracks().forEach((track) => track.stop());
|
||||
// 立刻停止所有音轨,释放麦克风
|
||||
stream.getTracks().forEach((track) => track.stop());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
} else {
|
||||
console.warn('❌ 当前环境不支持麦克风');
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('❌ 用户拒绝麦克风权限或不支持:', err);
|
||||
return false;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
try {
|
||||
// 微信小程序使用 uni.authorize 请求权限
|
||||
const res = await uni.authorize({
|
||||
scope: 'scope.record'
|
||||
});
|
||||
console.log('✅ 麦克风权限已授权');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.warn('❌ 用户拒绝麦克风权限:', err);
|
||||
// 用户拒绝授权,但不影响其他功能
|
||||
return false;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef H5 || MP-WEIXIN
|
||||
// 其他平台暂不支持
|
||||
console.warn('❌ 当前平台不支持麦克风权限检测');
|
||||
return false;
|
||||
// #endif
|
||||
};
|
||||
|
||||
function showControll(index) {
|
||||
@@ -475,10 +505,20 @@ const delfile = (file) => {
|
||||
const scrollToBottom = throttle(function () {
|
||||
nextTick(() => {
|
||||
try {
|
||||
// #ifdef MP-WEIXIN
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
const query = uni.createSelectorQuery();
|
||||
// #endif
|
||||
|
||||
query.select('.scrollView').boundingClientRect();
|
||||
query.select('.list-content').boundingClientRect();
|
||||
query.exec((res) => {
|
||||
if (!res || !res[0] || !res[1]) {
|
||||
console.warn('scrollToBottom: 元素未找到或尚未渲染');
|
||||
return;
|
||||
}
|
||||
const scrollViewHeight = res[0].height;
|
||||
const scrollContentHeight = res[1].height;
|
||||
if (scrollContentHeight > scrollViewHeight) {
|
||||
@@ -647,7 +687,7 @@ function changeShowFile() {
|
||||
showfile.value = !showfile.value;
|
||||
}
|
||||
|
||||
function colseFile() {
|
||||
function closeFile() {
|
||||
showfile.value = false;
|
||||
}
|
||||
|
||||
@@ -773,7 +813,7 @@ function getRandomJobQueries(queries, count = 2) {
|
||||
return shuffled.slice(0, count); // 取前 count 条
|
||||
}
|
||||
|
||||
defineExpose({ scrollToBottom, closeGuess, colseFile, changeQueries, handleTouchCancel });
|
||||
defineExpose({ scrollToBottom, closeGuess, closeFile, changeQueries, handleTouchCancel });
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, inject, defineEmits } from 'vue';
|
||||
import { ref, inject } from 'vue';
|
||||
const emit = defineEmits(['onSend']);
|
||||
const { $api } = inject('globalFunction');
|
||||
const popup = ref(null);
|
||||
|
||||
900
pages/complete-info/company-info.vue
Normal file
900
pages/complete-info/company-info.vue
Normal file
@@ -0,0 +1,900 @@
|
||||
<template>
|
||||
<AppLayout title="企业信息">
|
||||
<view class="company-info-container">
|
||||
<!-- 头部信息 -->
|
||||
<view class="header-info">
|
||||
<view class="progress-text">企业信息完善度</view>
|
||||
<view class="progress-value">{{ completionPercentage }}%</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<view class="form-content">
|
||||
<!-- 企业名称 -->
|
||||
<view class="form-item">
|
||||
<view class="label">企业名称</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="formData.companyName"
|
||||
placeholder="请输入企业名称"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 统一社会信用代码 -->
|
||||
<view class="form-item">
|
||||
<view class="label">统一社会信用代码</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="formData.socialCreditCode"
|
||||
placeholder="请输入统一社会信用代码"
|
||||
maxlength="18"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 企业注册地点 -->
|
||||
<view class="form-item clickable" @click="selectLocation">
|
||||
<view class="label">企业注册地点</view>
|
||||
<view class="input-content">
|
||||
<text class="input-text" :class="{ placeholder: !formData.registeredAddress }">
|
||||
{{ formData.registeredAddress || '请选择注册地点' }}
|
||||
</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 企业信息介绍 -->
|
||||
<view class="form-item clickable" @click="editCompanyIntro">
|
||||
<view class="label">企业信息介绍</view>
|
||||
<view class="input-content">
|
||||
<text class="input-text intro-text" :class="{ placeholder: !formData.companyIntro }">
|
||||
{{ formData.companyIntro || '请输入企业介绍' }}
|
||||
</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 企业法人姓名 -->
|
||||
<view class="form-item">
|
||||
<view class="label">企业法人姓名</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="formData.legalPersonName"
|
||||
placeholder="请输入法人姓名"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 企业类型 -->
|
||||
<view class="form-item clickable" @click="selectEnterpriseType">
|
||||
<view class="label">企业类型</view>
|
||||
<view class="input-content">
|
||||
<input class="input-con" v-model="formData.natureText" disabled placeholder="请选择企业类型" />
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 是否是就业见习基地 -->
|
||||
<view class="form-item clickable" @click="selectEmploymentBase">
|
||||
<view class="label">是否是就业见习基地</view>
|
||||
<view class="input-content">
|
||||
<text class="input-text" :class="{ placeholder: formData.enterpriseType === null }">
|
||||
{{ formData.enterpriseType === null ? '请选择' : (formData.enterpriseType ? '是' : '否') }}
|
||||
</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 法人身份证号 -->
|
||||
<view class="form-item">
|
||||
<view class="label">法人身份证号</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="formData.legalIdCard"
|
||||
placeholder="请输入法人身份证号"
|
||||
maxlength="18"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 法人联系方式 -->
|
||||
<view class="form-item">
|
||||
<view class="label">法人联系方式</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="formData.legalPhone"
|
||||
placeholder="请输入法人联系方式"
|
||||
maxlength="11"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 本地重点发展产业 -->
|
||||
<view class="form-item clickable" @click="selectIndustry">
|
||||
<view class="label">本地重点发展产业</view>
|
||||
<view class="input-content">
|
||||
<text class="input-text" :class="{ placeholder: !formData.industryType }">
|
||||
{{ formData.industryType || '请选择产业类型' }}
|
||||
</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 是否是本地企业 -->
|
||||
<view class="form-item clickable" @click="selectLocalCompany">
|
||||
<view class="label">是否是本地重点发展产业</view>
|
||||
<view class="input-content">
|
||||
<text class="input-text" :class="{ placeholder: formData.isLocalCompany === null }">
|
||||
{{ formData.isLocalCompany === null ? '请选择' : (formData.isLocalCompany ? '是' : '否') }}
|
||||
</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系人信息列表 -->
|
||||
<view class="contact-section">
|
||||
<view class="section-title">联系人信息</view>
|
||||
|
||||
<!-- 每个联系人作为一个分组 -->
|
||||
<view
|
||||
class="contact-group"
|
||||
v-for="(contact, index) in formData.companyContactList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="group-header">
|
||||
<text>联系人{{ index + 1 }}</text>
|
||||
<view
|
||||
class="delete-btn"
|
||||
@click="deleteContact(index)"
|
||||
v-if="formData.companyContactList.length > 1"
|
||||
>
|
||||
<uni-icons type="trash" size="16" color="#ff4757"></uni-icons>
|
||||
<text class="delete-text">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">联系人姓名</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="contact.contactPerson"
|
||||
placeholder="请输入联系人姓名"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="label">联系人电话</view>
|
||||
<input
|
||||
class="input-field"
|
||||
v-model="contact.contactPersonPhone"
|
||||
placeholder="请输入联系人电话"
|
||||
@input="updateCompletion"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 添加联系人按钮 -->
|
||||
<view class="add-contact-btn" @click="addContact" v-if="formData.companyContactList.length < 3">
|
||||
<uni-icons type="plus" size="20" color="#256BFA"></uni-icons>
|
||||
<text>添加联系人</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<view class="bottom-actions">
|
||||
<button class="cancel-btn" @click="cancel">取消</button>
|
||||
<button class="confirm-btn" @click="confirm">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 弹窗组件 -->
|
||||
<uni-popup ref="popup" type="center">
|
||||
<view class="popup-content">
|
||||
<view class="popup-title">{{ popupTitle }}</view>
|
||||
<input
|
||||
v-if="popupType === 'text'"
|
||||
class="popup-input"
|
||||
v-model="popupValue"
|
||||
:placeholder="popupPlaceholder"
|
||||
/>
|
||||
<textarea
|
||||
v-if="popupType === 'textarea'"
|
||||
class="popup-textarea"
|
||||
v-model="popupValue"
|
||||
:placeholder="popupPlaceholder"
|
||||
maxlength="500"
|
||||
></textarea>
|
||||
<view class="popup-actions">
|
||||
<button class="popup-cancel" @click="closePopup">取消</button>
|
||||
<button class="popup-confirm" @click="confirmPopup">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 地址选择器 -->
|
||||
<area-cascade-picker ref="areaPicker"></area-cascade-picker>
|
||||
|
||||
<!-- 滚动选择器 -->
|
||||
<SelectPopup ref="selectPopupRef"></SelectPopup>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, inject } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import AreaCascadePicker from '@/components/area-cascade-picker/area-cascade-picker.vue'
|
||||
import SelectPopup from '@/components/selectPopup/selectPopup.vue'
|
||||
import useDictStore from '@/stores/useDictStore'
|
||||
|
||||
const { $api } = inject('globalFunction')
|
||||
const dictStore = useDictStore()
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
companyName: '',
|
||||
socialCreditCode: '',
|
||||
registeredAddress: '',
|
||||
registeredAddressName: '',
|
||||
longitude: null,
|
||||
latitude: null,
|
||||
companyIntro: '',
|
||||
legalPersonName: '',
|
||||
nature: '', // 企业类型
|
||||
natureText: '', // 企业类型显示文本
|
||||
enterpriseType: null, // 是否是就业见习基地 (true/false/null)
|
||||
legalIdCard: '', // 法人身份证号
|
||||
legalPhone: '', // 法人联系方式
|
||||
industryType: '', // 是否是本地重点发展产业
|
||||
isLocalCompany: null, // 是否是本地企业 (true/false/null)
|
||||
companyContactList: [
|
||||
{ contactPerson: '', contactPersonPhone: '' }
|
||||
]
|
||||
})
|
||||
|
||||
// 弹窗相关
|
||||
const popup = ref(null)
|
||||
const popupTitle = ref('')
|
||||
const popupType = ref('text') // text | textarea
|
||||
const popupValue = ref('')
|
||||
const popupPlaceholder = ref('')
|
||||
const currentEditField = ref('')
|
||||
|
||||
// 地址选择器引用
|
||||
const areaPicker = ref(null)
|
||||
|
||||
// 滚动选择器引用
|
||||
const selectPopupRef = ref(null)
|
||||
|
||||
// 创建本地的 openSelectPopup 函数
|
||||
const openSelectPopup = (config) => {
|
||||
if (selectPopupRef.value) {
|
||||
selectPopupRef.value.open(config);
|
||||
}
|
||||
}
|
||||
|
||||
// 产业类型选项数据
|
||||
const industryOptions = [
|
||||
'人工智能',
|
||||
'生物医药',
|
||||
'新能源',
|
||||
'高端装备制造',
|
||||
'其他'
|
||||
]
|
||||
|
||||
// 备用企业类型选项(当字典数据加载失败时使用)
|
||||
const fallbackEnterpriseTypes = [
|
||||
{ label: '有限责任公司', value: '1' },
|
||||
{ label: '股份有限公司', value: '2' },
|
||||
{ label: '个人独资企业', value: '3' },
|
||||
{ label: '合伙企业', value: '4' },
|
||||
{ label: '外商投资企业', value: '5' },
|
||||
{ label: '其他', value: '6' }
|
||||
]
|
||||
|
||||
// 企业类型选项数据从字典中获取
|
||||
const enterpriseTypeOptions = computed(() => {
|
||||
const natureData = dictStore.state?.nature || []
|
||||
console.log('企业类型选项数据:', natureData)
|
||||
return natureData
|
||||
})
|
||||
|
||||
// 完成度计算
|
||||
const completionPercentage = computed(() => {
|
||||
const fields = [
|
||||
formData.companyName,
|
||||
formData.socialCreditCode,
|
||||
formData.registeredAddress,
|
||||
formData.companyIntro,
|
||||
formData.legalPersonName,
|
||||
formData.nature,
|
||||
formData.enterpriseType !== null ? 'filled' : '',
|
||||
formData.legalIdCard,
|
||||
formData.legalPhone,
|
||||
formData.industryType,
|
||||
formData.isLocalCompany !== null ? 'filled' : ''
|
||||
]
|
||||
|
||||
// 检查联系人信息
|
||||
const hasContact = formData.companyContactList.some(contact => contact.contactPerson && contact.contactPersonPhone)
|
||||
|
||||
const filledFields = fields.filter(field => field && field.trim()).length + (hasContact ? 1 : 0)
|
||||
const totalFields = fields.length + 1
|
||||
|
||||
return Math.round((filledFields / totalFields) * 100)
|
||||
})
|
||||
|
||||
// 更新完成度
|
||||
const updateCompletion = () => {
|
||||
// 完成度会自动通过computed更新
|
||||
}
|
||||
|
||||
// 选择注册地点
|
||||
const selectLocation = () => {
|
||||
// 打开五级联动地址选择器
|
||||
areaPicker.value?.open({
|
||||
title: '选择企业注册地点',
|
||||
maskClick: true,
|
||||
success: (addressData) => {
|
||||
// addressData 包含: address, province, city, district, street, community
|
||||
formData.registeredAddress = addressData.address
|
||||
formData.registeredAddressName = addressData.address
|
||||
|
||||
// 可以保存详细的地址信息
|
||||
formData.provinceCode = addressData.province?.code
|
||||
formData.provinceName = addressData.province?.name
|
||||
formData.cityCode = addressData.city?.code
|
||||
formData.cityName = addressData.city?.name
|
||||
formData.districtCode = addressData.district?.code
|
||||
formData.districtName = addressData.district?.name
|
||||
formData.streetCode = addressData.street?.code
|
||||
formData.streetName = addressData.street?.name
|
||||
formData.communityCode = addressData.community?.code
|
||||
formData.communityName = addressData.community?.name
|
||||
|
||||
updateCompletion()
|
||||
|
||||
$api.msg('地址选择成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理地图选择返回的地址
|
||||
const handleLocationSelected = (locationData) => {
|
||||
formData.registeredAddress = locationData.address
|
||||
formData.registeredAddressName = locationData.name
|
||||
formData.longitude = locationData.longitude
|
||||
formData.latitude = locationData.latitude
|
||||
updateCompletion()
|
||||
}
|
||||
|
||||
// 编辑企业介绍
|
||||
const editCompanyIntro = () => {
|
||||
currentEditField.value = 'companyIntro'
|
||||
popupTitle.value = '企业信息介绍'
|
||||
popupType.value = 'textarea'
|
||||
popupValue.value = formData.companyIntro
|
||||
popupPlaceholder.value = '请输入企业介绍'
|
||||
popup.value?.open()
|
||||
}
|
||||
|
||||
|
||||
// 选择产业类型
|
||||
const selectIndustry = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: industryOptions,
|
||||
success: (res) => {
|
||||
formData.industryType = industryOptions[res.tapIndex]
|
||||
updateCompletion()
|
||||
$api.msg('产业类型选择成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选择企业类型
|
||||
const selectEnterpriseType = () => {
|
||||
console.log('点击企业类型,当前数据:', enterpriseTypeOptions.value)
|
||||
console.log('字典store状态:', dictStore.state.nature)
|
||||
|
||||
// 获取企业类型选项,优先使用字典数据,失败时使用备用数据
|
||||
let options = enterpriseTypeOptions.value
|
||||
|
||||
if (!options || !options.length) {
|
||||
console.log('企业类型数据为空,尝试重新加载')
|
||||
// 尝试重新加载字典数据
|
||||
dictStore.getDictData().then(() => {
|
||||
if (dictStore.state.nature && dictStore.state.nature.length > 0) {
|
||||
selectEnterpriseType() // 递归调用
|
||||
} else {
|
||||
// 使用备用数据
|
||||
console.log('使用备用企业类型数据')
|
||||
options = fallbackEnterpriseTypes
|
||||
showEnterpriseTypeSelector(options)
|
||||
}
|
||||
}).catch(() => {
|
||||
// 使用备用数据
|
||||
console.log('字典加载失败,使用备用企业类型数据')
|
||||
options = fallbackEnterpriseTypes
|
||||
showEnterpriseTypeSelector(options)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
showEnterpriseTypeSelector(options)
|
||||
}
|
||||
|
||||
// 显示企业类型选择器
|
||||
const showEnterpriseTypeSelector = (options) => {
|
||||
console.log('企业类型选项列表:', options)
|
||||
|
||||
openSelectPopup({
|
||||
title: '企业类型',
|
||||
maskClick: true,
|
||||
data: [options],
|
||||
success: (_, [value]) => {
|
||||
console.log('选择的企业类型:', value)
|
||||
formData.nature = value.value
|
||||
formData.natureText = value.label
|
||||
updateCompletion()
|
||||
$api.msg('企业类型选择成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选择是否是就业见习基地
|
||||
const selectEmploymentBase = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['是', '否'],
|
||||
success: (res) => {
|
||||
formData.enterpriseType = res.tapIndex === 0
|
||||
updateCompletion()
|
||||
$api.msg('选择成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选择是否是本地企业
|
||||
const selectLocalCompany = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['是', '否'],
|
||||
success: (res) => {
|
||||
formData.isLocalCompany = res.tapIndex === 0
|
||||
updateCompletion()
|
||||
$api.msg('选择成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 添加联系人
|
||||
const addContact = () => {
|
||||
if (formData.companyContactList.length < 3) {
|
||||
formData.companyContactList.push({ contactPerson: '', contactPersonPhone: '' })
|
||||
}
|
||||
}
|
||||
|
||||
// 删除联系人
|
||||
const deleteContact = (index) => {
|
||||
if (formData.companyContactList.length <= 1) {
|
||||
$api.msg('至少需要保留一个联系人')
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '确认删除',
|
||||
content: '确定要删除这个联系人吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
formData.companyContactList.splice(index, 1)
|
||||
updateCompletion()
|
||||
$api.msg('联系人已删除')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
const closePopup = () => {
|
||||
popup.value?.close()
|
||||
popupValue.value = ''
|
||||
}
|
||||
|
||||
// 确认弹窗
|
||||
const confirmPopup = () => {
|
||||
const field = currentEditField.value
|
||||
|
||||
if (field === 'companyIntro') {
|
||||
formData.companyIntro = popupValue.value
|
||||
}
|
||||
|
||||
updateCompletion()
|
||||
closePopup()
|
||||
}
|
||||
|
||||
// 取消
|
||||
const cancel = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 确认提交
|
||||
const confirm = () => {
|
||||
// 验证必填字段
|
||||
if (!formData.companyName.trim()) {
|
||||
$api.msg('请输入企业名称')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.socialCreditCode.trim()) {
|
||||
$api.msg('请输入统一社会信用代码')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.registeredAddress.trim()) {
|
||||
$api.msg('请选择注册地点')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.companyIntro.trim()) {
|
||||
$api.msg('请输入企业介绍')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.legalPersonName.trim()) {
|
||||
$api.msg('请输入法人姓名')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.nature.trim()) {
|
||||
$api.msg('请选择企业类型')
|
||||
return
|
||||
}
|
||||
|
||||
if (formData.enterpriseType === null) {
|
||||
$api.msg('请选择是否是就业见习基地')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.legalIdCard.trim()) {
|
||||
$api.msg('请输入法人身份证号')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.legalPhone.trim()) {
|
||||
$api.msg('请输入法人联系方式')
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.industryType.trim()) {
|
||||
$api.msg('请选择产业类型')
|
||||
return
|
||||
}
|
||||
|
||||
if (formData.isLocalCompany === null) {
|
||||
$api.msg('请选择是否是本地企业')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证身份证号格式
|
||||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
|
||||
if (!idCardRegex.test(formData.legalIdCard)) {
|
||||
$api.msg('请输入正确的身份证号')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证法人电话格式
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
if (!phoneRegex.test(formData.legalPhone)) {
|
||||
$api.msg('请输入正确的法人联系方式')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证至少有一个联系人
|
||||
const hasValidContact = formData.companyContactList.some(contact =>
|
||||
contact.contactPerson.trim() && contact.contactPersonPhone.trim()
|
||||
)
|
||||
|
||||
if (!hasValidContact) {
|
||||
$api.msg('请至少添加一个联系人信息')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证联系人电话格式
|
||||
for (let contact of formData.companyContactList) {
|
||||
if (contact.contactPerson.trim() && contact.contactPersonPhone.trim()) {
|
||||
if (!phoneRegex.test(contact.contactPersonPhone)) {
|
||||
$api.msg('请输入正确的手机号码')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
uni.showLoading({ title: '保存中...' })
|
||||
|
||||
// 构建提交数据,按照要求的字段映射
|
||||
const companyData = {
|
||||
name: formData.companyName,
|
||||
code: formData.socialCreditCode,
|
||||
registeredAddress: formData.registeredAddress,
|
||||
description: formData.companyIntro,
|
||||
legalPerson: formData.legalPersonName,
|
||||
nature: formData.nature,
|
||||
enterpriseType: formData.enterpriseType,
|
||||
legalIdCard: formData.legalIdCard,
|
||||
legalPhone: formData.legalPhone,
|
||||
industryType: formData.industryType,
|
||||
isLocalCompany: formData.isLocalCompany,
|
||||
companyContactList: formData.companyContactList.filter(contact => contact.contactPerson.trim() && contact.contactPersonPhone.trim())
|
||||
}
|
||||
|
||||
// 调用新的接口地址,数据格式为company数组
|
||||
const submitData = {
|
||||
company: companyData
|
||||
}
|
||||
|
||||
$api.createRequest('/app/user/registerUser', submitData, 'post')
|
||||
.then((resData) => {
|
||||
uni.hideLoading()
|
||||
$api.msg('企业信息保存成功')
|
||||
|
||||
// 跳转到首页或企业相关页面
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
uni.hideLoading()
|
||||
$api.msg(err.msg || '保存失败,请重试')
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
console.log('企业信息补全页面开始加载')
|
||||
try {
|
||||
// 初始化字典数据
|
||||
await dictStore.getDictData()
|
||||
console.log('字典数据加载完成:', {
|
||||
nature: dictStore.state.nature,
|
||||
complete: dictStore.complete
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('字典数据加载失败:', error)
|
||||
$api.msg('数据加载失败,请重试')
|
||||
}
|
||||
console.log('企业信息补全页面加载完成')
|
||||
})
|
||||
|
||||
// 暴露方法给其他页面调用
|
||||
defineExpose({
|
||||
handleLocationSelected
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.company-info-container
|
||||
min-height: 100vh
|
||||
background: #f5f5f5
|
||||
padding-bottom: 120rpx
|
||||
|
||||
.header-info
|
||||
background: #fff
|
||||
padding: 40rpx 32rpx
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
margin-bottom: 20rpx
|
||||
|
||||
.progress-text
|
||||
font-size: 32rpx
|
||||
color: #000000
|
||||
font-weight: 500
|
||||
|
||||
.progress-value
|
||||
font-size: 32rpx
|
||||
color: #256BFA
|
||||
font-weight: 600
|
||||
|
||||
.form-content
|
||||
background: #fff
|
||||
margin-bottom: 20rpx
|
||||
|
||||
.form-item
|
||||
padding: 32rpx
|
||||
border-bottom: 1rpx solid #f0f0f0
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
|
||||
&:last-child
|
||||
border-bottom: none
|
||||
|
||||
&.clickable
|
||||
cursor: pointer
|
||||
|
||||
.label
|
||||
font-size: 28rpx
|
||||
color: #000000
|
||||
min-width: 200rpx
|
||||
|
||||
.input-field
|
||||
flex: 1
|
||||
font-size: 28rpx
|
||||
color: #333
|
||||
text-align: right
|
||||
|
||||
&::placeholder
|
||||
color: #999
|
||||
font-size: 26rpx
|
||||
|
||||
.input-con
|
||||
flex: 1
|
||||
font-size: 28rpx
|
||||
color: #333
|
||||
text-align: right
|
||||
background: transparent
|
||||
border: none
|
||||
outline: none
|
||||
|
||||
&::placeholder
|
||||
color: #999
|
||||
font-size: 26rpx
|
||||
|
||||
.input-content
|
||||
flex: 1
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
|
||||
.input-text
|
||||
font-size: 28rpx
|
||||
color: #333
|
||||
flex: 1
|
||||
text-align: right
|
||||
|
||||
&.placeholder
|
||||
color: #999
|
||||
font-size: 26rpx
|
||||
|
||||
&.intro-text
|
||||
max-height: 80rpx
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
white-space: nowrap
|
||||
|
||||
.contact-section
|
||||
margin-top: 20rpx
|
||||
|
||||
.section-title
|
||||
padding: 32rpx
|
||||
font-size: 32rpx
|
||||
color: #333
|
||||
font-weight: 500
|
||||
background: #fff
|
||||
border-bottom: 1rpx solid #f0f0f0
|
||||
|
||||
.contact-group
|
||||
background: #fff
|
||||
margin-bottom: 20rpx
|
||||
border-radius: 16rpx
|
||||
overflow: hidden
|
||||
|
||||
.group-header
|
||||
padding: 24rpx 32rpx
|
||||
font-size: 28rpx
|
||||
color: #000000
|
||||
background: #f8f9fa
|
||||
font-weight: 500
|
||||
border-bottom: 1rpx solid #e8e8e8
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
|
||||
.delete-btn
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 8rpx 16rpx
|
||||
background: #fff5f5
|
||||
border: 1rpx solid #ff4757
|
||||
border-radius: 8rpx
|
||||
cursor: pointer
|
||||
|
||||
.delete-text
|
||||
margin-left: 8rpx
|
||||
font-size: 24rpx
|
||||
color: #ff4757
|
||||
|
||||
.form-item
|
||||
border-bottom: 1rpx solid #f0f0f0
|
||||
|
||||
&:last-child
|
||||
border-bottom: none
|
||||
|
||||
.add-contact-btn
|
||||
padding: 32rpx
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
color: #256BFA
|
||||
font-size: 28rpx
|
||||
background: #fff
|
||||
border-top: 1rpx solid #f0f0f0
|
||||
|
||||
text
|
||||
margin-left: 12rpx
|
||||
|
||||
.bottom-actions
|
||||
position: fixed
|
||||
bottom: 0
|
||||
left: 0
|
||||
right: 0
|
||||
background: #fff
|
||||
padding: 20rpx 32rpx
|
||||
display: flex
|
||||
gap: 20rpx
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.1)
|
||||
|
||||
.cancel-btn, .confirm-btn
|
||||
flex: 1
|
||||
height: 80rpx
|
||||
border-radius: 40rpx
|
||||
font-size: 32rpx
|
||||
border: none
|
||||
|
||||
.cancel-btn
|
||||
background: #f5f5f5
|
||||
color: #666
|
||||
|
||||
.confirm-btn
|
||||
background: #256BFA
|
||||
color: #fff
|
||||
|
||||
// 弹窗样式
|
||||
.popup-content
|
||||
width: 600rpx
|
||||
background: #fff
|
||||
border-radius: 20rpx
|
||||
padding: 40rpx
|
||||
|
||||
.popup-title
|
||||
font-size: 36rpx
|
||||
color: #333
|
||||
font-weight: 500
|
||||
margin-bottom: 30rpx
|
||||
text-align: center
|
||||
|
||||
.popup-input, .popup-textarea
|
||||
width: 100%
|
||||
padding: 20rpx
|
||||
border: 1rpx solid #e0e0e0
|
||||
border-radius: 10rpx
|
||||
font-size: 28rpx
|
||||
margin-bottom: 30rpx
|
||||
box-sizing: border-box
|
||||
text-align: center
|
||||
|
||||
.popup-textarea
|
||||
height: 200rpx
|
||||
|
||||
.popup-actions
|
||||
display: flex
|
||||
gap: 20rpx
|
||||
|
||||
.popup-cancel, .popup-confirm
|
||||
flex: 1
|
||||
height: 70rpx
|
||||
border-radius: 35rpx
|
||||
font-size: 28rpx
|
||||
border: none
|
||||
|
||||
.popup-cancel
|
||||
background: #f5f5f5
|
||||
color: #666
|
||||
|
||||
.popup-confirm
|
||||
background: #256BFA
|
||||
color: #fff
|
||||
|
||||
// 按钮重置样式
|
||||
button::after
|
||||
border: none
|
||||
</style>
|
||||
|
||||
596
pages/complete-info/complete-info.vue
Normal file
596
pages/complete-info/complete-info.vue
Normal file
@@ -0,0 +1,596 @@
|
||||
<template>
|
||||
<AppLayout title="AI+就业服务程序">
|
||||
<view class="tab-container">
|
||||
<view class="uni-margin-wrap">
|
||||
<swiper
|
||||
class="swiper"
|
||||
:current="tabCurrent"
|
||||
:circular="false"
|
||||
:indicator-dots="false"
|
||||
:autoplay="false"
|
||||
:duration="500"
|
||||
>
|
||||
<swiper-item @touchmove.stop="false">
|
||||
<view class="login-content">
|
||||
<image class="logo" src="@/static/logo.png"></image>
|
||||
<view class="logo-title">就业</view>
|
||||
</view>
|
||||
<view class="btns">
|
||||
<button class="wxlogin" @click="loginTest">内测登录</button>
|
||||
<view class="wxaddress">{{ config.appInfo.areaName }}公共就业和人才服务中心</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
<swiper-item @touchmove.stop="false">
|
||||
<view class="content-one">
|
||||
<view>
|
||||
<view class="content-title">
|
||||
<view class="title-lf">
|
||||
<view class="lf-h1">请您完善求职名片</view>
|
||||
<view class="lf-text">个人信息仅用于推送优质内容</view>
|
||||
</view>
|
||||
<view class="title-ri">
|
||||
<text style="color: #256bfa">1</text>
|
||||
<text>/2</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-input" @click="changeExperience">
|
||||
<view class="input-titile">工作经验</view>
|
||||
<input
|
||||
class="input-con"
|
||||
v-model="state.experienceText"
|
||||
disabled
|
||||
placeholder="请选择您的工作经验"
|
||||
/>
|
||||
</view>
|
||||
<view class="content-sex">
|
||||
<view class="sex-titile">求职区域</view>
|
||||
<view class="sext-ri">
|
||||
<view
|
||||
class="sext-box"
|
||||
:class="{ 'sext-boxactive': fromValue.sex === 0 }"
|
||||
@click="changeSex(0)"
|
||||
>
|
||||
男
|
||||
</view>
|
||||
<view
|
||||
class="sext-box"
|
||||
:class="{ 'sext-boxactive': fromValue.sex === 1 }"
|
||||
@click="changeSex(1)"
|
||||
>
|
||||
女
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-input" @click="changeEducation">
|
||||
<view class="input-titile">学历</view>
|
||||
<input class="input-con" v-model="state.educationText" disabled placeholder="本科" />
|
||||
</view>
|
||||
<view class="content-input" :class="{ 'input-error': idCardError }">
|
||||
<view class="input-titile">身份证</view>
|
||||
<input
|
||||
class="input-con2"
|
||||
v-model="fromValue.idcard"
|
||||
maxlength="18"
|
||||
placeholder="请输入身份证号码"
|
||||
@input="validateIdCard"
|
||||
/>
|
||||
<view v-if="idCardError" class="error-message">{{ idCardError }}</view>
|
||||
<view v-if="fromValue.idcard && !idCardError" class="success-message">✓ 身份证格式正确</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="next-btn" @tap="nextStep">下一步</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
<swiper-item @touchmove.stop="false">
|
||||
<view class="content-one">
|
||||
<view>
|
||||
<view class="content-title">
|
||||
<view class="title-lf">
|
||||
<view class="lf-h1">请您完善求职名片</view>
|
||||
<view class="lf-text">个人信息仅用于推送优质内容</view>
|
||||
</view>
|
||||
<view class="title-ri">
|
||||
<text style="color: #256bfa">2</text>
|
||||
<text>/2</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-input" @click="changeArea">
|
||||
<view class="input-titile">求职区域</view>
|
||||
<input
|
||||
class="input-con"
|
||||
v-model="state.areaText"
|
||||
disabled
|
||||
placeholder="请选择您的求职区域"
|
||||
/>
|
||||
</view>
|
||||
<view class="content-input" @click="changeJobs">
|
||||
<view class="input-titile">求职岗位</view>
|
||||
<input
|
||||
class="input-con"
|
||||
disabled
|
||||
v-if="!state.jobsText.length"
|
||||
placeholder="请选择您的求职岗位"
|
||||
/>
|
||||
<view class="input-nx" @click="changeJobs" v-else>
|
||||
<view class="nx-item" v-for="item in state.jobsText">{{ item }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-input" @click="changeSalay">
|
||||
<view class="input-titile">期望薪资</view>
|
||||
<input
|
||||
class="input-con"
|
||||
v-model="state.salayText"
|
||||
disabled
|
||||
placeholder="请选择您的期望薪资"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="next-btn" @tap="complete">开启求职之旅</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</view>
|
||||
<SelectJobs ref="selectJobsModel"></SelectJobs>
|
||||
<SelectPopup ref="selectPopupRef"></SelectPopup>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SelectJobs from '@/components/selectJobs/selectJobs.vue';
|
||||
import SelectPopup from '@/components/selectPopup/selectPopup.vue';
|
||||
import { reactive, inject, watch, ref, onMounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
const { $api, navTo, config, IdCardValidator } = inject('globalFunction');
|
||||
const { loginSetToken, getUserResume } = useUserStore();
|
||||
const { getDictSelectOption, oneDictData } = useDictStore();
|
||||
|
||||
// #ifdef H5
|
||||
const injectedOpenSelectPopup = inject('openSelectPopup', null);
|
||||
// #endif
|
||||
|
||||
// status
|
||||
const selectJobsModel = ref();
|
||||
const selectPopupRef = ref();
|
||||
|
||||
// 创建本地的 openSelectPopup 函数
|
||||
const openSelectPopup = (config) => {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (selectPopupRef.value) {
|
||||
selectPopupRef.value.open(config);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
if (injectedOpenSelectPopup) {
|
||||
injectedOpenSelectPopup(config);
|
||||
}
|
||||
// #endif
|
||||
};
|
||||
const tabCurrent = ref(1);
|
||||
const salay = [2, 5, 10, 15, 20, 25, 30, 50, 80, 100];
|
||||
const state = reactive({
|
||||
station: [],
|
||||
stationCateLog: 1,
|
||||
lfsalay: [2, 5, 10, 15, 20, 25, 30, 50],
|
||||
risalay: JSON.parse(JSON.stringify(salay)),
|
||||
areaText: '',
|
||||
educationText: '',
|
||||
experienceText: '',
|
||||
salayText: '',
|
||||
jobsText: [],
|
||||
});
|
||||
const fromValue = reactive({
|
||||
sex: 1,
|
||||
education: '4',
|
||||
salaryMin: 2000,
|
||||
salaryMax: 2000,
|
||||
area: 0,
|
||||
jobTitleId: '',
|
||||
experience: '1',
|
||||
idcard: '',
|
||||
});
|
||||
|
||||
// 身份证校验相关
|
||||
const idCardError = ref('');
|
||||
|
||||
onLoad((parmas) => {
|
||||
getTreeselect();
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
function changeSex(sex) {
|
||||
fromValue.sex = sex;
|
||||
}
|
||||
|
||||
// 身份证实时校验
|
||||
function validateIdCard() {
|
||||
const idCard = fromValue.idcard.trim();
|
||||
|
||||
// 如果为空,清除错误信息
|
||||
if (!idCard) {
|
||||
idCardError.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用身份证校验器进行校验
|
||||
const result = IdCardValidator.validate(idCard);
|
||||
|
||||
if (result.valid) {
|
||||
idCardError.value = '';
|
||||
} else {
|
||||
idCardError.value = result.message;
|
||||
}
|
||||
}
|
||||
function changeExperience() {
|
||||
openSelectPopup({
|
||||
title: '工作经验',
|
||||
maskClick: true,
|
||||
data: [oneDictData('experience')],
|
||||
success: (_, [value]) => {
|
||||
fromValue.experience = value.value;
|
||||
state.experienceText = value.label;
|
||||
},
|
||||
change(_, [value]) {
|
||||
// this.setColunm(1, [123, 123]);
|
||||
console.log(this);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeEducation() {
|
||||
openSelectPopup({
|
||||
title: '学历',
|
||||
maskClick: true,
|
||||
data: [oneDictData('education')],
|
||||
success: (_, [value]) => {
|
||||
fromValue.area = value.value;
|
||||
state.educationText = value.label;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeArea() {
|
||||
openSelectPopup({
|
||||
title: '区域',
|
||||
maskClick: true,
|
||||
data: [oneDictData('area')],
|
||||
success: (_, [value]) => {
|
||||
fromValue.area = value.value;
|
||||
state.areaText = config.appInfo.areaName + '-' + value.label;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeSalay() {
|
||||
let leftIndex = 0;
|
||||
openSelectPopup({
|
||||
title: '薪资',
|
||||
maskClick: true,
|
||||
data: [state.lfsalay, state.risalay],
|
||||
unit: 'k',
|
||||
success: (_, [min, max]) => {
|
||||
fromValue.salaryMin = min.value * 1000;
|
||||
fromValue.salaryMax = max.value * 1000;
|
||||
state.salayText = `${fromValue.salaryMin}-${fromValue.salaryMax}`;
|
||||
},
|
||||
change(e) {
|
||||
const salayData = e.detail.value;
|
||||
if (leftIndex !== salayData[0]) {
|
||||
const copyri = JSON.parse(JSON.stringify(salay));
|
||||
const [lf, ri] = e.detail.value;
|
||||
const risalay = copyri.slice(lf, copyri.length);
|
||||
this.setColunm(1, risalay);
|
||||
leftIndex = salayData[0];
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeJobs() {
|
||||
selectJobsModel.value?.open({
|
||||
title: '添加岗位',
|
||||
success: (ids, labels) => {
|
||||
fromValue.jobTitleId = ids;
|
||||
state.jobsText = labels.split(',');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function nextStep() {
|
||||
// 校验身份证号码
|
||||
const idCard = fromValue.idcard.trim();
|
||||
if (!idCard) {
|
||||
$api.msg('请输入身份证号码');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = IdCardValidator.validate(idCard);
|
||||
if (!result.valid) {
|
||||
$api.msg(result.message);
|
||||
return;
|
||||
}
|
||||
|
||||
tabCurrent.value += 1;
|
||||
}
|
||||
|
||||
// 获取职位
|
||||
function getTreeselect() {
|
||||
$api.createRequest('/app/common/jobTitle/treeselect', {}, 'GET').then((resData) => {
|
||||
state.station = resData.data;
|
||||
});
|
||||
}
|
||||
|
||||
// 登录
|
||||
function loginTest() {
|
||||
// uni.share({
|
||||
// provider: 'weixin',
|
||||
// scene: 'WXSceneSession',
|
||||
// type: 2,
|
||||
// imageUrl: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/uni@2x.png',
|
||||
// success: function (res) {
|
||||
// console.log('success:' + JSON.stringify(res));
|
||||
// },
|
||||
// fail: function (err) {
|
||||
// console.log('fail:' + JSON.stringify(err));
|
||||
// },
|
||||
// });
|
||||
const params = {
|
||||
username: 'test',
|
||||
password: 'test',
|
||||
};
|
||||
$api.createRequest('/app/login', params, 'post').then((resData) => {
|
||||
$api.msg('模拟帐号密码测试登录成功');
|
||||
loginSetToken(resData.token).then((resume) => {
|
||||
if (resume.data.jobTitleId) {
|
||||
// 设置推荐列表,每次退出登录都需要更新
|
||||
useUserStore().initSeesionId();
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
});
|
||||
} else {
|
||||
nextStep();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function complete() {
|
||||
const result = IdCardValidator.validate(fromValue.idcard);
|
||||
if (result.valid) {
|
||||
$api.createRequest('/app/user/resume', fromValue, 'post').then((resData) => {
|
||||
$api.msg('完成');
|
||||
// 获取用户信息并存储到store中
|
||||
getUserResume().then((userInfo) => {
|
||||
console.log('用户信息已存储到store:', userInfo);
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$api.msg('身份证校验失败');
|
||||
console.log('验证失败:', result.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.tab-container
|
||||
height: 100%
|
||||
width: 100%
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
flex-direction: row
|
||||
.uni-margin-wrap
|
||||
width: 100%
|
||||
height: 100%
|
||||
.swiper
|
||||
width: 100%
|
||||
height: 100%
|
||||
.swiper-item
|
||||
display: block;
|
||||
width: 100%
|
||||
height: 100%
|
||||
.input-nx
|
||||
position: relative
|
||||
border-bottom: 2rpx solid #EBEBEB
|
||||
padding-bottom: 30rpx
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
.nx-item
|
||||
padding: 20rpx 28rpx
|
||||
width: fit-content
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
border: 2rpx solid #E8EAEE;
|
||||
margin-right: 24rpx
|
||||
margin-top: 24rpx
|
||||
.nx-item::before
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 60rpx;
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 2rpx
|
||||
background: #697279;
|
||||
transform: translate(0, -50%) rotate(-45deg) ;
|
||||
.nx-item::after
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 61rpx;
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 2rpx
|
||||
background: #697279;
|
||||
transform: rotate(45deg)
|
||||
.container
|
||||
// background: linear-gradient(#4778EC, #002979);
|
||||
width: 100%;
|
||||
height: calc(100vh - var(--window-top) - var(--status-bar-height) - var(--window-bottom));
|
||||
position: fixed;
|
||||
background: url('@/static/icon/background2.png') 0 0 no-repeat;
|
||||
background-size: 100% 728rpx;
|
||||
display: flex;
|
||||
flex-direction: column
|
||||
.container-hader
|
||||
height: 88rpx;
|
||||
text-align: center;
|
||||
line-height: 88rpx;
|
||||
color: #000000;
|
||||
font-weight: bold
|
||||
font-size: 32rpx
|
||||
|
||||
.login-content
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 40%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
flex-wrap: nowrap;
|
||||
.logo
|
||||
width: 266rpx;
|
||||
height: 182rpx;
|
||||
.logo-title
|
||||
font-size: 88rpx;
|
||||
color: #22c984;
|
||||
width: 180rpx;
|
||||
.btns
|
||||
position: absolute;
|
||||
top: 70%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0)
|
||||
.wxlogin
|
||||
width: 562rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 70rpx;
|
||||
background-color: #13C57C;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
line-height: 140rpx;
|
||||
font-size: 70rpx;
|
||||
.wxaddress
|
||||
color: #BBBBBB;
|
||||
margin-top: 70rpx;
|
||||
text-align: center;
|
||||
.content-one
|
||||
padding: 60rpx 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between
|
||||
height: calc(100% - 120rpx)
|
||||
.content-title
|
||||
display: flex
|
||||
justify-content: space-between;
|
||||
align-items: center
|
||||
margin-bottom: 70rpx
|
||||
.title-lf
|
||||
font-size: 44rpx;
|
||||
color: #000000;
|
||||
font-weight: 600;
|
||||
.lf-text
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
.title-ri
|
||||
font-size: 36rpx;
|
||||
color: #000000;
|
||||
font-weight: 600;
|
||||
.content-input
|
||||
margin-bottom: 52rpx
|
||||
.input-titile
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6A6A6A;
|
||||
.input-con2
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
line-height: 80rpx;
|
||||
height: 80rpx;
|
||||
border-bottom: 2rpx solid #EBEBEB
|
||||
.input-con
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
line-height: 80rpx;
|
||||
height: 80rpx;
|
||||
border-bottom: 2rpx solid #EBEBEB
|
||||
position: relative;
|
||||
.input-con::before
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: calc(50% - 2rpx);
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 2rpx
|
||||
background: #697279;
|
||||
transform: translate(0, -50%) rotate(-45deg) ;
|
||||
.input-con::after
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 50%;
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 18rpx;
|
||||
border-radius: 2rpx
|
||||
background: #697279;
|
||||
transform: rotate(45deg)
|
||||
.error-message
|
||||
color: #ff4757;
|
||||
font-size: 24rpx;
|
||||
margin-top: 10rpx;
|
||||
line-height: 1.4;
|
||||
.success-message
|
||||
color: #2ed573;
|
||||
font-size: 24rpx;
|
||||
margin-top: 10rpx;
|
||||
line-height: 1.4;
|
||||
.input-error
|
||||
.input-con2
|
||||
border-bottom-color: #ff4757;
|
||||
.content-sex
|
||||
height: 110rpx;
|
||||
display: flex
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
border-bottom: 2rpx solid #EBEBEB
|
||||
margin-bottom: 52rpx
|
||||
.sex-titile
|
||||
line-height: 80rpx;
|
||||
.sext-ri
|
||||
display: flex
|
||||
align-items: center;
|
||||
.sext-box
|
||||
height: 76rpx;
|
||||
width: 152rpx;
|
||||
text-align: center;
|
||||
line-height: 80rpx;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx
|
||||
border: 2rpx solid #E8EAEE;
|
||||
margin-left: 28rpx
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
.sext-boxactive
|
||||
color: #256BFA
|
||||
background: rgba(37,107,250,0.1);
|
||||
border: 2rpx solid #256BFA;
|
||||
.next-btn
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
background: #256BFA;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
line-height: 90rpx
|
||||
</style>
|
||||
|
||||
820
pages/complete-info/components/map-location-picker.vue
Normal file
820
pages/complete-info/components/map-location-picker.vue
Normal file
@@ -0,0 +1,820 @@
|
||||
<template>
|
||||
<AppLayout title="选择地址" :showBack="true">
|
||||
<view class="map-container">
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-box">
|
||||
<view class="search-input-wrapper">
|
||||
<uni-icons type="search" size="20" color="#999"></uni-icons>
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="searchKeyword"
|
||||
placeholder="输入关键词搜索地址(支持模糊搜索)"
|
||||
@input="onSearchInput"
|
||||
@confirm="searchLocation"
|
||||
/>
|
||||
<uni-icons
|
||||
v-if="searchKeyword"
|
||||
type="clear"
|
||||
size="18"
|
||||
color="#999"
|
||||
@click="clearSearch"
|
||||
></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索结果列表 -->
|
||||
<view class="search-results" v-if="showSearchResults">
|
||||
<scroll-view scroll-y class="results-scroll" v-if="searchResults.length > 0">
|
||||
<view
|
||||
class="result-item"
|
||||
v-for="(item, index) in searchResults"
|
||||
:key="index"
|
||||
@click="selectSearchResult(item)"
|
||||
>
|
||||
<view class="result-name">{{ item.name }}</view>
|
||||
<view class="result-address">{{ item.address }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="empty-results" v-else-if="isSearching">
|
||||
<view class="loading-icon">
|
||||
<uni-icons type="loop" size="40" color="#999"></uni-icons>
|
||||
</view>
|
||||
<text>搜索中...</text>
|
||||
</view>
|
||||
<view class="empty-results" v-else>
|
||||
<uni-icons type="info" size="40" color="#999"></uni-icons>
|
||||
<text>未找到相关地址,请尝试其他关键词</text>
|
||||
<view class="search-tips">
|
||||
<text class="tip-title">搜索建议:</text>
|
||||
<text class="tip-item">• 输入具体地址名称</text>
|
||||
<text class="tip-item">• 输入地标建筑名称</text>
|
||||
<text class="tip-item">• 输入街道或区域名称</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 地图 -->
|
||||
<view class="map-wrapper" v-show="!showSearchResults">
|
||||
<!-- #ifdef H5 -->
|
||||
<view id="amap-container" class="amap-container"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef H5 -->
|
||||
<map
|
||||
id="map"
|
||||
class="map"
|
||||
:latitude="latitude"
|
||||
:longitude="longitude"
|
||||
:markers="markers"
|
||||
:show-location="true"
|
||||
@markertap="onMarkerTap"
|
||||
@regionchange="onRegionChange"
|
||||
@tap="onMapTap"
|
||||
>
|
||||
<cover-view class="map-center-marker">
|
||||
<cover-image src="/static/icon/Location.png" class="marker-icon"></cover-image>
|
||||
</cover-view>
|
||||
</map>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
|
||||
<!-- 当前位置信息 -->
|
||||
<view class="location-info" v-if="currentAddress && !showSearchResults">
|
||||
<view class="info-title">当前选择位置</view>
|
||||
<view class="info-name">{{ currentAddress.name }}</view>
|
||||
<view class="info-address">{{ currentAddress.address }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<view class="bottom-actions">
|
||||
<button class="locate-btn" @click="getCurrentLocation" :disabled="isLocating">
|
||||
<uni-icons type="location-filled" size="20" color="#256BFA"></uni-icons>
|
||||
<text>{{ isLocating ? '定位中...' : '定位' }}</text>
|
||||
</button>
|
||||
<button class="confirm-btn" @click="confirmLocation">确认选择</button>
|
||||
</view>
|
||||
</view>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, inject, onMounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const { $api } = inject('globalFunction')
|
||||
|
||||
// 搜索相关
|
||||
const searchKeyword = ref('')
|
||||
const searchResults = ref([])
|
||||
const showSearchResults = ref(false)
|
||||
const isSearching = ref(false)
|
||||
const isLocating = ref(false)
|
||||
let searchTimer = null
|
||||
|
||||
// 地图相关
|
||||
const latitude = ref(36.066938)
|
||||
const longitude = ref(120.382665)
|
||||
const markers = ref([])
|
||||
const currentAddress = ref(null)
|
||||
|
||||
// H5地图实例
|
||||
let map = null
|
||||
let AMap = null
|
||||
let geocoder = null
|
||||
let placeSearch = null
|
||||
|
||||
onLoad((options) => {
|
||||
// 可以接收初始位置参数
|
||||
if (options.latitude && options.longitude) {
|
||||
latitude.value = parseFloat(options.latitude)
|
||||
longitude.value = parseFloat(options.longitude)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// #ifdef H5
|
||||
initAmapH5()
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 先设置默认位置,避免地图显示空白
|
||||
markers.value = [{
|
||||
id: 1,
|
||||
latitude: latitude.value,
|
||||
longitude: longitude.value,
|
||||
iconPath: '/static/icon/Location.png',
|
||||
width: 30,
|
||||
height: 30
|
||||
}]
|
||||
|
||||
// 延迟执行定位,避免页面加载时立即定位失败
|
||||
setTimeout(() => {
|
||||
getCurrentLocation()
|
||||
}, 1000)
|
||||
// #endif
|
||||
})
|
||||
|
||||
// H5端初始化高德地图
|
||||
const initAmapH5 = () => {
|
||||
// #ifdef H5
|
||||
if (window.AMap) {
|
||||
AMap = window.AMap
|
||||
initMap()
|
||||
} else {
|
||||
const script = document.createElement('script')
|
||||
script.src = 'https://webapi.amap.com/maps?v=2.0&key=9cfc9370bd8a941951da1cea0308e9e3&plugin=AMap.Geocoder,AMap.PlaceSearch'
|
||||
script.onload = () => {
|
||||
AMap = window.AMap
|
||||
initMap()
|
||||
}
|
||||
document.head.appendChild(script)
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 初始化地图
|
||||
const initMap = () => {
|
||||
// #ifdef H5
|
||||
map = new AMap.Map('amap-container', {
|
||||
zoom: 15,
|
||||
center: [longitude.value, latitude.value],
|
||||
resizeEnable: true
|
||||
})
|
||||
|
||||
// 创建标记
|
||||
const marker = new AMap.Marker({
|
||||
position: [longitude.value, latitude.value],
|
||||
draggable: true
|
||||
})
|
||||
|
||||
marker.on('dragend', (e) => {
|
||||
const position = e.target.getPosition()
|
||||
longitude.value = position.lng
|
||||
latitude.value = position.lat
|
||||
reverseGeocode(position.lng, position.lat)
|
||||
})
|
||||
|
||||
map.add(marker)
|
||||
|
||||
// 初始化地理编码
|
||||
geocoder = new AMap.Geocoder({
|
||||
city: '全国'
|
||||
})
|
||||
|
||||
// 初始化地点搜索
|
||||
placeSearch = new AMap.PlaceSearch({
|
||||
city: '全国',
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
// 地图点击事件
|
||||
map.on('click', (e) => {
|
||||
const { lng, lat } = e.lnglat
|
||||
longitude.value = lng
|
||||
latitude.value = lat
|
||||
marker.setPosition([lng, lat])
|
||||
reverseGeocode(lng, lat)
|
||||
})
|
||||
|
||||
// 获取当前位置信息
|
||||
reverseGeocode(longitude.value, latitude.value)
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 搜索输入
|
||||
const onSearchInput = () => {
|
||||
if (searchTimer) {
|
||||
clearTimeout(searchTimer)
|
||||
}
|
||||
|
||||
if (!searchKeyword.value.trim()) {
|
||||
showSearchResults.value = false
|
||||
searchResults.value = []
|
||||
isSearching.value = false
|
||||
return
|
||||
}
|
||||
|
||||
showSearchResults.value = true
|
||||
isSearching.value = true
|
||||
|
||||
searchTimer = setTimeout(() => {
|
||||
if (searchKeyword.value.trim()) {
|
||||
searchLocation()
|
||||
}
|
||||
}, 300) // 优化防抖时间从500ms改为300ms
|
||||
}
|
||||
|
||||
// 搜索地点
|
||||
const searchLocation = () => {
|
||||
if (!searchKeyword.value.trim()) {
|
||||
return
|
||||
}
|
||||
|
||||
showSearchResults.value = true
|
||||
isSearching.value = true
|
||||
|
||||
// #ifdef H5
|
||||
if (placeSearch) {
|
||||
placeSearch.search(searchKeyword.value, (status, result) => {
|
||||
isSearching.value = false
|
||||
if (status === 'complete' && result.poiList) {
|
||||
searchResults.value = result.poiList.pois.map(poi => ({
|
||||
name: poi.name,
|
||||
address: poi.address || poi.pname + poi.cityname + poi.adname,
|
||||
location: poi.location,
|
||||
lng: poi.location.lng,
|
||||
lat: poi.location.lat
|
||||
}))
|
||||
} else {
|
||||
searchResults.value = []
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 小程序端使用uni.request调用高德API
|
||||
uni.request({
|
||||
url: 'https://restapi.amap.com/v3/place/text',
|
||||
data: {
|
||||
key: '9cfc9370bd8a941951da1cea0308e9e3',
|
||||
keywords: searchKeyword.value,
|
||||
city: '全国',
|
||||
offset: 20,
|
||||
citylimit: false, // 不限制城市,支持全国搜索
|
||||
extensions: 'all' // 返回详细信息
|
||||
},
|
||||
success: (res) => {
|
||||
isSearching.value = false
|
||||
console.log('搜索响应:', res.data) // 调试日志
|
||||
|
||||
if (res.data.status === '1' && res.data.pois && res.data.pois.length > 0) {
|
||||
searchResults.value = res.data.pois.map(poi => {
|
||||
const [lng, lat] = poi.location.split(',')
|
||||
return {
|
||||
name: poi.name,
|
||||
address: poi.address || `${poi.pname || ''}${poi.cityname || ''}${poi.adname || ''}`,
|
||||
lng: parseFloat(lng),
|
||||
lat: parseFloat(lat)
|
||||
}
|
||||
})
|
||||
console.log('搜索结果:', searchResults.value) // 调试日志
|
||||
} else {
|
||||
// 如果第一次搜索没有结果,尝试更宽泛的搜索
|
||||
if (searchKeyword.value.length > 2) {
|
||||
tryAlternativeSearch()
|
||||
} else {
|
||||
searchResults.value = []
|
||||
console.log('搜索无结果:', res.data) // 调试日志
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
isSearching.value = false
|
||||
searchResults.value = []
|
||||
console.error('搜索请求失败:', err) // 调试日志
|
||||
$api.msg('搜索失败,请检查网络连接')
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 备用搜索策略
|
||||
const tryAlternativeSearch = () => {
|
||||
// 尝试使用地理编码API搜索
|
||||
uni.request({
|
||||
url: 'https://restapi.amap.com/v3/geocode/geo',
|
||||
data: {
|
||||
key: '9cfc9370bd8a941951da1cea0308e9e3',
|
||||
address: searchKeyword.value,
|
||||
city: '全国'
|
||||
},
|
||||
success: (res) => {
|
||||
isSearching.value = false
|
||||
console.log('备用搜索响应:', res.data) // 调试日志
|
||||
|
||||
if (res.data.status === '1' && res.data.geocodes && res.data.geocodes.length > 0) {
|
||||
searchResults.value = res.data.geocodes.map(geo => {
|
||||
const [lng, lat] = geo.location.split(',')
|
||||
return {
|
||||
name: geo.formatted_address,
|
||||
address: geo.formatted_address,
|
||||
lng: parseFloat(lng),
|
||||
lat: parseFloat(lat)
|
||||
}
|
||||
})
|
||||
console.log('备用搜索结果:', searchResults.value) // 调试日志
|
||||
} else {
|
||||
searchResults.value = []
|
||||
console.log('备用搜索也无结果:', res.data) // 调试日志
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
isSearching.value = false
|
||||
searchResults.value = []
|
||||
console.error('备用搜索失败:', err) // 调试日志
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选择搜索结果
|
||||
const selectSearchResult = (item) => {
|
||||
longitude.value = item.lng
|
||||
latitude.value = item.lat
|
||||
currentAddress.value = {
|
||||
name: item.name,
|
||||
address: item.address,
|
||||
longitude: item.lng,
|
||||
latitude: item.lat
|
||||
}
|
||||
|
||||
// #ifdef H5
|
||||
if (map) {
|
||||
map.setCenter([item.lng, item.lat])
|
||||
const marker = map.getAllOverlays('marker')[0]
|
||||
if (marker) {
|
||||
marker.setPosition([item.lng, item.lat])
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
markers.value = [{
|
||||
id: 1,
|
||||
latitude: item.lat,
|
||||
longitude: item.lng,
|
||||
iconPath: '/static/icon/Location.png',
|
||||
width: 30,
|
||||
height: 30
|
||||
}]
|
||||
// #endif
|
||||
|
||||
showSearchResults.value = false
|
||||
searchKeyword.value = ''
|
||||
}
|
||||
|
||||
// 清除搜索
|
||||
const clearSearch = () => {
|
||||
searchKeyword.value = ''
|
||||
searchResults.value = []
|
||||
showSearchResults.value = false
|
||||
isSearching.value = false
|
||||
if (searchTimer) {
|
||||
clearTimeout(searchTimer)
|
||||
}
|
||||
}
|
||||
|
||||
// 逆地理编码(根据坐标获取地址)
|
||||
const reverseGeocode = (lng, lat) => {
|
||||
// #ifdef H5
|
||||
if (geocoder) {
|
||||
geocoder.getAddress([lng, lat], (status, result) => {
|
||||
if (status === 'complete' && result.regeocode) {
|
||||
const addressComponent = result.regeocode.addressComponent
|
||||
const formattedAddress = result.regeocode.formattedAddress
|
||||
currentAddress.value = {
|
||||
name: addressComponent.building || addressComponent.township,
|
||||
address: formattedAddress,
|
||||
longitude: lng,
|
||||
latitude: lat
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.request({
|
||||
url: 'https://restapi.amap.com/v3/geocode/regeo',
|
||||
data: {
|
||||
key: '9cfc9370bd8a941951da1cea0308e9e3',
|
||||
location: `${lng},${lat}`
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.data.status === '1' && res.data.regeocode) {
|
||||
const addressComponent = res.data.regeocode.addressComponent
|
||||
const formattedAddress = res.data.regeocode.formatted_address
|
||||
currentAddress.value = {
|
||||
name: addressComponent.building || addressComponent.township || '选择的位置',
|
||||
address: formattedAddress,
|
||||
longitude: lng,
|
||||
latitude: lat
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 获取当前定位
|
||||
const getCurrentLocation = () => {
|
||||
if (isLocating.value) return // 防止重复定位
|
||||
|
||||
isLocating.value = true
|
||||
uni.showLoading({ title: '定位中...' })
|
||||
|
||||
// 先检查定位权限
|
||||
uni.getSetting({
|
||||
success: (settingRes) => {
|
||||
if (settingRes.authSetting['scope.userLocation'] === false) {
|
||||
// 用户拒绝了定位权限,引导用户开启
|
||||
isLocating.value = false
|
||||
uni.hideLoading()
|
||||
uni.showModal({
|
||||
title: '定位权限',
|
||||
content: '需要获取您的位置信息来提供更好的服务,请在设置中开启定位权限',
|
||||
confirmText: '去设置',
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
uni.openSetting()
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 执行定位
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
altitude: false,
|
||||
success: (res) => {
|
||||
console.log('定位成功:', res) // 调试日志
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
|
||||
// #ifdef H5
|
||||
if (map) {
|
||||
map.setCenter([res.longitude, res.latitude])
|
||||
const marker = map.getAllOverlays('marker')[0]
|
||||
if (marker) {
|
||||
marker.setPosition([res.longitude, res.latitude])
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
// 更新小程序端标记
|
||||
markers.value = [{
|
||||
id: 1,
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
iconPath: '/static/icon/Location.png',
|
||||
width: 30,
|
||||
height: 30
|
||||
}]
|
||||
// #endif
|
||||
|
||||
reverseGeocode(res.longitude, res.latitude)
|
||||
uni.hideLoading()
|
||||
isLocating.value = false
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('定位失败:', err) // 调试日志
|
||||
uni.hideLoading()
|
||||
isLocating.value = false
|
||||
|
||||
// 根据错误类型给出不同提示
|
||||
let errorMsg = '定位失败'
|
||||
if (err.errMsg.includes('auth deny')) {
|
||||
errorMsg = '定位权限被拒绝,请在设置中开启'
|
||||
} else if (err.errMsg.includes('timeout')) {
|
||||
errorMsg = '定位超时,请重试'
|
||||
} else if (err.errMsg.includes('network')) {
|
||||
errorMsg = '网络异常,请检查网络连接'
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '定位失败',
|
||||
content: errorMsg + ',是否使用默认位置?',
|
||||
confirmText: '使用默认位置',
|
||||
cancelText: '重试',
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
// 使用默认位置(北京)
|
||||
longitude.value = 116.397428
|
||||
latitude.value = 39.90923
|
||||
reverseGeocode(longitude.value, latitude.value)
|
||||
} else {
|
||||
// 重试定位
|
||||
setTimeout(() => {
|
||||
getCurrentLocation()
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading()
|
||||
isLocating.value = false
|
||||
$api.msg('无法获取定位权限设置')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 地图区域变化(小程序端)
|
||||
const onRegionChange = (e) => {
|
||||
// #ifndef H5
|
||||
// 只有在用户手动拖动地图结束时才更新位置
|
||||
if (e.type === 'end' && e.causedBy === 'drag') {
|
||||
const mapContext = uni.createMapContext('map')
|
||||
mapContext.getCenterLocation({
|
||||
success: (res) => {
|
||||
longitude.value = res.longitude
|
||||
latitude.value = res.latitude
|
||||
reverseGeocode(res.longitude, res.latitude)
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 地图点击事件(小程序端)
|
||||
const onMapTap = (e) => {
|
||||
// #ifndef H5
|
||||
const { latitude: lat, longitude: lng } = e.detail
|
||||
longitude.value = lng
|
||||
latitude.value = lat
|
||||
|
||||
// 更新标记
|
||||
markers.value = [{
|
||||
id: 1,
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
iconPath: '/static/icon/Location.png',
|
||||
width: 30,
|
||||
height: 30
|
||||
}]
|
||||
|
||||
reverseGeocode(lng, lat)
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 确认选择
|
||||
const confirmLocation = () => {
|
||||
if (!currentAddress.value) {
|
||||
$api.msg('请选择地址')
|
||||
return
|
||||
}
|
||||
|
||||
// 返回上一页并传递数据
|
||||
const pages = getCurrentPages()
|
||||
const prevPage = pages[pages.length - 2]
|
||||
|
||||
if (prevPage) {
|
||||
prevPage.$vm.handleLocationSelected({
|
||||
address: currentAddress.value.address,
|
||||
name: currentAddress.value.name,
|
||||
longitude: currentAddress.value.longitude,
|
||||
latitude: currentAddress.value.latitude
|
||||
})
|
||||
}
|
||||
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const onMarkerTap = (e) => {
|
||||
console.log('marker点击', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.map-container
|
||||
width: 100%
|
||||
height: 100vh
|
||||
position: relative
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
.search-box
|
||||
position: absolute
|
||||
top: 20rpx
|
||||
left: 32rpx
|
||||
right: 32rpx
|
||||
z-index: 10
|
||||
|
||||
.search-input-wrapper
|
||||
background: #fff
|
||||
border-radius: 40rpx
|
||||
padding: 20rpx 30rpx
|
||||
display: flex
|
||||
align-items: center
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1)
|
||||
|
||||
.search-input
|
||||
flex: 1
|
||||
margin: 0 20rpx
|
||||
font-size: 28rpx
|
||||
|
||||
uni-icons
|
||||
flex-shrink: 0
|
||||
|
||||
.search-results
|
||||
position: absolute
|
||||
top: 100rpx
|
||||
left: 32rpx
|
||||
right: 32rpx
|
||||
bottom: 180rpx
|
||||
background: #fff
|
||||
border-radius: 20rpx
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1)
|
||||
z-index: 9
|
||||
overflow: hidden
|
||||
|
||||
.results-scroll
|
||||
height: 100%
|
||||
|
||||
.result-item
|
||||
padding: 30rpx
|
||||
border-bottom: 1rpx solid #f0f0f0
|
||||
|
||||
&:active
|
||||
background: #f5f5f5
|
||||
|
||||
.result-name
|
||||
font-size: 32rpx
|
||||
color: #333
|
||||
font-weight: 500
|
||||
margin-bottom: 10rpx
|
||||
|
||||
.result-address
|
||||
font-size: 26rpx
|
||||
color: #999
|
||||
|
||||
.empty-results
|
||||
height: 100%
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
color: #999
|
||||
font-size: 28rpx
|
||||
|
||||
.loading-icon
|
||||
animation: rotate 1s linear infinite
|
||||
margin-bottom: 20rpx
|
||||
|
||||
uni-icons
|
||||
margin-bottom: 20rpx
|
||||
|
||||
text
|
||||
padding: 0 60rpx
|
||||
text-align: center
|
||||
line-height: 1.5
|
||||
|
||||
.search-tips
|
||||
margin-top: 40rpx
|
||||
padding: 0 40rpx
|
||||
|
||||
.tip-title
|
||||
font-size: 26rpx
|
||||
color: #666
|
||||
font-weight: 500
|
||||
margin-bottom: 20rpx
|
||||
display: block
|
||||
|
||||
.tip-item
|
||||
font-size: 24rpx
|
||||
color: #999
|
||||
line-height: 1.8
|
||||
display: block
|
||||
margin-bottom: 8rpx
|
||||
|
||||
@keyframes rotate
|
||||
from
|
||||
transform: rotate(0deg)
|
||||
to
|
||||
transform: rotate(360deg)
|
||||
|
||||
.map-wrapper
|
||||
flex: 1
|
||||
position: relative
|
||||
|
||||
.map, .amap-container
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
.map-center-marker
|
||||
position: absolute
|
||||
left: 50%
|
||||
top: 50%
|
||||
transform: translate(-50%, -100%)
|
||||
z-index: 5
|
||||
|
||||
.marker-icon
|
||||
width: 60rpx
|
||||
height: 80rpx
|
||||
|
||||
.location-info
|
||||
position: absolute
|
||||
bottom: 180rpx
|
||||
left: 32rpx
|
||||
right: 32rpx
|
||||
background: #fff
|
||||
border-radius: 20rpx
|
||||
padding: 30rpx
|
||||
box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.1)
|
||||
z-index: 10
|
||||
|
||||
.info-title
|
||||
font-size: 24rpx
|
||||
color: #999
|
||||
margin-bottom: 10rpx
|
||||
|
||||
.info-name
|
||||
font-size: 32rpx
|
||||
color: #333
|
||||
font-weight: 500
|
||||
margin-bottom: 10rpx
|
||||
|
||||
.info-address
|
||||
font-size: 28rpx
|
||||
color: #666
|
||||
|
||||
.bottom-actions
|
||||
position: absolute
|
||||
bottom: 0
|
||||
left: 0
|
||||
right: 0
|
||||
background: #fff
|
||||
padding: 20rpx 32rpx
|
||||
display: flex
|
||||
gap: 20rpx
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.1)
|
||||
z-index: 11
|
||||
|
||||
.locate-btn
|
||||
width: 120rpx
|
||||
height: 80rpx
|
||||
background: #fff
|
||||
border: 2rpx solid #256BFA
|
||||
border-radius: 40rpx
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
font-size: 24rpx
|
||||
color: #256BFA
|
||||
|
||||
&:disabled
|
||||
opacity: 0.5
|
||||
color: #999
|
||||
border-color: #999
|
||||
|
||||
text
|
||||
margin-top: 4rpx
|
||||
|
||||
.confirm-btn
|
||||
flex: 1
|
||||
height: 80rpx
|
||||
background: #256BFA
|
||||
color: #fff
|
||||
border-radius: 40rpx
|
||||
font-size: 32rpx
|
||||
border: none
|
||||
|
||||
button::after
|
||||
border: none
|
||||
</style>
|
||||
|
||||
272
pages/demo/iconfont-demo.vue
Normal file
272
pages/demo/iconfont-demo.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<view class="demo-page">
|
||||
<view class="demo-title">阿里图标库使用示例</view>
|
||||
|
||||
<!-- 方式一:直接使用 iconfont 类 -->
|
||||
<view class="demo-section">
|
||||
<view class="section-title">方式一:直接使用</view>
|
||||
<view class="icon-list">
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-home"></text>
|
||||
<text class="icon-name">icon-home</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-user"></text>
|
||||
<text class="icon-name">icon-user</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-search"></text>
|
||||
<text class="icon-name">icon-search</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 方式二:使用封装的组件 -->
|
||||
<view class="demo-section">
|
||||
<view class="section-title">方式二:使用封装组件</view>
|
||||
<view class="icon-list">
|
||||
<view class="icon-item">
|
||||
<IconfontIcon name="home" :size="48" color="#13C57C" />
|
||||
<text class="icon-name">绿色 48rpx</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<IconfontIcon name="user" :size="36" color="#256BFA" />
|
||||
<text class="icon-name">蓝色 36rpx</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<IconfontIcon name="search" :size="32" color="#FF9800" />
|
||||
<text class="icon-name">橙色 32rpx</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 方式三:使用配置常量 -->
|
||||
<view class="demo-section">
|
||||
<view class="section-title">方式三:使用配置常量</view>
|
||||
<view class="icon-list">
|
||||
<view class="icon-item">
|
||||
<IconfontIcon
|
||||
:name="ICONS.PHONE"
|
||||
:size="ICON_SIZES.LARGE"
|
||||
:color="ICON_COLORS.PRIMARY"
|
||||
/>
|
||||
<text class="icon-name">电话</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<IconfontIcon
|
||||
:name="ICONS.MESSAGE"
|
||||
:size="ICON_SIZES.LARGE"
|
||||
:color="ICON_COLORS.SECONDARY"
|
||||
/>
|
||||
<text class="icon-name">消息</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<IconfontIcon
|
||||
:name="ICONS.LOCATION"
|
||||
:size="ICON_SIZES.LARGE"
|
||||
:color="ICON_COLORS.DANGER"
|
||||
/>
|
||||
<text class="icon-name">位置</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 方式四:按钮中使用 -->
|
||||
<view class="demo-section">
|
||||
<view class="section-title">方式四:在按钮中使用</view>
|
||||
<button class="demo-button primary">
|
||||
<IconfontIcon name="phone" :size="32" color="#FFFFFF" />
|
||||
<text>手机号登录</text>
|
||||
</button>
|
||||
<button class="demo-button secondary">
|
||||
<IconfontIcon name="user" :size="32" color="#256BFA" />
|
||||
<text>个人中心</text>
|
||||
</button>
|
||||
<button class="demo-button success">
|
||||
<IconfontIcon name="star" :size="32" color="#FFFFFF" />
|
||||
<text>收藏职位</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 方式五:列表中使用 -->
|
||||
<view class="demo-section">
|
||||
<view class="section-title">方式五:在列表中使用</view>
|
||||
<view class="demo-list">
|
||||
<view class="list-item" v-for="item in menuList" :key="item.id">
|
||||
<IconfontIcon :name="item.icon" :size="40" :color="item.color" />
|
||||
<view class="item-content">
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
<view class="item-desc">{{ item.desc }}</view>
|
||||
</view>
|
||||
<IconfontIcon name="arrow-right" :size="28" color="#999" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 注意事项 -->
|
||||
<view class="demo-section">
|
||||
<view class="section-title">⚠️ 注意事项</view>
|
||||
<view class="tips-box">
|
||||
<view class="tip-item">1. 确保已从阿里图标库下载字体文件到 static/iconfont/ 目录</view>
|
||||
<view class="tip-item">2. 确保已在 App.vue 中引入 iconfont.css</view>
|
||||
<view class="tip-item">3. 图标名称需要与阿里图标库中的类名保持一致</view>
|
||||
<view class="tip-item">4. 推荐使用封装的 IconfontIcon 组件,便于统一管理</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import IconfontIcon from '@/components/IconfontIcon/IconfontIcon.vue'
|
||||
import { ICONS, ICON_SIZES, ICON_COLORS } from '@/config/icons'
|
||||
|
||||
const menuList = ref([
|
||||
{
|
||||
id: 1,
|
||||
icon: 'home',
|
||||
title: '首页',
|
||||
desc: '查看推荐职位',
|
||||
color: '#13C57C'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
icon: 'search',
|
||||
title: '搜索',
|
||||
desc: '搜索心仪职位',
|
||||
color: '#256BFA'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: 'message',
|
||||
title: '消息',
|
||||
desc: '查看聊天消息',
|
||||
color: '#FF9800'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
icon: 'user',
|
||||
title: '我的',
|
||||
desc: '个人中心',
|
||||
color: '#9C27B0'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.demo-page
|
||||
padding: 40rpx
|
||||
background: #F5F5F5
|
||||
min-height: 100vh
|
||||
|
||||
.demo-title
|
||||
font-size: 48rpx
|
||||
font-weight: bold
|
||||
color: #333
|
||||
text-align: center
|
||||
margin-bottom: 40rpx
|
||||
|
||||
.demo-section
|
||||
background: #FFFFFF
|
||||
border-radius: 16rpx
|
||||
padding: 32rpx
|
||||
margin-bottom: 32rpx
|
||||
|
||||
.section-title
|
||||
font-size: 32rpx
|
||||
font-weight: 600
|
||||
color: #333
|
||||
margin-bottom: 24rpx
|
||||
padding-bottom: 16rpx
|
||||
border-bottom: 2rpx solid #F0F0F0
|
||||
|
||||
.icon-list
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
gap: 32rpx
|
||||
|
||||
.icon-item
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
gap: 12rpx
|
||||
min-width: 120rpx
|
||||
|
||||
.iconfont
|
||||
font-size: 48rpx
|
||||
color: #333
|
||||
|
||||
.icon-name
|
||||
font-size: 24rpx
|
||||
color: #666
|
||||
text-align: center
|
||||
|
||||
.demo-button
|
||||
width: 100%
|
||||
height: 88rpx
|
||||
border-radius: 44rpx
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
gap: 12rpx
|
||||
font-size: 32rpx
|
||||
margin-bottom: 20rpx
|
||||
border: none
|
||||
|
||||
&.primary
|
||||
background: linear-gradient(135deg, #13C57C 0%, #0FA368 100%)
|
||||
color: #FFFFFF
|
||||
|
||||
&.secondary
|
||||
background: #F7F8FA
|
||||
color: #256BFA
|
||||
|
||||
&.success
|
||||
background: linear-gradient(135deg, #FF9800 0%, #F57C00 100%)
|
||||
color: #FFFFFF
|
||||
|
||||
.demo-list
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 20rpx
|
||||
|
||||
.list-item
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 24rpx
|
||||
background: #F7F8FA
|
||||
border-radius: 12rpx
|
||||
gap: 20rpx
|
||||
|
||||
.item-content
|
||||
flex: 1
|
||||
|
||||
.item-title
|
||||
font-size: 30rpx
|
||||
color: #333
|
||||
font-weight: 500
|
||||
margin-bottom: 8rpx
|
||||
|
||||
.item-desc
|
||||
font-size: 24rpx
|
||||
color: #999
|
||||
|
||||
.tips-box
|
||||
padding: 24rpx
|
||||
background: #FFF3E0
|
||||
border-radius: 12rpx
|
||||
|
||||
.tip-item
|
||||
font-size: 26rpx
|
||||
color: #E65100
|
||||
line-height: 1.8
|
||||
margin-bottom: 12rpx
|
||||
|
||||
&:last-child
|
||||
margin-bottom: 0
|
||||
|
||||
// 按钮重置样式
|
||||
button::after
|
||||
border: none
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<view class="nav-hidden hidden-animation" :class="{ 'hidden-height': isScrollingDown }">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- 小程序背景图片 -->
|
||||
<image class="mp-background" src="/static/icon/background2.png" mode="aspectFill"></image>
|
||||
<!-- #endif -->
|
||||
|
||||
<view
|
||||
class="nav-hidden hidden-animation"
|
||||
:class="{ 'hidden-height': shouldHideTop }"
|
||||
>
|
||||
<view class="container-search">
|
||||
<view class="search-input button-click" @click="navTo('/pages/search/search')">
|
||||
<uni-icons class="iconsearch" color="#666666" type="search" size="18"></uni-icons>
|
||||
@@ -8,18 +16,87 @@
|
||||
</view>
|
||||
<!-- <view class="chart button-click">职业图谱</view> -->
|
||||
</view>
|
||||
<view class="cards" v-if="userInfo.isCompanyUser">
|
||||
<view class="card press-button" @click="navTo('/pages/nearby/nearby')">
|
||||
<view class="cards" v-if="userInfo.userType !== 0">
|
||||
<view class="card press-button" @click="handleNearbyClick">
|
||||
<view class="card-title">附近工作</view>
|
||||
<view class="card-text">好岗职等你来</view>
|
||||
</view>
|
||||
<view class="card press-button" @click="navTo('/packageA/pages/choiceness/choiceness')">
|
||||
<!-- <view class="card press-button" @click="navTo('/packageA/pages/choiceness/choiceness')">
|
||||
<view class="card-title">精选企业</view>
|
||||
<view class="card-text">优选职得信赖</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 服务功能网格 -->
|
||||
<view class="service-grid" v-if="userInfo.userType !== 0">
|
||||
<view class="service-item press-button" @click="handleServiceClick('service-guidance')">
|
||||
<view class="service-icon service-icon-1">
|
||||
<uni-icons type="auth-filled" size="32" color="#FFFFFF"></uni-icons>
|
||||
</view>
|
||||
<view class="service-title">服务指导</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('public-recruitment')">
|
||||
<view class="service-icon service-icon-2">
|
||||
<IconfontIcon name="zhengfulou" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">事业单位招录</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('resume-creation')">
|
||||
<view class="service-icon service-icon-3">
|
||||
<IconfontIcon name="jianli" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">简历制作</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('labor-policy')">
|
||||
<view class="service-icon service-icon-4">
|
||||
<IconfontIcon name="zhengce" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">劳动政策指引</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('skill-training')">
|
||||
<view class="service-icon service-icon-5">
|
||||
<IconfontIcon name="jinengpeixun" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">技能培训信息</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('skill-evaluation')">
|
||||
<view class="service-icon service-icon-6">
|
||||
<IconfontIcon name="jinengpingjia" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">技能评价指引</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('question-bank')">
|
||||
<view class="service-icon service-icon-7">
|
||||
<IconfontIcon name="suzhicepingtiku" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">题库和考试</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('quality-assessment')">
|
||||
<view class="service-icon service-icon-8">
|
||||
<IconfontIcon name="suzhicepingtiku" :size="48" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">素质测评</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="handleServiceClick('ai-interview')">
|
||||
<view class="service-icon service-icon-9">
|
||||
<IconfontIcon name="ai" :size="68" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">AI智能面试</view>
|
||||
</view>
|
||||
<view class="service-item press-button" @click="navTo('/packageB/pages/search/search')">
|
||||
<view class="service-icon service-icon-9">
|
||||
<IconfontIcon name="ai" :size="68" color="#FFFFFF" />
|
||||
</view>
|
||||
<view class="service-title">AI智慧就业服务</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="nav-filter" v-if="userInfo.isCompanyUser">
|
||||
<!-- 吸顶筛选区域占位 -->
|
||||
<view class="filter-placeholder" v-if="shouldStickyFilter && userInfo.userType !== 0"></view>
|
||||
|
||||
|
||||
<view class="nav-filter" :class="{ 'sticky-filter': shouldStickyFilter }" v-if="userInfo.userType !== 0">
|
||||
<view class="filter-top" @touchmove.stop.prevent>
|
||||
<scroll-view :scroll-x="true" :show-scrollbar="false" class="tab-scroll">
|
||||
<view class="jobs-left">
|
||||
@@ -65,8 +142,94 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="table-list">
|
||||
<scroll-view :scroll-y="true" class="falls-scroll" @scroll="handleScroll" @scrolltolower="scrollBottom">
|
||||
<scroll-view
|
||||
:scroll-y="true"
|
||||
class="falls-scroll"
|
||||
@scroll="handleScroll"
|
||||
@scrolltolower="scrollBottom"
|
||||
:enable-back-to-top="false"
|
||||
:scroll-with-animation="false"
|
||||
>
|
||||
<view class="falls" v-if="list.length">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- 小程序使用具名插槽 -->
|
||||
<custom-waterfalls-flow
|
||||
:column="columnCount"
|
||||
:columnSpace="columnSpace"
|
||||
ref="waterfallsFlowRef"
|
||||
:value="list"
|
||||
>
|
||||
<view v-for="(job, index) in list" :key="index" :slot="`slot${index}`">
|
||||
<view class="item btn-feel" v-if="!job.recommend">
|
||||
<view class="falls-card" @click="nextDetail(job)">
|
||||
<view class="falls-card-pay">
|
||||
<view class="pay-text">
|
||||
<Salary-Expectation
|
||||
:max-salary="job.maxSalary"
|
||||
:min-salary="job.minSalary"
|
||||
:is-month="true"
|
||||
></Salary-Expectation>
|
||||
</view>
|
||||
<image v-if="job.isHot" class="flame" src="/static/icon/flame.png"></image>
|
||||
</view>
|
||||
<view class="falls-card-title">{{ job.jobTitle }}</view>
|
||||
<view class="fl_box fl_warp">
|
||||
<view class="falls-card-education mar_ri10" v-if="job.education">
|
||||
<dict-Label dictType="education" :value="job.education"></dict-Label>
|
||||
</view>
|
||||
<view class="falls-card-experience" v-if="job.experience">
|
||||
<dict-Label dictType="experience" :value="job.experience"></dict-Label>
|
||||
</view>
|
||||
</view>
|
||||
<view class="falls-card-company" v-show="isShowJw !== 3">
|
||||
{{ config.appInfo.areaName }}
|
||||
<!-- {{ job.jobLocation }} -->
|
||||
<dict-Label dictType="area" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
</view>
|
||||
<view class="falls-card-pepleNumber">
|
||||
<view>
|
||||
<image class="point2" src="/static/icon/pintDate.png"></image>
|
||||
<view class="fl_1">
|
||||
{{ job.postingDate || '发布日期' }}
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<image class="point3" src="/static/icon/pointpeople.png"></image>
|
||||
<view class="fl_1">
|
||||
{{ vacanciesTo(job.vacancies) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="falls-card-company2">
|
||||
<image class="point3" src="/static/icon/point3.png"></image>
|
||||
<view class="fl_1">
|
||||
{{ job.companyName }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="falls-card-matchingrate">
|
||||
<view class=""><matchingDegree :job="job"></matchingDegree></view>
|
||||
<uni-icons type="star" size="30"></uni-icons>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<view class="item" :class="{ isBut: job.isBut }" v-else>
|
||||
<view class="recommend-card">
|
||||
<view class="card-content">
|
||||
<view class="recommend-card-title">在找「{{ job.jobCategory }}」工作吗?</view>
|
||||
<view class="recommend-card-tip">{{ job.tip }}</view>
|
||||
<view class="recommend-card-line"></view>
|
||||
<view class="recommend-card-controll">
|
||||
<view class="controll-no" @click="clearfindJob(job)">不是</view>
|
||||
<view class="controll-yes" @click="findJob(job)">是的</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</custom-waterfalls-flow>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<!-- H5/App使用作用域插槽 -->
|
||||
<custom-waterfalls-flow
|
||||
:column="columnCount"
|
||||
:columnSpace="columnSpace"
|
||||
@@ -141,6 +304,7 @@
|
||||
</view>
|
||||
</template>
|
||||
</custom-waterfalls-flow>
|
||||
<!-- #endif -->
|
||||
<loadmore ref="loadmoreRef"></loadmore>
|
||||
</view>
|
||||
<empty v-else pdTop="200"></empty>
|
||||
@@ -149,6 +313,9 @@
|
||||
<!-- 筛选 -->
|
||||
<select-filter ref="selectFilterModel"></select-filter>
|
||||
|
||||
<!-- 微信授权登录弹窗 -->
|
||||
<WxAuthLogin ref="wxAuthLoginRef" @success="handleLoginSuccess"></WxAuthLogin>
|
||||
|
||||
<!-- <view class="maskFristEntry" v-if="maskFristEntry">
|
||||
<view class="entry-content">
|
||||
<text class="text1">左滑查看视频</text>
|
||||
@@ -161,14 +328,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted, watchEffect, nextTick } from 'vue';
|
||||
import { reactive, inject, watch, ref, onMounted, onUnmounted, watchEffect, nextTick } from 'vue';
|
||||
import img from '@/static/icon/filter.png';
|
||||
import dictLabel from '@/components/dict-Label/dict-Label.vue';
|
||||
const { $api, navTo, vacanciesTo, formatTotal, config } = inject('globalFunction');
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
const { userInfo } = storeToRefs(useUserStore());
|
||||
const { userInfo, hasLogin, token } = storeToRefs(useUserStore());
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
const { getTransformChildren, oneDictData } = useDictStore();
|
||||
import useLocationStore from '@/stores/useLocationStore';
|
||||
@@ -176,15 +343,60 @@ import selectFilter from '@/components/selectFilter/selectFilter.vue';
|
||||
import { useRecommedIndexedDBStore, jobRecommender } from '@/stores/useRecommedIndexedDBStore.js';
|
||||
import { useScrollDirection } from '@/hook/useScrollDirection';
|
||||
import { useColumnCount } from '@/hook/useColumnCount';
|
||||
const { isScrollingDown, handleScroll } = useScrollDirection();
|
||||
import WxAuthLogin from '@/components/WxAuthLogin/WxAuthLogin.vue';
|
||||
import IconfontIcon from '@/components/IconfontIcon/IconfontIcon.vue'
|
||||
// 滚动状态管理
|
||||
const shouldHideTop = ref(false);
|
||||
const shouldStickyFilter = ref(false);
|
||||
const lastScrollTop = ref(0);
|
||||
const scrollTop = ref(0);
|
||||
// 当用户与筛选/导航交互时,临时锁定头部显示状态,避免因数据刷新导致回弹显示
|
||||
const isInteractingWithFilter = ref(false);
|
||||
|
||||
// 滚动阈值配置
|
||||
const HIDE_THRESHOLD = 50; // 隐藏顶部区域的滚动阈值(降低阈值,更容易触发)
|
||||
const SHOW_THRESHOLD = 5; // 显示顶部区域的滚动阈值(接近顶部)
|
||||
const STICKY_THRESHOLD = 80; // 筛选区域吸顶的滚动阈值
|
||||
|
||||
// 简化的滚动处理函数
|
||||
function handleScroll(e) {
|
||||
const currentScrollTop = e.detail.scrollTop || 0;
|
||||
|
||||
// 简单的滚动逻辑:向下滚动超过阈值就隐藏,滚动到顶部就显示
|
||||
if (currentScrollTop > HIDE_THRESHOLD) {
|
||||
if (!shouldHideTop.value) {
|
||||
shouldHideTop.value = true;
|
||||
}
|
||||
} else if (currentScrollTop <= SHOW_THRESHOLD) {
|
||||
// 仅在非交互期才允许自动显示顶部区域
|
||||
if (shouldHideTop.value && !isInteractingWithFilter.value) {
|
||||
shouldHideTop.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 控制筛选区域吸顶
|
||||
if (currentScrollTop > STICKY_THRESHOLD) {
|
||||
if (!shouldStickyFilter.value) {
|
||||
shouldStickyFilter.value = true;
|
||||
}
|
||||
} else {
|
||||
if (shouldStickyFilter.value) {
|
||||
shouldStickyFilter.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新最后的滚动位置
|
||||
lastScrollTop.value = currentScrollTop;
|
||||
scrollTop.value = currentScrollTop;
|
||||
}
|
||||
const recommedIndexDb = useRecommedIndexedDBStore();
|
||||
const emits = defineEmits(['onShowTabbar']);
|
||||
console.log(userInfo.value);
|
||||
const waterfallsFlowRef = ref(null);
|
||||
const loadmoreRef = ref(null);
|
||||
const conditionSearch = ref({});
|
||||
const waterfallcolumn = ref(2);
|
||||
const maskFristEntry = ref(false);
|
||||
const wxAuthLoginRef = ref(null);
|
||||
const state = reactive({
|
||||
tabIndex: 'all',
|
||||
});
|
||||
@@ -219,6 +431,42 @@ const { columnCount, columnSpace } = useColumnCount(() => {
|
||||
});
|
||||
});
|
||||
|
||||
// 组件初始化时加载数据
|
||||
onMounted(() => {
|
||||
getJobRecommend('refresh');
|
||||
});
|
||||
|
||||
// 登录检查函数
|
||||
const checkLogin = () => {
|
||||
const tokenValue = uni.getStorageSync('token') || '';
|
||||
if (!tokenValue || !hasLogin.value) {
|
||||
// 未登录,打开授权弹窗
|
||||
wxAuthLoginRef.value?.open();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 登录成功回调
|
||||
const handleLoginSuccess = () => {
|
||||
// 登录成功后刷新数据
|
||||
getJobRecommend('refresh');
|
||||
};
|
||||
|
||||
// 处理附近工作点击
|
||||
const handleNearbyClick = () => {
|
||||
if (checkLogin()) {
|
||||
navTo('/pages/nearby/nearby');
|
||||
}
|
||||
};
|
||||
|
||||
// 处理服务功能点击
|
||||
const handleServiceClick = (serviceType) => {
|
||||
if (checkLogin()) {
|
||||
navToService(serviceType);
|
||||
}
|
||||
};
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
if (isLoaded.value) return;
|
||||
@@ -289,15 +537,48 @@ function clearfindJob(job) {
|
||||
}
|
||||
|
||||
function nextDetail(job) {
|
||||
// 记录岗位类型,用作数据分析
|
||||
if (job.jobCategory) {
|
||||
const recordData = recommedIndexDb.JobParameter(job);
|
||||
recommedIndexDb.addRecord(recordData);
|
||||
// 登录检查
|
||||
if (checkLogin()) {
|
||||
// 记录岗位类型,用作数据分析
|
||||
if (job.jobCategory) {
|
||||
const recordData = recommedIndexDb.JobParameter(job);
|
||||
recommedIndexDb.addRecord(recordData);
|
||||
}
|
||||
navTo(`/packageA/pages/post/post?jobId=${encodeURIComponent(job.jobId)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function navToService(serviceType) {
|
||||
// 根据服务类型跳转到不同页面
|
||||
const serviceRoutes = {
|
||||
'service-guidance': '/pages/service/guidance',
|
||||
'public-recruitment': '/pages/service/public-recruitment',
|
||||
'resume-creation': '/packageA/pages/myResume/myResume',
|
||||
'labor-policy': '/pages/service/labor-policy',
|
||||
'skill-training': '/pages/service/skill-training',
|
||||
'skill-evaluation': '/pages/service/skill-evaluation',
|
||||
'question-bank': '/pages/service/question-bank',
|
||||
'quality-assessment': '/pages/service/quality-assessment',
|
||||
'ai-interview': '/pages/chat/chat',
|
||||
'job-search': '/pages/search/search',
|
||||
'career-planning': '/pages/service/career-planning',
|
||||
'salary-query': '/pages/service/salary-query',
|
||||
'company-info': '/pages/service/company-info',
|
||||
'interview-tips': '/pages/service/interview-tips',
|
||||
'employment-news': '/pages/service/employment-news',
|
||||
'more-services': '/pages/service/more-services'
|
||||
};
|
||||
|
||||
const route = serviceRoutes[serviceType];
|
||||
if (route) {
|
||||
navTo(route);
|
||||
} else {
|
||||
$api.msg('功能开发中,敬请期待');
|
||||
}
|
||||
navTo(`/packageA/pages/post/post?jobId=${btoa(job.jobId)}`);
|
||||
}
|
||||
|
||||
function openFilter() {
|
||||
isInteractingWithFilter.value = true;
|
||||
showFilter.value = true;
|
||||
emits('onShowTabbar', false);
|
||||
selectFilterModel.value?.open({
|
||||
@@ -308,23 +589,32 @@ function openFilter() {
|
||||
...pageState.search,
|
||||
};
|
||||
for (const [key, value] of Object.entries(values)) {
|
||||
pageState.search[key] = value.join(',');
|
||||
// 特殊处理岗位类型,直接传递数字值
|
||||
if (key === 'jobType') {
|
||||
pageState.search.type = value.join(',');
|
||||
} else {
|
||||
pageState.search[key] = value.join(',');
|
||||
}
|
||||
}
|
||||
showFilter.value = false;
|
||||
getJobList('refresh');
|
||||
// 短暂延迟后解除交互锁,避免数据刷新导致顶部区域回弹
|
||||
setTimeout(() => { isInteractingWithFilter.value = false; }, 400);
|
||||
},
|
||||
cancel: () => {
|
||||
showFilter.value = false;
|
||||
emits('onShowTabbar', true);
|
||||
setTimeout(() => { isInteractingWithFilter.value = false; }, 200);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleFilterConfirm(e) {
|
||||
console.log(e);
|
||||
// 处理筛选确认
|
||||
}
|
||||
|
||||
function choosePosition(index) {
|
||||
isInteractingWithFilter.value = true;
|
||||
state.tabIndex = index;
|
||||
list.value = [];
|
||||
if (index === 'all') {
|
||||
@@ -339,10 +629,11 @@ function choosePosition(index) {
|
||||
inputText.value = '';
|
||||
getJobList('refresh');
|
||||
}
|
||||
nextTick(() => { setTimeout(() => { isInteractingWithFilter.value = false; }, 300); });
|
||||
}
|
||||
const isShowJw = ref(0);
|
||||
function handelHostestSearch(val) {
|
||||
console.log(val.value);
|
||||
isInteractingWithFilter.value = true;
|
||||
isShowJw.value = val.value;
|
||||
pageState.search.order = val.value;
|
||||
pageState.search.jobType = val.value === 3 ? 1 : 0;
|
||||
@@ -351,6 +642,7 @@ function handelHostestSearch(val) {
|
||||
} else {
|
||||
getJobList('refresh');
|
||||
}
|
||||
nextTick(() => { setTimeout(() => { isInteractingWithFilter.value = false; }, 300); });
|
||||
}
|
||||
|
||||
function getJobRecommend(type = 'add') {
|
||||
@@ -392,7 +684,8 @@ function getJobRecommend(type = 'add') {
|
||||
list.value.push(...reslist);
|
||||
});
|
||||
} else {
|
||||
list.value = dataToImg(data);
|
||||
const reslist = dataToImg(data);
|
||||
list.value = reslist;
|
||||
}
|
||||
// 切换状态
|
||||
if (loadmoreRef.value && typeof loadmoreRef.value.change === 'function') {
|
||||
@@ -451,11 +744,12 @@ function getJobList(type = 'add') {
|
||||
}
|
||||
|
||||
function dataToImg(data) {
|
||||
return data.map((item) => ({
|
||||
const result = data.map((item) => ({
|
||||
...item,
|
||||
image: img,
|
||||
hide: true,
|
||||
}));
|
||||
return result;
|
||||
}
|
||||
|
||||
defineExpose({ loadData });
|
||||
@@ -544,20 +838,46 @@ defineExpose({ loadData });
|
||||
|
||||
.app-container
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
/* #ifdef H5 */
|
||||
height: calc(100vh - var(--window-top) - var(--status-bar-height) - var(--window-bottom));
|
||||
background: url('@/static/icon/background2.png') 0 0 no-repeat;
|
||||
background-size: 100% 728rpx;
|
||||
/* #endif */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
/* 小程序不支持CSS中的本地图片,使用image标签替代 */
|
||||
position: relative;
|
||||
/* #endif */
|
||||
background-color: #FFFFFF;
|
||||
display: flex;
|
||||
flex-direction: column
|
||||
.hidden-animation
|
||||
max-height: 1000px;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
.hidden-height
|
||||
max-height: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.mp-background
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 728rpx;
|
||||
z-index: 0;
|
||||
/* #endif */
|
||||
.hidden-animation
|
||||
transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
will-change: transform, opacity;
|
||||
|
||||
.hidden-height
|
||||
opacity: 0 !important;
|
||||
transform: translateY(-100%) !important;
|
||||
pointer-events: none !important;
|
||||
max-height: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
.container-search
|
||||
padding: 16rpx 24rpx
|
||||
display: flex
|
||||
@@ -596,7 +916,7 @@ defineExpose({ loadData });
|
||||
padding: 10rpx 28rpx
|
||||
display: grid
|
||||
grid-gap: 38rpx;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: 1fr;
|
||||
.card
|
||||
height: calc(158rpx - 40rpx);
|
||||
padding: 22rpx 26rpx
|
||||
@@ -624,16 +944,217 @@ defineExpose({ loadData });
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
// 服务功能网格样式
|
||||
.service-grid
|
||||
padding: 20rpx 28rpx
|
||||
display: grid
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr
|
||||
grid-gap: 20rpx
|
||||
.service-item
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
height: 120rpx
|
||||
background: transparent
|
||||
padding: 10px 0px
|
||||
.service-icon
|
||||
width: 88rpx
|
||||
height: 88rpx
|
||||
border-radius: 12rpx
|
||||
margin-bottom: 8rpx
|
||||
flex-shrink: 0
|
||||
.service-icon-1
|
||||
background: linear-gradient(180deg, #FF8E8E 0%, #E53E3E 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-2
|
||||
background: linear-gradient(180deg, #6ED5CE 0%, #38B2AC 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-3
|
||||
background: linear-gradient(180deg, #6BC5D8 0%, #3182CE 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-4
|
||||
background: linear-gradient(180deg, #FFB74D 0%, #ED8936 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-5
|
||||
background: linear-gradient(180deg, #F06292 0%, #C2185B 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-6
|
||||
background: linear-gradient(180deg, #FFB74D 0%, #ED8936 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-7
|
||||
background: linear-gradient(180deg, #6BC5D8 0%, #3182CE 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-8
|
||||
background: linear-gradient(180deg, #81C784 0%, #4CAF50 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-9
|
||||
background: linear-gradient(180deg, #6BC5D8 0%, #3182CE 100%)
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
.service-icon-10
|
||||
background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '🔍'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-icon-11
|
||||
background: linear-gradient(135deg, #FF9800 0%, #FFB74D 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '📈'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-icon-12
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #81C784 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '💰'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-icon-13
|
||||
background: linear-gradient(135deg, #607D8B 0%, #90A4AE 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '🏢'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-icon-14
|
||||
background: linear-gradient(135deg, #E91E63 0%, #F06292 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '💡'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-icon-15
|
||||
background: linear-gradient(135deg, #795548 0%, #A1887F 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '📰'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-icon-16
|
||||
background: linear-gradient(135deg, #424242 0%, #757575 100%)
|
||||
position: relative
|
||||
&::before
|
||||
content: '⚙️'
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
font-size: 32rpx
|
||||
.service-title
|
||||
font-family: 'PingFangSC-Medium', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif
|
||||
font-weight: 500
|
||||
font-size: 24rpx
|
||||
color: #333333
|
||||
text-align: center
|
||||
line-height: 1.2
|
||||
white-space: nowrap
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
width: 100%
|
||||
max-width: 100%
|
||||
|
||||
// 吸顶筛选区域占位
|
||||
.filter-placeholder
|
||||
height: 140rpx; // 根据筛选区域的实际高度调整(包含padding)
|
||||
width: 100%;
|
||||
/* #ifdef H5 */
|
||||
height: 0; // H5使用sticky,不需要占位
|
||||
/* #endif */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
height: 140rpx; // 小程序需要占位
|
||||
/* #endif */
|
||||
|
||||
.nav-filter
|
||||
padding: 16rpx 28rpx 0 28rpx
|
||||
padding: 16rpx 28rpx
|
||||
box-sizing: border-box;
|
||||
font-family: 'PingFangSC-Medium', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||
transition: box-shadow 0.3s cubic-bezier(0.4, 0.0, 0.2, 1),
|
||||
transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||
background: #FFFFFF;
|
||||
z-index: 10;
|
||||
|
||||
&.sticky-filter
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
z-index: 100;
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
/* #ifdef H5 */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
/* #endif */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
padding: 16rpx 28rpx;
|
||||
padding-top: calc(16rpx + env(safe-area-inset-top));
|
||||
padding-left: calc(28rpx + env(safe-area-inset-left));
|
||||
padding-right: calc(28rpx + env(safe-area-inset-right));
|
||||
/* #endif */
|
||||
.filter-top
|
||||
display: flex
|
||||
justify-content: space-between;
|
||||
.tab-scroll
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-right: 20rpx
|
||||
margin-right: 20rpx;
|
||||
/* #ifdef MP-WEIXIN */
|
||||
margin-right: 0;
|
||||
/* #endif */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
@@ -661,6 +1182,9 @@ defineExpose({ loadData });
|
||||
font-size: 32rpx;
|
||||
color: #666D7F;
|
||||
line-height: 38rpx;
|
||||
min-width: 80rpx;
|
||||
padding: 8rpx 12rpx;
|
||||
white-space: nowrap;
|
||||
.filter-bottom
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
@@ -692,9 +1216,12 @@ defineExpose({ loadData });
|
||||
background: #F4F4F4
|
||||
flex: 1
|
||||
overflow: hidden
|
||||
height: 0; // 确保flex容器正确计算高度
|
||||
.falls-scroll
|
||||
width: 100%
|
||||
height: 100%
|
||||
// 确保滚动容器可以正常滚动
|
||||
-webkit-overflow-scrolling: touch;
|
||||
.falls
|
||||
padding: 28rpx 28rpx;
|
||||
.item
|
||||
|
||||
@@ -151,7 +151,7 @@ function nextDetail(job) {
|
||||
const recordData = recommedIndexDb.JobParameter(job);
|
||||
recommedIndexDb.addRecord(recordData);
|
||||
}
|
||||
navTo(`/packageA/pages/post/post?jobId=${btoa(job.jobId)}`);
|
||||
navTo(`/packageA/pages/post/post?jobId=${encodeURIComponent(job.jobId)}`);
|
||||
}
|
||||
|
||||
function nextVideo(job) {
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
<IndexOne @onShowTabbar="changeShowTabbar" />
|
||||
</view>
|
||||
|
||||
<Tabbar :currentpage="0"></Tabbar>
|
||||
<!-- 统一使用系统tabBar -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted } from 'vue';
|
||||
import Tabbar from '@/components/tabbar/midell-box.vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import IndexOne from './components/index-one.vue';
|
||||
// import IndexTwo from './components/index-two.vue';
|
||||
@@ -22,7 +21,7 @@ import { useReadMsg } from '@/stores/useReadMsg';
|
||||
const { unreadCount } = storeToRefs(useReadMsg());
|
||||
|
||||
onLoad(() => {
|
||||
useReadMsg().fetchMessages();
|
||||
// useReadMsg().fetchMessages();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
454
pages/job/publishJob.vue
Normal file
454
pages/job/publishJob.vue
Normal file
@@ -0,0 +1,454 @@
|
||||
<template>
|
||||
<view class="publish-job-page">
|
||||
<!-- 头部导航 -->
|
||||
<view class="header">
|
||||
<view class="header-left" @click="goBack">
|
||||
<image src="@/static/icon/back.png" class="back-icon"></image>
|
||||
</view>
|
||||
<view class="header-title">发布岗位</view>
|
||||
<view class="header-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<view class="content">
|
||||
<!-- 岗位基本信息 -->
|
||||
<view class="section">
|
||||
<view class="section-title">岗位基本信息</view>
|
||||
<view class="form-group">
|
||||
<view class="label">岗位名称 *</view>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="请输入岗位名称"
|
||||
v-model="formData.jobTitle"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">岗位类型 *</view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="jobTypes"
|
||||
@change="onJobTypeChange"
|
||||
class="picker"
|
||||
>
|
||||
<view class="picker-text">{{ selectedJobType || '请选择岗位类型' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">工作地点 *</view>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="请输入工作地点"
|
||||
v-model="formData.workLocation"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 薪资待遇 -->
|
||||
<view class="section">
|
||||
<view class="section-title">薪资待遇</view>
|
||||
<view class="salary-row">
|
||||
<view class="form-group">
|
||||
<view class="label">最低薪资</view>
|
||||
<input
|
||||
class="input salary-input"
|
||||
placeholder="0"
|
||||
type="number"
|
||||
v-model="formData.minSalary"
|
||||
/>
|
||||
</view>
|
||||
<view class="salary-separator">-</view>
|
||||
<view class="form-group">
|
||||
<view class="label">最高薪资</view>
|
||||
<input
|
||||
class="input salary-input"
|
||||
placeholder="0"
|
||||
type="number"
|
||||
v-model="formData.maxSalary"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">薪资单位</view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="salaryUnits"
|
||||
@change="onSalaryUnitChange"
|
||||
class="picker"
|
||||
>
|
||||
<view class="picker-text">{{ selectedSalaryUnit || '请选择薪资单位' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 任职要求 -->
|
||||
<view class="section">
|
||||
<view class="section-title">任职要求</view>
|
||||
<view class="form-group">
|
||||
<view class="label">学历要求</view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="educationLevels"
|
||||
@change="onEducationChange"
|
||||
class="picker"
|
||||
>
|
||||
<view class="picker-text">{{ selectedEducation || '请选择学历要求' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">工作经验</view>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="experienceLevels"
|
||||
@change="onExperienceChange"
|
||||
class="picker"
|
||||
>
|
||||
<view class="picker-text">{{ selectedExperience || '请选择工作经验' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">招聘人数</view>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="请输入招聘人数"
|
||||
type="number"
|
||||
v-model="formData.recruitCount"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 岗位描述 -->
|
||||
<view class="section">
|
||||
<view class="section-title">岗位描述</view>
|
||||
<view class="form-group">
|
||||
<view class="label">岗位职责</view>
|
||||
<textarea
|
||||
class="textarea"
|
||||
placeholder="请详细描述岗位职责和工作内容"
|
||||
v-model="formData.jobDescription"
|
||||
></textarea>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">任职要求</view>
|
||||
<textarea
|
||||
class="textarea"
|
||||
placeholder="请描述对候选人的具体要求"
|
||||
v-model="formData.jobRequirements"
|
||||
></textarea>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<view class="section">
|
||||
<view class="section-title">联系方式</view>
|
||||
<view class="form-group">
|
||||
<view class="label">联系人</view>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="请输入联系人姓名"
|
||||
v-model="formData.contactPerson"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">联系电话</view>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="请输入联系电话"
|
||||
v-model="formData.contactPhone"
|
||||
/>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<view class="label">联系邮箱</view>
|
||||
<input
|
||||
class="input"
|
||||
placeholder="请输入联系邮箱"
|
||||
v-model="formData.contactEmail"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="footer">
|
||||
<view class="btn-group">
|
||||
<button class="btn btn-cancel" @click="goBack">取消</button>
|
||||
<button class="btn btn-publish" @click="publishJob">发布岗位</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
jobTitle: '',
|
||||
workLocation: '',
|
||||
minSalary: '',
|
||||
maxSalary: '',
|
||||
recruitCount: '',
|
||||
jobDescription: '',
|
||||
jobRequirements: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
contactEmail: ''
|
||||
});
|
||||
|
||||
// 选择器数据
|
||||
const jobTypes = ['技术类', '销售类', '管理类', '服务类', '其他'];
|
||||
const salaryUnits = ['元/月', '元/年', '元/小时'];
|
||||
const educationLevels = ['不限', '高中', '大专', '本科', '硕士', '博士'];
|
||||
const experienceLevels = ['不限', '应届毕业生', '1-3年', '3-5年', '5-10年', '10年以上'];
|
||||
|
||||
// 选中的值
|
||||
const selectedJobType = ref('');
|
||||
const selectedSalaryUnit = ref('');
|
||||
const selectedEducation = ref('');
|
||||
const selectedExperience = ref('');
|
||||
|
||||
// 选择器事件处理
|
||||
const onJobTypeChange = (e) => {
|
||||
selectedJobType.value = jobTypes[e.detail.value];
|
||||
};
|
||||
|
||||
const onSalaryUnitChange = (e) => {
|
||||
selectedSalaryUnit.value = salaryUnits[e.detail.value];
|
||||
};
|
||||
|
||||
const onEducationChange = (e) => {
|
||||
selectedEducation.value = educationLevels[e.detail.value];
|
||||
};
|
||||
|
||||
const onExperienceChange = (e) => {
|
||||
selectedExperience.value = experienceLevels[e.detail.value];
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
|
||||
// 发布岗位
|
||||
const publishJob = () => {
|
||||
// 简单验证
|
||||
if (!formData.jobTitle) {
|
||||
uni.showToast({
|
||||
title: '请输入岗位名称',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.workLocation) {
|
||||
uni.showToast({
|
||||
title: '请输入工作地点',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 模拟发布成功
|
||||
uni.showLoading({
|
||||
title: '发布中...'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '发布成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 延迟返回
|
||||
setTimeout(() => {
|
||||
goBack();
|
||||
}, 1500);
|
||||
}, 2000);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.publish-job-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
|
||||
.header-left {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
width: 60rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: #fff;
|
||||
border-radius: 0;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 2rpx solid #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: #fff;
|
||||
border: none;
|
||||
border-bottom: 2rpx solid #e0e0e0;
|
||||
border-radius: 0;
|
||||
padding: 0 0 10rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
|
||||
&:focus {
|
||||
border-bottom-color: #256BFA;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.textarea {
|
||||
width: 100%;
|
||||
min-height: 120rpx;
|
||||
background: #fff;
|
||||
border: none;
|
||||
border-bottom: 2rpx solid #e0e0e0;
|
||||
border-radius: 0;
|
||||
padding: 20rpx 0 10rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
|
||||
&:focus {
|
||||
border-bottom-color: #256BFA;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.picker {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: #fff;
|
||||
border: none;
|
||||
border-bottom: 2rpx solid #e0e0e0;
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0 10rpx 0;
|
||||
|
||||
.picker-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.salary-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.form-group {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.salary-separator {
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.salary-input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
padding: 20rpx 30rpx;
|
||||
border-top: 1rpx solid #eee;
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
|
||||
&.btn-cancel {
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
&.btn-publish {
|
||||
background: #256BFA;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -97,7 +97,7 @@
|
||||
</uni-popup>
|
||||
</view>
|
||||
<template #footer>
|
||||
<Tabbar :currentpage="4"></Tabbar>
|
||||
<!-- 统一使用系统tabBar -->
|
||||
</template>
|
||||
</AppLayout>
|
||||
</template>
|
||||
@@ -105,7 +105,6 @@
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import Tabbar from '@/components/tabbar/midell-box.vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
const { $api, navTo } = inject('globalFunction');
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<Tabbar :currentpage="3"></Tabbar>
|
||||
<!-- 统一使用系统tabBar -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -166,7 +166,7 @@ onLoad(() => {
|
||||
});
|
||||
|
||||
function navToPost(jobId) {
|
||||
navTo(`/packageA/pages/post/post?jobId=${btoa(jobId)}`);
|
||||
navTo(`/packageA/pages/post/post?jobId=${encodeURIComponent(jobId)}`);
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="container safe-area-top">
|
||||
<view>
|
||||
<view class="top">
|
||||
<image class="btnback button-click" src="@/static/icon/back.png" @click="navBack"></image>
|
||||
@@ -135,7 +135,7 @@ function nextDetail(job) {
|
||||
const recordData = recommedIndexDb.JobParameter(job);
|
||||
recommedIndexDb.addRecord(recordData);
|
||||
}
|
||||
navTo(`/packageA/pages/post/post?jobId=${btoa(job.jobId)}`);
|
||||
navTo(`/packageA/pages/post/post?jobId=${encodeURIComponent(job.jobId)}`);
|
||||
}
|
||||
|
||||
function nextVideo(job) {
|
||||
|
||||
218
pages/test/userTypeTest.vue
Normal file
218
pages/test/userTypeTest.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<view class="test-page">
|
||||
<view class="header">
|
||||
<text class="title">用户类型测试页面</text>
|
||||
</view>
|
||||
|
||||
<view class="content">
|
||||
<view class="current-info">
|
||||
<text class="label">当前用户类型:</text>
|
||||
<text class="value">{{ getCurrentTypeLabel() }}</text>
|
||||
</view>
|
||||
|
||||
<view class="type-switcher">
|
||||
<text class="section-title">切换用户类型:</text>
|
||||
<view class="buttons">
|
||||
<button
|
||||
v-for="(type, index) in userTypes"
|
||||
:key="index"
|
||||
:class="['btn', { active: currentUserType === type.value }]"
|
||||
@click="switchUserType(type.value)"
|
||||
>
|
||||
{{ type.label }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="navigation-info">
|
||||
<text class="section-title">底部导航栏配置:</text>
|
||||
<view class="nav-items">
|
||||
<view
|
||||
v-for="(item, index) in tabbarConfig"
|
||||
:key="index"
|
||||
class="nav-item"
|
||||
>
|
||||
<text class="nav-text">{{ item.text || 'AI助手' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="description">
|
||||
<text class="desc-title">说明:</text>
|
||||
<text class="desc-text">• 企业用户(userType=0):显示"发布岗位"导航,隐藏"招聘会"</text>
|
||||
<text class="desc-text">• 其他用户(userType=1,2,3):显示"招聘会"导航</text>
|
||||
<text class="desc-text">• 切换用户类型后,底部导航栏会自动更新</text>
|
||||
<text class="desc-text">• 企业用户模式下,"招聘会"导航项完全隐藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
|
||||
const { userInfo } = storeToRefs(useUserStore());
|
||||
|
||||
const userTypes = [
|
||||
{ value: 0, label: '企业用户' },
|
||||
{ value: 1, label: '求职者' },
|
||||
{ value: 2, label: '网格员' },
|
||||
{ value: 3, label: '政府人员' }
|
||||
];
|
||||
|
||||
const currentUserType = computed(() => userInfo.value?.userType || 0);
|
||||
|
||||
const switchUserType = (userType) => {
|
||||
userInfo.value.userType = userType;
|
||||
uni.setStorageSync('userInfo', userInfo.value);
|
||||
uni.showToast({
|
||||
title: `已切换到${getCurrentTypeLabel()}`,
|
||||
icon: 'success'
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentTypeLabel = () => {
|
||||
const type = userTypes.find(t => t.value === currentUserType.value);
|
||||
return type ? type.label : '未知';
|
||||
};
|
||||
|
||||
const tabbarConfig = computed(() => {
|
||||
const baseItems = [
|
||||
{ text: '首页' },
|
||||
{ text: currentUserType.value === 0 ? '发布岗位' : '招聘会' },
|
||||
{ text: '' }, // AI助手
|
||||
{ text: '消息' },
|
||||
{ text: '我的' }
|
||||
];
|
||||
return baseItems;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.test-page {
|
||||
padding: 40rpx;
|
||||
background: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.current-info {
|
||||
background: #fff;
|
||||
padding: 30rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #256BFA;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.type-switcher {
|
||||
background: #fff;
|
||||
padding: 30rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15rpx;
|
||||
|
||||
.btn {
|
||||
padding: 15rpx 30rpx;
|
||||
border: 2rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
background: #fff;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
|
||||
&.active {
|
||||
background: #256BFA;
|
||||
color: #fff;
|
||||
border-color: #256BFA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-info {
|
||||
background: #fff;
|
||||
padding: 30rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-items {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
.nav-item {
|
||||
text-align: center;
|
||||
|
||||
.nav-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
background: #fff;
|
||||
padding: 30rpx;
|
||||
border-radius: 10rpx;
|
||||
|
||||
.desc-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.desc-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user