HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

ios开发scroll-view横向滚动失效,scroll-view添加flex-row样式、内部子元素添加一个flex-row的父容器即可

scrollview

更正——这个问题有了最新的解决方案:
给scroll-view添加flex-row样式、内部子元素添加一个flex-row的父容器即可。经验证,ios、安卓段子展示均正常。
示例代码如下:

     <!-- 头部二级导航 -->  
        <scroll-view  
          class="header flex_wrap flex_row flex_left flex_middle"  
          scroll-x="true"  
          :scroll-into-view="scroll_into_view0"  
          scroll-with-animation="true"  
          show-scrollbar="false"  
        >  
          <view class="flex_row">  
            <text  
              :id="'nav' + index"  
              class="btn f12 border_radius30"  
              :class="activeIndex2 == index ? 'active bg_grey' : 'bg_white'"  
              @click="headerNavClick(index)"  
              v-for="(item, index) in topNavList"  
              :key="index"  
            >  
              {{ item.sub_rack_name }}  
            </text>  
          </view>  
        </scroll-view>

如上, flex_row,flex_left,flex_middle均为封装好的样式,即横向、水平靠左、垂直居中布局。
————————————————————————————————————————————
老bug了,本来横向滚动只要子元素宽度大于scroll-view固定宽度就可以滚动的,但是IOS App开发中子元素高度必须要大于scroll-view宽度才能滚动,应该是拿错参数了。
官方一直没解决这个问题,都是在自己摸索。
做了不少尝试,总算找到不影响页面布局展示的方法了。
解决方案:
用一个透明的宽度为1像素的占位View把内部容器的高度撑大,高度为子元素的总宽度,这样无论有多少个子元素,都能做到自适应滚动了。

具体操作如下:

  1. 在ScrollView里边给子元素列表添加一个父容器btnBox(如果已有就不用了)。
  2. 在获取到子元素数据之后通过dom.getComponentRect来获取btnBox的总宽度(图中蓝框所示)。
  3. 在btnBox前边或者后边添加一个同级的宽度为1像素的占位view,高度设置为btnBox的宽度(颜色设置为透明,避免部分机型可能会有的默认颜色)。

问题完美解决,横向可以滚动了,也不影响界面展示。
示例代码如下:

       <scroll-view  
          class="header flex_wrap flex_center bg_grey flex_top on"  
          scroll-x="true"  
          :scroll-into-view="scroll_into_view0"  
          scroll-with-animation="true"  
          show-scrollbar="false"  
        >  
         <view id="navStart" class="bg_red" style="width:1rpx;" :style="{ height: subNavWidth * 2 + 'rpx' }"></view>  
          <view ref="subNav" class="flex_row">  
            <text  
              :id="'nav' + index"  
              class="btn f12 border_radius30"  
              :class="activeIndex2 == index ? 'active bg_grey' : 'bg_white'"  
              @click="headerNavClick(index)"  
              v-for="(item, index) in topNavList"  
              :key="index"  
            >  
              {{ item.sub_rack_name }}  
            </text>  
          </view>  
        </scroll-view>
      var refDom = this.$refs.subNav;  
      var result = dom.getComponentRect(refDom, option => {  
        this.subNavWidth = option.size.width;  
      });
继续阅读 »

更正——这个问题有了最新的解决方案:
给scroll-view添加flex-row样式、内部子元素添加一个flex-row的父容器即可。经验证,ios、安卓段子展示均正常。
示例代码如下:

     <!-- 头部二级导航 -->  
        <scroll-view  
          class="header flex_wrap flex_row flex_left flex_middle"  
          scroll-x="true"  
          :scroll-into-view="scroll_into_view0"  
          scroll-with-animation="true"  
          show-scrollbar="false"  
        >  
          <view class="flex_row">  
            <text  
              :id="'nav' + index"  
              class="btn f12 border_radius30"  
              :class="activeIndex2 == index ? 'active bg_grey' : 'bg_white'"  
              @click="headerNavClick(index)"  
              v-for="(item, index) in topNavList"  
              :key="index"  
            >  
              {{ item.sub_rack_name }}  
            </text>  
          </view>  
        </scroll-view>

如上, flex_row,flex_left,flex_middle均为封装好的样式,即横向、水平靠左、垂直居中布局。
————————————————————————————————————————————
老bug了,本来横向滚动只要子元素宽度大于scroll-view固定宽度就可以滚动的,但是IOS App开发中子元素高度必须要大于scroll-view宽度才能滚动,应该是拿错参数了。
官方一直没解决这个问题,都是在自己摸索。
做了不少尝试,总算找到不影响页面布局展示的方法了。
解决方案:
用一个透明的宽度为1像素的占位View把内部容器的高度撑大,高度为子元素的总宽度,这样无论有多少个子元素,都能做到自适应滚动了。

具体操作如下:

  1. 在ScrollView里边给子元素列表添加一个父容器btnBox(如果已有就不用了)。
  2. 在获取到子元素数据之后通过dom.getComponentRect来获取btnBox的总宽度(图中蓝框所示)。
  3. 在btnBox前边或者后边添加一个同级的宽度为1像素的占位view,高度设置为btnBox的宽度(颜色设置为透明,避免部分机型可能会有的默认颜色)。

