feat: 重构首页与全局UI,新增服务选择抽屉与统一导航组件

新增统一导航栏组件YjyfNavBar,替换各页面原生导航
新增服务选择抽屉组件GoodsPickerDrawer,实现分类选品流程
新增tabbar图标矢量资源,优化底部导航样式
新增后端运力统计接口与多商品下单逻辑
修复页面跳转逻辑,统一使用switchTab/redirectTo
优化订单创建与评价页的UI组件化,提升一致性
这个提交包含在:
YuanWen
2026-11-02 18:27:37 +08:00
父节点 1323b8877f
当前提交 9ae2b976a8
共有 55 个文件被更改,包括 3184 次插入 和 599 次删除

查看文件

@@ -34,15 +34,31 @@ class Order extends Base
$remark = trim((string)$this->request->post('remark'));
$goodsImages = $this->request->post('goods_images', '');
$service = Db::name('service')->where('id', $serviceId)->where('status', 'normal')->find();
if (!$service) {
// 支持多商品组合 (goods_list)
$goodsListRaw = $this->request->post('goods_list');
$goodsList = [];
if (!empty($goodsListRaw)) {
$goodsList = is_array($goodsListRaw) ? $goodsListRaw : json_decode((string)$goodsListRaw, true);
}
$service = null;
if (!empty($goodsList) && is_array($goodsList)) {
$first = reset($goodsList);
$serviceId = isset($first['service_id']) ? (int)$first['service_id'] : $serviceId;
}
if ($serviceId > 0) {
$service = Db::name('service')->where('id', $serviceId)->where('status', 'normal')->find();
}
if (!$service && empty($goodsList)) {
$this->error('服务不存在或已下架');
}
// 下单页可选择:一口价 / 师傅报价 / 用户自主定价(未传则沿用服务默认)
$priceType = trim((string)$this->request->post('price_type', $service['price_type']));
$defaultPriceType = $service ? $service['price_type'] : 'fixed';
$priceType = trim((string)$this->request->post('price_type', $defaultPriceType));
if (!in_array($priceType, ['fixed', 'quote', 'custom', 'range'], true)) {
$priceType = $service['price_type'];
$priceType = $defaultPriceType;
}
$userPrice = round((float)$this->request->post('user_price', 0), 2);
if ($priceType === 'custom') {
@@ -53,35 +69,105 @@ class Order extends Base
$userPrice = 0;
}
$address = Db::name('address')->where('id', $addressId)->where('user_id', $this->uid())->find();
// 支持直接传 address_id 或传入新地址对象并自动保存
$address = null;
if ($addressId > 0) {
$address = Db::name('address')->where('id', $addressId)->where('user_id', $this->uid())->find();
}
if (!$address) {
$this->error('请选择服务地址');
$contact = trim((string)$this->request->post('contact'));
$mobile = trim((string)$this->request->post('mobile'));
$province = trim((string)$this->request->post('province', '贵州省'));
$city = trim((string)$this->request->post('city', '贵阳市'));
$district = trim((string)$this->request->post('district', ''));
$detailAddr = trim((string)$this->request->post('address'));
$floor = max(1, (int)$this->request->post('floor', 1));
$hasElevator = (int)$this->request->post('has_elevator', 1);
if ($contact && $mobile && $detailAddr) {
$addressId = Db::name('address')->insertGetId([
'user_id' => $this->uid(),
'contact' => $contact,
'mobile' => $mobile,
'province' => $province,
'city' => $city,
'district' => $district,
'address' => $detailAddr,
'floor' => $floor,
'has_elevator' => $hasElevator,
'createtime' => time(),
'updatetime' => time(),
]);
$address = Db::name('address')->where('id', $addressId)->find();
}
}
if (!$address) {
$this->error('请选择或填写完整服务地址');
}
if ($appointTime > 0 && $appointTime < time()) {
$this->error('预约时间不能早于当前时间');
}
// 多商品组合名称、总件数与图片汇总
$serviceName = $service ? $service['name'] : '同城综合上门服务';
$catId = $service ? (int)$service['cat_id'] : 1;
$serviceImage = $service ? $service['image'] : '';
if (!empty($goodsList) && is_array($goodsList)) {
$names = [];
$totalCount = 0;
$allImages = [];
foreach ($goodsList as $idx => $g) {
$gName = isset($g['item_name']) ? $g['item_name'] : (isset($g['name']) ? $g['name'] : '服务项');
$gNum = isset($g['num']) ? (int)$g['num'] : 1;
$names[] = $gName . 'x' . $gNum;
$totalCount += $gNum;
if (!empty($g['images']) && is_array($g['images'])) {
$allImages = array_merge($allImages, $g['images']);
}
}
if (!empty($names)) {
$serviceName = implode(' + ', array_slice($names, 0, 3)) . (count($names) > 3 ? ' 等' : '');
}
if ($totalCount > 0) {
$goodsNum = $totalCount;
}
if (!empty($allImages)) {
$goodsImages = $allImages;
}
}
// 金额统一由 Home::calcAmount 计算,保证与试算口径一致
$amount = Home::calcAmount($service, [
'floor' => (int)$address['floor'],
'has_elevator' => (int)$address['has_elevator'],
'urgent' => $urgent,
], $couponId, $this->uid(), $priceType, $userPrice);
], $couponId, $this->uid(), $priceType, $userPrice, $goodsList);
$amount['price_type'] = $priceType;
$orderNo = $this->makeOrderNo();
$verifyCode = (string)mt_rand(1000, 9999);
// 如果有多商品明细,结构化存入 goods_images 或在 remark 补充
$goodsImagesPayload = is_array($goodsImages) ? json_encode($goodsImages, JSON_UNESCAPED_UNICODE) : (string)$goodsImages;
if (!empty($goodsList)) {
$extraPayload = [
'goods_list' => $goodsList,
'images' => is_array($goodsImages) ? $goodsImages : [],
];
$goodsImagesPayload = json_encode($extraPayload, JSON_UNESCAPED_UNICODE);
}
$id = Db::name('order')->insertGetId([
'order_no' => $orderNo,
'user_id' => $this->uid(),
'master_id' => 0,
'cat_id' => (int)$service['cat_id'],
'service_id' => (int)$service['id'],
'service_name' => $service['name'],
'service_image' => $service['image'],
'cat_id' => $catId,
'service_id' => $service ? (int)$service['id'] : 0,
'service_name' => $serviceName,
'service_image' => $serviceImage,
'price_type' => $priceType,
'goods_images' => is_array($goodsImages) ? json_encode($goodsImages, JSON_UNESCAPED_UNICODE) : (string)$goodsImages,
'goods_images' => $goodsImagesPayload,
'goods_model' => mb_substr($goodsModel, 0, 100),
'goods_num' => $goodsNum,
'floor' => (int)$address['floor'],