weixin_52219567 преди 3 седмици
родител
ревизия
d62f475eb1

+ 7 - 0
src/addon/shop/api/order.ts

@@ -43,6 +43,13 @@ export function getShopOrderDetail(order_id: any) {
     return request.get(`order/miniOrder/${ order_id }`)
 }
 
+/**
+ * 查询当前订单的流程节点列表
+ */
+export function getOrderFlowNodes(order_id: any) {
+    return request.get(`/order/miniOrder/getOrderFlowNodes/${ order_id }`)
+}
+
 /**
  * 根据订单id查询发货订单
  */

+ 35 - 12
src/addon/shop/pages/goods/category.vue

@@ -4,9 +4,12 @@
         <top-tabbar :data="topTabbarData" scrollBool="1" :isBack="false" />
         <!-- #endif -->
         <view class="relative">
-            <category-template-one-one class="category" v-if="config.level===1 && config.template === 'style-1'" :categoryId="categoryId" :config="config" />
-            <category-template-two-one v-if="config.level===2 && config.template === 'style-1'" :categoryId="categoryId" :config="config" />
-            <category-template-two-two class="category" v-if="config.level===2 && config.template === 'style-2'" :categoryId="categoryId" :config="config" />
+            <category-template-one-one class="category" v-if="config.level === 1 && config.template === 'style-1'"
+                :categoryId="categoryId" :config="config" />
+            <category-template-two-one v-if="config.level === 2 && config.template === 'style-1'"
+                :categoryId="categoryId" :config="config" />
+            <category-template-two-two class="category" v-if="config.level === 2 && config.template === 'style-2'"
+                :categoryId="categoryId" :config="config" />
         </view>
     </view>
 </template>
@@ -23,19 +26,41 @@ const systemStore = useSystemStore()
 
 /********* 自定义头部 - start ***********/
 const topTabarObj = topTabar()