问题完美解决,横向可以滚动了,也不影响界面展示。
示例代码如下:

       <scroll-view  
          class="header flex_wrap flex_center bg_grey flex_top on"  
          scroll-x="true"  
          :scroll-into-view="scroll_into_view0"  
          scroll-with-animation="true"  
          show-scrollbar="false"  
        >  
         <view id="navStart" class="bg_red" style="width:1rpx;" :style="{ height: subNavWidth * 2 + 'rpx' }"></view>  
          <view ref="subNav" class="flex_row">  
            <text  
              :id="'nav' + index"  
              class="btn f12 border_radius30"  
              :class="activeIndex2 == index ? 'active bg_grey' : 'bg_white'"  
              @click="headerNavClick(index)"  
              v-for="(item, index) in topNavList"  
              :key="index"  
            >  
              {{ item.sub_rack_name }}  
            </text>  
          </view>  
        </scroll-view>
      var refDom = this.$refs.subNav;  
      var result = dom.getComponentRect(refDom, option => {  
        this.subNavWidth = option.size.width;  
      });
收起阅读 »

关于uni-forms组件的bug【提交的字段['*']在数据库中并不存在】问题的复现和临时解决办法

上午搞的好好的,还添加了数据,下午再测就出现这个问题了,重新编译也不行,真是大白天见*了。。。
百度了一番,发现该问题最早出现是在2021-08-13这样就过分了啊,快三年了还没解决呢?
提示提交的字段["dirty_data"]在数据库中并不存在
然后又找了几个类似的问题贴,看到官方的回复说是一直没办法复现,所以没法解决,好吧,你们真牛
经过几个小时的不断百度和测试,终于发现规律了,复现代码如下

<template>  
    <view class="uni-container">  
        <uni-forms ref="form" v-model="formData" :rules="rules">  
            <uni-forms-item name="goods_name" label="商品名称" label-width="100px" label-align="right" required>  
                <uni-easyinput v-model="formData.goods_name" :clearable="false" placeholder="请输入商品名称" />  
            </uni-forms-item>  

            <uni-forms-item name="is_more_specs" label="是否多规格" label-width="100px" label-align="right">  
                <uni-data-checkbox v-model="formData.is_more_specs" mode="button" :localdata="[{text: '是', value: 1}, {text: '否', value: 0}]"></uni-data-checkbox>  
            </uni-forms-item>  

            <template v-if="formData.is_more_specs">  
                <uni-forms-item name="goods_sku" label="商品sku" label-width="100px" label-align="right">  
                     <uni-easyinput v-model="formData.goods_sku" :clearable="false" placeholder="请输入商品sku" />  
                </uni-forms-item>  
            </template>  

            <template v-else>  
                <uni-forms-item name="remain_stock" label="商品库存" label-width="100px" label-align="right" required>  
                    <uni-easyinput v-model="formData.remain_stock" :clearable="false" placeholder="商品库存" />  
                </uni-forms-item>  
            </template>  

            <view class="uni-button-group">  
                <button style="width: 100px;" type="primary" class="uni-button" @click="submitForm">{{$t('common.button.submit')}}</button>  
                <navigator open-type="navigateBack" style="margin-left: 15px;">  
                    <button style="width: 100px;" class="uni-button">{{$t('common.button.back')}}</button>  
                </navigator>  
            </view>  
        </uni-forms>  
    </view>  
</template>  

<script>  
export default {  
    data() {  
        return {  
            formDataId: '',  
            //表单数据  
            formData: {  
                goods_id: '',  
                goods_name: '',  
                market_price: '',  
                goods_sku: '',  
                is_more_specs: 0  
            },  
            //数据验证  
            rules: {  
                goods_name: {  
                    rules: [  
                        {  
                            "required": true,  
                            "errorMessage": '请选择活动商品'  
                        }  
                    ]  
                },  
                remain_stock: {  
                    rules: [  
                        {  
                            "required": true,  
                            "errorMessage": '请输入商品库存'  
                        }, {  
                            format: 'number',  
                            errorMessage: '商品库存必须为数字'  
                        }  
                    ]  
                }  
            }  
        }  
    },  
    methods: {  
        submitForm() {  
            this.$refs.form.validate((err, res) => {  
                if (err) return false;  

                //入库  
            });  
        }  
    }  
}  
</script>  

<style lang="scss">  

</style>


问题的原因就是某个表单项设置了验证规则,但是又因为业务需要增加v-if过滤掉了该表单项,这时候bug的出现了,但实际上这个提示才是最坑的,因为根本没有到去验证数据表那一步啊。

解决办法
如果把v-else节点的template更换成view就没问题了,至于原因我不知道,没心思去分析源码。
希望官方有空把这个烂尾处理了吧。。。

继续阅读 »

上午搞的好好的,还添加了数据,下午再测就出现这个问题了,重新编译也不行,真是大白天见*了。。。
百度了一番,发现该问题最早出现是在2021-08-13这样就过分了啊,快三年了还没解决呢?
提示提交的字段["dirty_data"]在数据库中并不存在
然后又找了几个类似的问题贴,看到官方的回复说是一直没办法复现,所以没法解决,好吧,你们真牛
经过几个小时的不断百度和测试,终于发现规律了,复现代码如下

