阅读视图

发现新文章,点击刷新页面。

hexo-rs:玩 Vibe Coding

月底升级了 Copilot Pro+,月初额度重置,这几天可以放开用,想到什么就 vibe 一把。

我的博客跑在 Hexo 上很多年了。其实没什么大问题,就是每次看到那几百 MB 的 node_modules,心里总有点膈应——生成几百个静态 HTML,真的需要这么多依赖吗?但迁移到别的博客系统又懒得折腾,所以一直拖着。

这次干脆试试:能不能用 AI 一个下午撸一个 Rust 版的 Hexo?我的目标比较简单:生成跟原来一样的静态文件,兼容我现在用的主题就行。

我用的是 OpenCode + Opus 4.5。陆陆续续聊了一下午,产出了 hexo-rs。能用,但还有些边边角角的问题。

Vibe Coding 的工具和体会以后再写,这篇主要聊 hexo-rs 的实现和踩过的坑。

技术选型

EJS 模板引擎

Hexo 主题基本都用 EJS 模板——就是把 JavaScript 嵌到 HTML 里,跟 PHP 差不多。

QuickJS 跑 JS,通过 quick-js crate 调用。好处是不用依赖 Node.js,坏处是 Windows 上编不过(libquickjs-sys 挂了),所以暂时只支持 Linux 和 macOS。

其他

Markdown 用 pulldown-cmark,代码高亮用 syntect,本地服务器用 axum。都是常规选择,没什么特别的。

踩过的坑

HashMap 的坑

这个 bug 藏得很深。生成 tag 和 category 页面时,一开始用 HashMap 存文章分组:

let mut tags: HashMap<String, Vec<&Post>> = HashMap::new();

HashMap 迭代顺序不确定,每次生成的 HTML 可能不一样。页面看着没问题,但 diff 一下就发现乱了。改成 BTreeMap 就好了:

let mut tags: BTreeMap<String, Vec<&Post>> = BTreeMap::new();

Helper 函数

Hexo 有一堆 helper 函数:url_forcssjsdate 之类的。都得在 Rust 里实现一遍,然后塞进 QuickJS。

最烦的是 date。Hexo 用 Moment.js 的格式(YYYY-MM-DD),Rust 的 chrono 用 strftime(%Y-%m-%d)。得写个转换函数,挺无聊的活。

Partial 嵌套

EJS 的 partial 可以套娃,A 引用 B,B 又引用 C,变量还得一层层传下去。搞了个作用域栈,进 partial 压栈,出来弹栈。不难,但容易写错。

Vibe Coding 体感

代码 100% 是 AI 写的。我干的事:描述需求、review 代码、把报错贴给它让它改、偶尔拍板选方案。

像 EJS 模板引擎这种东西,自己从头写估计得半天,AI 几分钟就吐出来了。

但 AI 也挺蠢的:

  • HashMap 那个 bug 它就没注意到,我提出界面上的变化它也没反应过来
  • 一开始它写的 EJS parser 全是字符串 hardcode,丑得不行,我让它按 lexer -> AST 的套路重写了一遍
  • 代码多了以后它会忘事,前面写过的逻辑后面又写一遍

但 AI 又确实非常强,我想到应该使用现在线上的 catcoding.me 来和新生成的内容一一对比,然后它就呼啦啦地一通操作把问题都找出来了,自己修改完。

使用

cargo binstall hexo-rs  # 或 cargo install hexo-rs

hexo-rs generate  # 生成静态文件
hexo-rs server    # 本地预览
hexo-rs clean     # 清理
hexo-rs new "标题"

局限

不支持 Hexo 插件,不支持 Stylus 编译(.styl 文件得先用 Node 编译好),Windows 也不行。

简单的博客应该够用。复杂主题可能会有兼容问题。


代码在这:github.com/chenyukang/hexo-rs

用 Hexo 的可以试试。有问题提 issue,我让 AI 来修 :)

这篇文章到底是人写的,还是 AI 写的?

大模型发展史-01

前言 2017年,一篇论文悄然发表,题为《Attention Is All You Need》。 当时没人预料到,这篇论文中提出的 Transformer 架构,会在短短几年内彻底改变人工智能的格局。

什么是大语言模型-00

前言 你有没有想过,当你问 ChatGPT 一个问题时,它是如何"思考"并给出回答的? 今天天气怎么样?——抱歉,我无法获取实时天气信息。 请用 JavaScript 写一个快速排序——几秒钟内,代码