-let topTabbarData = topTabarObj.setTopTabbarParam({ title: '商品分类', topStatusBar: { textColor: '#333' }})
+let topTabbarData = topTabarObj.setTopTabbarParam({ title: '商品分类', topStatusBar: { textColor: '#333' } })
 /********* 自定义头部 - end ***********/
 
 const config: any = ref({})
 const categoryId = ref(0)
 
 const getGoodsCategoryConfigFn = () => {
-    getGoodsCategoryConfig().then(res => {
-        config.value = res.data
-        uni.setNavigationBarTitle({
-            title: config.value.page_title
-        });
-    })
+    config.value = {
+        "level": 1,
+        "template": "style-1",
+        "page_title": "商品分类",
+        "search": {
+            "control": 1,
+            "title": "请搜索您想要的商品"
+        },
+        "goods": {
+            "style": "single-cols"
+        },
+        "sort": "default",
+        "cart": {
+            "control": 1,
+            "event": "cart",
+            "style": "style-4",
+            "text": "购买"
+        }
+    }
+    uni.setNavigationBarTitle({
+        title: config.value.page_title
+    });
+    // getGoodsCategoryConfig().then(res => {
+    //     config.value = res.data
+    //     uni.setNavigationBarTitle({
+    //         title: config.value.page_title
+    //     });
+    // })
 }
 
 onLoad((options: any) => {
@@ -48,7 +73,6 @@ onShow(() => {
 
 </script>
 <style>
-
 /*  #ifdef  H5  */
 :deep(.category .detail .mescroll-body) {
     padding-bottom: calc(50px + constant(safe-area-inset-bottom)) !important;
@@ -115,7 +139,6 @@ onShow(() => {
 }
 
 /*  #endif  */
-
 </style>
 <style lang="scss" scoped>
 :deep(.mescroll-upwarp) {

+ 97 - 1
src/addon/shop/pages/order/detail.vue

@@ -61,6 +61,13 @@
                         </view>
                     </view>
                 </view>
+                <view class="sidebar-margin mt-[var(--top-m)] card-template">
+                    <u-steps :current="currentStep" dot direction="column" activeColor="var(--primary-color)">
+                        <template v-for="(item, index) in progressSteps" :key="index + 'id'">
+                            <u-steps-item :title="item.title" :desc="item.time"></u-steps-item>
+                        </template>
+                    </u-steps>
+                </view>
                 <view class="sidebar-margin card-template p-[0] py-[var(--pad-top-m)] overflow-hidden" :class="{
                     'pb-[var(--pad-top-m)]': detail.orderProductList.length <= 0,
                 }" :style="detail.delivery_type == 'virtual' || detail.taker_name == ''
@@ -211,6 +218,7 @@ import { onLoad } from "@dcloudio/uni-app";
 import { img, redirect, copy, goback } from "@/utils/common";
 import {
     getShopOrderDetail,
+    getOrderFlowNodes,
     orderClose,
     orderFinish,
     getAddressInfo,
@@ -229,6 +237,8 @@ const detail: any = ref<Object>({});
 const addressInfo = ref<any>({});
 const loading = ref<boolean>(true);
 const orderId = ref("");
+const progressSteps = ref<any>([]);
+const currentStep = ref(1);
 
 const wxPrivacyPopupRef: any = ref(null);
 
@@ -255,7 +265,7 @@ const orderDetailFn = (id: any) => {
     getShopOrderDetail(id)
         .then((res: any) => {
             detail.value = res.data;
-
+            loadFlowNodes()
             getAddressInfo(res.data.shippingAddressId).then((res1: any) => {
                 addressInfo.value = res1.data;
             });
@@ -265,6 +275,92 @@ const orderDetailFn = (id: any) => {
         .catch(() => {
             loading.value = false;
         });
+
+
+};
+
+// 加载审批流程节点
+const loadFlowNodes = async () => {
+    try {
+        const res = (await getOrderFlowNodes(orderId.value)) as any;
+        if (res.code === 200) {
+            const apiNodes: any = res.data || [];
+
+            // 提交订单节点:显示订单的 createTime
+            const steps: { title: string; desc: string; time: string; reviewStatus?: number }[] = [
+                { title: '提交订单', desc: '订单已提交', time: formatTime(detail.value.createTime), reviewStatus: 2 }
+            ];
+
+            // 并行解析所有节点的审批人名称
+            const handlerNames = await Promise.all(apiNodes.map((node: any) => resolveHandlerName(node.handlerId || node.handlerName || '')));
+
+            apiNodes.forEach((node: any, idx: any) => {
+                steps.push({
+                    title: node.nodeName || '审批',
+                    desc: handlerNames[idx] || '',
+                    time: formatTime(node.updateTime || ''),
+                    reviewStatus: node.reviewStatus ?? 0
+                });
+            });
+
+            // 完成节点:显示订单的 updateTime
+            // steps.push({ title: '完成', icon: CircleCheck, desc: '交易完成', time: formatTime(orderTimeInfo.updateTime), reviewStatus: 0 });
+
+            progressSteps.value = steps;
+
+            console.log(progressSteps, '???????????')
+
+            // 计算当前步骤:找最后一个已处理节点的索引(reviewStatus > 0)
+            let lastActiveIndex = 0;
+            steps.forEach((step, idx) => {
+                if (step.reviewStatus && step.reviewStatus > 0) {
+                    lastActiveIndex = idx;
+                }
+            });
+            // 若所有中间节点均已完成(reviewStatus === 2),则激活结束节点
+            const middleNodes = steps.slice(1, steps.length - 1);
+            if (middleNodes.length > 0 && middleNodes.every((s) => s.reviewStatus === 2)) {
+                lastActiveIndex = steps.length - 1;
+            }
+            currentStep.value = lastActiveIndex;
+        }
+    } catch (error) {
+        console.error('加载流程节点失败', error);
+    }
+};
+
+// 格式化时间为 "2026/3/17 上午10:49" 格式
+const formatTime = (timeStr: any): string => {
+    if (!timeStr) return '';
+    const date = new Date(timeStr);
+    if (isNaN(date.getTime())) return timeStr;
+    return date.toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: 'numeric',
+        day: 'numeric',
+        hour: 'numeric',
+        minute: '2-digit',
+        hour12: true
+    });
+};
+
+// 单人:返回姓名;多人:返回"xx或xx审核"
+const resolveHandlerName = async (handlerId: string): Promise<string> => {
+    if (!handlerId) return '';
+    const ids = handlerId
+        .split(',')
+        .map((s) => s.trim())
+        .filter(Boolean);
+    if (ids.length === 0) return '';
+    try {
+        const results = await Promise.all(ids.map((id) => getContactInfo(String(id)).catch(() => null)));
+        const names = results.map((res: any) => res?.data?.contactName || '').filter(Boolean);
+        if (names.length === 0) return '';
+        if (names.length === 1) return names[0];
+        return names.join('或') + '审核';
+    } catch {
+        return '';
+    }
 };
 
 //关闭订单

+ 4 - 4
src/addon/shop_giftcard/api/giftcard.ts

@@ -3,11 +3,11 @@ import request from '@/utils/request'
 // ************************************** 礼品卡活动 **************************************
 
 /**
- * 获取礼品卡活动分类列表
+ * 优易咨迅(平台公告)
  * @param params
  */
 export function getGiftCardPageList(params: Record<string, any>) {
-    return request.get(`shop_giftcard/giftcard`, params)
+    return request.get(`system/miniSystem/getYouYiZiXunPage`, params)
 }
 
 /**
@@ -22,8 +22,8 @@ export function getGiftCardListByComponents(params: Record<string, any>) {
  * 获取礼品卡活动详情
  * @param giftcard_id   礼品卡id
  */
-export function getGiftCardDetail(giftcard_id: any) {
-    return request.get(`shop_giftcard/giftcard/${ giftcard_id }`)
+export function getGiftCardDetail(id: any) {
+    return request.get(`system/miniSystem/getYouYiZiXunInfo/${ id }`)
 }
 
 /**

+ 65 - 328
src/addon/shop_giftcard/pages/detail.vue

@@ -2,191 +2,65 @@
     <view :style="themeColor()">
         <view class="bg-[#f8f8f8] min-h-[100vh]" v-if="Object.keys(detail).length">
             <view class="px-[var(--sidebar-m)] py-[var(--top-m)]">
-                <view class="w-full h-[430rpx] relative">
-                    <image v-if="detail.material_list[activeIndex]" class="w-full h-[430rpx] rounded-[var(--rounded-mid)]" :src="img(detail.material_list[activeIndex].url || '')" @error="detail.material_list[activeIndex].url= defaultCard(detail)" mode="aspectFill" />
-                    <image v-else class="w-full h-[430rpx] rounded-[var(--rounded-mid)]" :src="img(defaultCard(detail))" mode="aspectFill" />
-                    <view class="absolute bottom-[var(--pad-top-m)] left-0 right-0 flex justify-center">
-                        <button class="h-[44rpx] font-500 text-[24rpx] !text-[#fff] m-0 rounded-[var(--rounded-big)] remove-border"
-                            :class="{'!bg-[#EF000C]':detail.card_right_type=='balance','!bg-[#FF7700]':detail.card_right_type=='goods'}">
-                            <text class="text-[24rpx] leading-[44rpx] iconfont mr-[5rpx]" :class="{'iconchuzhikaV6mm':detail.card_right_type=='balance','iconduihuankaV6mm-1':detail.card_right_type=='goods'}"></text>
-                            <text class="leading-[44rpx]">{{ detail.card_right_type_name }}</text>
-                        </button>
-                    </view>
-                </view>
-                <!-- <view v-if="detail.material_list.length>1" class="text-[28rpx] leading-[39rpx] font-400 mt-[var(--top-m)]">选择封面</view> -->
-                <view class="h-[110rpx] mt-[var(--top-m)]" v-if="detail.material_list.length>1">
-
-                    <!-- <swiper v-if="detail.material_list.length > 5" class="h-full" display-multiple-items='5' circular='true' previous-margin='0rpx'
-                      next-margin='-160rpx' :current="current" @change="swiperChange" indicator-active-color='red'>
-                      <template v-for="(item,index) in detail.material_list" :key='index'>
-                        <swiper-item class="sitem" @click.stop="swiperCardClick(index)">
-                          <view class="swiper-item flex justify-center items-center">
-                            <image class="w-[144rpx] h-[90rpx] rounded-[var(--goods-rounded-small)] flex-shrink-0"
-                            :class="{'!w-[180rpx] !h-[110rpx] box-border border-[2rpx] border-solid border-[var(--primary-color)]':index==activeIndex}"
-                            :src="img(item.url)" mode="aspectFit"/>
-                          </view>
-                        </swiper-item>
-                      </template>
-                    </swiper> -->
-                    <!-- :scroll-into-view="'id' + activeIndex" -->
-                    <scroll-view :scroll-x="true">
-                        <view class="flex items-center">
-                            <template v-for="(item,index) in detail.material_list" :key='index'>
-                                <image v-if="item.url" :id="'id' + index" @click.stop="swiperCardClick(index)"
-                                       class="w-[144rpx] h-[90rpx] rounded-[var(--goods-rounded-small)] flex-shrink-0 mr-[20rpx]"
-                                       :class="{'!w-[180rpx] !h-[110rpx] box-border border-[2rpx] border-solid border-[var(--primary-color)]':index==activeIndex}"
-                                       :src="img(item.url)" @error="item.url= defaultCard(detail)"
-                                       mode="aspectFill" />
-                                <image v-else :id="'id' + index" @click.stop="swiperCardClick(index)"
-                                       class="w-[144rpx] h-[90rpx] rounded-[var(--goods-rounded-small)] flex-shrink-0 mr-[20rpx]"
-                                       :class="{'!w-[180rpx] !h-[110rpx] box-border border-[2rpx] border-solid border-[var(--primary-color)]':index==activeIndex}"
-                                       :src="img(defaultCard(detail))" mode="aspectFill" />
-                            </template>
-                        </view>
-                    </scroll-view>
-                </view>
-                <view class="mt-[var(--top-m)] card-template flex items-center justify-between !pr-0">
-                    <text class="text-[28rpx] leading-[39rpx] font-400">{{ t('purchaseQuantity') }}</text>
-                    <u-number-box v-model="giftcard_data.num" :min="1" :max="99" integer :step="1" input-width="68rpx" input-height="52rpx" @change="numBlurFn">
-                        <template #minus>
-                            <view class="text-[#303133] text-[24rpx] font-500 nc-iconfont nc-icon-jianV6xx leading-[50rpx] w-[60rpx] text-right box-border pr-[10rpx]" :class="{ '!text-[var(--text-color-light9)]': giftcard_data.num <= 1 }"></view>
-                        </template>
-                        <template #input>
-                            <input class="text-[#303133] text-[28rpx] w-[80rpx] h-[44rpx] bg-[var(--temp-bg)] leading-[44rpx] text-center rounded-[6rpx]" type="number" v-model="giftcard_data.num" @input="numInputFn" @blur="numBlurFn" />
-                        </template>
-                        <template #plus>
-                            <view class="text-[#303133] text-[24rpx] font-500 nc-iconfont nc-icon-jiahaoV6xx leading-[50rpx] w-[50rpx] box-border pl-[10rpx]" :class="{ '!text-[var(--text-color-light9)]': giftcard_data.num >= 99 }"></view>
-                        </template>
-                    </u-number-box>
+                <!-- 标题 -->
+                <view class="text-[36rpx] font-bold text-[#333] leading-[50rpx] mb-[24rpx]">
+                    {{ detail.announcementTitle }}
                 </view>
-                <view v-if="detail.card_right_type=='balance'" class="mt-[var(--top-m)] card-template">
-                    <view class="text-[#333] text-[26rpx] leading-[30rpx] font-400 shrink-0 mr-[20rpx]">{{ t('selectDenomination') }}</view>
-                    <view class="flex flex-wrap gap-x-[31rpx]">
-                        <template v-for="(item,index) in detail.balance_json" :key='index'>
-                            <view @click="moneyClick(item.balance)" :class="{'border-0 primary-btn-bg !text-[#fff]':item.balance == giftcard_data.balance }"
-                                  class="w-[200rpx] h-[88rpx] box-border flex items-center justify-center mt-[30rpx] border-[2rpx] border-solid border-[#ddd] rounded-[var(--rounded-small)]">
-                                <view class="flex items-baseline font-500">
-                                    <text class="text-[24rpx]">¥</text>
-                                    <text class="text-[34rpx]">{{ item.balance }}</text>
-                                </view>
-                            </view>
-                        </template>
+                
+                <!-- 信息栏 -->
+                <view class="flex items-center text-[24rpx] text-[#999] mb-[32rpx] pb-[24rpx] border-b border-[#eee]">
+                    <view class="flex items-center mr-[30rpx]">
+                        <text class="nc-iconfont nc-icon-riliV6xx1 mr-[10rpx]"></text>
+                        <text>{{ detail.updateTime }}</text>
                     </view>
-                </view>
-                <view v-if="detail.card_right_type=='goods'" class="mt-[var(--top-m)] card-template">
                     <view class="flex items-center">
-                        <text class="title !mb-0">{{ t('convertibleGoods') }}</text>
-                        <text v-if="detail.card_goods_type=='diy'" class="text-[24rpx] text-[var(--text-color-light9)] leading-[34rpx] ml-[10rpx]">{{ t('goodsTitleOne') }}{{ detail.card_goods_count }}{{ t('goodsTitleOneAnd') }}</text>
-                        <text v-else class="text-[24rpx] text-[var(--text-color-light9)] leading-[34rpx] ml-[10rpx]">{{ t('goodsTitleTwo ') }}</text>
+                        <text class="nc-iconfont nc-icon-liulanV6xx1 mr-[8rpx]"></text>
+                        <text>{{ detail.viewCount }}次浏览</text>
                     </view>
-                    <template v-for="(item,index) in detail.goods_sku_list" :key='index'>
-                        <view class="mt-[var(--pad-top-m)] flex" @click.stop="redirect({ url: '/addon/shop/pages/goods/detail',param:{ sku_id:item.sku_id }})">
-                            <image v-if="item.sku.sku_image" class="w-[190rpx] h-[190rpx] rounded-[var(--rounded-mid)]" :src="img(item.sku.sku_image)" mode="aspectFill" @error="item.sku.sku_image='static/resource/images/diy/shop_default.jpg'" />
-                            <image v-else class="w-[170rpx] h-[170rpx] rounded-[var(--rounded-mid)]" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill" />
-                            <view class="ml-[20rpx] flex-1 flex flex-col justify-between">
-                                <view class="w-full">
-                                    <view class="max-w-[472rpx] text-[28rpx] leading-[40rpx] font-400 truncate text-[#303133]">{{ item.goods.goods_name }}</view>
-                                    <view class="mt-[15rpx] flex items-center justify-between">
-                                        <view class="text-[24rpx] text-[var(--text-color-light6)] font-400 truncate leading-[34rpx] w-[452rpx]">{{ item.sku.sku_name }}</view>
-                                    </view>
-                                </view>
-                                <view class="flex items-center justify-between">
-                                    <view class="text-[var(--price-text-color)] flex items-baseline">
-                                        <text class="text-[22rpx] price-font">¥</text>
-                                        <text class="text-[36rpx] font-500 price-font">{{ parseFloat(item.sku.price).toFixed(2).split('.')[0] }}</text>
-                                        <text class="text-[22rpx] font-500 price-font">.{{ parseFloat(item.sku.price).toFixed(2).split('.')[1] }}</text>
-                                    </view>
-                                    <view v-if="detail.card_goods_type=='all'"
-                                          class="font-400 text-[28rpx] text-[#303133]">
-                                        <text>x</text>
-                                        <text>{{ item.num }}</text>
-                                    </view>
-                                </view>
-                            </view>
-                        </view>
-                    </template>
                 </view>
-                <view v-if="detail.instruction" class="mt-[var(--top-m)] card-template">
-                    <view class="title">{{ t('instruction') }}</view>
-                    <view class="u-content">
-                        <u-parse :content="detail.instruction" :tagStyle="{img: 'vertical-align: top;',p:'overflow: hidden;word-break:break-word;' }"></u-parse>
-                    </view>
+                
+                <!-- 封面图 -->
+                <view v-if="detail.coverImage" class="w-full rounded-[var(--rounded-mid)] overflow-hidden mb-[32rpx]">
+                    <image 
+                        class="w-full h-auto" 
+                        :src="img(detail.coverImage)" 
+                        mode="widthFix"
+                        @error="detail.coverImage = ''"
+                    />
                 </view>
-                <view v-if="detail.card_desc" class="mt-[var(--top-m)] card-template">
-                    <view class="title">{{ t('cardDesc') }}</view>
-                    <view class="u-content">
-                        <u-parse :content="detail.card_desc" :tagStyle="{img: 'vertical-align: top;',p:'overflow: hidden;word-break:break-word;' }"></u-parse>
-                    </view>
+                
+                <!-- 富文本内容 -->
+                <view class="u-content bg-white p-[30rpx] rounded-[var(--rounded-mid)]">
+                    <u-parse 
+                        :content="detail.announcementContent" 
+                        :tagStyle="{
+                            img: 'max-width:100%;height:auto;display:block;margin:20rpx auto;',
+                            p: 'overflow: hidden;word-break:break-word;line-height:1.6;margin-bottom:20rpx;font-size:28rpx;color:#333;'
+                        }"
+                    ></u-parse>
                 </view>
             </view>
 
             <view class="tab-bar-placeholder"></view>
-
-            <view class="border-[0] border-t-[2rpx] border-solid border-[#f5f5f5] w-[100%] flex justify-between pl-[30rpx] pr-[20rpx] bg-[#fff] box-border fixed left-0 bottom-0 tab-bar z-1 items-center">
-                <view class="flex items-baseline">
-                    <view class="text-[24rpx] leading-[34rpx] font-500 mr-[6rpx]">{{ t('price') }}:</view>
-                    <view class="text-[var(--price-text-color)] flex items-baseline">
-                        <text class="text-[26rpx] price-font">¥</text>
-                        <text class="text-[44rpx] font-500 price-font">{{ parseFloat(totalPrice).toFixed(2).split('.')[0] }}</text>
-                        <text class="text-[26rpx] font-500 price-font">.{{ parseFloat(totalPrice).toFixed(2).split('.')[1] }}</text>
-                    </view>
-                </view>
-                <template v-if="detail.status == 1">
-                    <template v-if="!isExpired">
-                        <!-- #ifdef H5 -->
-                        <button class="w-[300rpx] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] primary-btn-bg !m-0 leading-[70rpx] rounded-full remove-border" @click="confirm">{{ t('buyNow') }}</button>
-                        <!-- #endif -->
-
-                        <!-- #ifdef MP-WEIXIN -->
-                        <!--<button v-if="isBindMobile && info && !info.mobile" class="w-[300rpx] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] primary-btn-bg !m-0 leading-[70rpx] rounded-full remove-border" open-type="getPhoneNumber" @getphonenumber="memberStore.bindMobile">{{t('buyNow')}}</button>-->
-                        <!--<button v-else class="w-[300rpx] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] primary-btn-bg !m-0 leading-[70rpx] rounded-full remove-border" @click="confirm">{{t('buyNow')}}</button>-->
-                        <button class="w-[300rpx] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] primary-btn-bg !m-0 leading-[70rpx] rounded-full remove-border" @click="confirm">{{ t('buyNow') }}</button>
-                        <!-- #endif -->
-                    </template>
-                    <button v-else class="w-[300rpx] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] bg-[#ccc] !m-0 leading-[70rpx] rounded-full remove-border">{{ t('EventEnds') }}</button>
-                </template>
-                <button v-if="detail.status == 0" class="w-[300rpx] !h-[70rpx] font-500 text-[26rpx] !text-[#fff] bg-[#ccc] !m-0 leading-[70rpx] rounded-full remove-border">{{ t('removed') }}</button>
-
-            </view>
-
         </view>
 
-        <!-- 强制绑定手机号 -->
-        <bind-mobile ref="bindMobileRef" />
-
         <loading-page :loading="loading"></loading-page>
 
-        <!-- #ifdef MP-WEIXIN -->
-        <!-- 小程序隐私协议 -->
-        <wx-privacy-popup ref="wxPrivacyPopupRef"></wx-privacy-popup>
-        <!-- #endif -->
     </view>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, nextTick } from 'vue'
-import { redirect, img, goback, timeTurnTimeStamp, handleOnloadParams } from '@/utils/common'
+import { ref, nextTick } from 'vue'
+import { img, goback, handleOnloadParams } from '@/utils/common'
 import { onLoad, onShow } from '@dcloudio/uni-app'
 import { t } from '@/locale'
 import { getGiftCardDetail } from '@/addon/shop_giftcard/api/giftcard';
-import { useLogin } from '@/hooks/useLogin'
-import useMemberStore from '@/stores/member'
 import { useShare } from '@/hooks/useShare'
 
 const detail: any = ref({})
-const activeIndex = ref(0) // 当前激活展示图
-// const current = ref(0) // swiper当前处于最左侧item下标
 const loading = ref(true)
-
 const wxPrivacyPopupRef: any = ref(null)
-const giftcard_data = ref({
-    giftcard_id: '',
-    num: 1,
-    balance: '',
-    material_id: ''
-})
 
-const totalPrice = ref(0)
 // 分享
 const { setShare } = useShare()
 
@@ -195,210 +69,73 @@ onLoad((option: any) => {
     // 处理小程序场景值参数
     option = handleOnloadParams(option);
     // #endif
-    giftcard_data.value.giftcard_id = option.giftcard_id || ''
-    if (!option.giftcard_id) {
+    const id = option.id || ''
+    if (!id) {
         let parameter = {
             url: '/addon/shop_giftcard/pages/list',
-            title: t('lackCardId'),
+            title: '缺少id',
             mode: 'reLaunch'
         };
         goback(parameter);
     } else {
-        getGiftCardDetailFn(option.giftcard_id)
+        getConsultationDetailFn(id)
     }
 })
+
 onShow(() => {
-    if (giftcard_data.value.giftcard_id) getGiftCardDetailFn(giftcard_data.value.giftcard_id);
+    // 页面显示时刷新数据(可选)
 })
-// const swiperChange = (event:any)=> {
-// 	if (detail.value.material_list.length > 5) {
-// 		let index = event.detail.current
-// 		activeIndex.value = index >= (detail.value.material_list.length - 2) ? index - (detail.value.material_list.length - 2) : index + 2
-// 	}
-
-// }
-
-//img item点击
-const swiperCardClick = (index: any) => {
-    activeIndex.value = index
-    // if (detail.value.material_list.length > 5) {
-    // 	current.value = index > 1 ? index - 2 : detail.value.material_list.length - 1 - (1 - index)
-    // }
-}
 
-const getGiftCardDetailFn = (giftcard_id: any) => {
+const getConsultationDetailFn = (id: any) => {
     loading.value = true
-    getGiftCardDetail(giftcard_id).then((res: any) => {
+    getGiftCardDetail(id).then((res: any) => {
         if (!res.data || JSON.stringify(res.data) === '[]') {
-            uni.showToast({ title: t('unableToFindGiftCard'), icon: 'none' })
+            uni.showToast({ title: '无法找到资讯详情', icon: 'none' })
             setTimeout(() => {
-                redirect({ url: '/addon/shop_giftcard/pages/index', mode: 'reLaunch' })
+                goback({ url: '/addon/shop_giftcard/pages/list', mode: 'navigateBack' })
             }, 600)
             return false
         }
 
         detail.value = res.data
 
-        setTimeout(() => {
-            let share = {
-                title: detail.value.card_name,
-                desc: '',
-                url: img(detail.value.material_list[activeIndex.value].url)
-            }
-
-            setShare({
-                wechat: {
-                    ...share
-                },
-                weapp: {
-                    ...share
-                }
-            });
-        }, 600);
-
         uni.setNavigationBarTitle({
-            title: detail.value.card_name
+            title: detail.value.announcementTitle
         })
-        if (detail.value.card_right_type == 'goods') {
-            totalPrice.value = detail.value.card_price * (giftcard_data.value.num || 1)
-        }
-        if (detail.value.card_right_type == 'balance') {
-            giftcard_data.value.balance = detail.value.balance_json[0].balance
-            let index = detail.value.balance_json.findIndex(item => item.balance == giftcard_data.value.balance)
-            totalPrice.value = detail.value.balance_json[index].price * (giftcard_data.value.num || 1)
-        }
+        
         loading.value = false
-        swiperCardClick(0)
 
-        nextTick(() => {
-            // #ifdef MP
-            if (wxPrivacyPopupRef.value) wxPrivacyPopupRef.value.proactive();
-            // #endif
-        })
+    }).catch(() => {
+        loading.value = false
+        uni.showToast({ title: '加载失败', icon: 'none' })
     })
 }
 
-//数量
-const numInputFn = () => {
-    setTimeout(() => {
-        if (giftcard_data.value.num >= 99) {
-            giftcard_data.value.num = 99;
-        }
-        giftcard_data.value.num = parseInt(giftcard_data.value.num)
-    }, 0)
-}
-
-const numBlurFn = () => {
-    setTimeout(() => {
-        if (!giftcard_data.value.num || giftcard_data.value.num <= 0) {
-            giftcard_data.value.num = 1;
-        }
-        if (giftcard_data.value.num >= 99) {
-            giftcard_data.value.num = 99;
-        }
-        if (detail.value.card_right_type == 'goods') {
-            totalPrice.value = detail.value.card_price * (giftcard_data.value.num || 1)
-        }
-        if (detail.value.card_right_type == 'balance') {
-            let index = detail.value.balance_json.findIndex(item => item.balance == giftcard_data.value.balance)
-            totalPrice.value = detail.value.balance_json[index].price * (giftcard_data.value.num || 1)
-        }
-    }, 0)
-
-}
-
-//面值点击
-const moneyClick = (money: any) => {
-    giftcard_data.value.balance = money
-    let index = detail.value.balance_json.findIndex(item => item.balance == money)
-    totalPrice.value = detail.value.balance_json[index].price * (giftcard_data.value.num || 1)
-}
-
-// 会员信息
-const memberStore = useMemberStore()
-const userInfo = computed(() => memberStore.info)
-//强制绑定手机号
-const bindMobileRef: any = ref(null)
-const isBindMobile = ref(uni.getStorageSync('isBindMobile'))
-const confirm = () => {
-
-    // #ifdef H5
-    // 绑定手机号
-    if (!userInfo.value && uni.getStorageSync('isBindMobile')) {
-        uni.setStorage({
-            key: 'loginBack', data: {
-                url: '/addon/shop_giftcard/pages/detail',
-                param: { giftcard_id: detail.value.giftcard_id }
-            }
-        })
-        bindMobileRef.value.open()
-        return false
-    }
-    // #endif
-
-    // 检测是否登录
-    if (!userInfo.value) {
-        useLogin().setLoginBack({
-            url: '/addon/shop_giftcard/pages/detail',
-            param: { giftcard_id: detail.value.giftcard_id }
-        })
-        return false
-    }
-
-    giftcard_data.value.material_id = detail.value.material_list[activeIndex.value].material_id
-    if (!giftcard_data.value.num || giftcard_data.value.num <= 0) {
-        giftcard_data.value.num = 1;
-    }
-    uni.setStorage({
-        key: 'giftCardOrderCreateData',
-        data: {
-            giftcard_data: giftcard_data.value,
-        },
-        success: () => {
-            redirect({ url: '/addon/shop_giftcard/pages/payment' })
-        }
-    });
-}
-
-// 判断卡片是否过期
-const isExpired = computed(() => {
-    let bool = false;
-    if (detail.value && detail.value.validity_type == 'date' && (Date.now() / 1000) > timeTurnTimeStamp(detail.value.validity_time)) {
-        bool = true;
-    }
-    return bool;
-})
-const defaultCard = (data) => {
-    let imgUrl = '';
-    if (data.card_right_type == 'balance') {
-        imgUrl = 'addon/shop_giftcard/diy/index/value_card.jpg';
-    } else {
-        imgUrl = 'addon/shop_giftcard/diy/index/redemption_card.jpg';
-    }
-    return imgUrl;
-}
 </script>
 
 <style lang="scss" scoped>
-uni-swiper-item {
-    overflow: inherit; //改为默认的就行
-}
-
-.swiper-item {
-    width: 144rpx;
-    height: 100%;
-    border-radius: var(--rounded-small);
-    position: relative;
-}
-
 .tab-bar-placeholder {
     padding-bottom: calc(constant(safe-area-inset-bottom) + 100rpx);
     padding-bottom: calc(env(safe-area-inset-bottom) + 100rpx);
 }
 
-.tab-bar {
-    padding-top: 16rpx;
-    padding-bottom: calc(constant(safe-area-inset-bottom) + 16rpx);
-    padding-bottom: calc(env(safe-area-inset-bottom) + 16rpx);
+.u-content {
+    :deep(.u-parse) {
+        font-size: 28rpx;
+        color: #333;
+        line-height: 1.6;
+        
+        img {
+            max-width: 100%;
+            height: auto;
+            display: block;
+            margin: 20rpx auto;
+        }
+        
+        p {
+            margin-bottom: 20rpx;
+            word-break: break-word;
+        }
+    }
 }
 </style>

+ 60 - 125
src/addon/shop_giftcard/pages/list.vue

@@ -1,170 +1,105 @@
 <template>
-    <view class="bg-[#f8f8f8] min-h-[100vh] giftcard-list" :style="themeColor()">
+    <view class="bg-gray-100 min-h-[100vh] consultation-list" :style="themeColor()">
         <!-- #ifdef MP-WEIXIN -->
         <top-tabbar :data="topTabbarData" scrollBool="1" :isBack="false"/>
-        <!-- #endif -->        
-        <view class="fixed left-0 top-0 right-0 z-10" v-if="categoryLoading" :style="{'top': systemStore.topTabbarInfo.fullHeight || 0}">
-            <scroll-view :scroll-x="true" class="tab-style-2" scroll-with-animation :scroll-into-view="'id' + cardCategory">
-                <view class="tab-content">
-                    <view class="tab-items mx-[20rpx]" :class="{ 'class-select': cardCategory === item.category_id }" :id="'id' + index" @click="cardCategoryFn(item.category_id)" v-for="(item, index) in cardCategoryList">{{ item.category_name }}</view>
+        <!-- #endif -->
+        <view class="pt-[var(--top-m)] px-[30rpx]">
+            <view 
+                v-for="(item, index) in list" 
+                :key="index" 
+                class="bg-white rounded-[20rpx] p-[30rpx] mb-[20rpx] shadow-sm"
+                @click.stop="toDetail(item)"
+            >
+                <view class="text-[28rpx] font-bold text-[#333] mb-[16rpx] line-clamp-2">
+                    {{ item.announcementTitle }}
                 </view>
-            </scroll-view>
-        </view>
-        <mescroll-body ref="mescrollRef" top="88rpx" @init="mescrollInit" :down="{ use: false }" @up="getGiftCardPageListFn">
-            <view class="sidebar-margin pt-[var(--top-m)] flex justify-between flex-wrap" v-if="list.length">
-                <view class="flex flex-col template-item rounded-[var(--goods-rounded-big)] overflow-hidden bg-[#fff] box-border border-[2rpx] border-solid border-[#F8F8F8] mb-[var(--top-m)]"
-                    v-for="(item, index) in list" :key="index" @click.stop="toDetail(item)">
-                    <image v-if="item.cover" class="w-[100%] h-[210rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img(item.cover? item.cover.split(',')[0] : '')" @error="item.cover= defaultCard(item)" mode="aspectFill" />
-                    <image v-else class="w-[100%] h-[210rpx] rounded-[var(--goods-rounded-big)] overflow-hidden" :src="img(defaultCard(item))" mode="aspectFill" />
-                    <view class="flex justify-between h-[80rpx] items-center px-[var(--pad-sidebar-m)]">
-                        <view class="max-w-[250rpx] text-[28rpx] font-400 truncate text-[#303133]">{{ item.card_name }}</view>
-                        <text class="text-[30rpx] iconfont" :class="{'iconchuzhikaV6mm text-[#EF000C]':item.card_right_type=='balance','iconduihuankaV6mm-1 text-[#FF7700]':item.card_right_type=='goods'}"></text>
+                <view class="flex items-center text-[24rpx] text-[#999]">
+                    <text class="nc-iconfont nc-icon-riliV6xx1 mr-[10rpx]"></text>
+                    <text>{{ formatDate(item.createTime) }}</text>
+                    <view class="ml-auto flex items-center">
+                        <text class="nc-iconfont nc-icon-liulanV6xx1 mr-[8rpx]"></text>
+                        <text>{{ item.viewCount }}次浏览</text>
                     </view>
                 </view>
             </view>
-            <mescroll-empty v-if="!list.length && !loading" :option="{tip : t('cardEmpty'), icon: img('addon/shop_giftcard/empty.png')}"></mescroll-empty>
-        </mescroll-body>
+            <mescroll-empty 
+                v-if="!list.length && !loading" 
+                :option="{tip: '暂无咨询', icon: img('addon/shop_giftcard/empty.png')}"
+            ></mescroll-empty>
+        </view>
         <tabbar />
     </view>
 </template>
 
 <script setup lang="ts">
 import { ref } from 'vue';
-import { t } from '@/locale'
 import { img, redirect } from '@/utils/common'
-import { getGiftCardCategoryList } from '@/addon/shop_giftcard/api/category';
+import { onLoad, onShow } from '@dcloudio/uni-app';
 import { getGiftCardPageList } from '@/addon/shop_giftcard/api/giftcard';
-import MescrollBody from '@/components/mescroll/mescroll-body/mescroll-body.vue';
-import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
-import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
-import { onLoad, onShow, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
-import useSystemStore from '@/stores/system';
-const systemStore = useSystemStore()
-
-const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom);
+
+
 import { topTabar } from '@/utils/topTabbar';
 
 /********* 自定义头部 - start ***********/
 const topTabarObj = topTabar()
-let topTabbarData = topTabarObj.setTopTabbarParam({ title: '礼品卡列表', topStatusBar: { textColor: '#333' }})
+let topTabbarData = topTabarObj.setTopTabbarParam({ title: '资讯列表', topStatusBar: { textColor: '#333' }})
 /********* 自定义头部 - end ***********/
 
-const list = ref<Array<Object>>([]);
+const list = ref<Array<any>>([]);
 const loading = ref<boolean>(true);
-const categoryLoading = ref<boolean>(false);
-const cardCategory = ref('')
-const cardCategoryList: any = ref([]);
+
 
 onLoad((option: any) => {
-    getGiftCardCategoryListFn();
+  fetchConsultationList();
 });
 
 onShow(() => {
-    if (getMescroll()) getMescroll().resetUpScroll();
 })
 
-const getGiftCardPageListFn = (mescroll: any) => {
-    loading.value = true;
-    let data: object = {
-        page: mescroll.num,
-        limit: mescroll.size,
-        category_id: cardCategory.value
-    };
-
-    getGiftCardPageList(data).then((res: any) => {
-        let newArr = (res.data.data as Array<Object>);
-        //设置列表数据
-        if (mescroll.num == 1) {
-            list.value = []; //如果是第一页需手动制空列表
-        }
-        list.value = list.value.concat(newArr);
-        mescroll.endSuccess(newArr.length);
-        loading.value = false;
-    }).catch(() => {
-        loading.value = false;
-        mescroll.endErr(); // 请求失败, 结束加载
-    })
+const fetchConsultationList = () => {
+  loading.value = true;
+  getGiftCardPageList({}).then((res: any) => {
+    list.value = res.data;
+    loading.value = false;
+  }).catch(() => {
+    loading.value = false;
+  })
 }
 
-const getGiftCardCategoryListFn = () => {
-    categoryLoading.value = false;
-    cardCategoryList.value = [];
-    let obj = { category_name: '全部', category_id: '' };
-    cardCategoryList.value.push(obj);
-
-    getGiftCardCategoryList().then((res: any) => {
-        Object.values(res.data).forEach((item, index) => {
-            cardCategoryList.value.push(item);
-        });
-        categoryLoading.value = true;
-    }).catch(() => {
-        categoryLoading.value = true;
-    })
-}
-
-const cardCategoryFn = (category_id: any) => {
-    cardCategory.value = category_id;
-    list.value = [];
-    getMescroll().resetUpScroll();
-};
 
 const toDetail = (data: any) => {
-    redirect({ url: '/addon/shop_giftcard/pages/detail', param: { giftcard_id: data.giftcard_id } })
+  redirect({ url: '/addon/shop_giftcard/pages/detail', param: { id: data.id } })
 }
-const defaultCard = (data) => {
-    let imgUrl = '';
-    if (data.card_right_type == 'balance') {
-        imgUrl = 'addon/shop_giftcard/diy/index/value_card.jpg';
-    } else {
-        imgUrl = 'addon/shop_giftcard/diy/index/redemption_card.jpg';
-    }
-    return imgUrl;
+
+const formatDate = (dateStr: string) => {
+  if (!dateStr) return '';
+  const date = new Date(dateStr.replace(/-/g, '/'));
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, '0');
+  const day = String(date.getDate()).padStart(2, '0');
+  return `${year}-${month}-${day}`;
 }
+
 </script>
-<style lang="scss" scoped>
-.tab-style-2 {
-    .tab-content {
-        justify-content: flex-start;
-
-        .tab-items {
-            &:nth-child(1) {
-                margin-left: 0;
-            }
-
-            &:last-of-type {
-                margin-right: 0;
-            }
-        }
-    }
-}
 
-.template-item {
-    width: calc(50% - 10rpx);
+<style lang="scss" scoped>
+.consultation-list {
+  padding-bottom: calc(100rpx + constant(safe-area-inset-bottom));
+  padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
+  
+  .line-clamp-2 {
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+  }
 }
 
 :deep(.tab-bar-placeholder) {
-    display: none !important;
+  display: none !important;
 }
 
 :deep(.u-tabbar__placeholder) {
-    display: none !important;
-}
-
-/*  #ifdef  H5  */
-:deep(.mescroll-body) {
-    padding-bottom: calc(50px + constant(safe-area-inset-bottom)) !important;
-    padding-bottom: calc(50px + env(safe-area-inset-bottom)) !important;
-}
-
-/*  #endif  */
-/*  #ifndef  H5  */
-:deep(.mescroll-body) {
-    padding-bottom: calc(100rpx + constant(safe-area-inset-bottom)) !important;
-    padding-bottom: calc(100rpx + env(safe-area-inset-bottom)) !important;
+  display: none !important;
 }
-
-/*  #endif  */
-// :deep(.mescroll-upwarp) {
-// 	display: none;
-// }
 </style>

+ 23 - 0
src/app/api/auth.ts

@@ -14,6 +14,27 @@ export function clientLogin(data: any) {
     return request.post('auth/client/login', data, { showErrorMessage: true })
 }
 
+/**
+ * 微信小程序一键登录
+ */
+export function wxLogin(data: any) {
+    return request.post('auth/wx/login', data, { showErrorMessage: true })
+}
+
+/**
+ * 获取OpenId
+ */
+export function getOpenId(data: any) {
+    return request.post('auth/wx/getOpenId', data, { showErrorMessage: true })
+}
+
+/**
+ * 获取手机号
+ */
+export function loginByPhone(data: any) {
+    return request.post('auth/wx/loginByPhone', data, { showErrorMessage: true })
+}
+
 /**
  * 用户名登录
  */
@@ -34,6 +55,8 @@ export function mobileLogin(data: AnyObject) {
     return request.post('login/mobile', data, { showErrorMessage: true })
 }
 
+
+
 /**
  * 获取登录配置
  */

+ 244 - 238
src/app/pages/auth/login.vue

@@ -40,14 +40,12 @@
 				</template>
 				<!-- #ifdef MP-WEIXIN -->
 				<template v-if="type == 'wx'">
+					<!-- 微信登录按钮始终显示 -->
 					<button
 						class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff] !mx-[0]"
-						open-type="getPhoneNumber" @getphonenumber="getphonenumber">
-						授权手机号登录
+						@click="getOpenid">
+						微信登录
 					</button>
-					<button
-						class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff] !mx-[0]"
-						 @click="getOpenid">授权手机号登录</button>
 				</template>
 				<!-- #endif -->
 				<template v-if="type == 'mobile'">
@@ -95,270 +93,278 @@
 			</view>
 			<view class="flex justify-center gap-[40rpx]"> </view>
 		</view>
+
+		<!-- 手机号授权弹窗 -->
+		<u-popup v-model:show="showPhoneAuthPopup" mode="bottom" border-radius="24" :mask-closeable="false">
+			<view class="p-[60rpx] w-[630rpx] bg-white rounded-[24rpx]">
+				<view class="text-center text-[32rpx] font-500 text-[#333] mb-[20rpx]">完善手机号</view>
+				<view class="text-center text-[26rpx] text-[#666] mb-[40rpx]">
+					为保障您的账户安全,请授权绑定手机号
+				</view>
+				<button
+					class="w-full h-[80rpx] !bg-[var(--primary-color)] text-[26rpx] rounded-[40rpx] leading-[80rpx] font-500 !text-[#fff]"
+					open-type="getPhoneNumber" @getphonenumber="getphonenumber">
+					授权手机号
+				</button>
+				<view class="text-center text-[26rpx] text-[#999] mt-[30rpx] cursor-pointer"
+					@click="showPhoneAuthPopup = false">
+					稍后再说
+				</view>
+			</view>
+		</u-popup>
 	</view>
 </template>
+
 <script setup lang="ts">
-	import { ref, reactive, computed, onMounted } from "vue";
-	import { usernameLogin, mobileLogin, clientLogin ,wxLogin} from "@/app/api/auth";
-	import useMemberStore from "@/stores/member";
-	import useConfigStore from "@/stores/config";
-	import { useLogin } from "@/hooks/useLogin";
-	import { t } from "@/locale";
-	import { redirect, getToken, pxToRpx, isWeixinBrowser } from "@/utils/common";
-	import { onLoad } from "@dcloudio/uni-app";
-	import { topTabar } from "@/utils/topTabbar";
-	import useSystemStore from "@/stores/system";
-
-	const systemStore = useSystemStore();
-	/********* 自定义头部 - start ***********/
-	const topTabarObj = topTabar();
-	const param = topTabarObj.setTopTabbarParam({
-		title: "",
-		topStatusBar: { bgColor: "#fff", textColor: "#333" },
-	});
-	/********* 自定义头部 - end ***********/
-	const headerHeight = computed(() => {
-		return Object.keys(systemStore.menuButtonInfo).length
-			? pxToRpx(Number(systemStore.menuButtonInfo.height)) +
-			pxToRpx(systemStore.menuButtonInfo.top) +
-			pxToRpx(8) +
-			"rpx"
-			: "auto";
+import { ref, reactive, computed, onMounted } from "vue";
+import { usernameLogin, mobileLogin, clientLogin, wxLogin, loginByPhone } from "@/app/api/auth";
+import useMemberStore from "@/stores/member";
+import useConfigStore from "@/stores/config";
+import { useLogin } from "@/hooks/useLogin";
+import { t } from "@/locale";
+import { redirect, getToken, pxToRpx, isWeixinBrowser } from "@/utils/common";
+import { onLoad } from "@dcloudio/uni-app";
+import { topTabar } from '@/utils/topTabbar'
+import useSystemStore from "@/stores/system";
+
+const systemStore = useSystemStore();
+/********* 自定义头部 - start ***********/
+const topTabarObj = topTabar();
+const param = topTabarObj.setTopTabbarParam({
+	title: "",
+	topStatusBar: { bgColor: "#fff", textColor: "#333" },
+});
+/********* 自定义头部 - end ***********/
+const headerHeight = computed(() => {
+	return Object.keys(systemStore.menuButtonInfo).length
+		? pxToRpx(Number(systemStore.menuButtonInfo.height)) +
+		pxToRpx(systemStore.menuButtonInfo.top) +
+		pxToRpx(8) +
+		"rpx"
+		: "auto";
+});
+const real_name_input = ref(true);
+const memberStore = useMemberStore();
+const configStore = useConfigStore();
+const type = ref("");
+const isAgree = ref(false);
+const isShowQuickLogin = ref(false); // 是否显示快捷登录
+const popupRef = ref();
+const isPassword = ref(true);
+// 修改:使用弹窗控制变量替代原按钮显示变量
+const showPhoneAuthPopup = ref(false);
+const openId = ref<any>("");
+
+const changePassword = () => {
+	isPassword.value = !isPassword.value;
+};
+
+const dialogClose = () => {
+	popupRef.value.close();
+};
+
+const dialogConfirm = () => {
+	isAgree.value = true;
+	popupRef.value.close();
+	handleLogin();
+};
+
+
+
+onLoad(async (option: any) => {
+	// #ifdef H5
+	uni.getStorageSync("openid") &&
+		Object.assign(formData, { wx_openid: uni.getStorageSync("openid") });
+	// #endif
+
+	// #ifdef MP-WEIXIN
+	uni.getStorageSync("openid") &&
+		Object.assign(formData, { weapp_openid: uni.getStorageSync("openid") });
+	// #endif
+
+	type.value = option.type ? option.type : "username";
+
+	// 重置状态:每次进入页面都从微信登录开始
+	showPhoneAuthPopup.value = false;
+});
+
+const formData = reactive({
+	username: "",
+	password: "",
+	code: "",
+	mobile_key: "",
+});
+
+// 第一步
+const getOpenid = () => {
+	uni.login({
+		provider: 'weixin',
+		success: function (loginRes) {
+			wxLogin({ code: loginRes.code })
+				.then((res: any) => {
+					// // 登录成功,应跳转或处理 token
+					// memberStore.setToken(res.data.access_token);
+					// useLogin().handleLoginBack();
+				})
+				.catch((res: any) => {
+					if (res.code == 601) {
+						openId.value = res.data.openId;
+						showPhoneAuthPopup.value = true;
+					}
+				});
+		},
+		fail: function (err) {
+			console.error('uni.login 失败:', err);
+			uni.showToast({ title: '微信登录失败', icon: 'none' });
+		}
 	});
-	const real_name_input = ref(true);
-	const memberStore = useMemberStore();
-	const configStore = useConfigStore();
-	const type = ref("");
-	const isAgree = ref(false);
-	const isShowQuickLogin = ref(false); // 是否显示快捷登录
-	const popupRef = ref();
-	const isPassword = ref(true);
-
-	const changePassword = () => {
-		isPassword.value = !isPassword.value;
-	};
+}
+const getphonenumber = async (res1: any) => {
+	if (res1.detail.errMsg == "getPhoneNumber:ok") {
+		loginByPhone({ code: res1.detail.code, openId: openId.value })
+			.then((res2: any) => {
+
+			}).catch((res2: any) => {
+				if (res2.code == 500) {
+					redirect({ url: '/app/pages/auth/register' })
+				}
+			});
+	}
 
-	const dialogClose = () => {
-		popupRef.value.close();
-	};
 
-	const dialogConfirm = () => {
-		isAgree.value = true;
-		popupRef.value.close();
-		handleLogin();
-	};
+	// getPhoneNumber({ code: res1.detail.code })
+	// 	.then((res2: any) => {
+	// 		if (res2.msg) {
 
-	const getOpenid = () => {
-		uni.login({
-			provider: 'weixin',
-			success: function (loginRes) {
-				wxLogin({code:loginRes.code})
-				.then((res : any) => {
-				}).catch((res : any) => {
-					if(res.code == 601){
-						redirect({ url: '/app/pages/auth/register', param: { openId: res.data.openId } })
-					}
-				});
+	// 		} else {
 
-			},
-			fail: function (err) {
-				console.error('uni.login 失败:', err);
-			}
-		});
-	}
+	// 		}
+	// 	}).catch(() => { });
+	// TODO: 后续可在此处理手机号授权逻辑
+	// 授权成功后关闭弹窗并继续登录流程
+	showPhoneAuthPopup.value = false;
+};
 
-	onLoad(async (option : any) => {
+onMounted(() => {
+	setTimeout(() => {
+		real_name_input.value = false;
+	}, 800);
+});
 
+const agreeChange = () => {
+	isAgree.value = !isAgree.value;
+};
 
-		// #ifdef H5
-		uni.getStorageSync("openid") &&
-			Object.assign(formData, { wx_openid: uni.getStorageSync("openid") });
+const setType = () => {
+	if (type.value == "username") {
+		// #ifndef MP-WEIXIN
+		type.value = "mobile";
 		// #endif
 
 		// #ifdef MP-WEIXIN
-		uni.getStorageSync("openid") &&
-			Object.assign(formData, { weapp_openid: uni.getStorageSync("openid") });
+		type.value = "wx";
 		// #endif
+	} else if ((type.value == "wx")) {
+		type.value = "mobile";
+	} else {
+		type.value = "username";
+	}
+	// 切换类型时重置状态
+	showPhoneAuthPopup.value = false;
+};
 
-		type.value = option.type ? option.type : "username";
-
-		// if (option.type) {
-		// 	if (option.type == 'mobile') {
-		// 		if (configStore.login.is_mobile) {
-		// 			type.value = option.type
-		// 			uni.getStorageSync('pid') && (Object.assign(formData, { pid: uni.getStorageSync('pid') }))
-		// 		}
-		// 	} else if (option.type == 'username' && configStore.login.is_username) {
-		// 		type.value = option.type
-		// 	}
-		// } else {
-		// 	if (configStore.login.is_username) {
-		// 		type.value = 'username'
-		// 	} else if (configStore.login.is_mobile) {
-		// 		type.value = 'mobile'
-		// 	}
-
-		// }
-
-		// // 如果只开启了账号密码登录,那么就不需要跳转到登录中间页了
-
-		// // #ifdef MP-WEIXIN
-		// if (!configStore.login.is_auth_register) {
-		// 	isShowQuickLogin.value = false;
-		// } else {
-		// 	isShowQuickLogin.value = true;
-		// }
-		// // #endif
-
-		// // #ifdef H5
-		// if (isWeixinBrowser()) {
-		// 	// 微信浏览器
-		// 	if (!configStore.login.is_auth_register) {
-		// 		isShowQuickLogin.value = false;
-		// 	} else {
-		// 		isShowQuickLogin.value = true;
-		// 	}
-		// } else {
-		// 	// 普通浏览器
-		// 	isShowQuickLogin.value = false;
-		// }
-		// // #endif
-	});
-
-	const formData = reactive({
-		username: "",
-		password: "",
-		code: "",
-		mobile_key: "",
-	});
-
-	const getphonenumber = async (res : any) => {
-		console.log(res, "?????????????");
-	};
-
-	onMounted(() => {
-		// 防止浏览器自动填充
-		setTimeout(() => {
-			real_name_input.value = false;
-		}, 800);
-	});
-
-	const agreeChange = () => {
-		isAgree.value = !isAgree.value;
-	};
-
-	const setType = () => {
-		if (type.value == "username") {
-			// #ifndef MP-WEIXIN
-			type.value = "mobile";
-			// #endif
-
-			// #ifdef MP-WEIXIN
-			type.value = "wx";
-			// #endif
-		} else if ((type.value == "wx")) {
-			type.value = "mobile";
-		} else {
-			type.value = "username";
-		}
-	};
-
-	const loading = ref(false);
+const loading = ref(false);
 
-	const rules = computed(() => {
-		return {
-			username: [
-				{
-					type: "string",
-					required: true,
-					message: "请输入账号",
-					trigger: ["blur", "change"],
-				},
-				{
-					validator(rule : any, value : any) {
-						return uni.$u.test.mobile(value);
-					},
-					message: "请输入正确的手机号",
-					trigger: ["change", "blur"],
-				},
-			],
-			password: {
+const rules = computed(() => {
+	return {
+		username: [
+			{
 				type: "string",
 				required: true,
-				message: "请输入密码",
+				message: "请输入账号",
 				trigger: ["blur", "change"],
 			},
-		};
-	});
-
-	const formRef : any = ref(null);
-
-	const handleLogin = () => {
-		formRef.value.validate().then(() => {
-			if (loading.value) return;
-			let datas : any = {}
-			if (type.value == "username") {
-				datas.username = formData.username
-				datas.password = formData.password
-			}
-			loading.value = true;
-			clientLogin(datas)
-				.then((res : any) => {
-					memberStore.setToken(res.data.access_token);
-					useLogin().handleLoginBack();
-				})
-				.catch(() => {
-					loading.value = false;
-				});
-		});
+			{
+				validator(rule: any, value: any) {
+					return uni.$u.test.mobile(value);
+				},
+				message: "请输入正确的手机号",
+				trigger: ["change", "blur"],
+			},
+		],
+		password: {
+			type: "string",
+			required: true,
+			message: "请输入密码",
+			trigger: ["blur", "change"],
+		},
 	};
+});
 
-	const toLink = () => {
-		const pages = getCurrentPages(); // 获取页面栈
-		if (pages.length > 1) {
-			const currentPage = pages[pages.length - 2].route;
-			if (currentPage == "app/pages/auth/index") {
-				// 返回上一页
-				uni.navigateBack({
-					delta: 1, // 默认值是1,表示返回的页面层数
-				});
-			} else {
-				redirect({ url: "/app/pages/auth/index", mode: "redirectTo" });
-			}
+const formRef: any = ref(null);
+
+const handleLogin = () => {
+	formRef.value.validate().then(() => {
+		if (loading.value) return;
+		let datas: any = {}
+		if (type.value == "username") {
+			datas.username = formData.username
+			datas.password = formData.password
+		}
+		loading.value = true;
+		clientLogin(datas)
+			.then((res: any) => {
+				memberStore.setToken(res.data.access_token);
+				useLogin().handleLoginBack();
+			})
+			.catch(() => {
+				loading.value = false;
+			});
+	});
+};
+
+const toLink = () => {
+	const pages = getCurrentPages();
+	if (pages.length > 1) {
+		const currentPage = pages[pages.length - 2].route;
+		if (currentPage == "app/pages/auth/index") {
+			uni.navigateBack({ delta: 1 });
 		} else {
 			redirect({ url: "/app/pages/auth/index", mode: "redirectTo" });
 		}
-	};
+	} else {
+		redirect({ url: "/app/pages/auth/index", mode: "redirectTo" });
+	}
+};
 
-	const toResetpwd = () => {
-		uni.showToast({ title: "请联系管理员重置密码", icon: "none" });
-	};
+const toResetpwd = () => {
+	uni.showToast({ title: "请联系管理员重置密码", icon: "none" });
+};
 </script>
-<style lang="scss" scoped>
-	:deep(.u-input) {
-		background-color: transparent !important;
-	}
 
-	:deep(.u-checkbox) {
-		margin: 0 !important;
-	}
+<style lang="scss" scoped>
+:deep(.u-input) {
+	background-color: transparent !important;
+}
 
-	:deep(.u-form-item) {
-		flex: 1;
+:deep(.u-checkbox) {
+	margin: 0 !important;
+}
 
-		.u-line {
-			display: none;
-		}
-	}
+:deep(.u-form-item) {
+	flex: 1;
 
-	.footer {
-		// position: absolute;
-		// position: fixed;
-		margin-top: 200rpx;
-		bottom: 0;
-		left: 0;
-		right: 0;
-		padding-bottom: calc(151rpx + constant(safe-area-inset-bottom));
-		padding-bottom: calc(151rpx + env(safe-area-inset-bottom));
+	.u-line {
+		display: none;
 	}
+}
+
+.footer {
+	margin-top: 200rpx;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	padding-bottom: calc(151rpx + constant(safe-area-inset-bottom));
+	padding-bottom: calc(151rpx + env(safe-area-inset-bottom));
+}
 </style>

+ 40 - 14
src/app/pages/auth/register.vue

@@ -101,7 +101,7 @@
 
 <script setup lang="ts">
 import { ref, reactive, computed, onMounted } from "vue";
-import { usernameRegister, mobileRegister, registerMiniCustomer } from "@/app/api/auth";
+import { usernameRegister, mobileRegister, registerMiniCustomer, getOpenId } from "@/app/api/auth";
 import useMemberStore from "@/stores/member";
 import useConfigStore from "@/stores/config";
 import { t } from "@/locale";
@@ -116,6 +116,14 @@ const formData = reactive({
 	code: "",
 	password: "",
 	confirmPassword: "",
+	openId: "",
+	RegisterSource: ""
+});
+
+onLoad(async (option: any) => {
+	// #ifdef MP-WEIXIN
+	formData.RegisterSource = '2'
+	// #endif
 });
 
 const loading = ref(false)
@@ -140,7 +148,7 @@ const configStore = useConfigStore();
 const tips = ref('');
 const uCodeRef = ref<any>(null);
 // 获取验证码
-const codeChange = (text:any) => {
+const codeChange = (text: any) => {
 	tips.value = text;
 };
 const getCode = () => {
@@ -244,18 +252,36 @@ const handleRegister = () => {
 		if (loading.value) return;
 		loading.value = true;
 
-		registerMiniCustomer(formData)
-			.then((res : any) => {
-				if (res.code == 200) {
-	                redirect({ url: '/wap/app/pages/auth/login', mode: 'redirectTo' })
-					// memberStore.setToken(res.data.token);
-				}
-				// 
-				// useLogin().handleLoginBack();
-			})
-			.catch(() => {
-				loading.value = false;
-			});
+		// #ifdef MP-WEIXIN
+		uni.login({
+			provider: 'weixin',
+			success: function (loginRes) {
+				getOpenId({ code: loginRes.code })
+					.then((res1: any) => {
+						if (res1.msg) {
+							formData.openId = res1.msg
+							registerMiniCustomer(formData).then((res: any) => {
+								if (res.code == 200) {
+									redirect({ url: '/wap/app/pages/auth/login', mode: 'redirectTo' })
+								}
+							}).catch(() => { loading.value = false; });
+						}
+					}).catch((res: any) => { });
+			},
+			fail: function (err) {
+				uni.showToast({ title: '微信注册失败', icon: 'none' });
+			}
+		});
+		// #endif
+
+		// #ifndef MP-WEIXIN
+		registerMiniCustomer(formData).then((res: any) => {
+			if (res.code == 200) {
+				redirect({ url: '/wap/app/pages/auth/login', mode: 'redirectTo' })
+			}
+		}).catch(() => { loading.value = false; });
+		// #endif
+
 	});
 };
 const toLink = () => {