<template>  
    <view class="uni-container">  
        <uni-forms ref="form" v-model="formData" :rules="rules">  
            <uni-forms-item name="goods_name" label="商品名称" label-width="100px" label-align="right" required>  
                <uni-easyinput v-model="formData.goods_name" :clearable="false" placeholder="请输入商品名称" />  
            </uni-forms-item>  

            <uni-forms-item name="is_more_specs" label="是否多规格" label-width="100px" label-align="right">  
                <uni-data-checkbox v-model="formData.is_more_specs" mode="button" :localdata="[{text: '是', value: 1}, {text: '否', value: 0}]"></uni-data-checkbox>  
            </uni-forms-item>  

            <template v-if="formData.is_more_specs">  
                <uni-forms-item name="goods_sku" label="商品sku" label-width="100px" label-align="right">  
                     <uni-easyinput v-model="formData.goods_sku" :clearable="false" placeholder="请输入商品sku" />  
                </uni-forms-item>  
            </template>  

            <template v-else>  
                <uni-forms-item name="remain_stock" label="商品库存" label-width="100px" label-align="right" required>  
                    <uni-easyinput v-model="formData.remain_stock" :clearable="false" placeholder="商品库存" />  
                </uni-forms-item>  
            </template>  

            <view class="uni-button-group">  
                <button style="width: 100px;" type="primary" class="uni-button" @click="submitForm">{{$t('common.button.submit')}}</button>  
                <navigator open-type="navigateBack" style="margin-left: 15px;">  
                    <button style="width: 100px;" class="uni-button">{{$t('common.button.back')}}</button>  
                </navigator>  
            </view>  
        </uni-forms>  
    </view>  
</template>  

<script>  
export default {  
    data() {  
        return {  
            formDataId: '',  
            //表单数据  
            formData: {  
                goods_id: '',  
                goods_name: '',  
                market_price: '',  
                goods_sku: '',  
                is_more_specs: 0  
            },  
            //数据验证  
            rules: {  
                goods_name: {  
                    rules: [  
                        {  
                            "required": true,  
                            "errorMessage": '请选择活动商品'  
                        }  
                    ]  
                },  
                remain_stock: {  
                    rules: [  
                        {  
                            "required": true,  
                            "errorMessage": '请输入商品库存'  
                        }, {  
                            format: 'number',  
                            errorMessage: '商品库存必须为数字'  
                        }  
                    ]  
                }  
            }  
        }  
    },  
    methods: {  
        submitForm() {  
            this.$refs.form.validate((err, res) => {  
                if (err) return false;  

                //入库  
            });  
        }  
    }  
}  
</script>  

<style lang="scss">  

</style>


问题的原因就是某个表单项设置了验证规则,但是又因为业务需要增加v-if过滤掉了该表单项,这时候bug的出现了,但实际上这个提示才是最坑的,因为根本没有到去验证数据表那一步啊。

解决办法
如果把v-else节点的template更换成view就没问题了,至于原因我不知道,没心思去分析源码。
希望官方有空把这个烂尾处理了吧。。。

收起阅读 »

好看的APP下载分发页分享,仿App Store界面,代码简单,易修改,下载即用!

源码分享

好看的APP下载页分享,仿App Store,代码简单,易修改,下载即用!

☞插件市场下载地址

页面展示

☞插件市场下载地址

继续阅读 »

好看的APP下载页分享,仿App Store,代码简单,易修改,下载即用!

☞插件市场下载地址

页面展示

☞插件市场下载地址

收起阅读 »

playbackRate倍速播放真机无效啊?请问有解决的人吗?

bug提交

playbackRate倍速播放真机无效啊?请问有解决的人吗?

playbackRate倍速播放真机无效啊?请问有解决的人吗?

用惯了uniapp我又用wap2app接到一个外包项目,现在已成功上线,分享一下踩过的坑!

外包 uniapp wap2app

最近合作了一个客户,需求是把现成的这种网站包装成App,在各大应用商店也能下载,做用户留存。

需求不复杂,现在已经完工了,客户指明要使用wap2app这种技术,本来之前一直是用uniapp开发app的,一下子调整到wap2app还不适应。

别无他法,唯有熟读文档,文档东西不多,wap2app包装起来非常简单,但是有一些东西文档并没有,分享一下往后来人避之!

1. 增加底部原生选项卡,屏蔽h5选项卡,底部选项卡怎么设置都不对?
解答:底部选项卡每个选项都要单独匹配matchUrls

2. android和ios顶部状态栏时有时无?
解答:manifest.json文件中对statusbar单独设置

"plus" : {  
        "statusbar" : {  
            "style" : "dark",  
            "background" : "#F7F7F7",  
            "immersed" : "supportedDevice"  
        }  
}

3. 隐藏H5右侧滚动条
解答:页面js中设置代码

function init() {  
    const ws = plus.webview.currentWebview()  
    ws.setStyle({scrollIndicator:'none'})  
}  

init()

4. 没办法分享朋友圈?
解答:不要用plus中sendWithSystem的api,用这里的方案:原生分享 - wap2app教程

5. ios底部选项卡和手机dock重叠?
解答:cliend_index.html中调整高度

.tab {  
  height: 66px;  
}  
.tab-item {    
  color: #444444;//选项卡文字默认   
  height: 66px;  
  padding-bottom: 16px;  
} 

6. 头部导航栏需要换成图片logo?
解答:借助plus功能,绘制图片,https://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View

function init() {  
  document.querySelectorAll('.kfwrapbox')[1].style.display = 'none'  
  const ws = plus.webview.currentWebview()  
  const nView = ws.getTitleNView()  
  nView.drawBitmap('/icons/logo.png',{},{left: '35%', top: '10px', width: '98px', height: '30px'})  
  ws.setStyle({scrollIndicator:'none'})  
}  

init()

7. ios打包后提示Prompt:Unable to connect to server. please check network settings
解答:这个是最难搞的问题,ios未授权网络访问前app是没网的,方法是监听网络状态变化,授权网络访问后重启应用

8. ios打包后进入app白屏?
解答:使用IOS UIWebview

事后处于好奇我又分析了一下这个项目的商业模式发现还挺好的,看前台数据基本上已经实现盈利了。它的市场定位是县城级,我们都知道做比较大的生活平台是谁,58同城嘛!但是县城这种他覆盖有限。

