diff --git a/backend/application/api/controller/Demand.php b/backend/application/api/controller/Demand.php index ef1ef1a..7f516fa 100644 --- a/backend/application/api/controller/Demand.php +++ b/backend/application/api/controller/Demand.php @@ -117,69 +117,71 @@ class Demand extends Api } $btnNameFun = function ($opType, $item) { $button = ['name' => '查看详情', 'theme' => 'light']; + // key 为按钮动作标识(复用 DemandService::BUTTON_* 常量), + // 前端据此判断按钮类型,避免依赖中文文案;无动作的展示型按钮 key 为空 if ($opType == 'all') { if ($item->status == 1 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 1) { - $button = ['name' => '确认接单', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_CONFIRM, 'name' => '确认接单', 'theme' => 'danger']; } elseif ($item->status == 2 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) { if ($item->demand_contracts && $item->demand_contracts->status == ContractService::STATUS_FINISHED && $item->demand_contracts->status2 == ContractService::STATUS_ONGOING) { // 开发者已签约,等待客户签约 - $button = ['name' => '签约中', 'theme' => 'light']; + $button = ['key' => '', 'name' => '签约中', 'theme' => 'light']; } else { - $button = ['name' => '合同签约', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_CONTRACT_DEV, 'name' => '合同签约', 'theme' => 'danger']; } } elseif ($item->status == 3 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) { - $button = ['name' => '工作完成', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_COMPLETE_WORK, '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']; + $button = ['key' => '', 'name' => '已发货', 'theme' => 'light']; } else { - $button = ['name' => '去发货', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_DELIVER_GOODS, 'name' => '去发货', 'theme' => 'danger']; } } elseif ($item->is_bid) { - $button = ['name' => '已投标', 'theme' => 'light']; + $button = ['key' => '', 'name' => '已投标', 'theme' => 'light']; } elseif ($item->status == 1) { - $button = ['name' => '参与投标', 'theme' => 'danger']; + $button = ['key' => 'bid', 'name' => '参与投标', 'theme' => 'danger']; } } elseif ($opType == 'myQuote') { if ($item->status == 1 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 1) { - $button = ['name' => '确认接单', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_CONFIRM, 'name' => '确认接单', 'theme' => 'danger']; } elseif ($item->status == 2 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) { if ($item->demand_contracts && $item->demand_contracts->status == ContractService::STATUS_FINISHED && $item->demand_contracts->status2 == ContractService::STATUS_ONGOING) { // 开发者已签约,等待客户签约 - $button = ['name' => '签约中', 'theme' => 'light']; + $button = ['key' => '', 'name' => '签约中', 'theme' => 'light']; } else { - $button = ['name' => '合同签约', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_CONTRACT_DEV, 'name' => '合同签约', 'theme' => 'danger']; } } elseif ($item->status == 3 && $item->isMyWin && $item->winBidding->bidding_status == 2 && $item->winBidding->process_status == 2) { - $button = ['name' => '工作完成', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_COMPLETE_WORK, 'name' => '工作完成', 'theme' => 'danger']; } elseif ($item->status == 4 && $item->accept_status == 1) { - $button = ['name' => '待验收', 'theme' => 'light']; + $button = ['key' => '', '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']; + $button = ['key' => '', 'name' => '已发货', 'theme' => 'light']; } else { - $button = ['name' => '去发货', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_DELIVER_GOODS, 'name' => '去发货', 'theme' => 'danger']; } } elseif ($item->status == 5 && $item->accept_status == 2) { - $button = ['name' => '待收货', 'theme' => 'light']; + $button = ['key' => '', 'name' => '待收货', 'theme' => 'light']; } } elseif ($opType == 'my') { if ($item->status == 2 && $item->demand_contracts && $item->demand_contracts->status2 == 1) { - $button = ['name' => '合同签约', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_CONTRACT_CUSTOMER, 'name' => '合同签约', 'theme' => 'danger']; } elseif ($item->status == 2) { - $button = ['name' => '详聊沟通', 'theme' => 'danger']; + $button = ['key' => '', 'name' => '详聊沟通', 'theme' => 'danger']; } elseif ($item->status == 3 && $item->work_status == 1) { - $button = ['name' => '工作中', 'theme' => 'light']; + $button = ['key' => '', 'name' => '工作中', 'theme' => 'light']; } elseif ($item->status == 4 && $item->accept_status == 1) { - $button = ['name' => '前去验收', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_ACCEPT_PASSED, 'name' => '前去验收', 'theme' => 'danger']; } elseif ($item->status == 5 && $item->accept_status == 2) { //开发者发货后客户才能确认收货 if ($item->hasDelivery) { - $button = ['name' => '确认收货', 'theme' => 'danger']; + $button = ['key' => DemandService::BUTTON_CONFIRM_RECEIPT, 'name' => '确认收货', 'theme' => 'danger']; } else { - $button = ['name' => '待发货', 'theme' => 'light']; + $button = ['key' => '', 'name' => '待发货', 'theme' => 'light']; } } } diff --git a/frontend/miniapp/pages/demand/detail/index.js b/frontend/miniapp/pages/demand/detail/index.js index c344926..24d56da 100644 --- a/frontend/miniapp/pages/demand/detail/index.js +++ b/frontend/miniapp/pages/demand/detail/index.js @@ -435,7 +435,9 @@ Page({ wx.showModal({ title: '提示', showCancel: false, - content: agree ? '工作已完成,等待客户验收' : '很遗憾,工作完成失败', + content: agree + ? '工作已完成,请录制产品功能演示视频或提供在线体验地址,发送到需求沟通群,让客户观看后进行验收。' + : '很遗憾,工作完成失败', success: (res) => { if (res.confirm) { wx.$util.backAndRefresh(); @@ -672,6 +674,23 @@ Page({ }); this.loadDemandDetail(id); wx.$util.checkSubscriptionsSetting(); + //从列表「前去验收」进入时,提示先到沟通群查看/体验交付成果再验收 + if (options.fromAccept == '1') { + this.showAcceptTip(); + } + }, + + // 验收提示:引导客户先到需求沟通群查看或体验交付成果 + showAcceptTip() { + wx.showModal({ + title: '验收提示', + showCancel: false, + confirmText: '我知道了', + content: '请先到需求沟通群查看或体验交付成果演示,确认没有问题后再点击验收,进行验收。', + success: () => { + console.log('用户点击我知道了'); + } + }); }, /** diff --git a/frontend/miniapp/pages/demand/list/index.js b/frontend/miniapp/pages/demand/list/index.js index edcc20d..f90a59d 100644 --- a/frontend/miniapp/pages/demand/list/index.js +++ b/frontend/miniapp/pages/demand/list/index.js @@ -193,12 +193,16 @@ Page({ demandListClickHandle(e) { const { index } = e.currentTarget.dataset; - const { id } = this.data.demandList[index]; + const item = this.data.demandList[index]; + const { id } = item; let type = this.data.opType; const opType = type == 'all' || type == 'myQuote' ? 'quote' : 'view'; + //按后端返回的按钮动作标识判断(acceptPassed=验收), + //命中时带标记,详情页据此弹出「先到沟通群体验后再验收」的提示 + const isAccept = !!item.button && item.button.key === 'acceptPassed'; console.log(index, id); wx.navigateTo({ - url: `/pages/demand/detail/index?id=${id}&opType=${opType}`, + url: `/pages/demand/detail/index?id=${id}&opType=${opType}${isAccept ? '&fromAccept=1' : ''}`, }); }, diff --git a/frontend/miniapp/pages/demand/publish/index.js b/frontend/miniapp/pages/demand/publish/index.js index ad313ee..808e303 100644 --- a/frontend/miniapp/pages/demand/publish/index.js +++ b/frontend/miniapp/pages/demand/publish/index.js @@ -1,5 +1,9 @@ // pages/demand/publish/index.js import { fetchConfig } from '../../../services/home/home'; + +// 联系方式:仅允许英文、数字、下划线、中划线(不含中文与特殊字符) +const CONTACT_REG = /^[A-Za-z0-9_-]+$/; + Page({ /** @@ -10,6 +14,7 @@ Page({ content: '', budgetAmount: 0, limitTime: 0, + contactError: '', //联系方式错误提示 tabList: [], developType: -1, originFiles: [], @@ -66,14 +71,31 @@ Page({ }); }, onInputChange(e) { - console.log(e) const value = e.detail.value; const { field } = e.currentTarget.dataset; // 获取 data-field 的值 - console.log(value) - console.log(field) this.setData({ [field]: value // 使用动态属性名设置值 }); + //输入过程中若已修正为合法联系方式,立即清除错误提示 + if (field === 'contact' && this.data.contactError && CONTACT_REG.test(value)) { + this.setData({ contactError: '' }); + } + }, + + // 联系方式失焦校验 + onContactBlur(e) { + this.setData({ + contactError: this.getContactError(e.detail.value) + }); + }, + + // 联系方式错误提示文案,合法或为空时返回空串(为空由必填校验负责提示) + getContactError(value) { + const val = String(value === undefined || value === null ? '' : value).trim(); + if (!val || CONTACT_REG.test(val)) { + return ''; + } + return '联系方式只能包含英文、数字、下划线和中划线'; }, onDevelopTypeTap(e) { const { key } = e.currentTarget.dataset; @@ -99,6 +121,12 @@ Page({ onSubmit() { const { developType, attachFiles, originFiles, content, budgetAmount, limitTime, contact } = this.data; + //提交时同步联系方式错误态,让错误提示显示在输入框上 + const contactError = this.getContactError(contact); + if (contactError) { + this.setData({ contactError }); + } + const validations = [ { condition: developType == -1, @@ -115,6 +143,10 @@ Page({ { condition: !contact.trim(), message: '请填写联系方式' + }, + { + condition: !CONTACT_REG.test(contact.trim()), + message: '联系方式只能包含英文、数字、下划线和中划线' } ]; diff --git a/frontend/miniapp/pages/demand/publish/index.wxml b/frontend/miniapp/pages/demand/publish/index.wxml index d0f7e58..29d601f 100644 --- a/frontend/miniapp/pages/demand/publish/index.wxml +++ b/frontend/miniapp/pages/demand/publish/index.wxml @@ -57,10 +57,13 @@ />