flutter添加间隙gap源码解析

Flutter 小部件,可轻松在 Flex 小部件(如列和行)或滚动视图中添加间隙。 Gap的核心原理是使用RenderObject自定义实现布局。 Gap 从源码看, 它提供了两个构造方法, Gap

vite+vue3+antd4项目兼容低版本chrome86+ && chrome68+

兼容性问题解决方案汇总

一、兼容到chrome86+

1.antd4 向下兼容方案

1、文档流中的样式和位置问题,主入口app.vue添加如下内容, 解决方案:

App组件容易漏,会导致message提示无法显示,需要注意~

import { ConfigProvider, App } from 'ant-design-vue';
import { StyleProvider, legacyLogicalPropertiesTransformer } from 'ant-design-vue/es/_util/cssinjs';
        <StyleProvider
          hash-priority="high"
          :prefix="configProviderPrefixCls"
          :transformers="[legacyLogicalPropertiesTransformer]"
        >
          <a-style-provider
            hash-priority="high"
            :prefix="configProviderPrefixCls"
            :transformers="[legacyLogicalPropertiesTransformer]"
          >
            <App>              
            <RouterView />
            </App>
          </a-style-provider>
        </StyleProvider>

2、  全局弹出、全局提示等脱离文档流的位置问题。 解决方案:

import { App } from 'ant-design-vue';
使用 const { modal } = App.useApp(); 替换Modal.confirm等,message alert 同理
 

 

3、  前缀是antd,没有被prefix前缀处理到的组件位置问题(如画布节点) 解决方案:采用问题一的解决方案,把画布自定义节点包裹一次

4、  使用creatApp或者creatVnode创建的模块,脱离了vue的上下文文档流

解决方案:采用问题一的解决方案,把节点包裹一次

样式及js兼容修改

1.建议初始化配置时在vite.config中加上如下内容:

build:{
    ... 原来的内容,
    // js最低兼容的浏览器版本
    target: ['chrome86', 'edge88', 'firefox78', 'safari14'],
    // 禁用 CSS 代码压缩,防止 top/right/bottom/left 被转换成 inset
     cssMinify: false
 }
 

本地启动报错

1.集成unocss出现报错问题 image.png

 

注意⚠️:   如果使用了unocss或taiwindcss覆盖antd原有的样式会失效,兼容后antd的样式等级会提高。我采取的方法比较笨但可靠,在覆盖的css类名后面添加‘!’,编译后会给unocss或taiwindcss的css后面添加!important。这样能解决!

position导致的样式失效

360极速版position导致的样式失效 如果同时有top:0;left:0,bottom:0;right:0;vite会打包成一个insert:0,但是360极速版不支持该属性加上width:100%和height:100%

二、兼容到chrome68+

在兼容chrome86的基础上添加如下配置

全程基于项目根目录执行操作,核心依赖为 Vite5 官方兼容插件@vitejs/plugin-legacy+ API 补全库core-js@3,所有配置可直接复制使用,按步骤执行即可完成兼容。

前提:Chrome68 支持 ES2017,缺失 ES2018+ 新语法(?.、?? 等)和部分全局 API,兼容核心是语法转译 + 自动 polyfill 注入 + 适配构建目标

步骤 1:安装核心兼容依赖

# npm 安装
npm install @vitejs/plugin-legacy core-js @babel/core -D

步骤 2:配置 browserslist(统一所有工具的兼容目标)

在项目根目录的package.json新增browserslist字段,统一 Vite、Babel、PostCSS 的浏览器兼容规则,仅指定Chrome 68即可,修改后如下:

{
  "name": "your-vue-project", // 你的项目名
  "version": "0.0.0",
  "browserslist": ["Chrome 68"], // 新增这行,统一兼容目标
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  // 保留项目原有dependencies、devDependencies等配置...
}

步骤 3:修改 Vite 核心配置(vite.config.js/ts)

这是兼容的核心步骤,修改项目根目录的vite.config.js(ts 项目为vite.config.ts,配置完全一致),完成插件注册 + 构建目标降级 + CSS 适配,直接替换原有配置即可:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy' // 1. 引入legacy兼容插件