目标用户呢就是县城内的居民、商户、企业。

平台能提供需信息发布,像劳动力、服务、商品等、还有信息匹配与推荐、交易撮合与支付,根据县城的特点,平台可以提供一些本地化的服务,如农产品直销、家政服务预约、二手物品交易等。

盈利模式主要是广告收入:首页位置展示广告、发布消息置顶向广告主收取费用。

因为第一次合作,客户担心风险并且需要开发票,走的是https://www.duiyou360.com/service?keyword=%E5%89%8D%E7%AB%AFJason&from=Search,现在已经结清了项目款。

真心佩服这些有头脑的老板,脑子是真好使!

最后说一句,uniapp、wap2app真的是良心出品,快速做项目还得看它。

继续阅读 »

最近合作了一个客户,需求是把现成的这种网站包装成App,在各大应用商店也能下载,做用户留存。

需求不复杂,现在已经完工了,客户指明要使用wap2app这种技术,本来之前一直是用uniapp开发app的,一下子调整到wap2app还不适应。

别无他法,唯有熟读文档,文档东西不多,wap2app包装起来非常简单,但是有一些东西文档并没有,分享一下往后来人避之!

1. 增加底部原生选项卡,屏蔽h5选项卡,底部选项卡怎么设置都不对?
解答:底部选项卡每个选项都要单独匹配matchUrls

2. android和ios顶部状态栏时有时无?
解答:manifest.json文件中对statusbar单独设置

"plus" : {  
        "statusbar" : {  
            "style" : "dark",  
            "background" : "#F7F7F7",  
            "immersed" : "supportedDevice"  
        }  
}

3. 隐藏H5右侧滚动条
解答:页面js中设置代码

function init() {  
    const ws = plus.webview.currentWebview()  
    ws.setStyle({scrollIndicator:'none'})  
}  

init()

4. 没办法分享朋友圈?
解答:不要用plus中sendWithSystem的api,用这里的方案:原生分享 - wap2app教程

5. ios底部选项卡和手机dock重叠?
解答:cliend_index.html中调整高度

.tab {  
  height: 66px;  
}  
.tab-item {    
  color: #444444;//选项卡文字默认   
  height: 66px;  
  padding-bottom: 16px;  
} 

6. 头部导航栏需要换成图片logo?
解答:借助plus功能,绘制图片,https://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View

function init() {  
  document.querySelectorAll('.kfwrapbox')[1].style.display = 'none'  
  const ws = plus.webview.currentWebview()  
  const nView = ws.getTitleNView()  
  nView.drawBitmap('/icons/logo.png',{},{left: '35%', top: '10px', width: '98px', height: '30px'})  
  ws.setStyle({scrollIndicator:'none'})  
}  

init()

7. ios打包后提示Prompt:Unable to connect to server. please check network settings
解答:这个是最难搞的问题,ios未授权网络访问前app是没网的,方法是监听网络状态变化,授权网络访问后重启应用

8. ios打包后进入app白屏?
解答:使用IOS UIWebview

事后处于好奇我又分析了一下这个项目的商业模式发现还挺好的,看前台数据基本上已经实现盈利了。它的市场定位是县城级,我们都知道做比较大的生活平台是谁,58同城嘛!但是县城这种他覆盖有限。

目标用户呢就是县城内的居民、商户、企业。

平台能提供需信息发布,像劳动力、服务、商品等、还有信息匹配与推荐、交易撮合与支付,根据县城的特点,平台可以提供一些本地化的服务,如农产品直销、家政服务预约、二手物品交易等。

盈利模式主要是广告收入:首页位置展示广告、发布消息置顶向广告主收取费用。

因为第一次合作,客户担心风险并且需要开发票,走的是https://www.duiyou360.com/service?keyword=%E5%89%8D%E7%AB%AFJason&from=Search,现在已经结清了项目款。

真心佩服这些有头脑的老板,脑子是真好使!

最后说一句,uniapp、wap2app真的是良心出品,快速做项目还得看它。

收起阅读 »

uniCloud使用nodemailer发送邮件|SMTP

uni-cloud

环境: 支付宝小程序云.
云对象: emailSend_Time.

目标: 定时调用nodemailer 邮件发送.

实测以下代码可以发送成功.

package.json

{  
    "name": "emailSend_Time",  
    "dependencies": {  
        "nodemailer": "^6.9.12"  
    },  
    "extensions": {  
        "uni-cloud-jql": {}  
    },  
    "cloudfunction-config": {  
        "memorySize": 256,  
        "triggers": [{  
            "name": "myEmailTrigger",  
            "type": "timer",  
            "config": "0 0 * * * * *"  
        }]  
    }  
}

emailSend_Time

const nodemailer = require('nodemailer');  

const sendEmail = async ({  
    to,  
    subject,  
    html  
}) => {  
    const transporter = nodemailer.createTransport({  
        host: 'smtp.163.com',  
        port: 465,   
        secure: true,  
        auth: {  
            user: '邮箱',  
            pass: '授权码'  
        },  
    });  

    await transporter.sendMail({  
        from: {  
            name: 'XXXX',  
            address: '邮箱'  
        },  
        to,  
        subject,  
        html  
    });  
}  

module.exports = {  
    _timing: async function() {  
        await sendEmail({  
            to: '发送人邮箱',  
            subject: '幸福通知',  
            html: `<h1 style="text-align: center;"><span style="font-family: helvetica, arial, sans-serif; font-size: 36pt; color: #e03e2d;">测试</span></h1>`  
        })  
        return " "  
    },  
    sendEmail: sendEmail  
}
继续阅读 »

