添加开发者发货功能

This commit is contained in:
Dev
2026-09-30 14:56:56 +08:00
parent f13a5e682a
commit bc17810445
14 changed files with 768 additions and 4 deletions
+27 -1
View File
@@ -7,6 +7,7 @@ use app\common\service\CommonService;
use app\common\service\ContractService;
use app\common\service\DemandBiddingService;
use app\common\service\DemandService;
use app\common\service\OrderShipService;
use think\exception\HttpResponseException;
use think\Lang;
use think\Validate;
@@ -128,6 +129,13 @@ class Demand extends Api
}
} elseif ($item->status == 3 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) {
$button = ['name' => '工作完成', 'theme' => 'danger'];
} elseif ($item->status == 5 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) {
//客户支付尾款后,开发者发货交付
if ($item->hasDelivery) {
$button = ['name' => '已发货', 'theme' => 'light'];
} else {
$button = ['name' => '去发货', 'theme' => 'danger'];
}
} elseif ($item->is_bid) {
$button = ['name' => '已投标', 'theme' => 'light'];
} elseif ($item->status == 1) {
@@ -147,6 +155,13 @@ class Demand extends Api
$button = ['name' => '工作完成', 'theme' => 'danger'];
} elseif ($item->status == 4 && $item->accept_status == 1) {
$button = ['name' => '待验收', 'theme' => 'light'];
} elseif ($item->status == 5 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) {
//客户支付尾款后,开发者发货交付
if ($item->hasDelivery) {
$button = ['name' => '已发货', 'theme' => 'light'];
} else {
$button = ['name' => '去发货', 'theme' => 'danger'];
}
} elseif ($item->status == 5 && $item->accept_status == 2) {
$button = ['name' => '待收货', 'theme' => 'light'];
}
@@ -166,12 +181,15 @@ class Demand extends Api
return $button;
};
$list = DemandService::getList($where, $page_size, 'id desc', '', ['user', 'demandContracts', 'demandBiddings', 'winBidding']);
$list = $list->each(function ($item, $key) use ($user_id, $opType, $btnNameFun) {
//已发货的需求集合(发货表),用于开发者视角的发货按钮展示
$deliveredDemandIds = OrderShipService::where(['demand_id' => ['in', $list->column('id')]])->column('demand_id');
$list = $list->each(function ($item, $key) use ($user_id, $opType, $btnNameFun, $deliveredDemandIds) {
$demand_bidding = $item->demand_biddings;
$bidUserIds = array_column($demand_bidding, 'user_id');
$item->demand_biddings_count = count($demand_bidding);
$item->is_bid = in_array($user_id, $bidUserIds);
$item->isMyWin = $item->winBidding && $item->winBidding->user_id == $user_id;
$item->hasDelivery = in_array($item->id, $deliveredDemandIds);
$item->button = $btnNameFun($opType, $item);
});
$this->success('', $list);
@@ -229,6 +247,14 @@ class Demand extends Api
'url' => $fl,
'name' => urldecode(basename($fl))
], $info->files);
//交付内容(订单发货表,开发者发货后展示)
$ship = OrderShipService::where(['demand_id' => $id])->order('id', 'desc')->find();
$info->delivery = $ship ? [
'images' => json_decode($ship->images, true) ?: [],
'files' => json_decode($ship->files, true) ?: [],
'remark' => $ship->remark,
'delivery_time' => $ship->createtime,
] : null;
$this->success('', $info);
} catch (\Exception $e) {
@@ -6,6 +6,7 @@ use app\common\service\AccountLogService;
use app\common\service\AccountService;
use app\common\service\EasyWeChatService;
use app\common\service\OrderService;
use app\common\service\OrderShipService;
use app\common\service\SubscribeMessageService;
use think\console\Command;
use think\console\Input;
@@ -45,8 +46,13 @@ class DemandDeal extends Command
foreach ($demandList as $item) {
try {
Db::transaction(function () use ($item, $confirmReceiptTimeoutDay) {
//如果七天未确认收货,则自动确认收货
if (time() > strtotime("+$confirmReceiptTimeoutDay day", $item->accept_time)) {
//未发货的需求不自动确认收货,等待开发者发货交付
$ship = OrderShipService::where(['demand_id' => $item->id])->order('id', 'desc')->find();
if (!$ship) {
return;
}
//发货后N天未确认收货,则自动确认收货
if (time() > strtotime("+$confirmReceiptTimeoutDay day", intval($ship->createtime))) {
$item->status = DemandService::STATUS_FINISHED;
$item->settle_status = DemandService::$Common::ONGOING;
$saveRes = $item->save();
@@ -70,6 +70,7 @@ class DemandService
const BUTTON_COMPLETE_WORK = 'completeWork'; //完成工作
const BUTTON_ACCEPT_PASSED = 'acceptPassed'; //验收通过
const BUTTON_CONFIRM_RECEIPT = 'confirmReceipt'; //确认收货
const BUTTON_DELIVER_GOODS = 'deliverGoods'; //开发者发货
/**
* 创建需求
@@ -130,6 +131,9 @@ class DemandService
case self::BUTTON_CONFIRM_RECEIPT:
$res = self::confirmReceipt($requestData, $userId);
break;
case self::BUTTON_DELIVER_GOODS:
$res = self::deliverGoods($requestData, $userId);
break;
default:
throw new \Exception('非法操作');
}
@@ -434,6 +438,70 @@ class DemandService
}
}
/**
* 开发者发货(交付开发成果)
* 凭证截图、交付附件与备注写入订单发货表,尾款订单转为已发货(待收货)
* @param $requestData
* @param $userId
* @return bool
* @throws Exception
*/
public static function deliverGoods($requestData, $userId)
{
try {
$demandId = $requestData['demandId'] ?? 0;
$images = array_values($requestData['images'] ?? []);
$files = array_values($requestData['files'] ?? []);
$remark = trim($requestData['remark'] ?? '');
Db::startTrans();
if (!$images || !$files) {
throw new \Exception('请上传开发完成的凭证截图和交付附件');
}
$demand = self::getOne(['id' => $demandId], ['winBidding']);
if (!$demand) {
throw new \Exception('该需求不存在');
}
if ($demand->status != self::STATUS_CONFIRM_RECEIPT) {
throw new \Exception('该需求为' . self::STATUS_MAP[$demand->status] . ',不能发货');
}
if (!$demand->winBidding || $demand->winBidding->user_id != $userId) {
throw new \Exception('只有中标开发者才能发货');
}
//尾款订单必须已支付(待发货)才能发货
$finalOrder = OrderService::getDemandOrder($demand->parent_order_no, OrderService::ORDER_TYPE_FINAL_PAYMENT);
if (!$finalOrder) {
throw new \Exception('获取尾款订单失败');
}
if ($finalOrder->order_status != OrderService::ORDER_STATUS_WAIT_SHIP) {
throw new \Exception('该订单为' . OrderService::ORDER_STATUS_MAP[$finalOrder->order_status] . ',不能发货');
}
//交付内容写入订单发货表
$spuId = $finalOrder->orderGoods[0]->spu_id ?? 0;
OrderShipService::createDemandDelivery($finalOrder->order_no, $spuId, $demandId, $images, $files, $remark);
//尾款订单改为已发货(待收货)
$upRes = OrderService::where(['id' => $finalOrder->id])->update(['order_status' => OrderService::ORDER_STATUS_SHIPPED]);
if (!$upRes) {
exception('更新发货状态失败');
}
Db::commit();
//给客户发送发货通知
$msgData = [
'values' => [
$finalOrder->order_no,
mb_substr($demand->title, 0, 20),
'已发货',
'开发成果已交付,请到需求详情查看交付内容并确认收货',
]
];
(new EasyWeChatService)->sendMsg($demand->user_id, SubscribeMessageService::MSG_TEMP_ORDER_SHIPPED, $msgData);
return true;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
/**
* 确认收货
* @param $requestData
@@ -461,6 +529,11 @@ class DemandService
if (!$saveRes) {
exception('更新需求状态失败');
}
//关联订单状态改为已完成
$upRes = OrderService::where(['parent_order_no' => $demand->parent_order_no])->update(['order_status' => OrderService::ORDER_STATUS_FINISHED]);
if (!$upRes) {
exception('更新订单状态失败');
}
Db::commit();
return $demand;
} catch (\Exception $e) {
@@ -398,6 +398,23 @@ class OrderService
if (!$upRes) {
throw new \Exception('更新订单状态失败');
}
//需求订单(首付款/尾款):同步需求状态与关联订单
if (in_array($order->order_type, [self::ORDER_TYPE_FIRST_PAYMENT, self::ORDER_TYPE_FINAL_PAYMENT])) {
$demand = DemandService::getOne(['parent_order_no' => $order->parent_order_no]);
if ($demand && $demand->status == DemandService::STATUS_CONFIRM_RECEIPT) {
$demand->status = DemandService::STATUS_FINISHED;
$demand->settle_status = CommonService::ONGOING; //结算中
$saveRes = $demand->save();
if (!$saveRes) {
throw new \Exception('更新需求状态失败');
}
//同一需求下的关联订单全部改为已完成
$upRes = self::where(['parent_order_no' => $order->parent_order_no])->update(['order_status' => self::ORDER_STATUS_FINISHED]);
if (!$upRes) {
throw new \Exception('更新关联订单状态失败');
}
}
}
Db::commit();
$info = OrderService::getOne(['id' => $orderId]);
@@ -35,4 +35,38 @@ class OrderShipService
return $res;
}
/**
* 需求交付发货单(开发者发货:凭证截图+交付附件+备注)
* @param $order_no
* @param $order_goods_spu_id
* @param $demand_id
* @param array $images
* @param array $files
* @param string $remark
* @return mixed
* @throws \Exception
*/
public static function createDemandDelivery($order_no, $order_goods_spu_id, $demand_id, array $images, array $files, $remark = '')
{
$imagesStr = json_encode($images, JSON_UNESCAPED_UNICODE);
$filesStr = json_encode($files, JSON_UNESCAPED_UNICODE);
if (mb_strlen($imagesStr) > 1000 || mb_strlen($filesStr) > 1000) {
exception('附件内容过多,请精简后重新发货');
}
$data = [
'order_no' => $order_no,
'order_goods_spu_id' => $order_goods_spu_id,
'demand_id' => $demand_id,
'images' => $imagesStr,
'files' => $filesStr,
'remark' => $remark,
];
$res = self::create($data);
if (!$res) {
exception('创建发货单数据失败');
}
return $res;
}
}
+1
View File
@@ -35,6 +35,7 @@
"pages/demand/list/index",
"pages/demand/detail/index",
"pages/demand/contract/index",
"pages/demand/deliver/index",
"pages/demand/agreement/index",
"pages/usercenter/balance/index"
],
@@ -0,0 +1,299 @@
// pages/demand/deliver/index.js
import { demandOperate } from '../../../services/demand/fetchDemand';
Page({
/**
* 页面的初始数据
*/
data: {
demandId: 0,
remark: '',
// 开发完成凭证截图(软件成品展示图等)
imageFiles: [],
imageMax: 9,
imageSizeLimit: {
size: 20, unit: 'MB', message: '图片大小不超过 {sizeLimit} MB'
},
// 交付附件(源码或软件成品文件)
attachFiles: [],
attachMax: 5,
attachConfig: {
count: 5,
},
attachSizeLimit: {
size: 20, unit: 'MB', message: '附件大小不超过 {sizeLimit} MB'
},
gridConfig: {
column: 4,
width: 160,
height: 160,
},
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const demandId = options?.demandId;
this.setData({ demandId });
},
// 备注变更
onRemarkChange(e) {
this.setData({ remark: e.detail.value });
},
// 凭证截图选择成功
handleSuccessImage(e) {
const files = e.detail?.files || [];
const { imageFiles, imageMax } = this.data;
const remain = imageMax - imageFiles.length;
const accepted = files.slice(0, remain);
if (files.length > remain) {
wx.showToast({
title: `最多上传${imageMax}张截图`,
icon: 'none'
});
}
if (!accepted.length) {
return;
}
this.setData({
imageFiles: [...imageFiles, ...accepted]
});
this.uploadFiles(accepted, 'image');
},
// 移除凭证截图
handleRemoveImage(e) {
const { index } = e.detail;
const { imageFiles } = this.data;
imageFiles.splice(index, 1);
this.setData({ imageFiles });
},
// 交付附件选择成功
handleSuccessAttach(e) {
const files = e.detail?.files || [];
const { attachFiles, attachMax } = this.data;
const remain = attachMax - attachFiles.length;
const accepted = files.slice(0, remain);
if (files.length > remain) {
wx.showToast({
title: `最多上传${attachMax}个附件`,
icon: 'none'
});
}
if (!accepted.length) {
return;
}
const list = [...attachFiles, ...accepted];
this.setData({
attachFiles: list,
attachConfig: { count: attachMax - list.length }
});
this.uploadFiles(accepted, 'attach');
},
// 移除交付附件
handleRemoveAttach(e) {
const { index } = e.currentTarget.dataset;
const { attachFiles, attachMax } = this.data;
attachFiles.splice(index, 1);
this.setData({
attachFiles,
attachConfig: { count: attachMax - attachFiles.length }
});
},
// 上传文件到服务器
uploadFiles(files, type) {
files.forEach((file, index) => {
if (file.status === 'done') {
return;
}
wx.uploadFile({
url: wx.$api.UploadFile,
filePath: file.url,
name: 'file',
formData: {
'name': file.name,
'type': file.type
},
header: {
'Accept': 'application/json',
'token': wx.getStorageSync('token')
},
success: (res) => {
if (res.statusCode == 413) {
wx.showToast({
title: '文件大小超出限制',
icon: 'none'
});
this.removeFileByUrl(file.url, type);
return;
}
let response = {};
try {
response = JSON.parse(res.data);
} catch (err) {
response = {};
}
if (response.code === 1 && response.data && response.data.length > 0) {
this.updateFile(type, {
...file,
fullUrl: response.data[0].url,
status: 'done'
});
if (index === files.length - 1) {
wx.showToast({
title: '上传成功',
icon: 'success'
});
}
} else {
wx.showToast({
title: response.msg || '上传失败',
icon: 'none'
});
this.removeFileByUrl(file.url, type);
}
},
fail: () => {
wx.showToast({
title: '上传失败',
icon: 'none'
});
this.removeFileByUrl(file.url, type);
}
});
});
},
// 上传完成后回写
updateFile(type, newFile) {
if (type === 'image') {
const { imageFiles } = this.data;
const idx = imageFiles.findIndex(item => item.url === newFile.url);
if (idx === -1) {
return;
}
const list = [...imageFiles];
list[idx] = newFile;
this.setData({ imageFiles: list });
} else {
const { attachFiles, attachMax } = this.data;
const idx = attachFiles.findIndex(item => item.url === newFile.url);
if (idx === -1) {
return;
}
const list = [...attachFiles];
list[idx] = newFile;
this.setData({
attachFiles: list,
attachConfig: { count: attachMax - list.length }
});
}
},
// 上传失败时移除
removeFileByUrl(url, type) {
if (type === 'image') {
this.setData({
imageFiles: this.data.imageFiles.filter(item => item.url !== url)
});
} else {
const { attachFiles, attachMax } = this.data;
const list = attachFiles.filter(item => item.url !== url);
this.setData({
attachFiles: list,
attachConfig: { count: attachMax - list.length }
});
}
},
// 表单验证
validateForm() {
const { imageFiles, attachFiles } = this.data;
if (!imageFiles.length) {
wx.showToast({
title: '请上传开发完成的凭证截图',
icon: 'none'
});
return false;
}
if (!attachFiles.length) {
wx.showToast({
title: '请上传源码或软件附件',
icon: 'none'
});
return false;
}
if (imageFiles.some(item => item.status !== 'done') || attachFiles.some(item => item.status !== 'done')) {
wx.showToast({
title: '文件上传中,请稍候',
icon: 'none'
});
return false;
}
return true;
},
// 提交发货
onSubmit() {
if (!this.validateForm()) {
return;
}
wx.showLoading({
title: '提交中...'
});
const { demandId, imageFiles, attachFiles, remark } = this.data;
// 只提交服务端地址与文件名,避免提交本地临时路径
const params = {
demandId,
opType: 'deliverGoods',
images: imageFiles.map(file => ({
url: file.fullUrl || file.url,
name: file.name || ''
})),
files: attachFiles.map(file => ({
url: file.fullUrl || file.url,
name: file.name
})),
remark
};
demandOperate(params).then(
(res) => {
console.log(res);
wx.hideLoading();
if (res.code == 1) {
wx.showModal({
title: '提示',
showCancel: false,
content: '发货成功,等待客户确认收货',
success: (res2) => {
if (res2.confirm) {
//返回上一页(需求详情)并刷新
wx.$util.backAndRefresh();
console.log('用户点击确定');
}
}
});
} else {
wx.showToast({
title: res.msg || '发货失败',
icon: 'none'
});
}
},
() => {
//接口异常处理
wx.hideLoading();
wx.showToast({
title: '操作失败',
icon: 'none'
});
},
);
}
})
@@ -0,0 +1,8 @@
{
"navigationBarTitleText": "发货",
"usingComponents": {
"t-upload": "tdesign-miniprogram/upload/upload",
"t-tag": "tdesign-miniprogram/tag/tag",
"t-textarea": "tdesign-miniprogram/textarea/textarea"
}
}
@@ -0,0 +1,81 @@
<!--pages/demand/deliver/index.wxml-->
<view class="page-content">
<!-- 开发完成凭证截图 -->
<view class="form-card">
<view class="form-card__head">
<text class="form-card__title">开发完成凭证截图</text>
<text class="form-card__require">*</text>
<text class="form-card__extra">最多 {{imageMax}} 张</text>
</view>
<view class="form-card__desc">上传软件成品展示图等开发完成凭证,供客户核对</view>
<t-upload
media-type="{{['image']}}"
files="{{imageFiles}}"
gridConfig="{{gridConfig}}"
max="{{imageMax}}"
size-limit="{{imageSizeLimit}}"
bind:success="handleSuccessImage"
bind:remove="handleRemoveImage"
/>
</view>
<!-- 交付附件 -->
<view class="form-card">
<view class="form-card__head">
<text class="form-card__title">交付附件(源码或软件成品)</text>
<text class="form-card__require">*</text>
<text class="form-card__extra">{{attachFiles.length}}/{{attachMax}}</text>
</view>
<view class="form-card__desc">上传源码或软件成品文件,客户确认收货后可下载,从聊天记录选择文件</view>
<view class="attach-list" wx:if="{{attachFiles.length}}">
<t-tag
wx:for="{{attachFiles}}"
wx:for-index="index"
wx:key="index"
class="file-tag-margin"
max-width="{{220}}"
size="medium"
variant="light"
data-index="{{index}}"
bind:close="handleRemoveAttach"
closable
>{{item.name}}</t-tag>
</view>
<t-upload
wx:if="{{attachFiles.length < attachMax}}"
files="{{[]}}"
gridConfig="{{gridConfig}}"
config="{{attachConfig}}"
size-limit="{{attachSizeLimit}}"
source="messageFile"
bind:success="handleSuccessAttach"
/>
</view>
<!-- 发货备注 -->
<view class="form-card">
<view class="form-card__head">
<text class="form-card__title">发货备注</text>
<text class="form-card__extra">选填</text>
</view>
<t-textarea
autosize="{{ { maxHeight: 120, minHeight: 50 } }}"
disableDefaultPadding="{{true}}"
placeholder="填写交付说明,如软件安装方式、源码目录结构、注意事项等"
value="{{remark}}"
bindchange="onRemarkChange"
maxlength="255"
/>
</view>
</view>
<!-- 底部提交栏 -->
<view class="wx-pay-cover">
<view class="wx-pay">
<view class="submit-btn {{(imageFiles.length && attachFiles.length) ? '' : 'btn-gray'}}" bindtap="onSubmit">
确认发货
</view>
</view>
</view>
@@ -0,0 +1,103 @@
/* pages/demand/deliver/index.wxss */
.page-content {
padding: 24rpx 24rpx calc(112rpx + env(safe-area-inset-bottom) + 24rpx);
box-sizing: border-box;
}
/* ================= 分组卡片 ================= */
.form-card {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: var(--shadow-card);
}
.form-card__head {
display: flex;
align-items: baseline;
margin-bottom: 16rpx;
}
.form-card__title {
font-size: 30rpx;
font-weight: 600;
line-height: 44rpx;
color: var(--text-primary);
}
.form-card__require {
margin-left: 4rpx;
font-size: 28rpx;
line-height: 44rpx;
color: var(--error);
}
.form-card__extra {
margin-left: auto;
font-size: 22rpx;
line-height: 40rpx;
color: var(--text-tertiary);
}
.form-card__desc {
margin-bottom: 24rpx;
font-size: 24rpx;
line-height: 36rpx;
color: var(--text-tertiary);
}
/* ================= 附件标签 ================= */
.attach-list {
display: flex;
flex-wrap: wrap;
}
.file-tag-margin {
margin: 8rpx 16rpx 8rpx 0;
}
/* ================= 底部提交栏 ================= */
.wx-pay-cover {
position: fixed;
left: 0;
bottom: 0;
right: 0;
z-index: 10;
background: var(--bg-card);
height: 112rpx;
padding-bottom: env(safe-area-inset-bottom);
box-shadow: 0 -4rpx 20rpx rgba(29, 33, 41, 0.05);
}
.wx-pay-cover .wx-pay {
width: 100%;
height: 100rpx;
box-sizing: border-box;
padding: 0rpx 32rpx;
display: flex;
justify-content: center;
align-items: center;
}
.wx-pay-cover .wx-pay .submit-btn {
height: 80rpx;
width: 50%;
border-radius: 40rpx;
background: var(--brand-gradient);
box-shadow: var(--shadow-brand);
color: #ffffff;
line-height: 80rpx;
font-weight: 500;
font-size: 28rpx;
text-align: center;
}
.wx-pay-cover .wx-pay .submit-btn:active {
opacity: 0.88;
}
.wx-pay-cover .wx-pay .btn-gray {
background: var(--text-disabled);
box-shadow: none;
}
+19 -1
View File
@@ -11,7 +11,7 @@ Page({
needDays: '',
remark: '',
actualAccount: 0,
activeValues: [0, 1, 2, 3],
activeValues: [0, 1, 2, 3, 4],
demandInfo: null
},
@@ -103,6 +103,24 @@ Page({
urls: images
});
},
//交付凭证截图预览
onDeliveryImagePreview(e) {
const { images, index } = e.currentTarget.dataset;
const urls = (images || []).map(item => item.url);
if (!urls.length) {
return;
}
wx.previewImage({
current: urls[index],
urls
});
},
//去发货(交付开发成果)
deliverGoods() {
wx.navigateTo({
url: `/pages/demand/deliver/index?demandId=${this.data.id}`,
});
},
//参与竞标
submitBidding() {
const { id, quotedPrice, needDays, remark } = this.data;
@@ -53,6 +53,33 @@
<t-input label="{{opType=='quote' ? '我的优势' : 'Ta的优势'}}" placeholder="{{opType=='quote' ? '请填写您的优势技能' : '优势技能'}}" disabled="{{opType=='quote' && !demandInfo.myBidding ? false : true}}" value="{{opType=='quote' ? demandInfo.myBidding.remark : demandInfo.winBidding.remark}}" data-field="remark" bindchange="onInputChange" />
</view>
</t-collapse-panel>
<!-- 交付内容:开发者发货后展示,客户确认收货前可查看 -->
<block wx:if="{{_calc.getDelivery(demandInfo)}}">
<t-collapse-panel header="交付内容" value="{{4}}" expandIcon>
<view class="delivery-section">
<view class="delivery-title">开发完成凭证截图</view>
<view class="delivery-images">
<image wx:for="{{_calc.getDelivery(demandInfo).images}}" wx:key="index" class="delivery-image" src="{{item.url}}" mode="aspectFill" data-index="{{index}}" data-images="{{_calc.getDelivery(demandInfo).images}}" bindtap="onDeliveryImagePreview" />
</view>
<view class="delivery-title">交付附件</view>
<view class="file-list">
<view class="file-item" wx:for="{{_calc.getDelivery(demandInfo).files}}" wx:key="index">
<view class="file-item__name">
<t-tag max-width="420rpx" variant="light">{{item.name}}</t-tag>
</view>
<view class="file-item__ops">
<button class="mini-btn" type="default" size="mini" data-url="{{item.url}}" bindtap="fileUrlCopy">复制</button>
<button class="mini-btn mini-btn--primary" size="mini" data-url="{{item.url}}" bindtap="fileView">预览</button>
</view>
</view>
</view>
<block wx:if="{{_calc.getDelivery(demandInfo).remark}}">
<view class="delivery-title">发货备注</view>
<view class="delivery-remark">{{_calc.getDelivery(demandInfo).remark}}</view>
</block>
</view>
</t-collapse-panel>
</block>
</t-collapse>
@@ -73,6 +100,10 @@
<t-button theme="light" size="large" data-status="0" bind:tap="completeWork">完成失败</t-button>
<t-button theme="danger" size="large" data-status="1" bind:tap="completeWork" block>工作完成</t-button>
</view>
<view class="button-layout-flex" wx:elif="{{_calc.canDeliverGoods(demandInfo)}}">
<t-button theme="danger" size="large" bind:tap="deliverGoods" block>去发货</t-button>
</view>
<t-button wx:elif="{{_calc.isDelivered(demandInfo)}}" theme="light" size="large" disabled block>已发货(等待客户确认收货)</t-button>
</block>
</view>
<!-- 需求方 -->
@@ -64,6 +64,36 @@ var canConfirmReceipt = function (demandInfo) {
demandInfo.accept_status == 2;
}
// 获取交付内容(订单发货表,后端 detail 接口挂载的 delivery 字段,未发货返回 null)
var getDelivery = function (demandInfo) {
if (!demandInfo) {
return null;
}
return demandInfo.delivery || null;
}
// 开发者待发货:需求待收货(客户已付尾款)、自己中标且尚未交付
var canDeliverGoods = function (demandInfo) {
if (!demandInfo) return false;
return demandInfo.status == 5 &&
demandInfo.isMyWin &&
demandInfo.winBidding &&
demandInfo.winBidding.bidding_status == 2 &&
demandInfo.winBidding.process_status == 2 &&
!getDelivery(demandInfo);
}
// 开发者已发货、等待客户确认收货
var isDelivered = function (demandInfo) {
if (!demandInfo) return false;
return demandInfo.status == 5 &&
demandInfo.isMyWin &&
demandInfo.winBidding &&
demandInfo.winBidding.bidding_status == 2 &&
demandInfo.winBidding.process_status == 2 &&
!!getDelivery(demandInfo);
}
// 数值拼接单位,空值时不显示单位
var withUnit = function (value, unit) {
if (value === undefined || value === null || value === '') {
@@ -81,5 +111,8 @@ module.exports = {
canCompleteWork: canCompleteWork,
canAcceptPassed: canAcceptPassed,
canConfirmReceipt: canConfirmReceipt,
getDelivery: getDelivery,
canDeliverGoods: canDeliverGoods,
isDelivered: isDelivered,
withUnit: withUnit,
};
@@ -68,6 +68,40 @@ page {
color: var(--text-tertiary);
}
/* 交付内容 */
.delivery-section {
padding-bottom: 8rpx;
}
.delivery-title {
margin: 16rpx 0 12rpx;
font-size: 26rpx;
font-weight: 500;
color: var(--text-primary);
}
.delivery-images {
display: flex;
flex-wrap: wrap;
margin-right: -16rpx;
}
.delivery-image {
width: 160rpx;
height: 160rpx;
margin: 0 16rpx 16rpx 0;
border-radius: var(--radius-md, 12rpx);
background: var(--bg-grey);
}
.delivery-remark {
font-size: 26rpx;
line-height: 40rpx;
color: var(--text-tertiary);
white-space: pre-wrap;
word-break: break-all;
}
.button-submit {
margin: 32rpx;
}