提交 17c12bdd 作者: 廖在望

feat: app页面效果调整。

上级 b4b30b49
......@@ -685,6 +685,8 @@
"pageOrientation": "portrait",
"app-plus": {
"scrollIndicator": "none",
"popGesture": "close",
"bounce": "none",
"titleNView": {
"titleSize": "18"
}
......
......@@ -225,22 +225,26 @@
</view>
<!-- 商品展示 (商城化) -->
<view class="section-card">
<view class="section-card product-section">
<view class="section-title">主营产品</view>
<view v-if="!pageData.enterpriseProduct.length" class="empty-products">
商家暂未发布产品
</view>
<view class="product-grid">
<view class="product-item" v-for="item in pageData.enterpriseProduct" :key="item.id">
<image class="p-img" :src="item.image" mode="aspectFill" lazy-load />
<view class="p-img-wrap">
<image class="p-img" :src="item.image" mode="aspectFill" lazy-load />
</view>
<view class="p-info">
<text class="p-name">{{ item.name }}</text>
<view class="p-price-row">
<text class="p-symbol">¥</text>
<text class="p-price">{{ item.minSellPrice }}</text>
<text class="p-unit">/{{ item.unit }}</text>
<view class="p-bottom">
<view class="p-price-box">
<text class="p-symbol">¥</text>
<text class="p-price">{{ item.minSellPrice }}</text>
<text class="p-unit">/{{ item.unit }}</text>
</view>
<text class="p-tag">批发价</text>
</view>
<view class="p-buy-btn" @click="pageData.showConfirmDialog = true">咨询</view>
</view>
</view>
</view>
......@@ -256,12 +260,19 @@
<!-- 底部联系栏 -->
<view class="footer-bar">
<view class="contact-box" @click="pageData.showConfirmDialog = true">
<fui-icon name="mobile" :size="40" color="#5db66f"></fui-icon>
<text>联系商家</text>
<view class="footer-left">
<view class="action-item" @click="pageData.showConfirmDialog = true">
<fui-icon name="mobile" :size="44" color="#5db66f"></fui-icon>
<text>电话咨询</text>
</view>
<view class="action-divider"></view>
<view class="action-item" @click="pageData.showConfirmDialog = true">
<fui-icon name="notice" :size="44" color="#ff9500"></fui-icon>
<text>在线客服</text>
</view>
</view>
<view class="main-btn" @click="pageData.showConfirmDialog = true">
我要购买
<view class="buy-btn" @click="pageData.showConfirmDialog = true">
<text class="btn-text">立即购买</text>
</view>
</view>
......@@ -273,40 +284,97 @@
@confirm="makePhoneCall"
/>
<fui-fab position="right" distance="20" bottom="240" width="100" @click="handlePublish">
<view class="fab-content">
<fui-icon name="plus" :size="40" color="#fff"></fui-icon>
<text style="font-size: 20rpx; color:#fff">发布产品</text>
<!-- 发布产品悬浮按钮 -->
<view class="fab-container" @click="handlePublish">
<view class="ripple"></view>
<view class="ripple ripple-2"></view>
<view class="fab-entry">
<fui-icon name="plus" :size="44" color="#fff"></fui-icon>
<text>发布产品</text>
</view>
</fui-fab>
</view>
<!-- 发布产品弹窗 -->
<fui-bottom-popup :show="pageData.isPopupShow" @close="pageData.isPopupShow = false">
<view class="popup-wrap">
<!-- 弹窗头部 -->
<view class="popup-header">
<text>发布新产品</text>
<fui-icon name="close" @click="pageData.isPopupShow = false"></fui-icon>
<view class="header-left">
<view class="header-icon">
<fui-icon name="plus" :size="32" color="#fff"></fui-icon>
</view>
<text class="header-title">发布新产品</text>
</view>
<view class="close-btn" @click="pageData.isPopupShow = false">
<fui-icon name="close" :size="36" color="#999"></fui-icon>
</view>
</view>
<fui-form ref="formRef" top="20">
<fui-input label="产品名称" placeholder="请输入产品名称" v-model="productInfo.name" required />
<fui-input label="产品分类" placeholder="请选择" v-model="productInfo.category" disabled @click="pageData.categoryPopup = true" required />
<view class="price-edit-row">
<text class="label">售价范围</text>
<input type="number" v-model="productInfo.minSellPrice" placeholder="最低价" class="p-input" />
<text class="sep">-</text>
<input type="number" v-model="productInfo.maxSellPrice" placeholder="最高价" class="p-input" />
<view class="unit-sel" @click="pageData.unitPopup = true">
{{ productInfo.unit || '单位' }}
<!-- 表单区域 -->
<view class="form-section">
<!-- 产品名称 -->
<view class="form-item">
<view class="form-label">
<text class="label-text">产品名称</text>
<text class="required">*</text>
</view>
<input class="form-input" placeholder="请输入产品名称" v-model="productInfo.name" placeholder-class="input-placeholder" />
</view>
<!-- 产品分类 -->
<view class="form-item" @click="pageData.categoryPopup = true">
<view class="form-label">
<text class="label-text">产品分类</text>
<text class="required">*</text>
</view>
<view class="form-picker">
<text :class="['picker-text', productInfo.category ? '' : 'placeholder']">
{{ productInfo.category || '请选择分类' }}
</text>
<fui-icon name="arrowright" :size="28" color="#ccc"></fui-icon>
</view>
</view>
<!-- 售价范围 -->
<view class="form-item">
<view class="form-label">
<text class="label-text">售价范围</text>
<text class="required">*</text>
</view>
<view class="price-row">
<view class="price-input-wrap">
<text class="price-symbol">¥</text>
<input type="digit" class="price-input" placeholder="最低价" v-model="productInfo.minSellPrice" placeholder-class="input-placeholder" />
</view>
<text class="price-sep"></text>
<view class="price-input-wrap">
<text class="price-symbol">¥</text>
<input type="digit" class="price-input" placeholder="最高价" v-model="productInfo.maxSellPrice" placeholder-class="input-placeholder" />
</view>
<view class="unit-btn" @click="pageData.unitPopup = true">
{{ productInfo.unit || '单位' }}
</view>
</view>
</view>
<view class="upload-section">
<text class="label">产品图片</text>
<uni-file-picker v-model="pageData.productImageArr" limit="1" @select="handleUpload" @delete="handleDelete" />
<!-- 产品图片 -->
<view class="form-item">
<view class="form-label">
<text class="label-text">产品图片</text>
<text class="tip">(最多1张)</text>
</view>
<view class="upload-area">
<uni-file-picker v-model="pageData.productImageArr" limit="1" @select="handleUpload" @delete="handleDelete" />
</view>
</view>
<view class="submit-btn-box">
<fui-button text="立即发布" radius="100rpx" @click="addData"></fui-button>
</view>
<!-- 提交按钮 -->
<view class="submit-area">
<view class="submit-btn" @click="addData">
<text class="btn-text">立即发布</text>
</view>
</fui-form>
</view>
</view>
</fui-bottom-popup>
......@@ -381,18 +449,24 @@
.section-card {
background-color: #fff;
margin: 20rpx 24rpx;
border-radius: 20rpx;
padding: 30rpx;
border-radius: 16rpx;
padding: 28rpx 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.section-title {
font-size: 30rpx;
font-weight: bold;
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
padding-left: 16rpx;
border-left: 8rpx solid #5db66f;
margin-bottom: 20rpx;
padding-left: 14rpx;
border-left: 6rpx solid #5db66f;
}
}
.product-section {
margin: 20rpx 24rpx;
padding: 28rpx 20rpx 20rpx;
}
.cert-scroll {
width: 100%;
.cert-list {
......@@ -412,45 +486,69 @@
.product-grid {
display: flex;
flex-wrap: wrap;
margin: 0 -10rpx;
margin: 0 -6rpx;
.product-item {
width: 50%;
padding: 10rpx;
padding: 6rpx;
box-sizing: border-box;
.p-img {
.p-img-wrap {
width: 100%;
height: 280rpx;
height: 320rpx;
border-radius: 12rpx 12rpx 0 0;
background-color: #f8f8f8;
overflow: hidden;
background-color: #f5f5f5;
.p-img {
width: 100%;
height: 100%;
}
}
.p-info {
background-color: #f9f9f9;
padding: 16rpx;
background-color: #fafafa;
padding: 14rpx 12rpx 16rpx;
border-radius: 0 0 12rpx 12rpx;
.p-name {
font-size: 26rpx;
color: #333;
height: 72rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 36rpx;
height: 36rpx;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 500;
}
.p-price-row {
margin-top: 12rpx;
color: #ff4d4f;
.p-symbol { font-size: 20rpx; }
.p-price { font-size: 32rpx; font-weight: bold; }
.p-unit { font-size: 22rpx; color: #999; }
.p-bottom {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 6rpx;
}
.p-buy-btn {
margin-top: 16rpx;
background-color: #5db66f;
color: #fff;
font-size: 22rpx;
text-align: center;
padding: 8rpx 0;
border-radius: 24rpx;
.p-price-box {
display: flex;
align-items: baseline;
color: #e93b3b;
}
.p-symbol {
font-size: 20rpx;
font-weight: 600;
}
.p-price {
font-size: 30rpx;
font-weight: 700;
margin-left: 2rpx;
}
.p-unit {
font-size: 20rpx;
color: #999;
margin-left: 2rpx;
}
.p-tag {
font-size: 18rpx;
color: #ff6b35;
background: linear-gradient(135deg, #fff5f0 0%, #ffebe3 100%);
padding: 4rpx 8rpx;
border-radius: 4rpx;
border: 1rpx solid #ffd4c4;
}
}
}
......@@ -471,68 +569,313 @@
background-color: #fff;
display: flex;
align-items: center;
padding: 0 30rpx;
box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.05);
justify-content: space-between;
padding: 0 24rpx;
padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
z-index: 100;
.contact-box {
.footer-left {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 40rpx;
text { font-size: 20rpx; color: #666; margin-top: 4rpx; }
.action-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 0 24rpx;
text {
font-size: 20rpx;
color: #666;
margin-top: 4rpx;
}
}
.action-divider {
width: 1rpx;
height: 60rpx;
background-color: #eee;
}
}
.main-btn {
flex: 1;
height: 80rpx;
background-color: #5db66f;
color: #fff;
.buy-btn {
width: 240rpx;
height: 76rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
border-radius: 38rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40rpx;
font-size: 30rpx;
font-weight: bold;
box-shadow: 0 6rpx 20rpx rgba(93, 182, 111, 0.4);
.btn-text {
font-size: 28rpx;
font-weight: 600;
color: #fff;
letter-spacing: 2rpx;
}
}
}
.popup-wrap {
background-color: #fff;
padding: 30rpx;
border-radius: 30rpx 30rpx 0 0;
border-radius: 32rpx 32rpx 0 0;
overflow: hidden;
padding-bottom: env(safe-area-inset-bottom);
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
padding: 32rpx 32rpx 24rpx;
background: linear-gradient(135deg, #f8fdf9 0%, #fff 100%);
border-bottom: 1rpx solid #f0f0f0;
.header-left {
display: flex;
align-items: center;
}
.header-icon {
width: 56rpx;
height: 56rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(93, 182, 111, 0.3);
}
.header-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.close-btn {
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
border-radius: 50%;
}
}
.form-section {
padding: 24rpx 32rpx 0;
.form-item {
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
.form-label {
display: flex;
align-items: center;
margin-bottom: 16rpx;
.label-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.required {
color: #ff4d4f;
margin-left: 4rpx;
font-size: 28rpx;
}
.tip {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
}
.form-input {
width: 100%;
height: 80rpx;
background-color: #f7f8fa;
border-radius: 12rpx;
padding: 0 24rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
}
.form-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 80rpx;
background-color: #f7f8fa;
border-radius: 12rpx;
padding: 0 24rpx;
.picker-text {
font-size: 28rpx;
color: #333;
&.placeholder {
color: #bbb;
}
}
}
.price-row {
display: flex;
align-items: center;
.price-input-wrap {
flex: 1;
display: flex;
align-items: center;
height: 80rpx;
background-color: #f7f8fa;
border-radius: 12rpx;
padding: 0 20rpx;
.price-symbol {
font-size: 26rpx;
color: #5db66f;
font-weight: 600;
margin-right: 4rpx;
}
.price-input {
flex: 1;
font-size: 28rpx;
color: #333;
}
}
.price-sep {
margin: 0 16rpx;
color: #ccc;
font-size: 26rpx;
}
.unit-btn {
margin-left: 16rpx;
padding: 0 20rpx;
height: 80rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #fff;
font-weight: 500;
}
}
.upload-area {
margin-top: 8rpx;
}
}
.submit-area {
padding: 32rpx 32rpx 40rpx;
.submit-btn {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(93, 182, 111, 0.4);
.btn-text {
font-size: 30rpx;
color: #fff;
font-weight: 600;
letter-spacing: 4rpx;
}
}
}
}
.price-edit-row {
.input-placeholder {
color: #bbb;
font-size: 28rpx;
}
.empty-products { text-align: center; padding: 60rpx 0; color: #999; font-size: 26rpx; }
.area-sel-wrap { background-color: #fff; padding: 30rpx; }
.fab-container {
position: fixed;
right: 30rpx;
bottom: 140rpx;
width: 100rpx;
height: 100rpx;
z-index: 99;
display: flex;
align-items: center;
padding: 30rpx 0;
border-bottom: 1rpx solid #eee;
.label { width: 160rpx; font-size: 28rpx; color: #333; }
.p-input { flex: 1; font-size: 28rpx; }
.sep { margin: 0 20rpx; color: #ccc; }
.unit-sel {
padding: 4rpx 20rpx;
background-color: #f5f5f5;
font-size: 24rpx;
color: #666;
border-radius: 20rpx;
justify-content: center;
.ripple {
position: absolute;
width: 100%;
height: 100%;
background-color: #5db66f;
border-radius: 50%;
opacity: 0.4;
animation: ripple 2s infinite ease-out;
}
.ripple-2 {
animation-delay: 1s;
}
.fab-entry {
position: relative;
width: 100rpx;
height: 100rpx;
background: linear-gradient(135deg, #a5d63f 0%, #2e8b57 100%);
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 30rpx rgba(46, 139, 87, 0.5);
animation: breathe 2.5s infinite ease-in-out;
z-index: 2;
text {
font-size: 18rpx;
color: #fff;
margin-top: -4rpx;
font-weight: bold;
}
}
}
.upload-section {
padding: 30rpx 0;
.label { display: block; font-size: 28rpx; color: #333; margin-bottom: 20rpx; }
@keyframes ripple {
0% {
transform: scale(1);
opacity: 0.4;
}
100% {
transform: scale(1.8);
opacity: 0;
}
}
.submit-btn-box { padding: 40rpx 0; }
.fab-content { display: flex; flex-direction: column; align-items: center; }
.empty-products { text-align: center; padding: 60rpx 0; color: #999; font-size: 26rpx; }
.area-sel-wrap { background-color: #fff; padding: 30rpx; }
@keyframes breathe {
0%, 100% {
transform: scale(1);
box-shadow: 0 8rpx 30rpx rgba(46, 139, 87, 0.5);
}
50% {
transform: scale(1.05);
box-shadow: 0 12rpx 40rpx rgba(46, 139, 87, 0.6);
}
}
</style>
......@@ -291,7 +291,7 @@
<fui-icon name="search" color="#999" :size="32"></fui-icon>
<input class="search-input" v-model="model.searchText" placeholder="搜地点、技能..." placeholder-style="color:#ccc" @confirm="onSearch" />
</view>
<scroll-view scroll-x class="tab-scroll" v-if="model.currentEmploymentId != 2">
<scroll-view scroll-x class="tab-scroll">
<view class="tabs-row">
<view
v-for="tab in model.categoryTabs"
......@@ -310,18 +310,19 @@
<view class="map-container">
<Mapbox @register="registerMap" />
<!-- 回到当前位置 -->
<view class="map-ctrl location" @click="backToUserLocation">
<fui-icon name="location" color="#5db66f" :size="44"></fui-icon>
<!-- 悬浮控制按钮组 -->
<view class="map-ctrl-group">
<view class="map-ctrl location" @click="backToUserLocation">
<fui-icon name="location" color="#5db66f" :size="28"></fui-icon>
<text class="ctrl-text">定位</text>
</view>
<view class="map-ctrl list-btn" @click="onListModeClick">
<fui-icon name="list" color="#fff" :size="28"></fui-icon>
<text class="ctrl-text">列表</text>
</view>
</view>
</view>
<!-- 模式切换胶囊 (沉浸式风格) -->
<view class="view-toggle-pill" @tap="onListModeClick">
<fui-icon name="list" :size="32" color="#5db66f"></fui-icon>
<text class="toggle-text">返回列表</text>
</view>
<RegisterDialog ref="registerDialogRef" />
<ConfirmDialog
v-model:show="model.showConfirmDialog"
......@@ -397,43 +398,50 @@
position: relative;
}
.map-ctrl {
.map-ctrl-group {
position: absolute;
right: 30rpx;
bottom: 200rpx;
width: 88rpx;
height: 88rpx;
background-color: #fff;
border-radius: 50%;
right: 24rpx;
bottom: 100rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.12);
flex-direction: column;
gap: 16rpx;
z-index: 200;
&:active { transform: scale(0.92); }
}
.view-toggle-pill {
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 60rpx;
background-color: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(12px);
padding: 20rpx 48rpx;
border-radius: 100rpx;
display: flex;
align-items: center;
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.15);
border: 1rpx solid rgba(255,255,255,0.5);
z-index: 110;
transition: all 0.3s;
&:active { transform: translateX(-50%) scale(0.95); }
.toggle-text {
font-size: 28rpx;
color: #1a1a1a;
font-weight: bold;
margin-left: 16rpx;
.map-ctrl {
position: relative;
width: 88rpx;
height: 88rpx;
background-color: #fff;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.15);
transition: all 0.2s;
&:active {
transform: scale(0.92);
}
.ctrl-text {
font-size: 18rpx;
color: #fff;
margin-top: 2rpx;
}
&.location {
.ctrl-text {
color: #5db66f;
}
}
&.list-btn {
width: 100rpx;
height: 100rpx;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
box-shadow: 0 6rpx 20rpx rgba(24, 144, 255, 0.4);
}
}
}
......
......@@ -69,12 +69,48 @@
const { records, total } = res
const processedRecords = records.map((item) => {
// 统一地区展示逻辑:[区县/乡镇/村]
// 统一地区展示逻辑:去掉省份,只展示市区街道等
if (pageData.currentEmploymentId == 2) {
item.areaLabel = item.villageFullName || item.villageName || '未知地区'
// 找人干活 - villageFullName 或 villageName
let areaText = item.villageFullName || item.villageName || '未知地区'
// 处理多种分隔符:/ 或 空格
let parts = areaText.split('/')
if (parts.length === 1) {
parts = areaText.split(' ')
}
if (parts.length > 1) {
// 去掉第一部分(省份)
areaText = parts.slice(1).join('/')
}
// 如果还有"省"字开头,再去掉
if (areaText.includes('省')) {
const idx = areaText.indexOf('省')
if (idx >= 0 && idx < 6) {
areaText = areaText.substring(idx + 1)
}
}
item.areaLabel = areaText
} else {
// 出工赚钱 - area 或 scope
const rawArea = item.area || item.scope
item.areaLabel = getText(rawArea, ' / ') || rawArea || '未知地区'
let areaText = getText(rawArea, ' / ') || rawArea || '未知地区'
// 处理分隔符
let parts = areaText.split(' / ')
if (parts.length === 1) {
parts = areaText.split('/')
}
if (parts.length > 1) {
// 去掉第一部分(省份)
areaText = parts.slice(1).join(' / ')
}
// 如果还有"省"字开头,再去掉
if (areaText.includes('省')) {
const idx = areaText.indexOf('省')
if (idx >= 0 && idx < 6) {
areaText = areaText.substring(idx + 1)
}
}
item.areaLabel = areaText
}
if (item.starttime && item.estimatedendtime) {
......@@ -133,6 +169,28 @@
}
}
// 打开地图导航
function openMapNavigation(item: any) {
const lat = item.latitude || item.lat
const lon = item.longitude || item.lon || item.lng
const name = item.name || item.villageName || '目的地'
const address = item.areaLabel || item.address || ''
if (!lat || !lon) {
uni.showToast({ title: '暂无位置信息', icon: 'none' })
return
}
// 打开系统地图导航
uni.openLocation({
latitude: parseFloat(lat),
longitude: parseFloat(lon),
name: name,
address: address,
scale: 16
})
}
function getDaysDiff(date1, date2) {
const d1 = new Date(date1), d2 = new Date(date2)
if (isNaN(d1.getTime()) || isNaN(d2.getTime())) return 0
......@@ -192,72 +250,116 @@
<template>
<view class="page">
<view class="top-search-view">
<uni-search-bar radius="100" v-model="searchValue" placeholder="搜索标题、地点或技能" cancelButton="none" @confirm="onSearch" @clear="resetGetEmploymentList" />
<!-- 头部搜索区域 -->
<view class="header-section">
<view class="search-bar">
<view class="search-input-wrap">
<fui-icon name="search" :size="28" color="#bbb"></fui-icon>
<input class="search-input" v-model="searchValue" placeholder="搜索标题、地点或技能" placeholder-class="search-placeholder" @confirm="onSearch" @input="searchValue = $event.detail.value" />
</view>
<view class="search-btn" @click="onSearch({ value: searchValue })">
<text>搜索</text>
</view>
</view>
</view>
<view class="tab-box">
<view v-for="tab in employmentTabs" :key="tab.id" class="tab-item" :class="{ active: tab.id === currentEmploymentId }" @click="onEmploymentTabClick(tab)">
<text class="tab-text">{{ tab.name }}</text>
<!-- Tab切换 -->
<view class="tab-section">
<view class="tab-list">
<view v-for="tab in employmentTabs" :key="tab.id" class="tab-item" :class="{ active: tab.id === currentEmploymentId }" @click="onEmploymentTabClick(tab)">
<view class="tab-avatar farmer" v-if="tab.id === 2">
<text class="avatar-icon">👨‍🌾</text>
</view>
<view class="tab-avatar worker" v-else>
<text class="avatar-icon">👷</text>
</view>
<text class="tab-text">{{ tab.id === 2 ? '找人干活' : '出工赚钱' }}</text>
</view>
</view>
</view>
<!-- 列表区域 -->
<scroll-view scroll-y class="list-scroll" @scrolltolower="loadMoreData">
<view class="list-container">
<view v-if="!employmentList.length && !pageData.loading" class="empty-box">
<fui-empty src="/static/images/no-data.png" title="暂无相关招聘信息" />
<!-- 空状态 -->
<view v-if="!employmentList.length && !pageData.loading" class="empty-section">
<image class="empty-img" src="/static/images/no-data.png" mode="aspectFit" />
<text class="empty-title">暂无相关信息</text>
<text class="empty-desc">{{ currentEmploymentId === 2 ? '还没有发布招聘,快去发布吧' : '还没有求职信息' }}</text>
</view>
<view v-for="item in employmentList" :key="item.id" class="service-card" @click="onEmploymentItemClick(item)">
<view class="service-main">
<view class="service-img-box">
<image class="service-img" :src="item.picture || item.enterpriseLogoUrl || '/static/images/linghuoyonggong/avatar.png'" mode="aspectFill" lazy-load />
<view class="urgent-tag" v-if="item.urgentdegree > 3"></view>
<!-- 列表卡片 -->
<view v-for="item in employmentList" :key="item.id" class="job-card" @click="onEmploymentItemClick(item)">
<view class="card-header">
<view class="company-logo">
<image :src="item.picture || item.enterpriseLogoUrl || '/static/images/linghuoyonggong/avatar.png'" mode="aspectFill" />
<view class="urgent-badge" v-if="item.urgentdegree > 3">急招</view>
</view>
<view class="service-info">
<text class="service-title">{{ item.name || item.villageName }}</text>
<view class="service-tags">
<text class="s-tag" v-if="item.workers">{{ item.workers }}</text>
<text class="s-tag" v-if="item.daysDiff">{{ item.daysDiff }}</text>
<text class="s-tag skills" v-for="(skill, sIdx) in (item.skillList || []).slice(0, 2)" :key="sIdx">{{ skill }}</text>
</view>
<view class="service-price-row">
<text class="price-value">{{ item.price || '面议' }}</text>
<text class="price-unit" v-if="item.price">元/天</text>
<view class="card-title-area">
<text class="job-title">{{ item.name || item.villageName }}</text>
<view class="job-meta">
<text class="meta-item" v-if="item.workers">
<text class="meta-num">{{ item.workers }}</text>
</text>
<text class="meta-divider" v-if="item.workers && item.daysDiff">|</text>
<text class="meta-item" v-if="item.daysDiff">
<text class="meta-num">{{ item.daysDiff }}</text>
</text>
</view>
</view>
<view class="price-area">
<text class="price-symbol">¥</text>
<text class="price-num">{{ item.price || '面议' }}</text>
<text class="price-unit" v-if="item.price">/天</text>
</view>
</view>
<view class="card-tags" v-if="item.skillList && item.skillList.length">
<text class="skill-tag" v-for="(skill, sIdx) in item.skillList.slice(0, 3)" :key="sIdx">{{ skill }}</text>
</view>
<view class="service-footer">
<view class="footer-left">
<fui-icon name="location" :size="24" color="#999"></fui-icon>
<text class="footer-text">{{ item.areaLabel }}</text>
<text class="distance-text" v-if="getDistanceText(item)">{{ getDistanceText(item) }}</text>
<view class="card-footer">
<view class="location-info" @click.stop="openMapNavigation(item)">
<fui-icon name="location" :size="24" color="#5db66f"></fui-icon>
<text class="location-text">{{ item.areaLabel }}</text>
<text class="distance-tag" v-if="getDistanceText(item)">{{ getDistanceText(item) }}</text>
</view>
<view class="action-btn" :class="{ 'bg-orange': currentEmploymentId == 2 }">
{{ currentEmploymentId == 1 ? '我也去' : '查看详情' }}
<view class="action-btn" :class="{ 'btn-orange': currentEmploymentId === 2, 'btn-green': currentEmploymentId === 1 }" @click.stop="onQuoteClick(item)">
{{ currentEmploymentId === 1 ? '我要报名' : '查看详情' }}
</view>
</view>
</view>
<fui-loadmore v-if="pageData.loading" />
<view v-if="pageData.hasMore === 'noMore' && employmentList.length > 0" class="no-more">- 已经到底了 -</view>
<view v-if="pageData.hasMore === 'noMore' && employmentList.length > 0" class="no-more-tip">
<view class="line"></view>
<text>已经到底了</text>
<view class="line"></view>
</view>
</view>
<!-- 底部安全区占位 -->
<view class="safe-bottom"></view>
</scroll-view>
<!-- 统一的动效发布按钮 -->
<view class="fab-container" @click="handlePublish">
<view class="ripple"></view>
<view class="ripple ripple-2"></view>
<view class="fab-entry">
<fui-icon name="plus" :size="48" color="#fff"></fui-icon>
<text>发布</text>
<!-- 悬浮操作区 -->
<view class="float-actions">
<!-- 发布按钮 -->
<view class="fab-btn" @click="handlePublish">
<view class="fab-ripple"></view>
<view class="fab-ripple delay"></view>
<view class="fab-inner">
<fui-icon name="plus" :size="40" color="#fff"></fui-icon>
<text>发布</text>
</view>
</view>
</view>
<!-- 地图模式切换 (悬浮胶囊) -->
<view class="view-toggle-pill" @click="onMapModeClick">
<fui-icon name="map" :size="32" color="#5db66f"></fui-icon>
<text class="toggle-text">地图模式</text>
<!-- 地图模式 -->
<view class="map-btn" @click="onMapModeClick">
<view class="map-pulse"></view>
<fui-icon name="map" :size="32" color="#fff"></fui-icon>
<text>地图</text>
</view>
</view>
<RegisterDialog ref="registerDialogRef" />
......@@ -265,93 +367,471 @@
</template>
<style scoped lang="scss">
.page { background-color: #f7f8fa; height: 100vh; display: flex; flex-direction: column; font-family: 'DingTalk Sans', sans-serif; }
.top-search-view { background-color: #fff; padding: 10rpx 20rpx; }
.tab-box {
display: flex; background-color: #fff; padding: 0 30rpx 20rpx;
.page {
background-color: #f5f6f8;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* 头部搜索 */
.header-section {
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
padding: 16rpx 24rpx 20rpx;
}
.search-bar {
display: flex;
align-items: center;
gap: 12rpx;
.search-input-wrap {
flex: 1;
display: flex;
align-items: center;
height: 64rpx;
background-color: #fff;
border-radius: 32rpx;
padding: 0 20rpx;
.search-input {
flex: 1;
font-size: 26rpx;
color: #333;
margin-left: 10rpx;
}
}
.search-btn {
padding: 0 24rpx;
height: 64rpx;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 32rpx;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 26rpx;
color: #fff;
font-weight: 500;
}
}
}
.search-placeholder {
color: #bbb;
font-size: 26rpx;
}
/* Tab切换 */
.tab-section {
background-color: #fff;
padding: 12rpx 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.tab-list {
display: flex;
gap: 16rpx;
}
.tab-item {
flex: 1; height: 70rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: #666; position: relative;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 12rpx 0;
background-color: #f7f8fa;
border-radius: 12rpx;
transition: all 0.3s;
.tab-avatar {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
overflow: hidden;
margin-right: 10rpx;
border: 2rpx solid #fff;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
.avatar-icon {
font-size: 28rpx;
line-height: 1;
}
&.farmer {
background: linear-gradient(135deg, #e6f5e8 0%, #d4edda 100%);
}
&.worker {
background: linear-gradient(135deg, #fff7e6 0%, #ffe7ba 100%);
}
}
.tab-text {
font-size: 26rpx;
color: #333;
font-weight: 500;
}
&.active {
color: #5db66f; font-weight: bold;
&::after { content: ''; position: absolute; bottom: 0; width: 40rpx; height: 6rpx; background-color: #5db66f; border-radius: 3rpx; }
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
box-shadow: 0 4rpx 16rpx rgba(93, 182, 111, 0.35);
.tab-avatar {
border-color: rgba(255, 255, 255, 0.5);
}
.tab-text {
color: #fff;
}
}
&:nth-child(2).active {
background: linear-gradient(135deg, #fa8c16 0%, #fa541c 100%);
box-shadow: 0 4rpx 16rpx rgba(250, 140, 22, 0.35);
}
}
}
.list-scroll { flex: 1; overflow: hidden; }
.list-container { padding: 20rpx; }
.service-card {
background-color: #fff; border-radius: 24rpx; padding: 24rpx; margin-bottom: 24rpx; box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
.service-main { display: flex; }
.service-img-box {
width: 160rpx; height: 160rpx; border-radius: 12rpx; overflow: hidden; background-color: #f8f8f8; position: relative; flex-shrink: 0;
.service-img { width: 100%; height: 100%; }
.urgent-tag { position: absolute; left: 0; top: 0; background: #ff4d4f; color: #fff; font-size: 20rpx; padding: 4rpx 12rpx; border-bottom-right-radius: 12rpx; }
/* 列表区域 */
.list-scroll {
flex: 1;
overflow: hidden;
}
.list-container {
padding: 20rpx 24rpx;
}
/* 空状态 */
.empty-section {
display: flex;
flex-direction: column;
align-items: center;
padding: 120rpx 0;
.empty-img {
width: 240rpx;
height: 240rpx;
opacity: 0.6;
}
.empty-title {
font-size: 30rpx;
color: #333;
font-weight: 500;
margin-top: 24rpx;
}
.empty-desc {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
}
/* 职位卡片 */
.job-card {
background-color: #fff;
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.card-header {
display: flex;
align-items: flex-start;
.company-logo {
width: 96rpx;
height: 96rpx;
border-radius: 16rpx;
overflow: hidden;
background-color: #f5f5f5;
flex-shrink: 0;
position: relative;
image {
width: 100%;
height: 100%;
}
.urgent-badge {
position: absolute;
top: 0;
left: 0;
background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
color: #fff;
font-size: 18rpx;
padding: 4rpx 10rpx;
border-bottom-right-radius: 12rpx;
font-weight: 500;
}
}
.card-title-area {
flex: 1;
margin-left: 16rpx;
.job-title {
font-size: 30rpx;
color: #333;
font-weight: 600;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.job-meta {
display: flex;
align-items: center;
margin-top: 10rpx;
.meta-item {
font-size: 24rpx;
color: #666;
.meta-num {
color: #5db66f;
font-weight: 600;
font-size: 26rpx;
}
}
.meta-divider {
margin: 0 12rpx;
color: #ddd;
}
}
}
.price-area {
text-align: right;
flex-shrink: 0;
.price-symbol {
font-size: 24rpx;
color: #ff4d4f;
font-weight: 500;
}
.price-num {
font-size: 36rpx;
color: #ff4d4f;
font-weight: 700;
margin-left: 2rpx;
}
.price-unit {
font-size: 22rpx;
color: #999;
margin-left: 2rpx;
}
}
}
.service-info {
flex: 1; margin-left: 20rpx; display: flex; flex-direction: column;
.service-title { font-size: 30rpx; color: #333; font-weight: bold; line-height: 1.4; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; }
.service-tags {
display: flex; flex-wrap: wrap; margin-top: 12rpx;
.s-tag { padding: 4rpx 12rpx; background-color: #f5f5f5; color: #666; font-size: 22rpx; border-radius: 4rpx; margin-right: 12rpx; margin-bottom: 8rpx; &.skills { background-color: #e6f7ff; color: #1890ff; } }
.card-tags {
display: flex;
flex-wrap: wrap;
margin-top: 16rpx;
.skill-tag {
padding: 6rpx 16rpx;
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
color: #1890ff;
font-size: 22rpx;
border-radius: 8rpx;
margin-right: 12rpx;
margin-bottom: 8rpx;
font-weight: 500;
}
.service-price-row { margin-top: auto; display: flex; align-items: baseline; .price-value { font-size: 36rpx; color: #ff4d4f; font-weight: bold; } .price-unit { font-size: 24rpx; color: #999; margin-left: 4rpx; } }
}
.service-footer {
margin-top: 24rpx; display: flex; justify-content: space-between; align-items: center; padding-top: 20rpx; border-top: 1rpx dashed #eee;
.footer-left {
display: flex; align-items: center; flex: 1; overflow: hidden;
.footer-text { font-size: 22rpx; color: #999; margin-left: 4rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.distance-text { font-size: 22rpx; color: #5db66f; margin-left: 12rpx; font-weight: bold; flex-shrink: 0; }
.card-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f5f5f5;
.location-info {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
cursor: pointer;
.location-text {
font-size: 24rpx;
color: #5db66f;
margin-left: 6rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 280rpx;
}
.distance-tag {
font-size: 22rpx;
color: #5db66f;
background-color: #e6f5e8;
padding: 4rpx 12rpx;
border-radius: 6rpx;
margin-left: 12rpx;
font-weight: 500;
flex-shrink: 0;
}
}
.action-btn {
padding: 12rpx 28rpx;
border-radius: 24rpx;
font-size: 24rpx;
font-weight: 500;
flex-shrink: 0;
margin-left: 16rpx;
&.btn-green {
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(93, 182, 111, 0.3);
}
&.btn-orange {
background: linear-gradient(135deg, #fa8c16 0%, #fa541c 100%);
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(250, 140, 22, 0.3);
}
}
.action-btn { padding: 10rpx 28rpx; background-color: #5db66f; color: #fff; font-size: 24rpx; border-radius: 30rpx; margin-left: 20rpx; &.bg-orange { background-color: #fa8c16; } }
}
}
/* 动态发布按钮 */
.fab-container {
position: fixed; right: 30rpx; bottom: 150rpx; width: 110rpx; height: 110rpx; z-index: 100; display: flex; align-items: center; justify-content: center;
.ripple { position: absolute; width: 100%; height: 100%; background-color: #5db66f; border-radius: 50%; opacity: 0.4; animation: ripple 2s infinite ease-out; }
.ripple-2 { animation-delay: 1s; }
.fab-entry {
position: relative; width: 110rpx; height: 110rpx; background: linear-gradient(135deg, #a5d63f 0%, #2e8b57 100%); border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 8rpx 30rpx rgba(46, 139, 87, 0.5); animation: breathe 2.5s infinite ease-in-out; z-index: 2;
text { font-size: 18rpx; color: #fff; margin-top: -4rpx; font-weight: bold; }
/* 底部提示 */
.no-more-tip {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx 0;
.line {
width: 60rpx;
height: 1rpx;
background-color: #ddd;
}
text {
font-size: 24rpx;
color: #bbb;
margin: 0 20rpx;
}
}
@keyframes ripple { 0% { transform: scale(1); opacity: 0.4; } 100% { transform: scale(1.8); opacity: 0; } }
@keyframes breathe { 0%, 100% { transform: scale(1); box-shadow: 0 8rpx 30rpx rgba(46, 139, 87, 0.5); } 50% { transform: scale(1.08); box-shadow: 0 12rpx 45rpx rgba(46, 139, 87, 0.7); } }
.safe-bottom {
height: 180rpx;
}
/* 地图切换胶囊 */
.view-toggle-pill {
/* 悬浮操作区 */
.float-actions {
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 60rpx;
background-color: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
padding: 16rpx 40rpx;
border-radius: 100rpx;
right: 24rpx;
bottom: 120rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
border: 1rpx solid #eee;
z-index: 110;
transition: all 0.3s;
gap: 16rpx;
z-index: 100;
.fab-btn {
position: relative;
width: 100rpx;
height: 100rpx;
.fab-ripple {
position: absolute;
width: 100%;
height: 100%;
background-color: #5db66f;
border-radius: 50%;
opacity: 0.3;
animation: fabRipple 2s infinite ease-out;
}
&:active {
transform: translateX(-50%) scale(0.95);
background-color: #fff;
.fab-ripple.delay {
animation-delay: 1s;
}
.fab-inner {
position: relative;
width: 100rpx;
height: 100rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(93, 182, 111, 0.5);
z-index: 2;
animation: fabBreathe 2.5s infinite ease-in-out;
text {
font-size: 18rpx;
color: #fff;
font-weight: 600;
margin-top: 2rpx;
}
}
}
.toggle-text {
font-size: 26rpx;
color: #333;
font-weight: bold;
margin-left: 12rpx;
.map-btn {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 88rpx;
height: 88rpx;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
border-radius: 50%;
box-shadow: 0 6rpx 20rpx rgba(24, 144, 255, 0.4);
text {
font-size: 20rpx;
color: #fff;
font-weight: 500;
margin-top: 2rpx;
}
.map-pulse {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 2rpx solid #1890ff;
animation: mapPulse 2s infinite ease-out;
}
}
}
.no-more { text-align: center; font-size: 24rpx; color: #ccc; padding: 30rpx 0; }
.empty-box { padding-top: 100rpx; }
@keyframes fabRipple {
0% { transform: scale(1); opacity: 0.3; }
100% { transform: scale(1.8); opacity: 0; }
}
@keyframes fabBreathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes mapPulse {
0% { transform: scale(1); opacity: 0.6; }
100% { transform: scale(1.5); opacity: 0; }
}
</style>
......@@ -161,22 +161,28 @@
<!-- 干农活 (具体服务列表) -->
<template v-else>
<view v-for="item in farmMachineList" :key="item.id" class="service-card" @click="handleItemClick(item)">
<view class="card-main">
<image class="thumb" :src="item.picture || '/static/images/nongjifuwu/default-service.png'" mode="aspectFill" />
<view class="info">
<view class="service-grid">
<view v-for="item in farmMachineList" :key="item.id" class="service-card" @click="handleItemClick(item)">
<view class="card-media">
<image class="thumb" :src="item.picture || '/static/images/nongjifuwu/default-service.png'" mode="aspectFill" />
<view class="status-tag">服务中</view>
</view>
<view class="card-content">
<text class="title">{{ item.name }}</text>
<text class="desc">范围:{{ getText(item.scope, ' / ') }}</text>
<view class="scope-row">
<fui-icon name="location" :size="20" color="#999"></fui-icon>
<text class="scope-text">{{ getText(item.scope, ' / ') || '全国' }}</text>
</view>
<view class="price-row">
<text class="price">{{ item.price }}</text>
<text class="unit">元/亩</text>
<view class="price-box">
<text class="price-symbol">¥</text>
<text class="price-value">{{ item.price }}</text>
<text class="price-unit">/亩</text>
</view>
<view class="book-btn" @click.stop="handleApply(item)">预约</view>
</view>
</view>
</view>
<view class="card-footer">
<view class="status">服务中</view>
<view class="btn orange" @click.stop="handleApply(item)">立即预约</view>
</view>
</view>
</template>
......@@ -260,42 +266,59 @@
.enterprise-card {
background-color: #fff;
border-radius: 20rpx;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.card-main {
display: flex;
align-items: center;
.logo {
width: 140rpx;
height: 140rpx;
width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
background-color: #f8f8f8;
flex-shrink: 0;
}
.info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
.name {
font-size: 30rpx;
font-weight: bold;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.scope {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.tags {
display: flex;
margin-top: 12rpx;
.tag {
font-size: 20rpx;
padding: 2rpx 12rpx;
padding: 4rpx 12rpx;
background-color: #e6f5e8;
color: #5db66f;
border-radius: 4rpx;
border-radius: 6rpx;
margin-right: 10rpx;
font-weight: 500;
&.orange {
background-color: #fff7e6;
color: #fa8c16;
......@@ -304,93 +327,137 @@
}
}
}
.card-footer {
margin-top: 20rpx;
padding-top: 20rpx;
margin-top: 16rpx;
padding-top: 16rpx;
border-top: 1rpx solid #f5f5f5;
display: flex;
justify-content: space-between;
align-items: center;
.addr {
display: flex;
align-items: center;
font-size: 22rpx;
color: #999;
}
.btn {
padding: 8rpx 24rpx;
background-color: #5db66f;
padding: 10rpx 24rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
color: #fff;
font-size: 24rpx;
border-radius: 26rpx;
font-weight: 500;
border-radius: 24rpx;
}
}
}
.service-grid {
display: flex;
flex-wrap: wrap;
margin: 0 -10rpx;
}
.service-card {
width: calc(50% - 20rpx);
margin: 10rpx;
background-color: #fff;
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 24rpx;
.card-main {
display: flex;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.card-media {
position: relative;
width: 100%;
height: 260rpx;
background-color: #f5f5f5;
.thumb {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
width: 100%;
height: 100%;
}
.info {
flex: 1;
margin-left: 20rpx;
.status-tag {
position: absolute;
top: 16rpx;
left: 16rpx;
font-size: 20rpx;
color: #fff;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
padding: 4rpx 14rpx;
border-radius: 6rpx;
font-weight: 500;
}
}
.card-content {
padding: 16rpx;
.title {
font-size: 28rpx;
font-weight: 600;
color: #333;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 8rpx;
}
.scope-row {
display: flex;
flex-direction: column;
.title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.desc {
font-size: 24rpx;
align-items: center;
margin-bottom: 12rpx;
.scope-text {
font-size: 22rpx;
color: #999;
margin-top: 10rpx;
}
.price-row {
margin-top: auto;
.price {
font-size: 36rpx;
color: #ff4d4f;
font-weight: bold;
}
.unit {
font-size: 22rpx;
color: #999;
margin-left: 4rpx;
}
margin-left: 4rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 240rpx;
}
}
}
.card-footer {
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx dashed #eee;
display: flex;
justify-content: space-between;
align-items: center;
.status {
font-size: 22rpx;
color: #5db66f;
background-color: #e6f5e8;
padding: 4rpx 16rpx;
border-radius: 4rpx;
}
.btn {
padding: 10rpx 28rpx;
background-color: #5db66f;
color: #fff;
font-size: 24rpx;
border-radius: 30rpx;
&.orange {
background-color: #fa8c16;
.price-row {
display: flex;
align-items: center;
justify-content: space-between;
.price-box {
display: flex;
align-items: baseline;
}
.price-symbol {
font-size: 22rpx;
color: #ff4d4f;
font-weight: 600;
}
.price-value {
font-size: 34rpx;
color: #ff4d4f;
font-weight: 700;
margin-left: 2rpx;
}
.price-unit {
font-size: 20rpx;
color: #999;
margin-left: 2rpx;
}
.book-btn {
padding: 8rpx 20rpx;
background: linear-gradient(135deg, #fa8c16 0%, #fa541c 100%);
color: #fff;
font-size: 22rpx;
font-weight: 500;
border-radius: 20rpx;
}
}
}
......
......@@ -135,101 +135,186 @@
<template>
<view class="page">
<!-- 头部背景 -->
<view class="banner">
<view class="banner-title">农机服务入驻</view>
<view class="banner-tips">专业平台,连接万千农户</view>
</view>
<!-- 进度条 -->
<view class="step-container">
<view class="step-item active">
<view class="num">1</view>
<text>提交资料</text>
<view class="header-banner">
<view class="banner-bg">
<view class="circle circle-1"></view>
<view class="circle circle-2"></view>
<view class="circle circle-3"></view>
</view>
<view class="line active"></view>
<view class="step-item">
<view class="num">2</view>
<text>后台审核</text>
<view class="banner-content">
<view class="banner-icon">
<fui-icon name="shop" :size="48" color="#fff"></fui-icon>
</view>
<view class="banner-title">农机服务入驻</view>
<view class="banner-desc">专业平台 · 连接万千农户</view>
</view>
<view class="line"></view>
<view class="step-item">
<view class="num">3</view>
<text>完成入驻</text>
</view>
<!-- 进度指示器 -->
<view class="step-card">
<view class="step-list">
<view class="step-item active">
<view class="step-num">
<text>1</text>
</view>
<text class="step-text">提交资料</text>
</view>
<view class="step-line active"></view>
<view class="step-item">
<view class="step-num">
<text>2</text>
</view>
<text class="step-text">后台审核</text>
</view>
<view class="step-line"></view>
<view class="step-item">
<view class="step-num">
<text>3</text>
</view>
<text class="step-text">完成入驻</text>
</view>
</view>
</view>
<!-- 表单主体 -->
<view class="form-body">
<fui-form ref="formRef">
<!-- 基本信息卡片 -->
<view class="card">
<view class="card-header">
<view class="tag-line"></view>
<text>基本信息</text>
<!-- 基本信息卡片 -->
<view class="form-card">
<view class="card-header">
<view class="header-icon green">
<fui-icon name="info" :size="32" color="#fff"></fui-icon>
</view>
<fui-input label="服务队名称" placeholder="请输入农机服务队或公司名称" v-model="form.enterpriseName" required />
<fui-input label="统一代码" placeholder="营业执照信用代码" v-model="form.enterpriseCode" required />
<fui-input label="服务内容" placeholder="如:耕整、播种、植保等" v-model="form.businessScope" required />
<view class="field-item">
<view class="field-label"><text class="req">*</text>服务队介绍</view>
<fui-textarea placeholder="介绍您的设备实力、服务经验等..." v-model="form.profile" height="160rpx" background="#fcfcfc" radius="12" />
<text class="header-title">基本信息</text>
</view>
<view class="card-content">
<view class="form-group">
<view class="form-label">
<text class="label-text">服务队名称</text>
<text class="required">*</text>
</view>
<input class="form-input" placeholder="请输入农机服务队或公司名称" v-model="form.enterpriseName" placeholder-class="input-placeholder" />
</view>
<view class="field-item">
<view class="field-label"><text class="req">*</text>形象照片/Logo</view>
<uni-file-picker :value="pageData.enterpriseLogoArr" limit="1" @select="handleUpload($event, 'logo')" @delete="handleDelete($event, 'logo')" />
<view class="form-group">
<view class="form-label">
<text class="label-text">统一信用代码</text>
<text class="required">*</text>
</view>
<input class="form-input" placeholder="营业执照上的信用代码" v-model="form.enterpriseCode" placeholder-class="input-placeholder" />
</view>
<view class="form-group">
<view class="form-label">
<text class="label-text">服务内容</text>
<text class="required">*</text>
</view>
<input class="form-input" placeholder="如:耕整、播种、植保、收割等" v-model="form.businessScope" placeholder-class="input-placeholder" />
</view>
<view class="form-group">
<view class="form-label">
<text class="label-text">服务队介绍</text>
<text class="required">*</text>
</view>
<textarea class="form-textarea" placeholder="介绍您的设备实力、服务经验、团队规模等..." v-model="form.profile" placeholder-class="input-placeholder" />
</view>
<view class="form-group">
<view class="form-label">
<text class="label-text">形象照片/Logo</text>
<text class="required">*</text>
</view>
<view class="upload-box">
<uni-file-picker :value="pageData.enterpriseLogoArr" limit="1" @select="handleUpload($event, 'logo')" @delete="handleDelete($event, 'logo')" />
</view>
</view>
</view>
</view>
<!-- 经营地点卡片 -->
<view class="card">
<view class="card-header">
<view class="tag-line"></view>
<text>联系与位置</text>
<!-- 联系与位置卡片 -->
<view class="form-card">
<view class="card-header">
<view class="header-icon orange">
<fui-icon name="location" :size="32" color="#fff"></fui-icon>
</view>
<fui-input label="常驻地区" placeholder="点击选择" v-model="form.areaText" @click="show.area = true" disabled required />
<fui-input label="详细地址" placeholder="村组、街道门牌号" v-model="form.addr" required />
<fui-input label="负责人" placeholder="联系人姓名" v-model="form.contactPerson" required />
<fui-input label="联系电话" type="tel" placeholder="请输入手机号" v-model="form.contactMobile" required />
<text class="header-title">联系与位置</text>
</view>
<!-- 证照上传卡片 -->
<view class="card">
<view class="card-header">
<view class="tag-line"></view>
<text>资质证照</text>
<view class="card-content">
<view class="form-group" @click="show.area = true">
<view class="form-label">
<text class="label-text">常驻地区</text>
<text class="required">*</text>
</view>
<view class="form-picker">
<text :class="['picker-text', form.areaText ? '' : 'placeholder']">{{ form.areaText || '请选择所在地区' }}</text>
<fui-icon name="arrowright" :size="28" color="#ccc"></fui-icon>
</view>
</view>
<view class="cert-box">
<text class="tips">请上传营业执照、农机驾驶证或相关作业资质</text>
<view class="mt-20">
<uni-file-picker limit="9" :value="pageData.cersImageArr" @select="handleUpload($event, 'cers')" @delete="handleDelete($event, 'cers')" />
<view class="form-group">
<view class="form-label">
<text class="label-text">详细地址</text>
<text class="required">*</text>
</view>
<input class="form-input" placeholder="村组、街道门牌号" v-model="form.addr" placeholder-class="input-placeholder" />
</view>
<view class="form-row">
<view class="form-group half">
<view class="form-label">
<text class="label-text">负责人</text>
<text class="required">*</text>
</view>
<input class="form-input" placeholder="联系人姓名" v-model="form.contactPerson" placeholder-class="input-placeholder" />
</view>
<view class="form-group half">
<view class="form-label">
<text class="label-text">联系电话</text>
<text class="required">*</text>
</view>
<input class="form-input" type="tel" placeholder="手机号码" v-model="form.contactMobile" placeholder-class="input-placeholder" />
</view>
</view>
</view>
</view>
<!-- 协议勾选 -->
<view class="agree-wrap">
<fui-checkbox-group @change="e => form.agree = e.detail.value.length > 0">
<fui-label class="agree-label">
<view class="agree-inner">
<fui-checkbox :value="true" :scaleRatio="0.7" color="#5db66f" />
<view class="agree-text-content">
<text class="agree-text">我已阅读并同意</text>
<text class="agree-link" @click.stop="handleViewProtocol">《农机服务平台入驻协议》</text>
</view>
</view>
</fui-label>
</fui-checkbox-group>
<!-- 资质证照卡片 -->
<view class="form-card">
<view class="card-header">
<view class="header-icon blue">
<fui-icon name="auth" :size="32" color="#fff"></fui-icon>
</view>
<text class="header-title">资质证照</text>
</view>
<view class="card-content">
<view class="cert-tips">
<fui-icon name="info-fill" :size="28" color="#fa8c16"></fui-icon>
<text>请上传营业执照、农机驾驶证或相关作业资质</text>
</view>
<view class="upload-box">
<uni-file-picker limit="9" :value="pageData.cersImageArr" @select="handleUpload($event, 'cers')" @delete="handleDelete($event, 'cers')" />
</view>
</view>
</view>
<view class="btn-area">
<fui-button text="提交申请" radius="100rpx" @click="submit" :loading="pageData.loading" background="#5db66f" border-color="#5db66f" />
<!-- 协议勾选 -->
<view class="agree-section">
<fui-checkbox-group @change="e => form.agree = e.detail.value.length > 0">
<fui-label class="agree-label">
<view class="agree-inner">
<fui-checkbox :value="true" :scaleRatio="0.8" color="#5db66f" />
<view class="agree-text">
<text>我已阅读并同意</text>
<text class="link" @click.stop="handleViewProtocol">《农机服务平台入驻协议》</text>
</view>
</view>
</fui-label>
</fui-checkbox-group>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<view class="submit-btn" :class="{ disabled: pageData.loading }" @click="submit">
<text class="btn-text">{{ pageData.loading ? '提交中...' : '提交申请' }}</text>
</view>
</fui-form>
</view>
</view>
<AreaPicker v-model:show="show.area" :layer="4" @confirm="handleAreaConfirm" />
<fui-toast ref="toastRef" />
</view>
</template>
......@@ -237,151 +322,376 @@
<style lang="scss" scoped>
.page {
min-height: 100vh;
background-color: #f7f9f8;
padding-bottom: 80rpx;
font-family: 'DingTalk Sans', sans-serif;
background-color: #f5f6f8;
padding-bottom: 60rpx;
padding-bottom: calc(60rpx + env(safe-area-inset-bottom));
}
.banner {
background-color: #5db66f;
padding: 60rpx 40rpx 120rpx;
color: #fff;
.banner-title { font-size: 44rpx; font-weight: bold; }
.banner-tips { font-size: 24rpx; opacity: 0.8; margin-top: 10rpx; }
/* 头部Banner */
.header-banner {
position: relative;
height: 280rpx;
overflow: hidden;
.banner-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #5db66f 0%, #3da553 50%, #2e8b57 100%);
.circle {
position: absolute;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.1);
}
.circle-1 {
width: 300rpx;
height: 300rpx;
top: -100rpx;
right: -50rpx;
}
.circle-2 {
width: 200rpx;
height: 200rpx;
bottom: -80rpx;
left: -40rpx;
}
.circle-3 {
width: 120rpx;
height: 120rpx;
top: 60rpx;
left: 30%;
}
}
.banner-content {
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 60rpx;
.banner-icon {
width: 88rpx;
height: 88rpx;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.banner-title {
font-size: 40rpx;
font-weight: 600;
color: #fff;
letter-spacing: 2rpx;
}
.banner-desc {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.85);
margin-top: 12rpx;
letter-spacing: 4rpx;
}
}
}
.step-container {
/* 步骤指示器 */
.step-card {
background-color: #fff;
margin: -60rpx 30rpx 0;
border-radius: 24rpx;
padding: 40rpx 20rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10rpx 30rpx rgba(0,0,0,0.05);
margin: -40rpx 24rpx 0;
border-radius: 20rpx;
padding: 36rpx 40rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
position: relative;
z-index: 5;
.step-list {
display: flex;
align-items: center;
justify-content: center;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
.num {
width: 44rpx;
height: 44rpx;
background-color: #f0f0f0;
color: #999;
.step-num {
width: 52rpx;
height: 52rpx;
background-color: #e8e8e8;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
margin-bottom: 10rpx;
margin-bottom: 12rpx;
transition: all 0.3s;
text {
font-size: 26rpx;
color: #999;
font-weight: 600;
}
}
text { font-size: 20rpx; color: #999; }
.step-text {
font-size: 22rpx;
color: #999;
}
&.active {
.num { background-color: #5db66f; color: #fff; }
text { color: #333; font-weight: bold; }
.step-num {
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
box-shadow: 0 4rpx 12rpx rgba(93, 182, 111, 0.4);
text {
color: #fff;
}
}
.step-text {
color: #5db66f;
font-weight: 600;
}
}
}
.line {
width: 80rpx;
height: 2rpx;
background-color: #f0f0f0;
margin: 0 20rpx;
margin-top: -30rpx;
&.active { background-color: #5db66f; }
.step-line {
width: 60rpx;
height: 4rpx;
background-color: #e8e8e8;
margin: 0 16rpx;
margin-bottom: 36rpx;
border-radius: 2rpx;
&.active {
background: linear-gradient(90deg, #5db66f 0%, #7cc98a 100%);
}
}
}
/* 表单主体 */
.form-body {
padding: 24rpx;
}
.card {
/* 卡片样式 */
.form-card {
background-color: #fff;
border-radius: 24rpx;
padding: 30rpx;
border-radius: 20rpx;
overflow: hidden;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
.card-header {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.tag-line { width: 8rpx; height: 30rpx; background-color: #5db66f; border-radius: 4rpx; margin-right: 16rpx; }
text { font-size: 30rpx; font-weight: bold; color: #333; }
padding: 28rpx 28rpx 20rpx;
background: linear-gradient(180deg, #fafbfc 0%, #fff 100%);
border-bottom: 1rpx solid #f5f5f5;
.header-icon {
width: 56rpx;
height: 56rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
&.green {
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
box-shadow: 0 4rpx 12rpx rgba(93, 182, 111, 0.3);
}
&.orange {
background: linear-gradient(135deg, #fa8c16 0%, #fa541c 100%);
box-shadow: 0 4rpx 12rpx rgba(250, 140, 22, 0.3);
}
&.blue {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.3);
}
}
.header-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
}
.card-content {
padding: 12rpx 28rpx 28rpx;
}
}
.field-item {
/* 表单项 */
.form-group {
padding: 20rpx 0;
.field-label {
.form-label {
display: flex;
align-items: center;
margin-bottom: 16rpx;
.label-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.required {
color: #ff4d4f;
margin-left: 6rpx;
font-size: 28rpx;
}
}
.form-input {
width: 100%;
height: 88rpx;
background-color: #f7f8fa;
border-radius: 14rpx;
padding: 0 24rpx;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
.req { color: #ff4d4f; margin-right: 4rpx; }
box-sizing: border-box;
transition: all 0.2s;
&:focus {
background-color: #fff;
box-shadow: 0 0 0 2rpx rgba(93, 182, 111, 0.3);
}
}
.form-textarea {
width: 100%;
height: 180rpx;
background-color: #f7f8fa;
border-radius: 14rpx;
padding: 20rpx 24rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
line-height: 1.6;
}
.form-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
background-color: #f7f8fa;
border-radius: 14rpx;
padding: 0 24rpx;
.picker-text {
font-size: 28rpx;
color: #333;
&.placeholder {
color: #bbb;
}
}
}
}
.cert-box {
.tips { font-size: 22rpx; color: #999; background-color: #f9f9f9; padding: 16rpx; border-radius: 8rpx; }
.mt-20 { margin-top: 24rpx; }
.form-row {
display: flex;
margin: 0 -12rpx;
.form-group.half {
flex: 1;
padding-left: 12rpx;
padding-right: 12rpx;
}
}
.agree-wrap {
/* 资质提示 */
.cert-tips {
display: flex;
justify-content: center;
padding: 40rpx 0 20rpx;
align-items: center;
padding: 20rpx 24rpx;
background: linear-gradient(135deg, #fffbe6 0%, #fff7e6 100%);
border-radius: 12rpx;
margin-bottom: 20rpx;
border: 1rpx solid #ffe7ba;
text {
font-size: 24rpx;
color: #ad6800;
margin-left: 10rpx;
}
}
.upload-box {
margin-top: 8rpx;
}
.input-placeholder {
color: #bbb;
font-size: 28rpx;
}
/* 协议区域 */
.agree-section {
background-color: #fff;
border-radius: 16rpx;
padding: 28rpx;
margin-bottom: 24rpx;
.agree-label {
display: block;
}
.agree-inner {
display: flex;
align-items: center;
}
.agree-text-content {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-left: 12rpx;
}
.agree-text {
font-size: 24rpx;
color: #999;
}
.agree-link {
font-size: 24rpx;
color: #5db66f;
font-weight: 500;
font-size: 26rpx;
color: #666;
margin-left: 12rpx;
line-height: 1.6;
.link {
color: #5db66f;
font-weight: 500;
}
}
}
.btn-area {
padding: 20rpx 30rpx;
}
/* 提交按钮 */
.submit-section {
padding: 20rpx 0;
.area-pop {
background-color: #fff;
padding: 30rpx;
border-radius: 32rpx 32rpx 0 0;
.pop-title { font-size: 32rpx; font-weight: bold; text-align: center; margin-bottom: 30rpx; }
}
.submit-btn {
width: 100%;
height: 96rpx;
background: linear-gradient(135deg, #5db66f 0%, #3da553 100%);
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(93, 182, 111, 0.4);
/* 覆盖FirstUI的部分样式以统一字体 */
:deep(.fui-input__label),
:deep(.fui-input__input),
:deep(.fui-textarea__textarea),
:deep(.fui-textarea__label) {
font-family: 'DingTalk Sans', system-ui !important;
color: #333 !important;
}
:deep(.uni-input-placeholder),
:deep(.uni-textarea-placeholder) {
font-family: 'DingTalk Sans' !important;
font-size: 28rpx !important;
}
&.disabled {
opacity: 0.7;
}
:deep(.fui-button) {
font-family: 'DingTalk Sans' !important;
font-weight: bold !important;
.btn-text {
font-size: 32rpx;
color: #fff;
font-weight: 600;
letter-spacing: 4rpx;
}
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论