环境: 支付宝小程序云.
云对象: emailSend_Time.

目标: 定时调用nodemailer 邮件发送.

实测以下代码可以发送成功.

package.json

{  
    "name": "emailSend_Time",  
    "dependencies": {  
        "nodemailer": "^6.9.12"  
    },  
    "extensions": {  
        "uni-cloud-jql": {}  
    },  
    "cloudfunction-config": {  
        "memorySize": 256,  
        "triggers": [{  
            "name": "myEmailTrigger",  
            "type": "timer",  
            "config": "0 0 * * * * *"  
        }]  
    }  
}

emailSend_Time

const nodemailer = require('nodemailer');  

const sendEmail = async ({  
    to,  
    subject,  
    html  
}) => {  
    const transporter = nodemailer.createTransport({  
        host: 'smtp.163.com',  
        port: 465,   
        secure: true,  
        auth: {  
            user: '邮箱',  
            pass: '授权码'  
        },  
    });  

    await transporter.sendMail({  
        from: {  
            name: 'XXXX',  
            address: '邮箱'  
        },  
        to,  
        subject,  
        html  
    });  
}  

module.exports = {  
    _timing: async function() {  
        await sendEmail({  
            to: '发送人邮箱',  
            subject: '幸福通知',  
            html: `<h1 style="text-align: center;"><span style="font-family: helvetica, arial, sans-serif; font-size: 36pt; color: #e03e2d;">测试</span></h1>`  
        })  
        return " "  
    },  
    sendEmail: sendEmail  
}
收起阅读 »

UNIAPP 开发,java、php、nodejs 均可,欢迎来撩

您好,我们是平台的金牌服务商、国家高新技术企业,公司主营软件定制开发; 我司有sass系统的开发经验以及上线经验,系统包含crm、erp、oa等功能,公司在平台得多个成功案例;合作客户包含开源众包官方、北京航天智控、北京中钢等央企;希望可以与您详细沟通下,我司联系人:徐经理; 联系方式:17775057492 (可加微信沟通同)

继续阅读 »

您好,我们是平台的金牌服务商、国家高新技术企业,公司主营软件定制开发; 我司有sass系统的开发经验以及上线经验,系统包含crm、erp、oa等功能,公司在平台得多个成功案例;合作客户包含开源众包官方、北京航天智控、北京中钢等央企;希望可以与您详细沟通下,我司联系人:徐经理; 联系方式:17775057492 (可加微信沟通同)

收起阅读 »

招ios上架开发人员

ios内容无限,开发语言无限,只要能上架即可,量大,3-5k一个app

具体内容可加QQ:543610866

ios内容无限,开发语言无限,只要能上架即可,量大,3-5k一个app

具体内容可加QQ:543610866

uniapp 前端网页托管 域名 及 SSL 配置

我之前配置的域名只能通过 www.myenglishispoor.com 才能访问,不加 www. 直接 myenglishispoor.com 打不开网页。

现在我是这样配置的:在 uniapp 网站托管同一个域名配置一个带www.的一个不带www.的。 (我不懂原理,只是把我的设置步骤分享出来。)

然后域名解析时把两个都写上,再加一个 * 的配置到不带www.的上面。

现在两带不带 www. 都能访问了。

我严重怀疑在 uniapp 网站托管那里只要配置一个不带 www. 的域名就行了。但是


他这个示例配置的是带 www. 的。

对于我这样的边学边开发的人来说,uniapp 的文档就像是看一个对中文半懂不懂的外国人写的一样,每个字都认识,但就是不知道说的啥。
我不知道你们有没有竞争压力?应该是没有。你们要是能找个正常人把文档用正常人的思维重写一遍的话,我相信用户会增加不少。

对了,腾讯云配置 uniapp 网站托管的 ssl 证书用 (其他)选项中的那个。

为什么域名不加 www. 无法访问
我的域名不加 www. 打不开
uniapp 域名解析
uniapp 前端网页托管自定义域名

继续阅读 »

我之前配置的域名只能通过 www.myenglishispoor.com 才能访问,不加 www. 直接 myenglishispoor.com 打不开网页。

现在我是这样配置的:在 uniapp 网站托管同一个域名配置一个带www.的一个不带www.的。 (我不懂原理,只是把我的设置步骤分享出来。)

然后域名解析时把两个都写上,再加一个 * 的配置到不带www.的上面。

现在两带不带 www. 都能访问了。

我严重怀疑在 uniapp 网站托管那里只要配置一个不带 www. 的域名就行了。但是


他这个示例配置的是带 www. 的。

对于我这样的边学边开发的人来说,uniapp 的文档就像是看一个对中文半懂不懂的外国人写的一样,每个字都认识,但就是不知道说的啥。
我不知道你们有没有竞争压力?应该是没有。你们要是能找个正常人把文档用正常人的思维重写一遍的话,我相信用户会增加不少。

对了,腾讯云配置 uniapp 网站托管的 ssl 证书用 (其他)选项中的那个。

为什么域名不加 www. 无法访问
我的域名不加 www. 打不开
uniapp 域名解析
uniapp 前端网页托管自定义域名

收起阅读 »

引入腾讯即时通讯IM,导致vendor.js文件变大,无法发布小程序

1,分包处理
除了tabBar用的页面首页外,其他都放到分包里面,具体看分包的方法,components里的公共组件,分包用到的也放到分包里面。

