feat: harden phase one workflows
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
const { createSecondGoods } = require('../../api/secondGoodsApi')
|
||||
const { setField } = require('../../utils/request')
|
||||
const { setField, uploadFile } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
submitting: false,
|
||||
imageText: '',
|
||||
uploading: false,
|
||||
form: {
|
||||
title: '',
|
||||
priceCent: 0,
|
||||
@@ -28,14 +28,43 @@ Page({
|
||||
this.setData({ 'form.tradeMethod': event.detail.value })
|
||||
},
|
||||
|
||||
onImageInput(event) {
|
||||
const imageText = event.detail.value
|
||||
this.setData({
|
||||
imageText,
|
||||
'form.imageUrls': imageText ? imageText.split(',').map((item) => item.trim()).filter(Boolean) : []
|
||||
chooseImages() {
|
||||
if (this.data.uploading) {
|
||||
return
|
||||
}
|
||||
wx.chooseMedia({
|
||||
count: Math.max(1, 6 - this.data.form.imageUrls.length),
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (result) => {
|
||||
const files = result.tempFiles || []
|
||||
this.uploadImages(files.map((item) => item.tempFilePath))
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
uploadImages(filePaths) {
|
||||
if (!filePaths.length) {
|
||||
return
|
||||
}
|
||||
this.setData({ uploading: true })
|
||||
Promise.all(filePaths.map((filePath) => uploadFile(filePath)))
|
||||
.then((results) => {
|
||||
const urls = results.map((item) => item.url).filter(Boolean)
|
||||
this.setData({
|
||||
'form.imageUrls': this.data.form.imageUrls.concat(urls).slice(0, 6)
|
||||
})
|
||||
})
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
.finally(() => this.setData({ uploading: false }))
|
||||
},
|
||||
|
||||
removeImage(event) {
|
||||
const index = Number(event.currentTarget.dataset.index)
|
||||
const next = this.data.form.imageUrls.filter((item, itemIndex) => itemIndex !== index)
|
||||
this.setData({ 'form.imageUrls': next })
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (this.data.submitting) {
|
||||
return
|
||||
|
||||
@@ -1,16 +1,40 @@
|
||||
<view class="page">
|
||||
<view class="section-title">发布闲置</view>
|
||||
<view class="card">
|
||||
<view class="page-heading">
|
||||
<view class="heading-eyebrow">发布审核</view>
|
||||
<view class="heading-title">发布闲置</view>
|
||||
<view class="heading-subtitle">填写真实信息,审核通过后展示给邻居</view>
|
||||
</view>
|
||||
|
||||
<view class="form-card publish-form">
|
||||
<view class="field-label">标题</view>
|
||||
<input class="input" placeholder="标题" data-field="title" bindinput="onFieldInput" />
|
||||
|
||||
<view class="field-label">价格</view>
|
||||
<input class="input" placeholder="价格(元)" type="digit" bindinput="onPriceInput" />
|
||||
|
||||
<view class="field-label">分类</view>
|
||||
<input class="input" placeholder="分类" data-field="category" bindinput="onFieldInput" />
|
||||
|
||||
<view class="field-label">联系电话</view>
|
||||
<input class="input" placeholder="联系电话" data-field="contactPhone" bindinput="onFieldInput" />
|
||||
<radio-group bindchange="onTradeMethodChange">
|
||||
|
||||
<view class="field-label">交易方式</view>
|
||||
<radio-group class="radio-grid" bindchange="onTradeMethodChange">
|
||||
<label class="radio"><radio value="FACE_TO_FACE" checked="{{form.tradeMethod === 'FACE_TO_FACE'}}" />面交</label>
|
||||
<label class="radio"><radio value="DELIVERY" checked="{{form.tradeMethod === 'DELIVERY'}}" />可配送</label>
|
||||
</radio-group>
|
||||
<input class="input" placeholder="图片 URL,多个用英文逗号分隔" value="{{imageText}}" bindinput="onImageInput" />
|
||||
|
||||
<view class="field-label">图片</view>
|
||||
<view class="image-grid">
|
||||
<view wx:for="{{form.imageUrls}}" wx:key="*this" class="image-item">
|
||||
<image src="{{item}}" mode="aspectFill" />
|
||||
<button size="mini" class="remove" data-index="{{index}}" bindtap="removeImage">删除</button>
|
||||
</view>
|
||||
<button wx:if="{{form.imageUrls.length < 6}}" class="image-picker" loading="{{uploading}}" bindtap="chooseImages">上传图片</button>
|
||||
</view>
|
||||
|
||||
<view class="field-label">描述</view>
|
||||
<textarea class="textarea" placeholder="描述" data-field="description" bindinput="onFieldInput" />
|
||||
<button class="primary" loading="{{submitting}}" bindtap="submit">提交审核</button>
|
||||
<button class="primary action-button" loading="{{submitting}}" bindtap="submit">提交审核</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,78 @@
|
||||
.radio {
|
||||
display: block;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
.page-heading {
|
||||
margin-bottom: 22rpx;
|
||||
}
|
||||
|
||||
.publish-form {
|
||||
padding-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.radio-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.radio {
|
||||
min-height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 14rpx;
|
||||
color: #334155;
|
||||
background: #f8fafc;
|
||||
border: 1rpx solid #e2e8f0;
|
||||
border-radius: 14rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.image-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12rpx;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.image-item,
|
||||
.image-picker {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.image-item {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 14rpx;
|
||||
background: #eef3f7;
|
||||
}
|
||||
|
||||
.image-item image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.remove {
|
||||
position: absolute;
|
||||
right: 8rpx;
|
||||
bottom: 8rpx;
|
||||
margin: 0;
|
||||
font-size: 22rpx;
|
||||
color: #ffffff;
|
||||
background: rgba(15, 23, 42, 0.62);
|
||||
}
|
||||
|
||||
.image-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1rpx dashed #8aa0b5;
|
||||
color: #33536f;
|
||||
background: #f8fafc;
|
||||
border-radius: 14rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const { listSecondGoods } = require('../../api/secondGoodsApi')
|
||||
Page({
|
||||
data: {
|
||||
loading: false,
|
||||
keyword: '',
|
||||
goods: []
|
||||
},
|
||||
|
||||
@@ -16,12 +17,20 @@ Page({
|
||||
|
||||
load() {
|
||||
this.setData({ loading: true })
|
||||
listSecondGoods()
|
||||
listSecondGoods(null, this.data.keyword)
|
||||
.then((goods) => this.setData({ goods: goods || [] }))
|
||||
.catch((error) => wx.showToast({ title: error.message, icon: 'none' }))
|
||||
.finally(() => this.setData({ loading: false }))
|
||||
},
|
||||
|
||||
onKeywordInput(event) {
|
||||
this.setData({ keyword: event.detail.value })
|
||||
},
|
||||
|
||||
search() {
|
||||
this.load()
|
||||
},
|
||||
|
||||
goCreate() {
|
||||
wx.navigateTo({ url: '/pages/second-hand/create' })
|
||||
},
|
||||
|
||||
@@ -1,18 +1,36 @@
|
||||
<view class="page">
|
||||
<view class="row between section">
|
||||
<view class="section-title">二手闲置</view>
|
||||
<button size="mini" class="primary" bindtap="goCreate">发布</button>
|
||||
</view>
|
||||
<view wx:for="{{goods}}" wx:key="id" class="card">
|
||||
<view class="row between">
|
||||
<view>
|
||||
<view>{{item.title}}</view>
|
||||
<view class="muted">{{item.category}} / {{item.tradeMethod}}</view>
|
||||
</view>
|
||||
<view class="price">¥{{item.priceCent / 100}}</view>
|
||||
<view class="page-heading row between">
|
||||
<view>
|
||||
<view class="heading-eyebrow">邻里流转</view>
|
||||
<view class="heading-title">二手闲置</view>
|
||||
<view class="heading-subtitle">发布闲置,线下联系交易</view>
|
||||
</view>
|
||||
<view class="muted">{{item.description}}</view>
|
||||
<button size="mini" class="ghost" data-phone="{{item.contactPhone}}" bindtap="callSeller">联系卖家</button>
|
||||
<button size="mini" class="primary publish-button" bindtap="goCreate">发布</button>
|
||||
</view>
|
||||
<view wx:if="{{!goods.length && !loading}}" class="card muted">暂无闲置</view>
|
||||
|
||||
<view class="search-row">
|
||||
<input class="input search-input" placeholder="搜索闲置" value="{{keyword}}" bindinput="onKeywordInput" confirm-type="search" bindconfirm="search" />
|
||||
<button size="mini" class="primary search-button" bindtap="search">搜索</button>
|
||||
</view>
|
||||
|
||||
<view class="second-grid">
|
||||
<view wx:for="{{goods}}" wx:key="id" class="second-card">
|
||||
<image wx:if="{{item.imageUrls.length}}" class="second-cover" src="{{item.imageUrls[0]}}" mode="aspectFill" />
|
||||
<view wx:else class="second-cover media-placeholder">闲</view>
|
||||
<view class="second-body">
|
||||
<view class="card-title">{{item.title}}</view>
|
||||
<view class="row between">
|
||||
<view class="pill">{{item.category}}</view>
|
||||
<view class="price">¥{{item.priceCent / 100}}</view>
|
||||
</view>
|
||||
<view class="muted second-desc">{{item.description}}</view>
|
||||
<view class="row between">
|
||||
<view class="muted">{{item.tradeMethod}}</view>
|
||||
<button size="mini" class="ghost contact-button" data-phone="{{item.contactPhone}}" bindtap="callSeller">联系</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{!goods.length && !loading}}" class="empty-state">暂无闲置</view>
|
||||
</view>
|
||||
|
||||
@@ -1,3 +1,68 @@
|
||||
.ghost {
|
||||
margin-top: 16rpx;
|
||||
.page-heading {
|
||||
align-items: flex-start;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
gap: 14rpx;
|
||||
align-items: center;
|
||||
margin-bottom: 22rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
width: 132rpx;
|
||||
margin: 0;
|
||||
min-height: 80rpx;
|
||||
}
|
||||
|
||||
.publish-button {
|
||||
width: 124rpx;
|
||||
margin: 8rpx 0 0;
|
||||
}
|
||||
|
||||
.second-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.second-card {
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid #e4eaf0;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(20, 36, 58, 0.05);
|
||||
}
|
||||
|
||||
.second-cover {
|
||||
width: 100%;
|
||||
height: 210rpx;
|
||||
}
|
||||
|
||||
.second-body {
|
||||
padding: 18rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.second-desc {
|
||||
min-height: 72rpx;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.contact-button {
|
||||
min-width: 92rpx;
|
||||
margin: 0;
|
||||
padding: 0 12rpx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user