// Vite5 核心配置
export default defineConfig({
  plugins: [
    vue(), // Vue3 插件
    // 2. 配置legacy插件,自动转译语法+注入polyfill
    legacy({
      targets: 'Chrome 68', // 明确指定兼容Chrome68(也可读取browserslist)
      polyfills: true, // 开启自动polyfill注入(基于core-js@3)
      renderLegacyChunks: true, // 生成旧浏览器兼容产物
      modernPolyfills: true // 现代浏览器兜底polyfill
    })
  ],
  build: {
    target: 'es2017', // 3. 构建目标降级为Chrome68支持的ES2017(Vite5默认es2020)
    cssTarget: 'chrome68', // 4. CSS适配Chrome68,避免生成不兼容CSS语法
    minify: 'terser', // 可选:用terser压缩,避免新压缩特性导致兼容问题
    terserOptions: {
      compress: { drop_console: false } // 可选:保留console,方便调试
    }
  }
})

步骤 4:Vue3.3.0 兼容兜底(无额外配置,仅 2 个避坑点)

Vue3 本身已放弃 IE 但完全兼容 Chrome68,无需修改任何 Vue 相关配置,只需注意 2 个细节即可:

  1. 代码中避免使用 Vue3 的实验性特性(如defineModel高级用法、props解构的新特性),若使用,legacy 插件会自动转译;
  2. Vue3 模板编译产物为 ES5 级别,Chrome68 可直接解析,无需修改@vitejs/plugin-vue的编译配置。

步骤 5:执行生产构建(兼容逻辑仅对构建生效)

Vite 的 legacy 兼容处理仅在生产构建时生效(开发环境 Chrome68 已支持 ES 模块,可直接运行npm run dev开发,无需额外处理,本地还是无法访问的!!!!!),执行构建命令生成兼容包:

# 生成兼容Chrome68的生产包,输出到项目根目录的dist文件夹
npm run build

构建完成后,dist目录会自动生成 2 类产物,且index.html内置浏览器嗅探逻辑:Chrome68 会自动加载兼容产物 + polyfill,高版本浏览器加载现代产物,无需手动判断。

注意: 本地无法访问,看不到页面呈现,只有部署后才能打开!!!!!!!

步骤 6:Chrome68 兼容性测试(2 种便捷方法,无需安装旧浏览器)

方法 1:Chrome 开发者工具模拟(推荐,最快)

  1. 打开新版 Chrome 浏览器,运行npm run preview启动预览服务,打开项目预览地址;
  2. F12打开开发者工具 → 点击右上角「⋮」→ 更多工具 → 设备模拟;
  3. 左上角设备下拉框选择「自定义」→ 输入Chrome 68,刷新页面即可模拟运行。