2,引入腾讯即时通讯IM 会 增大主包大小(800k左右)官方客服说的。所以尽量留出这个大小。
引入腾讯即时通讯IM 要用 《无UI集成方案》这个会小一些,千万不要用《含UI集成方案》;自己来写后面的通话之类的逻辑。这些逻辑放到分包里面,就不用占用主包大小

安装 无UI集成方案 按照官方的文档就可以
我这边只是用到了 1v1 的聊天功能,如下

(1),在您的项目中使用 npm 安装相应的 IM SDK 依赖。

npm install @tencentcloud/chat

相对于 集成方案里的 npm i @tencentcloud/chat-uikit-uniapp unplugin-vue2-script-setup 小很多
官方提供的 后两个依赖,我聊天 所有不用,就没有安装

(2),引入模块,在main.js 文件中加入,最后两句非常重要,咱们在后面的页面要用到

import TencentCloudChat from '@tencentcloud/chat';  
let options = {  
  SDKAppID: 0 // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID  
};  
let chat = TencentCloudChat.create(options); // SDK 实例通常用 chat 表示  
chat.setLogLevel(0);  
Vue.prototype.chat = chat  
Vue.prototype.TencentCloudChat = TencentCloudChat

(3),登录==可以在进入对话之前就完成登录的操作,这样就可以一直保持登录的状态,防止初始化未完成就进入对话无法完成操作

<template>  
    <view>  
        <view id="j_page">  
            <view v-for="(item, index) in messageList" :key="index">  
                <!-- 第一条必显示时间 返回的是时间戳自己转化一下-->  
                <view v-if="index == 0">{{ item.time }}</view>  
                <view v-else-if="item.time - messageList[index - 1].time >= 600">{{ item.time }}</view>  
                <!--聊天的左右自行添加样式-->  
                <view><image class="msg-main-r-message-avatar-container-image" :src="item.avatar"></image>{{item.payload.text }}</view>  
            </view>  
        </view>  
        <view>  
            <input placeholder="请输入内容" v-model="msg" @confirm="sendMsg"></input>  
            <view @click="sendMsg">发送</view>  
        </view>  
    </view>  
</template>  

<script>  
export default {  
    components: { },  
    data() {  
        return {  
            userID: '你的 userID',  
            C2Cid: '对方的 C2Cid',  
            userSig: '',  
            messageList: [],  
            msg: '',  
        }  
    },  
    onLoad(option) {  
    },  
    onShow() {  
        this.loginBox()  
    },  
    onReachBottom() {  
    },  
    onReady() {  
    },  
    mounted() {  
        let onMessageReceived = function (event) {  
            this.messageList.push(event.data[0])  
            this.scrollToBottom()  
        };  
        this.chat.on(this.TencentCloudChat.EVENT.MESSAGE_RECEIVED, onMessageReceived, this);  

        let onSdkReady = function (event) {  
            console.log('SDK初始化完成');  
            this.sendFriend(this.C2Cid)  
            this.getMessageListBox(this.C2Cid)  
            this.setMessageReadBox(this.C2Cid)  
        };  
        this.chat.on(this.TencentCloudChat.EVENT.SDK_READY, onSdkReady, this);  
    },  
    methods: {  
        // 登录===================  
        // userSig 后台获取,或者通过 debug文件夹中的 GenerateTestUserSig.js 获取  
        loginBox() {  
            var _this = this  
            let promise = this.chat.login({ userID: this.userID, userSig: this.userSig });  
            promise.then(function (imResponse) {  
                console.log('登录成功2====>', imResponse.data); // 登录成功  
                if (imResponse.data.repeatLogin === true) {  
                    // 标识账号已登录,本次登录操作为重复登录。  
                    console.log(imResponse.data.errorInfo);  
                    _this.sendFriend(_this.C2Cid)  
                    _this.getMessageListBox(_this.C2Cid)  
                    _this.setMessageReadBox(_this.C2Cid)  
                }  
            }).catch(function (imError) {  
                console.warn('login error:', imError); // 登录失败的相关信息  
            });  
        },  
        // 获取会话资料===================  
        sendFriend(id) {  
            var _this = this  
            let promise = this.chat.getConversationProfile(id);  
            promise.then(function (imResponse) {  
                _this.conversation = imResponse.data.conversation  
            }).catch(function (imError) {  
                console.warn('getConversationProfile error:', imError); // 获取会话资料失败的相关信息  
            });  
        },  
        // 获取历史消息===================  
        getMessageListBox(C2CuserID) {  
            var _this = this  
            // 打开某个会话时,第一次拉取消息列表  
            let promise = this.chat.getMessageList({ conversationID: C2CuserID });  
            promise.then(function (imResponse) {  
                const messageList = imResponse.data.messageList; // 消息列表。  
                _this.messageList = messageList  
                _this.scrollToBottom()  
            });  
        },  
        // 到底部  
        scrollToBottom() {  
            wx.createSelectorQuery().select('#j_page').boundingClientRect(function (rect) {  
                // 使页面滚动到底部  
                wx.pageScrollTo({  
                    scrollTop: rect.bottom  
                })  
            }).exec()  
        },  
        // 发送消息===================  
        sendMsg() {  
            var _this = this  
            // 发送文本消息,Web 端与小程序端相同  
            // 1. 创建消息实例,接口返回的实例可以上屏  
            let message = this.chat.createTextMessage({  
                to: this.conversation.userProfile.userID,  
                conversationType: this.TencentCloudChat.TYPES.CONV_C2C,  
                payload: {  
                    text: this.msg  
                },  
            });  
            // 2. 发送消息  
            let promise = this.chat.sendMessage(message);  
            promise.then(function (imResponse) {  
                // 发送成功  
                _this.messageList.push(imResponse.data.message)  
                _this.scrollToBottom()  
                _this.msg = ''  
            }).catch(function (imError) {  
                // 发送失败  
                console.warn('sendMessage error:', imError);  
            });  
        },  
        // 将某会话下所有未读消息已读上报===================  
        setMessageReadBox(e) {  
            // 将某会话下所有未读消息已读上报  
            let promise = this.chat.setMessageRead({ conversationID: e });  
            promise.then(function (imResponse) {  
                // 已读上报成功,指定 ID 的会话的 unreadCount 属性值被置为0  
            }).catch(function (imError) {  
                // 已读上报失败  
                console.warn('setMessageRead error:', imError);  
            });  
        }  

    }  
}  
</script>  
<style lang="scss" scoped></style>

