提交 495ad646 作者: 宇宙超人

修改

上级 b2c5b582
...@@ -180,3 +180,39 @@ page { ...@@ -180,3 +180,39 @@ page {
.fui-relative { .fui-relative {
position: relative; position: relative;
} }
.fab-icon {
position: relative;
width: 40rpx;
height: 40rpx;
font-size: 0;
margin: auto;
}
.fab-icon::before,
.fab-icon::after {
content: '';
position: absolute;
border-radius: 20rpx;
background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%);
}
.fab-icon::before {
/* 横线 */
top: 50%;
left: 25%;
right: 25%;
height: 4rpx;
transform: translateY(-50%);
}
.fab-icon::after {
/* 竖线 */
left: 50%;
top: 25%;
bottom: 25%;
width: 4rpx;
transform: translateX(-50%);
border-radius: 20rpx;
background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%);
}
...@@ -395,14 +395,14 @@ ...@@ -395,14 +395,14 @@
</view> </view>
</view> </view>
</view> </view>
<fui-fab background="#5db66f" position="right" distance="10" bottom="240" width="120" @click="handlePublish"> <fui-fab position="right" distance="10" bottom="240" width="96" @click="handlePublish">
<view v-show="pageData.currentTransactionTab === 1" class="text-white text-center"> <view v-show="pageData.currentTransactionTab === 1" class="text-white text-center">
<image src="/src/static/images/chanxiao/notepad.svg" style="width: 40rpx" mode="widthFix"></image> <view class="fab-icon"></view>
<view style="font-size: 24rpx">发布需求</view> <view style="font-size: 24rpx">发布</view>
</view> </view>
<view v-show="pageData.currentTransactionTab === 2" class="text-white text-center"> <view v-show="pageData.currentTransactionTab === 2" class="text-white text-center">
<image src="/src/static/images/chanxiao/flower.svg" style="width: 50rpx" mode="widthFix"></image> <view class="fab-icon"></view>
<view style="font-size: 24rpx">发布供应</view> <view style="font-size: 24rpx">发布</view>
</view> </view>
</fui-fab> </fui-fab>
...@@ -827,4 +827,9 @@ ...@@ -827,4 +827,9 @@
border-top: solid 1rpx #e7e7e7; border-top: solid 1rpx #e7e7e7;
} }
} }
::v-deep .fui-fab__btn-main {
background: linear-gradient(124.25deg, #a5d63f 0%, #5db66f 100%) !important;
box-shadow: 0px 1px 8px #5db66f;
}
</style> </style>
...@@ -77,7 +77,13 @@ ...@@ -77,7 +77,13 @@
<fui-form ref="formRef" top="180" color="#000"> <fui-form ref="formRef" top="180" color="#000">
<fui-input marginTop="30" size="28" placeholder="请填写联系人姓名" v-model="pageData.form.supplyName" /> <fui-input marginTop="30" size="28" placeholder="请填写联系人姓名" v-model="pageData.form.supplyName" />
<fui-input marginTop="30" size="28" placeholder="请填写联系电话" v-model="pageData.form.phone" /> <fui-input marginTop="30" size="28" placeholder="请填写联系电话" v-model="pageData.form.phone" />
<fui-input marginTop="30" size="28" placeholder="请填写价格" v-model="pageData.form.supplyPrice"> <fui-input
type="number"
marginTop="30"
size="28"
placeholder="请填写价格"
v-model="pageData.form.supplyPrice"
>
<view class="flex"> <view class="flex">
<view class="mr-3" style="color: #ccc">|</view> <view class="mr-3" style="color: #ccc">|</view>
<view></view> <view></view>
......
<script setup lang="ts"> <script setup lang="ts">
import { reactive, toRefs } from 'vue' import { reactive, toRefs } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app' import { onLoad, onShow } from '@dcloudio/uni-app'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { useGlobSetting } from '/@/hooks/setting' import { useGlobSetting } from '/@/hooks/setting'
import * as ChanxiaoAPI from '@/api/model/chanxiao' import * as ChanxiaoAPI from '@/api/model/chanxiao'
import * as UserInfoAPI from '@/api/model/userInfo' import * as UserInfoAPI from '@/api/model/userInfo'
import { areaTree, getTextByCode, getCodeByText } from '@/utils/areaData' import { areaTree, getTextByCode, getCodeByText } from '@/utils/areaData'
import { useDictStore } from '@/store/modules/dict' import { useDictStore } from '@/store/modules/dict'
const dictStore = useDictStore() const dictStore = useDictStore()
const userStore = useUserStore() const userStore = useUserStore()
const globSetting = useGlobSetting() const globSetting = useGlobSetting()
onLoad((option) => { onLoad((option) => {
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: '发布采购需求', title: '发布采购需求',
})
// 获取数据详情
if (option.id) {
getDetails(option.id)
} else {
// 获取当前位置
getCurrentAddressInfo()
}
}) })
// 获取数据详情
if (option.id) { onShow(() => {
getDetails(option.id) // 数据字典赋值
} else { initDict()
// 获取当前位置 })
getCurrentAddressInfo()
} const pageData = reactive({
}) title: '发布采购需求',
show: {
onShow(() => { time: false,
// 数据字典赋值 classify: false,
initDict() address: false,
})
const pageData = reactive({
title: '发布采购需求',
show: {
time: false,
classify: false,
address: false,
},
options: {
address: [],
classify: [],
},
form: {
id: '',
title: '',
count: '',
unit: '',
deadLine: '',
priceStart: '',
priceEnd: '',
address: '',
province: '',
city: '',
country: '',
classify: '',
classifyText: '',
inputTextArea: '',
image: null,
imageObj: null,
},
rules: [
{
name: 'title',
rule: ['required'],
msg: ['请输入标题'],
},
{
name: 'count',
rule: ['required'],
msg: ['请输入数量'],
},
{
name: 'unit',
rule: ['required'],
msg: ['请输入单位'],
},
{
name: 'deadLine',
rule: ['required'],
msg: ['请选择截至时间'],
},
{
name: 'priceStart',
rule: ['required'],
msg: ['请输入最低价'],
},
{
name: 'priceEnd',
rule: ['required'],
msg: ['请输入最高价'],
},
{
name: 'address',
rule: ['required'],
msg: ['请选择省/市/区县'],
}, },
{ options: {
name: 'classify', address: [],
rule: ['required'], classify: [],
msg: ['请选择分类'],
}, },
{ form: {
name: 'image', id: '',
rule: ['required'], title: '',
msg: ['请上传示例图片'], count: '',
unit: '',
deadLine: '',
priceStart: '',
priceEnd: '',
address: '',
province: '',
city: '',
country: '',
classify: '',
classifyText: '',
inputTextArea: '',
image: null,
imageObj: null,
}, },
], rules: [
}) {
name: 'title',
rule: ['required'],
msg: ['请输入标题'],
},
{
name: 'count',
rule: ['required'],
msg: ['请输入数量'],
},
{
name: 'unit',
rule: ['required'],
msg: ['请输入单位'],
},
{
name: 'deadLine',
rule: ['required'],
msg: ['请选择截至时间'],
},
{
name: 'priceStart',
rule: ['required'],
msg: ['请输入最低价'],
},
{
name: 'priceEnd',
rule: ['required'],
msg: ['请输入最高价'],
},
{
name: 'address',
rule: ['required'],
msg: ['请选择省/市/区县'],
},
{
name: 'classify',
rule: ['required'],
msg: ['请选择分类'],
},
{
name: 'image',
rule: ['required'],
msg: ['请上传示例图片'],
},
],
})
const { show, options, form } = toRefs(pageData)
const { show, options, form } = toRefs(pageData) function initDict() {
pageData.options.classify = dictStore.getDictList['classify'].map((item) => {
return {
value: item.value,
text: item.text,
}
})
pageData.options.address = areaTree
}
function getCurrentAddressInfo() {
if (!uni.getStorageSync('location')) return
const { lon, lat } = uni.getStorageSync('location')
UserInfoAPI.location({
lon,
lat,
}).then((res) => {
pageData.form.province = res.province
pageData.form.city = res.city
pageData.form.country = res.country
pageData.form.address = `${res.province}/${res.city}/${res.country}`
})
}
function initDict() { function getDetails(id) {
pageData.options.classify = dictStore.getDictList['classify'].map((item) => { ChanxiaoAPI.purchaseSellDetails({ id }).then((res) => {
pageData.form = res
pageData.form.address = `${getTextByCode(res.province)}/${getTextByCode(res.city)}/${getTextByCode(res.country)}`
pageData.form.classifyText = pageData.options.classify.find((item) => item.value == res.classify)?.text
pageData.form.imageObj = pageData.form.image && parseUrlInfo(pageData.form.image)
})
}
function parseUrlInfo(url) {
// 从URL中提取文件名
const pathParts = url.split('/')
const fileName = pathParts[pathParts.length - 1]
// 提取扩展名
const fileParts = fileName.split('.')
const extname = fileParts[fileParts.length - 1]
// 返回格式化的对象
return { return {
value: item.value, name: fileName,
text: item.text, extname: extname,
url: url,
} }
})
pageData.options.address = areaTree
}
function getCurrentAddressInfo() {
if (!uni.getStorageSync('location')) return
const { lon, lat } = uni.getStorageSync('location')
UserInfoAPI.location({
lon,
lat,
}).then((res) => {
pageData.form.province = res.province
pageData.form.city = res.city
pageData.form.country = res.country
pageData.form.address = `${res.province}/${res.city}/${res.country}`
})
}
function getDetails(id) {
ChanxiaoAPI.purchaseSellDetails({ id }).then((res) => {
pageData.form = res
pageData.form.address = `${getTextByCode(res.province)}/${getTextByCode(res.city)}/${getTextByCode(res.country)}`
pageData.form.classifyText = pageData.options.classify.find((item) => item.value == res.classify)?.text
pageData.form.imageObj = pageData.form.image && parseUrlInfo(pageData.form.image)
})
}
function parseUrlInfo(url) {
// 从URL中提取文件名
const pathParts = url.split('/')
const fileName = pathParts[pathParts.length - 1]
// 提取扩展名
const fileParts = fileName.split('.')
const extname = fileParts[fileParts.length - 1]
// 返回格式化的对象
return {
name: fileName,
extname: extname,
url: url,
} }
}
function handleChangeTime(e) {
function handleChangeTime(e) { pageData.form.deadLine = e.result
pageData.form.deadLine = e.result pageData.show.time = false
pageData.show.time = false }
} function handleChangeClassify(e) {
function handleChangeClassify(e) { pageData.form.classify = e.value
pageData.form.classify = e.value pageData.form.classifyText = e.text
pageData.form.classifyText = e.text pageData.show.classify = false
pageData.show.classify = false }
} function handleChangeAddress(e) {
function handleChangeAddress(e) { pageData.form.address = e.text.join('/')
pageData.form.address = e.text.join('/') pageData.show.address = false
pageData.show.address = false }
}
const toastRef = ref()
const toastRef = ref() const uploadRef = ref()
const uploadRef = ref() // 文件上传
// 文件上传 function handleUpload(file) {
function handleUpload(file) { uni.uploadFile({
uni.uploadFile({ url: globSetting.apiUrl + globSetting.urlPrefix + '/sys/common/upload', // 直接使用上传接口URL
url: globSetting.apiUrl + globSetting.urlPrefix + '/sys/common/upload', // 直接使用上传接口URL filePath: file.tempFiles[0].path,
filePath: file.tempFiles[0].path, name: 'file',
name: 'file', formData: {
formData: { biz: 'temp',
biz: 'temp', },
}, header: {
header: { 'X-Access-Token': userStore.getToken,
'X-Access-Token': userStore.getToken, },
}, success: (res) => {
success: (res) => { if (res.statusCode === 200) {
if (res.statusCode === 200) { const data = JSON.parse(res.data)
const data = JSON.parse(res.data) if (data.code === 200 || data.code === 0) {
if (data.code === 200 || data.code === 0) { toastRef.value.show({
toastRef.value.show({ type: 'success',
type: 'success', text: '上传成功',
text: '上传成功', })
}) pageData.form.image = data.message // 保存返回的图片信息
pageData.form.image = data.message // 保存返回的图片信息 }
} }
} },
}, fail: () => {
fail: () => {
toastRef.value.show({
type: 'error',
text: '上传失败',
})
uploadRef.value.clearFiles()
pageData.form.image = null
},
})
}
// 文件删除
function handleDelete() {
uploadRef.value.clearFiles()
pageData.form.image = null
}
const formRef = ref()
function submit() {
formRef.value.validator(pageData.form, pageData.rules, true).then((res) => {
if (res.isPassed) {
changeAddressValue(pageData.form)
ChanxiaoAPI.purchaseSellAdd(pageData.form).then(() => {
toastRef.value.show({ toastRef.value.show({
type: 'success', type: 'error',
text: '需求发布成功', text: '上传失败',
}) })
uni.switchTab({ uploadRef.value.clearFiles()
url: '/pages/chanxiao/chanxiao', pageData.form.image = null
},
})
}
// 文件删除
function handleDelete() {
uploadRef.value.clearFiles()
pageData.form.image = null
}
const formRef = ref()
function submit() {
formRef.value.validator(pageData.form, pageData.rules, true).then((res) => {
if (res.isPassed) {
changeAddressValue(pageData.form)
ChanxiaoAPI.purchaseSellAdd(pageData.form).then(() => {
toastRef.value.show({
type: 'success',
text: '需求发布成功',
})
uni.switchTab({
url: '/pages/chanxiao/chanxiao',
})
}) })
}) }
})
}
/**
* 处理地区值
* @param formData 表单数据
*/
const changeAddressValue = (formData) => {
const addressValue = formData.address.split('/')
if (addressValue.length === 3) {
formData.province = getCodeByText(addressValue[0])
formData.city = getCodeByText(addressValue[1])
formData.country = getCodeByText(addressValue[2])
} }
})
}
/**
* 处理地区值
* @param formData 表单数据
*/
const changeAddressValue = (formData) => {
const addressValue = formData.address.split('/')
if (addressValue.length === 3) {
formData.province = getCodeByText(addressValue[0])
formData.city = getCodeByText(addressValue[1])
formData.country = getCodeByText(addressValue[2])
} }
}
</script> </script>
<template> <template>
...@@ -250,42 +250,76 @@ const changeAddressValue = (formData) => { ...@@ -250,42 +250,76 @@ const changeAddressValue = (formData) => {
<view class="formBox"> <view class="formBox">
<fui-form ref="formRef" label-weight="auto" top="60"> <fui-form ref="formRef" label-weight="auto" top="60">
<view class="mt20"> <view class="mt20">
<fui-input required label="采购标题" placeholder="请输入采购标题" v-model="form.title" labelSize="28" <fui-input
label-width="180"></fui-input> required
<fui-input label="说明" placeholder="请输入规格说明" v-model="form.inputTextArea" labelSize="28" label="采购标题"
label-width="180"></fui-input> placeholder="请输入采购标题"
v-model="form.title"
labelSize="28"
label-width="180"
></fui-input>
<fui-input
label="说明"
placeholder="请输入规格说明"
v-model="form.inputTextArea"
labelSize="28"
label-width="180"
></fui-input>
</view> </view>
<view class="mt20"> <view class="mt20">
<!-- 价格区间 --> <!-- 价格区间 -->
<view class="form-section" style="padding: 0 10rpx;"> <view class="form-section" style="padding: 0 10rpx">
<view class="form-item required flex align-center"> <view class="form-item required flex align-center">
<text class="label">价格区间</text> <text class="label">价格区间</text>
<view class="price-range"> <view class="price-range">
<input type="number" class="price-input" v-model="form.priceStart" placeholder="最低价" <input
:min="0" /> type="number"
class="price-input"
v-model="form.priceStart"
placeholder="最低价"
:min="0"
/>
<text class="price-separator">-</text> <text class="price-separator">-</text>
<input type="number" class="price-input" v-model="form.priceEnd" placeholder="最高价" <input
:min="0" /> type="number"
class="price-input"
v-model="form.priceEnd"
placeholder="最高价"
:min="0"
/>
</view> </view>
</view> </view>
</view> </view>
<fui-input type="number" required label="数量" placeholder="请输入数量" v-model="form.count" labelSize="28" <fui-input
label-width="180"></fui-input> type="number"
<fui-input required label="单位" placeholder="请输入计量单位" v-model="form.unit" labelSize="28" required
label-width="180"></fui-input> label="数量"
placeholder="请输入数量"
v-model="form.count"
labelSize="28"
label-width="180"
></fui-input>
<fui-input
required
label="单位"
placeholder="请输入计量单位"
v-model="form.unit"
labelSize="28"
label-width="180"
></fui-input>
</view> </view>
<view class="mt20"> <view class="mt20">
<view class="form-item required flex align-center" style="padding: 20rpx 10rpx;"> <view class="form-item required flex align-center" style="padding: 20rpx 10rpx">
<text class="label">省/市/区县</text> <text class="label">省/市/区县</text>
<view class="select-input" @click="show.address = true"> <view class="time-input" @click="show.address = true">
<text class="time-text" :class="{ placeholder: !form.address }"> <text class="select-text" :class="{ placeholder: !form.address }">
{{ form.address || '请选择省/市/区县' }} {{ form.address || '请选择省/市/区县' }}
</text> </text>
</view> </view>
</view> </view>
<!-- 截至时间 --> <!-- 截至时间 -->
<view class="form-section" style="padding: 0 10rpx;"> <view class="form-section" style="padding: 0 10rpx">
<view class="form-item required flex align-center"> <view class="form-item required flex align-center">
<text class="label">截至时间</text> <text class="label">截至时间</text>
<view class="time-input" @click="show.time = true"> <view class="time-input" @click="show.time = true">
...@@ -295,7 +329,7 @@ const changeAddressValue = (formData) => { ...@@ -295,7 +329,7 @@ const changeAddressValue = (formData) => {
</view> </view>
</view> </view>
</view> </view>
<view class="form-section" style="padding: 0 10rpx;"> <view class="form-section" style="padding: 0 10rpx">
<view class="form-item required flex align-center"> <view class="form-item required flex align-center">
<text class="label">分类</text> <text class="label">分类</text>
<view class="time-input" @click="show.classify = true"> <view class="time-input" @click="show.classify = true">
...@@ -309,275 +343,314 @@ const changeAddressValue = (formData) => { ...@@ -309,275 +343,314 @@ const changeAddressValue = (formData) => {
<view class="bg-white mt20" style="padding: 0.875rem 1rem"> <view class="bg-white mt20" style="padding: 0.875rem 1rem">
<view class="mb-1 flex justify-start"> 示例图片 </view> <view class="mb-1 flex justify-start"> 示例图片 </view>
<uni-file-picker :value="form.imageObj" ref="uploadRef" limit="1" :auto-upload="false" <uni-file-picker
@select="handleUpload" @delete="handleDelete"></uni-file-picker> :value="form.imageObj"
ref="uploadRef"
limit="1"
:auto-upload="false"
@select="handleUpload"
@delete="handleDelete"
></uni-file-picker>
</view> </view>
<view class="fui-btn__box" v-if="!form.id" style="margin-top: 30rpx;"> <view class="fui-btn__box" v-if="!form.id" style="margin-top: 30rpx">
<fui-button text="发布需求" bold radius="96rpx" @click="submit"></fui-button> <fui-button text="发布需求" bold radius="96rpx" @click="submit"></fui-button>
</view> </view>
</fui-form> </fui-form>
<fui-date-picker :show="show.time" type="3" @change="handleChangeTime" <fui-date-picker
@cancel="show.time = false"></fui-date-picker> :show="show.time"
<fui-picker :show="show.classify" :layer="1" :linkage="true" :options="options.classify" type="3"
@change="handleChangeClassify" @cancel="show.classify = false"></fui-picker> @change="handleChangeTime"
<fui-picker :show="show.address" :options="options.address" :linkage="true" :layer="3" @cancel="show.time = false"
@change="handleChangeAddress" @cancel="show.address = false"></fui-picker> ></fui-date-picker>
<fui-picker
:show="show.classify"
:layer="1"
:linkage="true"
:options="options.classify"
@change="handleChangeClassify"
@cancel="show.classify = false"
></fui-picker>
<fui-picker
:show="show.address"
:options="options.address"
:linkage="true"
:layer="3"
@change="handleChangeAddress"
@cancel="show.address = false"
></fui-picker>
<fui-toast ref="toastRef"></fui-toast> <fui-toast ref="toastRef"></fui-toast>
</view> </view>
</view> </view>
<fui-date-picker :show="show.time" type="3" @change="handleChangeTime" @cancel="show.time = false" <fui-date-picker
minDate="2025-01-01"></fui-date-picker> :show="show.time"
<fui-picker :show="show.classify" :layer="1" :linkage="true" :options="options.classify" type="3"
@change="handleChangeClassify" @cancel="show.classify = false"></fui-picker> @change="handleChangeTime"
<fui-picker :show="show.address" :options="options.address" :linkage="true" :layer="3" @change="handleChangeAddress" @cancel="show.time = false"
@cancel="show.address = false"></fui-picker> minDate="2025-01-01"
></fui-date-picker>
<fui-picker
:show="show.classify"
:layer="1"
:linkage="true"
:options="options.classify"
@change="handleChangeClassify"
@cancel="show.classify = false"
></fui-picker>
<fui-picker
:show="show.address"
:options="options.address"
:linkage="true"
:layer="3"
@change="handleChangeAddress"
@cancel="show.address = false"
></fui-picker>
<fui-toast ref="toastRef"></fui-toast> <fui-toast ref="toastRef"></fui-toast>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
body { body {
background-color: #E6F5E8; background-color: #e6f5e8;
}
.page {
background-color: #E6F5E8;
width: 750rpx;
overflow-x: hidden;
.mt20 {
margin-top: 30rpx;
background: #FFF;
padding: 20rpx;
border-radius: 10rpx;
} }
.formBox { .page {
width: 690rpx; background-color: #e6f5e8;
margin: 30rpx auto; width: 750rpx;
} overflow-x: hidden;
.purchase-form { .mt20 {
background: transparent; margin-top: 30rpx;
} background: #fff;
padding: 20rpx;
border-radius: 10rpx;
}
.form-section { .formBox {
// background: #ffffff; width: 690rpx;
// border-radius: 12rpx; margin: 30rpx auto;
// margin-bottom: 20rpx; }
// box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); .purchase-form {
border-bottom: 1rpx solid #f5f5f5; background: transparent;
} }
.form-item { .form-section {
padding: 30rpx 0; // background: #ffffff;
border-bottom: 1rpx solid #f5f5f5; // border-radius: 12rpx;
// margin-bottom: 20rpx;
&:last-child { // box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
border-bottom: none; border-bottom: 1rpx solid #f5f5f5;
} }
&.required .label::before { .form-item {
content: '*'; padding: 30rpx 0;
color: #ff4d4f; border-bottom: 1rpx solid #f5f5f5;
margin-right: 8rpx;
&:last-child {
border-bottom: none;
}
&.required .label::before {
content: '*';
color: #ff4d4f;
margin-right: 8rpx;
}
// 添加点击区域样式
.select-input {
position: relative;
}
} }
// 添加点击区域样式 .form-row {
.select-input { display: flex;
position: relative; justify-content: space-between;
} }
}
.form-row { .half-width {
display: flex; width: 48%;
justify-content: space-between; }
}
.half-width { .align-center {
width: 48%; align-items: center;
} }
.align-center { .label {
align-items: center; display: block;
} font-size: 28rpx;
color: #333333;
font-weight: 500;
width: 180rpx;
// margin-right: 20rpx;
}
.label { .input {
display: block; width: 100%;
font-size: 28rpx; height: 80rpx;
color: #333333; background: #f8f9fa;
font-weight: 500; border-radius: 8rpx;
width: 180rpx; padding: 0 20rpx;
// margin-right: 20rpx; font-size: 28rpx;
} color: #333333;
.input { &::placeholder {
width: 100%; color: #999999;
height: 80rpx; }
background: #f8f9fa; }
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
&::placeholder { .price-range {
color: #999999; display: flex;
align-items: center;
// justify-content: space-between;
} }
}
.price-range { .price-input {
display: flex; width: 15%;
align-items: center; // height: 80rpx;
// justify-content: space-between; // background: #f8f9fa;
} // border-radius: 8rpx;
padding: 0 10rpx;
font-size: 28rpx;
text-align: center;
}
.price-input { .price-separator {
width: 15%; color: #666666;
// height: 80rpx; font-size: 28rpx;
// background: #f8f9fa; margin: 0 10rpx;
// border-radius: 8rpx; }
padding: 0 10rpx;
font-size: 28rpx;
text-align: center;
}
.price-separator { .select-input {
color: #666666; flex: 1;
font-size: 28rpx; border-radius: 8rpx;
margin: 0 10rpx; padding: 0 20rpx;
} display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.select-input { .select-text {
flex: 1; font-size: 28rpx;
border-radius: 8rpx; color: #333333;
padding: 0 20rpx; padding: 0 20rpx;
display: flex; &.placeholder {
align-items: center; color: #999999;
justify-content: space-between; }
cursor: pointer; }
}
.select-text { .select-arrow {
font-size: 28rpx;
color: #333333;
padding: 0 20rpx;
&.placeholder {
color: #999999; color: #999999;
font-size: 24rpx;
line-height: 1;
} }
}
.select-arrow { .time-range {
color: #999999; display: flex;
font-size: 24rpx; align-items: center;
line-height: 1; justify-content: space-between;
} }
.time-range { .time-input {
display: flex; width: 45%;
align-items: center; // height: 80rpx;
justify-content: space-between; // background: #f8f9fa;
} border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-input { .time-text {
width: 45%; font-size: 28rpx;
// height: 80rpx; color: #333333;
// background: #f8f9fa; padding: 0 20rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-text { &.placeholder {
font-size: 28rpx; color: #999999;
color: #333333; }
padding: 0 20rpx; }
.time-separator {
color: #666666;
font-size: 28rpx;
margin: 0 10rpx;
}
.upload-area {
margin-top: 10rpx;
}
.custom-uploader {
:deep(.uni-file-picker__container) {
border: 2rpx dashed #d9d9d9;
border-radius: 8rpx;
background: #f8f9fa;
}
}
&.placeholder { .upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200rpx;
color: #999999; color: #999999;
} }
}
.time-separator { .upload-icon {
color: #666666; font-size: 48rpx;
font-size: 28rpx; margin-bottom: 10rpx;
margin: 0 10rpx; }
}
.upload-area { .upload-text {
margin-top: 10rpx; font-size: 24rpx;
} }
.custom-uploader { .submit-section {
:deep(.uni-file-picker__container) { background: transparent;
border: 2rpx dashed #d9d9d9; padding: 40rpx 0;
border-radius: 8rpx;
background: #f8f9fa;
} }
}
.upload-placeholder { .submit-btn {
display: flex; width: 100%;
flex-direction: column; height: 88rpx;
align-items: center; background: #5db66f;
justify-content: center; border-radius: 44rpx;
height: 200rpx; color: #ffffff;
color: #999999; font-size: 32rpx;
font-weight: 500;
border: none;
&:active {
background: #4ca85c;
opacity: 0.9;
}
}
} }
.upload-icon { ::v-deep .uni-input-placeholder {
font-size: 48rpx; font-size: 28rpx !important;
margin-bottom: 10rpx; color: #999999 !important;
} }
.upload-text { :deep(.fui-button) {
font-size: 24rpx; width: 690rpx;
border-color: #5db66f !important;
background: #5db66f !important;
} }
.submit-section { // 移除fui-form的默认样式
:deep(.fui-form) {
background: transparent; background: transparent;
padding: 40rpx 0;
} }
.submit-btn { :deep(.fui-form__item) {
width: 100%; background: transparent;
height: 88rpx;
background: #5DB66F;
border-radius: 44rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
border: none; border: none;
margin-bottom: 0;
&:active { padding: 0;
background: #4ca85c;
opacity: 0.9;
}
} }
}
::v-deep .uni-input-placeholder {
font-size: 28rpx !important;
color: #999999 !important;
}
:deep(.fui-button) {
width: 690rpx;
border-color: #5DB66F !important;
background: #5DB66F !important;
}
// 移除fui-form的默认样式
:deep(.fui-form) {
background: transparent;
}
:deep(.fui-form__item) {
background: transparent;
border: none;
margin-bottom: 0;
padding: 0;
}
</style> </style>
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import { onPullDownRefresh, onLoad } from '@dcloudio/uni-app' import { onPullDownRefresh, onLoad } from '@dcloudio/uni-app'
import * as NongzhiAPI from '@/api/model/nongzhi' import * as NongzhiAPI from '@/api/model/nongzhi'
import Navigate from '@/utils/page/navigate' import Navigate from '@/utils/page/navigate'
import { url } from 'inspector'
// 下拉刷新 // 下拉刷新
onPullDownRefresh(() => { onPullDownRefresh(() => {
...@@ -47,6 +48,7 @@ ...@@ -47,6 +48,7 @@
title: '季节性用工', title: '季节性用工',
description: '三农区域灵活用工服务', description: '三农区域灵活用工服务',
actionText: '去发布', actionText: '去发布',
url: '/pages/nongjifuwu/farm-form',
}, },
{ {
id: 2, id: 2,
...@@ -54,14 +56,15 @@ ...@@ -54,14 +56,15 @@
title: '农机手调度', title: '农机手调度',
description: '专业人员作业安排', description: '专业人员作业安排',
actionText: '去预约', actionText: '去预约',
url: '/pages/nongjifuwu/nongjifuwu',
}, },
{ // {
id: 3, // id: 3,
image: '/static/images/codefun/06319419d17b7630c1d5bc415bcd26ef.png', // image: '/static/images/codefun/06319419d17b7630c1d5bc415bcd26ef.png',
title: '作业质量评价', // title: '作业质量评价',
description: '农机定位精度≤50米', // description: '农机定位精度≤50米',
actionText: '去评价', // actionText: '去评价',
}, // },
], ],
// 助农金融产品 // 助农金融产品
...@@ -347,7 +350,12 @@ ...@@ -347,7 +350,12 @@
</view> </view>
<view class="codefun-flex-col group_8"> <view class="codefun-flex-col group_8">
<view class="codefun-flex-col section_4 flex gap-4"> <view class="codefun-flex-col section_4 flex gap-4">
<view v-for="service in pageData.flexibleEmploymentServices" :key="service.id"> <navigator
v-for="service in pageData.flexibleEmploymentServices"
:key="service.id"
:url="service.url"
hover-class="none"
>
<view class="codefun-flex-row codefun-justify-between codefun-items-center"> <view class="codefun-flex-row codefun-justify-between codefun-items-center">
<view class="codefun-flex-row"> <view class="codefun-flex-row">
<image class="image_8" :src="service.image" /> <image class="image_8" :src="service.image" />
...@@ -367,7 +375,7 @@ ...@@ -367,7 +375,7 @@
<text class="font_8" :class="`text_${9 + service.id}`">{{ service.actionText }}</text> <text class="font_8" :class="`text_${9 + service.id}`">{{ service.actionText }}</text>
</view> </view>
</view> </view>
</view> </navigator>
</view> </view>
<view class="codefun-flex-col codefun-mt-24"> <view class="codefun-flex-col codefun-mt-24">
<view class="codefun-flex-row codefun-justify-between codefun-items-center group_11"> <view class="codefun-flex-row codefun-justify-between codefun-items-center group_11">
...@@ -503,7 +511,7 @@ ...@@ -503,7 +511,7 @@
class="codefun-flex-row codefun-justify-between codefun-items-center codefun-self-stretch group_14" class="codefun-flex-row codefun-justify-between codefun-items-center codefun-self-stretch group_14"
> >
<text class="font_5">一站式解决农机问题</text> <text class="font_5">一站式解决农机问题</text>
<text class="font_6 text_37" @click="onViewAllMachineryCases">全部案例</text> <!-- <text class="font_6 text_37" @click="onViewAllMachineryCases">全部案例</text> -->
</view> </view>
<view class="codefun-flex-col codefun-self-stretch section_14"> <view class="codefun-flex-col codefun-self-stretch section_14">
<view class="codefun-flex-row"> <view class="codefun-flex-row">
......
...@@ -44,8 +44,8 @@ ...@@ -44,8 +44,8 @@
id: '', id: '',
name: '', name: '',
content: '', content: '',
workers: 0, workers: '',
price: 0, price: '',
typeText: '', typeText: '',
type: null, type: null,
area: '', area: '',
...@@ -276,67 +276,122 @@ ...@@ -276,67 +276,122 @@
</script> </script>
<template> <template>
<fui-form ref="formRef" :disabled="form.id ? true : false"> <view class="page">
<fui-input required label="名称" placeholder="请输入名称" v-model="form.name"></fui-input> <view class="formBox">
<fui-input required label="工作内容" borderTop placeholder="请输入工作内容" v-model="form.content"></fui-input> <fui-form ref="formRef" label-weight="auto" top="60" :disabled="form.id ? true : false">
<fui-input required label="工人数量" borderTop placeholder="请输入工人数量" v-model="form.workers"></fui-input> <view class="mt20">
<fui-input required label="价钱" borderTop placeholder="请输入价钱" v-model="form.price"></fui-input> <fui-input
<fui-input required
required label="名称"
label="类型" placeholder="请输入名称"
placeholder="请选择类型" v-model="form.name"
v-model="form.typeText" labelSize="28"
@click="show.type = true" label-width="180"
></fui-input> ></fui-input>
<fui-input <fui-input
required required
label="地区" label="工作内容"
placeholder="请选择地区" placeholder="请输入工作内容"
v-model="form.areaText" v-model="form.content"
@click="show.area = true" labelSize="28"
></fui-input> label-width="180"
<fui-input label="详细地址" placeholder="请输入详细地址" v-model="form.address"></fui-input> ></fui-input>
<fui-input </view>
required <view class="mt20">
label="紧急程度" <fui-input
placeholder="请选择紧急程度" required
v-model="form.urgentdegreeText" type="number"
@click="show.urgentdegree = true" :min="0"
></fui-input> label="工人数量"
<fui-input placeholder="请输入工人数量"
required v-model="form.workers"
label="开始时间" labelSize="28"
borderTop label-width="180"
placeholder="请选择开始时间" ></fui-input>
v-model="form.starttime" <fui-input
@click="show.time1 = true" required
></fui-input> type="number"
<fui-input label="价钱"
required :min="0"
label="结束时间" placeholder="请输入价钱"
borderTop v-model="form.price"
placeholder="请选择结束时间" labelSize="28"
v-model="form.estimatedendtime" label-width="180"
@click="show.time2 = true" ></fui-input>
></fui-input> <fui-input
<view class="bg-white" style="padding: 0.875rem 0.35rem"> required
<view class="mb-1 flex justify-start"> label="类型"
<text class="pr-1" style="color: #ff2b2b">*</text> placeholder="请选择类型"
图片 v-model="form.typeText"
</view> labelSize="28"
<uni-file-picker label-width="180"
:value="form.pictureObj" @click="show.type = true"
ref="uploadRef" ></fui-input>
limit="1" </view>
:auto-upload="false" <view class="mt20">
@select="handleUpload" <fui-input
@delete="handleDelete" required
></uni-file-picker> label="地区"
placeholder="请选择地区"
v-model="form.areaText"
labelSize="28"
label-width="180"
@click="show.area = true"
></fui-input>
<fui-input
required
label="详细地址"
placeholder="请输入详细地址"
v-model="form.address"
labelSize="28"
label-width="180"
></fui-input>
<fui-input
required
label="紧急程度"
placeholder="请选择紧急程度"
v-model="form.urgentdegreeText"
labelSize="28"
label-width="180"
@click="show.urgentdegree = true"
></fui-input>
<!-- 时间范围 -->
<view class="form-section" style="padding: 0 30rpx">
<view class="form-item flex align-center">
<text class="label">用工时间</text>
<view class="time-range">
<view class="time-input" @click="show.time1 = true">
<text class="time-text" :class="{ placeholder: !form.starttime }">
{{ form.starttime || '开始时间' }}
</text>
</view>
<text class="time-separator">-</text>
<view class="time-input" @click="show.time2 = true">
<text class="time-text" :class="{ placeholder: !form.estimatedendtime }">
{{ form.estimatedendtime || '结束时间' }}
</text>
</view>
</view>
</view>
</view>
</view>
<view class="bg-white mt20" style="padding: 0.875rem 1rem">
<view class="mb-1 flex justify-start"> 图片 </view>
<uni-file-picker
:value="form.pictureObj"
ref="uploadRef"
limit="1"
:auto-upload="false"
@select="handleUpload"
@delete="handleDelete"
></uni-file-picker>
</view>
<view class="fui-btn__box" v-if="!form.id" style="margin-top: 30rpx">
<fui-button text="发布用工" bold radius="96rpx" @click="submit"></fui-button>
</view>
</fui-form>
</view> </view>
<view class="fui-btn__box bg-white p-4" v-if="!form.id"> </view>
<fui-button text="发布用工" bold radius="96rpx" @click="submit"></fui-button>
</view>
</fui-form>
<fui-date-picker <fui-date-picker
:show="show.time1" :show="show.time1"
...@@ -380,8 +435,173 @@ ...@@ -380,8 +435,173 @@
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.fui-button) { body {
border-color: #ff9800 !important; background-color: #e6f5e8;
background: #ff9800 !important; }
.page {
background-color: #e6f5e8;
width: 750rpx;
overflow-x: hidden;
.mt20 {
margin-top: 30rpx;
background: #fff;
padding: 20rpx;
border-radius: 10rpx;
}
.formBox {
width: 690rpx;
margin: 30rpx auto;
}
.form-section {
// border-bottom: 1rpx solid #f5f5f5;
}
.form-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&.required .label::before {
content: '*';
color: #ff4d4f;
margin-right: 8rpx;
}
}
.form-row {
display: flex;
justify-content: space-between;
}
.half-width {
width: 48%;
}
.align-center {
align-items: center;
}
.label {
display: block;
font-size: 28rpx;
color: #333333;
font-weight: 500;
width: 180rpx;
}
.input {
width: 100%;
height: 80rpx;
background: #f8f9fa;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
&::placeholder {
color: #999999;
}
}
.time-range {
display: flex;
align-items: center;
justify-content: space-between;
}
.time-input {
width: 45%;
border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-text {
font-size: 28rpx;
color: #333333;
&.placeholder {
color: #999999;
}
}
.time-separator {
color: #666666;
font-size: 28rpx;
margin: 0 10rpx;
}
.upload-area {
margin-top: 10rpx;
}
.custom-uploader {
:deep(.uni-file-picker__container) {
border: 2rpx dashed #d9d9d9;
border-radius: 8rpx;
background: #f8f9fa;
}
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200rpx;
color: #999999;
}
.upload-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 24rpx;
}
.submit-section {
background: transparent;
padding: 40rpx 0;
}
.submit-btn {
width: 100%;
height: 88rpx;
background: #ff9800;
border-radius: 44rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
border: none;
&:active {
background: #e68900;
opacity: 0.9;
}
}
}
::v-deep .uni-input-placeholder {
font-size: 28rpx !important;
color: #999999 !important;
}
// 移除fui-form的默认样式
:deep(.fui-form) {
background: transparent;
}
:deep(.fui-form__item) {
background: transparent;
border: none;
margin-bottom: 0;
padding: 0;
} }
</style> </style>
...@@ -113,23 +113,107 @@ ...@@ -113,23 +113,107 @@
total: 0, total: 0,
}) })
// 缓存已处理的区域数据,避免重复计算
const areaCache = new Map()
// 请求重试配置
const requestConfig = {
maxRetries: 2,
retryDelay: 1000,
}
// 带重试机制的API调用
async function fetchWithRetry(apiCall, maxRetries = requestConfig.maxRetries) {
const startTime = Date.now()
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await apiCall()
const endTime = Date.now()
// 记录API性能数据(仅在开发环境)
if (import.meta.env.DEV) {
console.log(`API请求成功,耗时: ${endTime - startTime}ms`)
}
return result
} catch (error) {
console.warn(`API请求失败 (尝试 ${attempt}/${maxRetries}):`, error)
if (attempt === maxRetries) {
const endTime = Date.now()
if (import.meta.env.DEV) {
console.error(`API请求最终失败,总耗时: ${endTime - startTime}ms`)
}
throw error
}
// 指数退避策略
await new Promise((resolve) => setTimeout(resolve, requestConfig.retryDelay * attempt))
}
}
}
function getEmploymentList() { function getEmploymentList() {
// 如果正在加载或没有更多数据,直接返回
if (pageData.loading || (pageData.total > 0 && pageData.employmentList.length >= pageData.total)) {
return
}
pageData.loading = true pageData.loading = true
LinghuoyonggongAPI.employmentList(pageData.search)
.then((res) => { // 添加请求防抖,避免快速连续请求
if (pageData.requestDebounce) {
clearTimeout(pageData.requestDebounce)
}
pageData.requestDebounce = setTimeout(async () => {
try {
const res = await fetchWithRetry(() => LinghuoyonggongAPI.employmentList(pageData.search))
const { records, total } = res const { records, total } = res
console.log('records', records)
records.map((item) => { // 批量处理数据,避免多次DOM操作
item.area = item.area.split(',') const processedRecords = records.map((item) => {
item.area = getTextByCode(item.area[0]) + ' ' + getTextByCode(item.area[1]) // 缓存区域处理结果
const cacheKey = item.area
if (areaCache.has(cacheKey)) {
item.area = areaCache.get(cacheKey)
} else {
const areaCodes = item.area.split(',')
const areaText = getTextByCode(areaCodes[0]) + ' ' + getTextByCode(areaCodes[1])
areaCache.set(cacheKey, areaText)
item.area = areaText
}
// 计算天数并缓存结果
if (item.starttime && item.estimatedendtime) {
item.daysDiff = getDaysDiff(item.starttime, item.estimatedendtime)
}
return item return item
}) })
pageData.employmentList = [...pageData.employmentList, ...records]
// 一次性更新数据,避免多次响应式更新
if (pageData.search.pageNo === 1) {
pageData.employmentList = processedRecords
} else {
// 避免重复数据加载
const existingIds = new Set(pageData.employmentList.map((item) => item.id))
const newRecords = processedRecords.filter((item) => !existingIds.has(item.id))
pageData.employmentList = [...pageData.employmentList, ...newRecords]
}
pageData.total = total pageData.total = total
}) } catch (error) {
.finally(() => { console.error('获取用工列表失败:', error)
// 这里可以添加用户友好的错误提示
// uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' })
} finally {
pageData.loading = false pageData.loading = false
}) pageData.requestDebounce = null
}
}, 150)
} }
// 分类标签点击事件 // 分类标签点击事件
...@@ -251,7 +335,7 @@ ...@@ -251,7 +335,7 @@
@click="onEmploymentItemClick(item)" @click="onEmploymentItemClick(item)"
> >
<view class="codefun-flex-row"> <view class="codefun-flex-row">
<image class="image_7" :src="item.picture" /> <image class="image_7" :src="item.picture" lazy-load />
<view class="codefun-flex-col codefun-flex-1 codefun-self-center group_4"> <view class="codefun-flex-col codefun-flex-1 codefun-self-center group_4">
<view class="codefun-flex-row codefun-justify-between codefun-items-center"> <view class="codefun-flex-row codefun-justify-between codefun-items-center">
<text class="codefun-self-start font">{{ item.name }}</text> <text class="codefun-self-start font">{{ item.name }}</text>
...@@ -269,7 +353,9 @@ ...@@ -269,7 +353,9 @@
<view class="flex codefun-items-center gap-1"> <view class="flex codefun-items-center gap-1">
<image class="image_9" src="/src/static/images/time.svg" /> <image class="image_9" src="/src/static/images/time.svg" />
<text class="font_4" <text class="font_4"
>预计{{ getDaysDiff(item.starttime, item.estimatedendtime) }}</text >预计{{
item.daysDiff || getDaysDiff(item.starttime, item.estimatedendtime)
}}</text
> >
</view> </view>
<view class="flex codefun-items-center gap-1"> <view class="flex codefun-items-center gap-1">
...@@ -304,10 +390,11 @@ ...@@ -304,10 +390,11 @@
<!-- <image class="codefun-self-end image_7 image_12 mt-269" :src="pageData.bottomImage" /> --> <!-- <image class="codefun-self-end image_7 image_12 mt-269" :src="pageData.bottomImage" /> -->
</view> </view>
<fui-fab background="#5db66f" position="right" distance="10" bottom="240" width="120" @click="handlePublish"> <fui-fab position="right" distance="10" bottom="240" width="96" @click="handlePublish">
<view class="text-white text-center"> <view class="text-white text-center">
<image src="/src/static/images/chanxiao/notepad.svg" style="width: 40rpx" mode="widthFix"></image> <!-- <image src="/src/static/images/chanxiao/notepad.svg" style="width: 40rpx" mode="widthFix"></image> -->
<view style="font-size: 24rpx">发布用工</view> <view class="fab-icon"></view>
<view style="font-size: 24rpx">发布</view>
</view> </view>
</fui-fab> </fui-fab>
<register-dialog ref="registerDialogRef"></register-dialog> <register-dialog ref="registerDialogRef"></register-dialog>
...@@ -315,6 +402,9 @@ ...@@ -315,6 +402,9 @@
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
body {
background-color: #e6f5e8;
}
.mt-19 { .mt-19 {
margin-top: 38rpx; margin-top: 38rpx;
} }
...@@ -331,7 +421,7 @@ ...@@ -331,7 +421,7 @@
margin-top: 538rpx; margin-top: 538rpx;
} }
.page { .page {
padding-bottom: 128rpx; // padding-bottom: 128rpx;
background-color: #e6f5e8; background-color: #e6f5e8;
mix-blend-mode: NOTTHROUGH; mix-blend-mode: NOTTHROUGH;
width: 100%; width: 100%;
...@@ -544,9 +634,14 @@ ...@@ -544,9 +634,14 @@
.image_7 { .image_7 {
width: 96rpx; width: 96rpx;
height: 96rpx; height: 96rpx;
border-radius: 50%;
} }
.image_12 { .image_12 {
margin-right: 58rpx; margin-right: 58rpx;
} }
} }
::v-deep .fui-fab__btn-main {
background: linear-gradient(124.25deg, #a5d63f 0%, #5db66f 100%) !important;
box-shadow: 0px 1px 8px #5db66f;
}
</style> </style>
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import { useGlobSetting } from '/@/hooks/setting' import { useGlobSetting } from '/@/hooks/setting'
import * as nongjifuwu from '@/api/model/nongjifuwu' import * as nongjifuwu from '@/api/model/nongjifuwu'
import { useDictStore } from '@/store/modules/dict' import { useDictStore } from '@/store/modules/dict'
import { areaTree } from '@/utils/areaData'
const dictStore = useDictStore() const dictStore = useDictStore()
const userStore = useUserStore() const userStore = useUserStore()
...@@ -34,6 +35,10 @@ ...@@ -34,6 +35,10 @@
show: { show: {
time1: false, time1: false,
time2: false, time2: false,
address: false,
},
options: {
address: [],
}, },
form: { form: {
id: '', id: '',
...@@ -87,10 +92,11 @@ ...@@ -87,10 +92,11 @@
], ],
}) })
const { show, form } = toRefs(pageData) const { show, options, form } = toRefs(pageData)
function initDict() { function initDict() {
console.log(dictStore.getDictList) console.log(dictStore.getDictList)
pageData.options.address = areaTree
} }
function getDetails(id) { function getDetails(id) {
...@@ -131,6 +137,11 @@ ...@@ -131,6 +137,11 @@
pageData.form.endTime = e.result pageData.form.endTime = e.result
pageData.show.time2 = false pageData.show.time2 = false
} }
function handleChangeAddress(e) {
pageData.form.scope = e.text.join('/')
pageData.show.address = false
}
const toastRef = ref() const toastRef = ref()
const uploadRef = ref() const uploadRef = ref()
// 文件上传 // 文件上传
...@@ -191,64 +202,336 @@ ...@@ -191,64 +202,336 @@
</script> </script>
<template> <template>
<fui-form ref="formRef" :disabled="form.id ? true : false"> <view class="page">
<fui-input required label="服务名称" placeholder="请输入服务名称" v-model="form.name"></fui-input> <view class="formBox">
<fui-input required label="服务范围" placeholder="请输入服务范围" v-model="form.scope"></fui-input> <fui-form ref="formRef" label-weight="auto" top="60" :disabled="form.id ? true : false">
<fui-input required label="详细地址" placeholder="请输入详细地址" v-model="form.address"></fui-input> <view class="mt20">
<fui-input required label="联系方式" borderTop placeholder="请输入联系方式" v-model="form.phone"></fui-input> <fui-input
<fui-input required label="作业需求" borderTop placeholder="请输入作业需求" v-model="form.demand"></fui-input> required
<fui-input label="服务名称"
required placeholder="请输入服务名称"
label="作业开始时间" v-model="form.name"
borderTop labelSize="28"
placeholder="请选择开始时间" label-width="180"
v-model="form.startTime" ></fui-input>
@click="show.time1 = true" <view class="form-item required flex align-center" style="padding: 20rpx 10rpx">
></fui-input> <text class="label">服务范围</text>
<fui-input <view class="time-input" @click="show.address = true">
required <text class="select-text" :class="{ placeholder: !form.scope }">
label="作业结束时间" {{ form.scope || '请选择省/市/区县' }}
borderTop </text>
placeholder="请选择结束时间" </view>
v-model="form.endTime" </view>
@click="show.time2 = true" <view class="form-item required flex align-center" style="padding: 20rpx 10rpx">
></fui-input> <text class="label">所在地区</text>
<view class="bg-white" style="padding: 0.875rem 0.35rem"> <view class="time-input" @click="show.address = true">
<view class="mb-1 flex justify-start"> 图片 </view> <text class="select-text" :class="{ placeholder: !form.scope }">
<uni-file-picker {{ form.scope || '请选择省/市/区县' }}
:value="form.pictureObj" </text>
ref="uploadRef" </view>
limit="1" </view>
:auto-upload="false" <fui-input
@select="handleUpload" required
@delete="handleDelete" label="详细地址"
></uni-file-picker> placeholder="请输入详细地址"
</view> v-model="form.address"
<view class="fui-btn__box bg-white p-4" v-if="!form.id"> labelSize="28"
<fui-button text="发布作业" bold radius="96rpx" @click="submit"></fui-button> label-width="180"
></fui-input>
<fui-input
required
label="联系方式"
placeholder="请输入联系方式"
v-model="form.phone"
labelSize="28"
label-width="180"
></fui-input>
</view>
<view class="mt20">
<fui-input
required
label="作业需求"
placeholder="请输入作业需求"
v-model="form.demand"
labelSize="28"
label-width="180"
></fui-input>
<!-- 作业时间 -->
<view class="form-section" style="padding: 0 10rpx">
<view class="form-item flex align-center">
<text class="label">作业时间</text>
<view class="time-range">
<view class="time-input" @click="show.time1 = true">
<text class="time-text" :class="{ placeholder: !form.startTime }">
{{ form.startTime || '作业开始时间' }}
</text>
</view>
<text class="time-separator">-</text>
<view class="time-input" @click="show.time2 = true">
<text class="time-text" :class="{ placeholder: !form.endTime }">
{{ form.endTime || '作业结束时间' }}
</text>
</view>
</view>
</view>
</view>
</view>
<view class="bg-white mt20" style="padding: 0.875rem 1rem">
<view class="mb-1 flex justify-start"> 图片 </view>
<uni-file-picker
:value="form.pictureObj"
ref="uploadRef"
limit="1"
:auto-upload="false"
@select="handleUpload"
@delete="handleDelete"
></uni-file-picker>
</view>
<view class="fui-btn__box" v-if="!form.id" style="margin-top: 30rpx">
<fui-button text="发布作业" bold radius="96rpx" @click="submit"></fui-button>
</view>
</fui-form>
<fui-date-picker
:show="show.time1"
type="3"
@change="handleChangeTime1"
@cancel="show.time1 = false"
minDate="2025-01-01"
></fui-date-picker>
<fui-date-picker
:show="show.time2"
type="3"
@change="handleChangeTime2"
@cancel="show.time2 = false"
minDate="2025-01-01"
></fui-date-picker>
<fui-toast ref="toastRef"></fui-toast>
<fui-loading isFixed v-if="pageData.loading" backgroundColor="rgba(0, 0, 0, 0.4)"></fui-loading>
<fui-picker
:show="show.address"
:options="options.address"
:linkage="true"
:layer="3"
@change="handleChangeAddress"
@cancel="show.address = false"
></fui-picker>
</view> </view>
</fui-form> </view>
<fui-date-picker
:show="show.time1"
type="3"
@change="handleChangeTime1"
@cancel="show.time1 = false"
></fui-date-picker>
<fui-date-picker
:show="show.time2"
type="3"
@change="handleChangeTime2"
@cancel="show.time2 = false"
></fui-date-picker>
<fui-toast ref="toastRef"></fui-toast>
<fui-loading isFixed v-if="pageData.loading" backgroundColor="rgba(0, 0, 0, 0.4)"></fui-loading>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
body {
background-color: #e6f5e8;
min-height: 100vh;
}
.page {
background-color: #e6f5e8;
width: 750rpx;
overflow-x: hidden;
.mt20 {
margin-top: 30rpx;
background: #fff;
padding: 20rpx;
border-radius: 10rpx;
}
.formBox {
width: 690rpx;
margin: 30rpx auto;
}
.form-section {
// border-bottom: 1rpx solid #f5f5f5;
}
.form-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&.required .label::before {
content: '*';
color: #ff4d4f;
margin-right: 8rpx;
}
}
.form-row {
display: flex;
justify-content: space-between;
}
.half-width {
width: 48%;
}
.align-center {
align-items: center;
}
.label {
display: block;
font-size: 28rpx;
color: #333333;
font-weight: 500;
width: 180rpx;
}
.input {
width: 100%;
height: 80rpx;
background: #f8f9fa;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
&::placeholder {
color: #999999;
}
}
.time-range {
display: flex;
align-items: center;
justify-content: space-between;
}
.time-input {
width: 45%;
border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-text {
font-size: 28rpx;
color: #333333;
&.placeholder {
color: #999999;
}
}
.time-separator {
color: #666666;
font-size: 28rpx;
margin: 0 10rpx;
}
.upload-area {
margin-top: 10rpx;
}
.custom-uploader {
:deep(.uni-file-picker__container) {
border: 2rpx dashed #d9d9d9;
border-radius: 8rpx;
background: #f8f9fa;
}
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200rpx;
color: #999999;
}
.upload-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 24rpx;
}
.submit-section {
background: transparent;
padding: 40rpx 0;
}
.submit-btn {
width: 100%;
height: 88rpx;
background: #5db66f;
border-radius: 44rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
border: none;
&:active {
background: #4ca85c;
opacity: 0.9;
}
}
}
::v-deep .uni-input-placeholder {
font-size: 28rpx !important;
color: #999999 !important;
}
:deep(.fui-button) { :deep(.fui-button) {
border-color: #ff9800 !important; border-color: #5db66f !important;
background: #ff9800 !important; background: #5db66f !important;
}
// 移除fui-form的默认样式
:deep(.fui-form) {
background: transparent;
}
:deep(.fui-form__item) {
background: transparent;
border: none;
margin-bottom: 0;
padding: 0;
}
.time-input {
width: 45%;
border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-text {
font-size: 28rpx;
color: #333333;
padding: 0 20rpx;
&.placeholder {
color: #999999;
}
}
.select-input {
flex: 1;
border-radius: 8rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.select-text {
font-size: 28rpx;
color: #333333;
padding: 0 20rpx;
&.placeholder {
color: #999999;
}
} }
</style> </style>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import { useGlobSetting } from '/@/hooks/setting' import { useGlobSetting } from '/@/hooks/setting'
import * as nongjifuwu from '@/api/model/nongjifuwu' import * as nongjifuwu from '@/api/model/nongjifuwu'
import { useDictStore } from '@/store/modules/dict' import { useDictStore } from '@/store/modules/dict'
import { areaTree } from '@/utils/areaData'
const dictStore = useDictStore() const dictStore = useDictStore()
const userStore = useUserStore() const userStore = useUserStore()
const globSetting = useGlobSetting() const globSetting = useGlobSetting()
...@@ -31,11 +31,20 @@ ...@@ -31,11 +31,20 @@
const pageData = reactive({ const pageData = reactive({
title: '农机表单', title: '农机表单',
loading: false, loading: false,
show: {
time: false,
classify: false,
address: false,
},
options: {
address: [],
classify: [],
},
form: { form: {
id: '', id: '',
name: '', name: '',
phone: '', phone: '',
price: 0, price: '',
scope: '', scope: '',
type: '', type: '',
picture: '', picture: '',
...@@ -70,10 +79,10 @@ ...@@ -70,10 +79,10 @@
], ],
}) })
const { form } = toRefs(pageData) const { show, options, form } = toRefs(pageData)
function initDict() { function initDict() {
console.log(dictStore.getDictList) console.log(dictStore.getDictList)
pageData.options.address = areaTree
} }
function getDetails(id) { function getDetails(id) {
...@@ -164,47 +173,304 @@ ...@@ -164,47 +173,304 @@
} }
}) })
} }
function handleChangeAddress(e) {
pageData.form.scope = e.text.join('/')
pageData.show.address = false
}
</script> </script>
<template> <template>
<fui-form ref="formRef" :disabled="form.id ? true : false"> <view class="page">
<fui-input required label="农机名称" placeholder="请输入农机名称" v-model="form.name"></fui-input> <view class="formBox">
<fui-input required label="联系方式" placeholder="请输入联系方式" v-model="form.phone"></fui-input> <fui-form ref="formRef" label-weight="auto" top="60" :disabled="form.id ? true : false">
<fui-input required label="服务范围" placeholder="请输入服务范围" v-model="form.scope"></fui-input> <view class="mt20">
<fui-input <fui-input
required required
label="价格(元/亩)" label="农机名称"
borderTop placeholder="请输入农机名称"
placeholder="请输入价格" v-model="form.name"
number labelSize="28"
v-model="form.price" label-width="180"
></fui-input> ></fui-input>
<view class="bg-white" style="padding: 0.875rem 0.35rem"> <fui-input
<view class="mb-1 flex justify-start"> required
<text class="pr-1" style="color: #ff2b2b">*</text> label="联系方式"
图片 placeholder="请输入联系方式"
</view> v-model="form.phone"
<uni-file-picker labelSize="28"
:value="form.pictureObj" label-width="180"
:max-size="1024" ></fui-input>
ref="uploadRef" <view class="form-item required flex align-center" style="padding: 20rpx 10rpx">
limit="1" <text class="label">服务范围</text>
:auto-upload="false" <view class="time-input" @click="show.address = true">
@select="handleUpload" <text class="select-text" :class="{ placeholder: !form.scope }">
@delete="handleDelete" {{ form.scope || '请选择省/市/区县' }}
></uni-file-picker> </text>
</view> </view>
<view class="fui-btn__box bg-white p-4" v-if="!form.id"> </view>
<fui-button text="发布作业" bold radius="96rpx" @click="submit"></fui-button> <!-- <fui-input
</view> required
</fui-form> label="服务范围"
placeholder="请输入服务范围"
v-model="form.scope"
labelSize="28"
label-width="180"
></fui-input> -->
<fui-input
required
type="number"
label="价格(元/亩)"
placeholder="请输入价格"
number
v-model="form.price"
labelSize="28"
label-width="180"
></fui-input>
</view>
<view class="bg-white mt20" style="padding: 0.875rem 1rem">
<view class="mb-1 flex justify-start"> 图片 </view>
<uni-file-picker
:value="form.pictureObj"
:max-size="1024"
ref="uploadRef"
limit="1"
:auto-upload="false"
@select="handleUpload"
@delete="handleDelete"
></uni-file-picker>
</view>
<view class="fui-btn__box" v-if="!form.id" style="margin-top: 30rpx">
<fui-button text="发布作业" bold radius="96rpx" @click="submit"></fui-button>
</view>
</fui-form>
<fui-toast ref="toastRef"></fui-toast> <fui-toast ref="toastRef"></fui-toast>
<fui-loading isFixed v-if="pageData.loading" backgroundColor="rgba(0, 0, 0, 0.4)"></fui-loading> <fui-loading isFixed v-if="pageData.loading" backgroundColor="rgba(0, 0, 0, 0.4)"></fui-loading>
<fui-picker
:show="show.address"
:options="options.address"
:linkage="true"
:layer="3"
@change="handleChangeAddress"
@cancel="show.address = false"
></fui-picker>
</view>
</view>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
body {
background-color: #e6f5e8;
}
.page {
background-color: #e6f5e8;
width: 750rpx;
overflow-x: hidden;
.mt20 {
margin-top: 30rpx;
background: #fff;
padding: 20rpx;
border-radius: 10rpx;
}
.formBox {
width: 690rpx;
margin: 30rpx auto;
}
.form-section {
border-bottom: 1rpx solid #f5f5f5;
}
.form-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&.required .label::before {
content: '*';
color: #ff4d4f;
margin-right: 8rpx;
}
}
.form-row {
display: flex;
justify-content: space-between;
}
.half-width {
width: 48%;
}
.align-center {
align-items: center;
}
.label {
display: block;
font-size: 28rpx;
color: #333333;
font-weight: 500;
width: 180rpx;
}
.input {
width: 100%;
height: 80rpx;
background: #f8f9fa;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
&::placeholder {
color: #999999;
}
}
.time-range {
display: flex;
align-items: center;
justify-content: space-between;
}
.time-input {
width: 45%;
border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-text {
font-size: 28rpx;
color: #333333;
&.placeholder {
color: #999999;
}
}
.time-separator {
color: #666666;
font-size: 28rpx;
margin: 0 10rpx;
}
.upload-area {
margin-top: 10rpx;
}
.custom-uploader {
:deep(.uni-file-picker__container) {
border: 2rpx dashed #d9d9d9;
border-radius: 8rpx;
background: #f8f9fa;
}
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200rpx;
color: #999999;
}
.upload-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 24rpx;
}
.submit-section {
background: transparent;
padding: 40rpx 0;
}
.submit-btn {
width: 100%;
height: 88rpx;
background: #5db66f;
border-radius: 44rpx;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
border: none;
&:active {
background: #4ca85c;
opacity: 0.9;
}
}
}
::v-deep .uni-input-placeholder {
font-size: 28rpx !important;
color: #999999 !important;
}
:deep(.fui-button) { :deep(.fui-button) {
border-color: #ff9800 !important; width: 690rpx;
background: #ff9800 !important; border-color: #5db66f !important;
background: #5db66f !important;
}
// 移除fui-form的默认样式
:deep(.fui-form) {
background: transparent;
}
:deep(.fui-form__item) {
background: transparent;
border: none;
margin-bottom: 0;
padding: 0;
}
.time-input {
width: 45%;
// height: 80rpx;
// background: #f8f9fa;
border-radius: 8rpx;
display: flex;
align-items: center;
}
.time-text {
font-size: 28rpx;
color: #333333;
padding: 0 20rpx;
&.placeholder {
color: #999999;
}
}
.select-input {
flex: 1;
border-radius: 8rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.select-text {
font-size: 28rpx;
color: #333333;
padding: 0 20rpx;
&.placeholder {
color: #999999;
}
} }
</style> </style>
...@@ -126,7 +126,7 @@ ...@@ -126,7 +126,7 @@
:src="item.picture" :src="item.picture"
@click="handleDetails(item)" @click="handleDetails(item)"
/> />
<view style="width: 75%"> <view style="width: 70%">
<view <view
class="codefun-flex-col codefun-items-start codefun-flex-1 codefun-self-center" class="codefun-flex-col codefun-items-start codefun-flex-1 codefun-self-center"
@click="handleDetails(item)" @click="handleDetails(item)"
...@@ -270,12 +270,10 @@ ...@@ -270,12 +270,10 @@
</view> --> </view> -->
</view> </view>
</view> </view>
<fui-fab background="#5db66f" position="right" distance="10" bottom="240" width="130" @click="handlePublish"> <fui-fab position="right" distance="10" bottom="240" width="96" @click="handlePublish">
<view class="text-white text-center"> <view class="text-white text-center">
<image src="/src/static/images/chanxiao/notepad.svg" style="width: 40rpx" mode="widthFix"></image> <view class="fab-icon"></view>
<view style="font-size: 24rpx" <view style="font-size: 24rpx">发布</view>
>发布{{ pageData.categoryTabs.find((item) => item.id === pageData.search.serviceType)?.name }}</view
>
</view> </view>
</fui-fab> </fui-fab>
...@@ -462,6 +460,8 @@ ...@@ -462,6 +460,8 @@
.image_8 { .image_8 {
width: 160rpx; width: 160rpx;
height: 160rpx; height: 160rpx;
border-radius: 10rpx;
margin-right: 20rpx;
} }
.font_6 { .font_6 {
font-size: 24rpx; font-size: 24rpx;
...@@ -641,4 +641,9 @@ ...@@ -641,4 +641,9 @@
color: #333333; color: #333333;
} }
} }
::v-deep .fui-fab__btn-main {
background: linear-gradient(124.25deg, #a5d63f 0%, #5db66f 100%) !important;
box-shadow: 0px 1px 8px #5db66f;
}
</style> </style>
...@@ -54,8 +54,17 @@ export const convertToTree = (): AreaNode[] => { ...@@ -54,8 +54,17 @@ export const convertToTree = (): AreaNode[] => {
return tree return tree
} }
// 导出树形结构数据 // 导出树形结构数据(懒加载)
export const areaTree = convertToTree() let areaTreeCache: AreaNode[] | null = null
export const getAreaTree = (): AreaNode[] => {
if (!areaTreeCache) {
areaTreeCache = convertToTree()
}
return areaTreeCache
}
// 向后兼容的导出
export const areaTree = getAreaTree()
/** /**
* 根据地区代码获取完整的地区名称(如:广东省广州市天河区) * 根据地区代码获取完整的地区名称(如:广东省广州市天河区)
...@@ -85,46 +94,28 @@ export const getFullAreaName = (code: string): string => { ...@@ -85,46 +94,28 @@ export const getFullAreaName = (code: string): string => {
// 根据编号获取文本 // 根据编号获取文本
export function getTextByCode(code) { export function getTextByCode(code) {
// 遍历省份 // 直接查询,避免多层嵌套循环
for (const provinceCode in areaList.province_list) { if (areaList.province_list[code]) {
if (provinceCode === code) { return areaList.province_list[code]
return areaList.province_list[provinceCode] }
} if (areaList.city_list[code]) {
// 遍历城市 return areaList.city_list[code]
for (const cityCode in areaList.city_list) { }
if (cityCode === code) { if (areaList.county_list[code]) {
return areaList.city_list[cityCode] return areaList.county_list[code]
}
// 遍历区县
for (const countyCode in areaList.county_list) {
if (countyCode === code) {
return areaList.county_list[countyCode]
}
}
}
} }
return null return null
} }
// 根据文本获取编号 // 根据文本获取编号
export function getCodeByText(text) { export function getCodeByText(text) {
// 遍历省份 // 使用Object.entries和find方法优化性能
for (const provinceCode in areaList.province_list) { const findCode = (obj: Record<string, string>) => {
if (areaList.province_list[provinceCode] === text) { const entry = Object.entries(obj).find(([_, value]) => value === text)
return provinceCode return entry ? entry[0] : null
}
}
// 遍历城市
for (const cityCode in areaList.city_list) {
if (areaList.city_list[cityCode] === text) {
return cityCode
}
}
// 遍历区县
for (const countyCode in areaList.county_list) {
if (areaList.county_list[countyCode] === text) {
return countyCode
}
} }
return null
return findCode(areaList.province_list)
|| findCode(areaList.city_list)
|| findCode(areaList.county_list)
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论