方法 2:本地启静态服务测试

  1. 全局安装静态服务工具servenpm install serve -g
  2. 项目根目录执行:serve dist,会生成本地访问地址(如http://localhost:3000);
  3. 在模拟的 Chrome68 中访问该地址,验证页面渲染、按钮点击、接口请求等交互是否正常。

步骤 7:常见兼容问题解决(按需处理)

若测试时出现报错 / 样式错乱,按以下场景针对性解决,均为 Chrome68 兼容的高频问题:

问题 1:第三方依赖未被转译(如 Element Plus/axios 用了 ES2018 + 语法)

Vite5 默认不转译node_modules,需在vite.config.js强制指定转译的依赖,修改后如下:

export default defineConfig({
  // 保留原有配置...
  optimizeDeps: {
    include: ['element-plus', 'axios'] // 按需添加需要转译的第三方依赖
  },
  build: {
    // 保留原有配置...
    commonjsOptions: {
      include: [/element-plus/, /axios/, /node_modules/] // 强制转为CommonJS格式
    }
  }
})

问题 2:个别全局 API 未被 polyfill(如 URLSearchParams/fetch)

Chrome68 已支持大部分 API,若遇到缺失,在 src/main.js/ts 中手动引入即可:

// src/main.js(Vue入口文件)
import 'core-js/es/url-search-params' // 手动注入URLSearchParams polyfill
import 'core-js/es/fetch' // 按需注入fetch polyfill
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

问题 3:CSS 样式错乱(如高级 CSS 选择器 / 特性)

Chrome68 支持基础 CSS 变量 / Flex 布局,样式问题多为第三方 CSS 用了高级特性,安装postcss-preset-env兜底:

  1. 安装依赖:pnpm add postcss-preset-env -D
  2. 项目根目录新建postcss.config.js,添加配置:
module.exports = {
  plugins: [
    require('postcss-preset-env')({
      browsers: 'Chrome 68', // 适配Chrome68
      features: { 'custom-properties': { preserve: true } } // 保留CSS变量
    })
  ]
}

核心步骤总结

  1. 安装@vitejs/plugin-legacy@5.x + core-js@3 + @babel/core三大核心依赖;
  2. package.json中配置browserslist: ["Chrome 68"]统一兼容目标;
  3. Vite 配置中注册 legacy 插件,降级build.target为 es2017、cssTarget为 chrome68;
  4. 执行npm run build生成兼容包,通过 Chrome 开发者工具模拟 Chrome68 测试;
  5. 按需解决第三方依赖转译、手动 polyfill、CSS 适配等问题。

 

公司低代码框架-列表个性化开发最佳实践

一、引言

当前低代码组件的功能框架已趋于稳定,而业务侧的需求设计却持续迭代、不断涌现。要落地各类个性化需求,正需要我们秉持‘人有多大胆,地有多大产’的探索精神,勇于构思、大胆尝试。比如低代码列表中,针对字段内容过多的问题,就需要自己开发部分展示的功能。

二、使用场景

1、列表只展示前三行,剩余的放在查看按钮内,弹框展示

image.png

实现思路:借助列表字段的自定义内容-复杂模式,实现设计稿里的展示效果,在页面加载事件里监听点击事件,实现弹窗效果

image.pngimage.png


let pileList = rowData.row.AccountList || [];
let resStrShow = '';
let resStrHide = '';
    for (let id = 0; id < pileList.length; id++) {
        let itemShow = "";
       ...
        if (rowData.row.AutoChargeType == '2') {//企业总账户
            if (pileList.length > 1) {
                itemShow += `${pileList[id].BusUnitAttrCompanyName}:`;
            }
            let BusUnitOrGroupBalance = Funcs.FormatDecimal(pileList[id]?.BusUnitOrGroupBalance, 2, "", ".").replace(/,/g, '');
            itemShow += `企业现金余额${BusUnitOrGroupBalance}元`;

        }
       ...
        }
        if (id < 3) {
            if (resStrShow) {
                resStrShow += '</br>';
            }
            resStrShow += itemShow;
        }
        if (resStrHide) {
            resStrHide += '</br>';
        }
        resStrHide += itemShow;

    }
if (pileList.length > 3) {

    let tipQuestion = `<div style="opacity: 1;    margin-top: 2px;" instancecode=""tabindex="1">`
        + `<span>`
        + `<span class="qiestionIcon" style="position: relative;display: flex;justify-content:  flex-start;">`
        + `<i class="material-icons" style="display:none" aria-hidden="true" role="presentation">help_outline</i>`
        + `<div  class="viewAll"
  style="
  padding:0px 7px;
  border-radius:10px;
  border:1px solid #D9D9D9;
  font-family:AlibabaPuHuiTi;
  font-size:12px;
  cursor: pointer;
  color:#3656FF;"
  >查看全部(${pileList.length})</div>`
        + `<div style="visibility:hidden;position: absolute; width: 560px; height: auto; left: 0px; top: -8px; box-sizing: content-box; padding-top: 8px;margin-left:20px;">`
        + `<span style="" class="resStrHide">${resStrHide}</span>`

        + `</div>`
        + `</span>`
        + `</span>`
        + `</div>`;

    return `<div   style="position: relative; display: inline-flex;flex-direction: column;">
          <div>${resStrShow}</div>          
          ${tipQuestion}
</div>`;

} else {
    return resStrShow;
}

js代码,点击实现弹框效果:


    var content = document.querySelector(`.UIControl_VehicleSettingList_Ecms_New`);
    content.addEventListener('click', function (event) {
        if (event.target.className == "viewAll") {
            const nextSiblingElement = event.target.nextElementSibling;
            if (nextSiblingElement) {
                const targetSpan = nextSiblingElement.querySelector('span.resStrHide');
                if (targetSpan) {
                    LocalVars.Variable_viewAll = targetSpan.innerHTML//把全部内容赋值给弹窗变量
                    Widget.fasr_dialog_viewALLNew.showDialog()//展示弹框
                
                }
            }
        }   
    });

2、列表只展示前三个,剩余的放在悬浮气泡里展示

image.png
实现思路:借助列表字段的自定义内容-复杂模式,实现设计稿里的展示效果,在页面加载事件里监听mousemove事件,实现气泡效果

image.pngimage.png

let PileRangeDesc = rowData.row.PileRangeDesc || '';
let pileList = PileRangeDesc.split(',') || [];