最后看一下依赖分析的大小

继续阅读 »

1,分包处理
除了tabBar用的页面首页外,其他都放到分包里面,具体看分包的方法,components里的公共组件,分包用到的也放到分包里面。

2,引入腾讯即时通讯IM 会 增大主包大小(800k左右)官方客服说的。所以尽量留出这个大小。
引入腾讯即时通讯IM 要用 《无UI集成方案》这个会小一些,千万不要用《含UI集成方案》;自己来写后面的通话之类的逻辑。这些逻辑放到分包里面,就不用占用主包大小

安装 无UI集成方案 按照官方的文档就可以
我这边只是用到了 1v1 的聊天功能,如下

(1),在您的项目中使用 npm 安装相应的 IM SDK 依赖。

npm install @tencentcloud/chat

相对于 集成方案里的 npm i @tencentcloud/chat-uikit-uniapp unplugin-vue2-script-setup 小很多
官方提供的 后两个依赖,我聊天 所有不用,就没有安装

(2),引入模块,在main.js 文件中加入,最后两句非常重要,咱们在后面的页面要用到

import TencentCloudChat from '@tencentcloud/chat';  
let options = {  
  SDKAppID: 0 // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID  
};  
let chat = TencentCloudChat.create(options); // SDK 实例通常用 chat 表示  
chat.setLogLevel(0);  
Vue.prototype.chat = chat  
Vue.prototype.TencentCloudChat = TencentCloudChat

(3),登录==可以在进入对话之前就完成登录的操作,这样就可以一直保持登录的状态,防止初始化未完成就进入对话无法完成操作

<template>  
    <view>  
        <view id="j_page">  
            <view v-for="(item, index) in messageList" :key="index">  
                <!-- 第一条必显示时间 返回的是时间戳自己转化一下-->  
                <view v-if="index == 0">{{ item.time }}</view>  
                <view v-else-if="item.time - messageList[index - 1].time >= 600">{{ item.time }}</view>  
                <!--聊天的左右自行添加样式-->  
                <view><image class="msg-main-r-message-avatar-container-image" :src="item.avatar"></image>{{item.payload.text }}</view>  
            </view>  
        </view>  
        <view>  
            <input placeholder="请输入内容" v-model="msg" @confirm="sendMsg"></input>  
            <view @click="sendMsg">发送</view>  
        </view>  
    </view>  
</template>  

<script>  
export default {  
    components: { },  
    data() {  
        return {  
            userID: '你的 userID',  
            C2Cid: '对方的 C2Cid',  
            userSig: '',  
            messageList: [],  
            msg: '',  
        }  
    },  
    onLoad(option) {  
    },  
    onShow() {  
        this.loginBox()  
    },  
    onReachBottom() {  
    },  
    onReady() {  
    },  
    mounted() {  
        let onMessageReceived = function (event) {  
            this.messageList.push(event.data[0])  
            this.scrollToBottom()  
        };  
        this.chat.on(this.TencentCloudChat.EVENT.MESSAGE_RECEIVED, onMessageReceived, this);  

        let onSdkReady = function (event) {  
            console.log('SDK初始化完成');  
            this.sendFriend(this.C2Cid)  
            this.getMessageListBox(this.C2Cid)  
            this.setMessageReadBox(this.C2Cid)  
        };  
        this.chat.on(this.TencentCloudChat.EVENT.SDK_READY, onSdkReady, this);  
    },  
    methods: {  
        // 登录===================  
        // userSig 后台获取,或者通过 debug文件夹中的 GenerateTestUserSig.js 获取  
        loginBox() {  
            var _this = this  
            let promise = this.chat.login({ userID: this.userID, userSig: this.userSig });  
            promise.then(function (imResponse) {  
                console.log('登录成功2====>', imResponse.data); // 登录成功  
                if (imResponse.data.repeatLogin === true) {  
                    // 标识账号已登录,本次登录操作为重复登录。  
                    console.log(imResponse.data.errorInfo);  
                    _this.sendFriend(_this.C2Cid)  
                    _this.getMessageListBox(_this.C2Cid)  
                    _this.setMessageReadBox(_this.C2Cid)  
                }  
            }).catch(function (imError) {  
                console.warn('login error:', imError); // 登录失败的相关信息  
            });  
        },  
        // 获取会话资料===================  
        sendFriend(id) {  
            var _this = this  
            let promise = this.chat.getConversationProfile(id);  
            promise.then(function (imResponse) {  
                _this.conversation = imResponse.data.conversation  
            }).catch(function (imError) {  
                console.warn('getConversationProfile error:', imError); // 获取会话资料失败的相关信息  
            });  
        },  
        // 获取历史消息===================  
        getMessageListBox(C2CuserID) {  
            var _this = this  
            // 打开某个会话时,第一次拉取消息列表  
            let promise = this.chat.getMessageList({ conversationID: C2CuserID });  
            promise.then(function (imResponse) {  
                const messageList = imResponse.data.messageList; // 消息列表。  
                _this.messageList = messageList  
                _this.scrollToBottom()  
            });  
        },  
        // 到底部  
        scrollToBottom() {  
            wx.createSelectorQuery().select('#j_page').boundingClientRect(function (rect) {  
                // 使页面滚动到底部  
                wx.pageScrollTo({  
                    scrollTop: rect.bottom  
                })  
            }).exec()  
        },  
        // 发送消息===================  
        sendMsg() {  
            var _this = this  
            // 发送文本消息,Web 端与小程序端相同  
            // 1. 创建消息实例,接口返回的实例可以上屏  
            let message = this.chat.createTextMessage({  
                to: this.conversation.userProfile.userID,  
                conversationType: this.TencentCloudChat.TYPES.CONV_C2C,  
                payload: {  
                    text: this.msg  
                },  
            });  
            // 2. 发送消息  
            let promise = this.chat.sendMessage(message);  
            promise.then(function (imResponse) {  
                // 发送成功  
                _this.messageList.push(imResponse.data.message)  
                _this.scrollToBottom()  
                _this.msg = ''  
            }).catch(function (imError) {  
                // 发送失败  
                console.warn('sendMessage error:', imError);  
            });  
        },  
        // 将某会话下所有未读消息已读上报===================  
        setMessageReadBox(e) {  
            // 将某会话下所有未读消息已读上报  
            let promise = this.chat.setMessageRead({ conversationID: e });  
            promise.then(function (imResponse) {  
                // 已读上报成功,指定 ID 的会话的 unreadCount 属性值被置为0  
            }).catch(function (imError) {  
                // 已读上报失败  
                console.warn('setMessageRead error:', imError);  
            });  
        }  

    }  
}  
</script>  
<style lang="scss" scoped></style>

最后看一下依赖分析的大小

收起阅读 »

无意中找到nvue国际化场景下每个nvue页面都需要引入VueI18n的解决方案

国际化 nvue
// nvue 目前的国际化方案需要在每个页面单独引入uni-i18n,后续框架会抹平差异,抹平差异后和 vue 页面一样只需要在 main.js 中引入  
<script>  
  import {  
    initVueI18n  
  } from '@dcloudio/uni-i18n'  

  // const messages = {} 此处内容省略,和 vue 全局引入的写法一致  

  const { t } = initVueI18n(messages)  

  export default {  
    data() {  
      return {  
      }  
    }  
  }  
</script>  

这是官网推荐的方案,需要在每个nvue都有这段代码

今天发现我们其实可以在app.vue的onLaunch中把t挂载到uni下,类似 uni.$locale = t去挂载,然后在nvue页面内直接使用 uni.$locale('common.edit')

下面是app.vue的示例代码

<script>  
    import {  
        initVueI18n  
    } from '@dcloudio/uni-i18n';  
    import messages from '@/locale';  
    const {  
        t  
    } = initVueI18n(messages);  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
            uni.$locale = t  
            uni.$language = uni.getLocale()  
        },  
        onShow: function() {  
            console.log('App Show')  
        },  
        onHide: function() {  
            console.log('App Hide')  
        },  
    }  
</script>  

<style lang="scss">  
</style>
继续阅读 »
// nvue 目前的国际化方案需要在每个页面单独引入uni-i18n,后续框架会抹平差异,抹平差异后和 vue 页面一样只需要在 main.js 中引入  
<script>  
  import {  
    initVueI18n  
  } from '@dcloudio/uni-i18n'  

  // const messages = {} 此处内容省略,和 vue 全局引入的写法一致  

  const { t } = initVueI18n(messages)  

  export default {  
    data() {  
      return {  
      }  
    }  
  }  
</script>  

这是官网推荐的方案,需要在每个nvue都有这段代码

今天发现我们其实可以在app.vue的onLaunch中把t挂载到uni下,类似 uni.$locale = t去挂载,然后在nvue页面内直接使用 uni.$locale('common.edit')

下面是app.vue的示例代码

<script>  
    import {  
        initVueI18n  
    } from '@dcloudio/uni-i18n';  
    import messages from '@/locale';  
    const {  
        t  
    } = initVueI18n(messages);  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
            uni.$locale = t  
            uni.$language = uni.getLocale()  
        },  
        onShow: function() {  
            console.log('App Show')  
        },  
        onHide: function() {  
            console.log('App Hide')  
        },  
    }  
</script>  

<style lang="scss">  
</style>
收起阅读 »

uts踩坑集锦

uts

一、自定义类型永远不要直接定义在函数声明中,特别是长一点的自定义类型。类型定义要设置在专门的声明文件中
二、类implements接口编辑器自动补全的代码,只留下函数名,(),{},这三个东西就行,其他的删掉自己写。记得 public和override这两个修饰符
三、uts暴露给js的函数的参数,参数类型不要设置成复杂的嵌套,uniapp1.0下会有问题。

继续阅读 »

一、自定义类型永远不要直接定义在函数声明中,特别是长一点的自定义类型。类型定义要设置在专门的声明文件中
二、类implements接口编辑器自动补全的代码,只留下函数名,(),{},这三个东西就行,其他的删掉自己写。记得 public和override这两个修饰符
三、uts暴露给js的函数的参数,参数类型不要设置成复杂的嵌套,uniapp1.0下会有问题。

收起阅读 »