let resStrShow = '';
let resStrHide = '';

for (let id = 0; id < pileList.length; id++) {
    if (id < 3) {
        if (resStrShow) {
            resStrShow += '、';
        }
        resStrShow += pileList[id];
    } else {
        if (resStrHide) {
            resStrHide += '、';
        }
        resStrHide += pileList[id]
    }
}

if (pileList.length > 3) {

    let tipQuestion = `<div style="opacity: 1;margin-left:7px" instancecode=""tabindex="1">`
        + `<span>`
        + `<span class="qiestionIcon" style="position: relative;display: flex;justify-content: center;">`
        + `<i class="material-icons" style="display:none" aria-hidden="true" role="presentation">help_outline</i>`
        + `<div 
  style="
  padding:0px 7px;
  border-radius:10px;
  border:1px solid #D9D9D9;
  font-family:AlibabaPuHuiTi;
  font-size:12px;
  color:#3656FF;"
  >+${pileList.length - 3}</div>`
        + `<div style="visibility:hidden;position: absolute; width: 560px; height: auto; left: 0px; top: -8px; box-sizing: content-box; padding-top: 8px;margin-left:20px;">`
        + `<div class="q-tooltip--style q-position-engine arrow-top question-tooltip" style="--q-transition-duration: 30oms; --left: 27px; --top: false; -px;width:auto;
    padding: 16px 20px;
    background:#fff;
    font-family: AlibabaPuHuiTi;
    font-weight: 400;
    font-size: 14px;
    line-height: 20px;
    border-radius: 12px;
    color: rgba(0,0,0,0.8);
    max-width: 560px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
    margin-bottom:0;">`
        + `<span style="width:100%;
  display: inline-block;
  white-space: pre-wrap;">${resStrHide}</span>`
        + `<div style="width:12px;
  position:absolute;
  visibility:visible;
  bottom:-12px;
  height:12px;
  left:calc(50% - 6px);
  background-color:#fff;
  margin-top:0;
  clip-path: polygon(0 0,100% 0, 50% 100%);"></div>`
        + `</div>`
        + `</div>`
        + `</span>`
        + `</span>`
        + `</div>`;

    return `<div   style="position: relative; display: inline-flex;align-items:center">
          <div>${resStrShow}</div>          
          ${tipQuestion}
</div>`;

} else {
    return resStrShow;
}

js代码,实现气泡效果:

    var content = document.querySelector(`.${LocalVars.InParam_UISign}`);
    content.addEventListener('mousemove', function (event) {
        let table = content.querySelector(".fasr_mixview");
        if (table) {
            let tipElems = table.getElementsByClassName("question-tooltip");
            if (tipElems.length > 0) {
                for (let id in tipElems) {
                    if (tipElems[id].style) {
                        let parentNode = tipElems[id].parentNode;
                        // 清除TD提示框 
                        let signParentNode = parentNode;
                        for (let i = 0; i < 10; i++) {
                            if (signParentNode && signParentNode.tagName === 'TD') {
                                signParentNode.title = '';
                                break;
                            }
                            signParentNode = signParentNode.parentNode;
                        }

                        let position = parentNode.getBoundingClientRect()
                        let pX = position.left;
                        let pY = position.top;
                        let height = tipElems[id].offsetHeight
                        let width = tipElems[id].offsetWidth
                        // tipElems[id].style.left = pX - width / 2 - 14.3 + 'px';
                        tipElems[id].style.left = pX - width / 2 - 4 + 'px';
                        tipElems[id].style.top = pY - height - 7 + 'px';
                    }
                }
            }
        }
    });

3、列表格式化时间/字段内容

如果后台给我们的数据未经格式化,需要前端再次处理,我们除了在action里的返回值自定义扩展里面进行修改外,也可以利用列表字段的自定义设置,更加方便

image.png

例如,格式化时间:

return Funcs.FormatDateTime(rowData.row.LastModifyTime, 'yyyy-MM-dd HH:mm:ss');

格式化字段,没有值时展示‘--’

return `<div id="div">${rowData.value?rowData.value:'--'}</div>`

4、列表字段不固定,根据某个值动态展示部分字段,隐藏部分字段

实现思路:设置pc查询方案延迟加载,在onLoaded中,根据当前的模式处理当前方案下需要展示的字段,手动触发查询

 setTimeout(() => {
        let Scheme = Widget.fasr_mixed_view.getScheme()
            let ListViewSet = JSON.parse(Scheme.ListViewSet);
            if( LocalVars.Variable_mdoe=="1"){
               ListViewSet.displayColumns = ListViewSet.displayColumns.filter(item => (item.value != "AccountDesc"&&item.value != "AutoChargeTypeName"))
            }
            if( LocalVars.Variable_mdoe=="2"|| LocalVars.Variable_mdoe=="3"|| LocalVars.Variable_mdoe=="4"){
               ListViewSet.displayColumns = ListViewSet.displayColumns.filter(item => (item.value != "CarUseCustMobile"))
            }
                 
            Scheme.ListViewSet = JSON.stringify(ListViewSet)
            Widget.fasr_mixed_view.setScheme(Scheme)
     
    }, 0)

5、列表的查询条件不固定,根据某个值动态展示隐藏

实现思路:我们可以利用js操作原始dom,对条件进行显示隐藏,简单实现这个需求(低代码提供了硬写的方案,但是很繁琐)

 if(LocalVars.Variable_PVMismatchSwitch == "0"){
   document.querySelector(".Page_DriverManageListALLForNew .el-col-6:last-of-type").style.display = "none";
   }else{
    document.querySelector(".Page_DriverManageListALLForNew .el-col-6:last-of-type").style.display = "block";
   }

6、列表某些场景下列表每页的条数不允许切换,始终保持每页10条

image.png

实现思路:通过修改元素的行内样式,让这个元素彻底无法响应任何鼠标 / 触摸交互事件

 document.querySelector(".t-prefab-pagination__perfective-page-size").style.pointerEvents='none';

7、列表按钮点击触发服务响应缓慢,超过3秒,为防止白屏手动弹出loading

低代码提供了Funcs.ShowLoading(),但是不生效,考虑使用原生dom实现

    let eload1 = window.top.document.getElementById('tff_page_loading');
   eload1 && (eload1.style.display = "block");//显示
   eload1 && (eload1.style.display = "none");//隐藏

8、列表按钮触发后端服务每次只能校验一个,批量操作下需等待所有检查完成后才能进行下一步

实现思路:Promise.all()

  const checkPromises = [];
                let hasBoundVehicle = false;
                let CarLicenseArr = [];

                // 1. 检查所有车辆是否被绑定
                selectedRowDatas.forEach(item => {
                    if (item.VehiclesID) {
                        checkPromises.push(
                            Action.Action_CheckVehicleIfBindMembers_Ecms({ VehicleID: item.VehiclesID })
                                .then(result => {
                                    const hasCarObj = JSON.parse(result.Record.Data);
                                    if (hasCarObj.data) {
                                        CarLicenseArr.push(item.CarLicense);
                                        hasBoundVehicle = true;
                                    }
                                })
                        );
                    }
                });
                // 2. 等待所有检查完成
                Promise.all(checkPromises).then(() => {
                    debugger
                    var loadingMask = document.querySelector("#mvcFrameDiv")?.querySelector(".web-loading_mask");
                    loadingMask && (loadingMask.style.display = "none");
                    console.log(CarLicenseArr)
                    let tipI = ""
                    if (CarLicenseArr.length) {
                        CarLicenseArr.map(item => {
                            tipI += item + "、"
                        });
                        tipI = "当前车辆" + tipI.substring(0, tipI.length - 1) + "已被司机绑定,删除后司机将无法使用企业账户为该车辆充电,确认要删除吗?";
                        Funcs.Confirm(confirm, tipI, function () {
                            VehicleInformationNewUnbind()
                        })
                    } else {
                        Funcs.Confirm(confirm, confirmInfo, function () {
                            VehicleInformationNewUnbind()

                        })
                    }
                });

9、移动端列表没有数据时‘暂无数据’不展示

image.png

后端接口返回的count值不对,低代码模版根据count值决定是否展示暂无数据

10、移动端app框架内页面修改表头标题

需写在onloading方法里

 if(TFF.common.ctx.CLIENT_TYPE.TeldApp == TFF.common.ctx.getClientType()){

 window.pageNameTerm = ''
window.pageTitle = '添加司机'

if(LocalVars.InParam_FormState ==1){
window.pageTitle  ='编辑司机'
}
  TFF.jsdk.ready({ url: '', sgUrlPrefix: '' }, () => {
     window.envApi.setTitle({
            "title" : window.pageTitle ,
            success : function (res) {
             
            }
        })
            });

}
❌