【校猩猩】前端开发经验介绍

前言

校猩猩一直注重简约而不简单,因此在体验上会额外关注,每一个功能都会反复讨论怎么做用户才会觉得好用,不光是用户体验,在校猩猩开发过程中,我也时刻致力于提升开发体验。

我们是怎么从excel导入数据的

常规实现方式

  1. 让用户下载excel模板
  2. 用户将数据先填充到模板中
  3. 然后导入模版

这种方式不够简约,既然用户需要将数据复制到模板中, 那为什么不直接复制到我们的表格中呢?

直接复制到表格中,乍一看实现起来有点复杂,经过一番摸索,其实也相当简单,经过以下三步即可实现。

读取复制的字符卷,转换二维数组

浏览器中,原生提供了读取粘贴板内容的接口,异步返回字符卷。

// 获取复制的字符卷
const text = await navigator.clipboard.readText()

从excel复制的字符卷有3个特点:

  • 行与行之间\n分割
  • 单元格之间\t分割
  • 若单元格中有换行,该字符卷有双引号,以\n换行

根据这3个特点,很容易将其转换为二维数组

const rows = text
    .replace(/("[^"]*")/g, function (match, captured) {
      // excel单元格中若换行,则字符卷包含双引号且中间有\n
      return captured.replace(/\n/g, '🦪')
    })
    .split(/\n/)
const gridDatas = []
rows.forEach((it, i) => {
    const cols = it.replace(/🦪/g, '\n').replace(/"|\r/g, '').split(/\t/)
    // 省略了去掉空行的代码
    gridDatas.push(cols)
})

将二维数组转换成对象数组

得到gridDatas之后,接下来的就是应用到我们的表格中,该过程有个关键点是,这个二维数组的数据应该赋值表格的哪些行和哪些列

这个时候就需要记录用户选中了表格的哪个单元格,在sessionStorage中存储单元格的索引,我们在执行转换时,就以索引为准

const dataLen = data.value.length
// 获取索引
const [row = dataLen, col = 1] = JSON.parse(sessionStorage.getItem('poniter')) || []
// 表格的字段
const keys = ['name', 'phone', 'relation', 'sex', 'birthday', ...customKeys, 'remark']
// 转换
const newArr = []
gridDatas.forEach((item) => {
    const newObj = {}
    const newKeys = keys.slice(col - 1)
    item.forEach((it, index) => {
        const key = newKeys[index]
        switch (key) {
            case 'phone':
                if (/^\d+$/.test(it)) {
                  newObj[key] = it.substring(0, 11)
                } else {
                  newObj[key] = ''
                }
                break
            // .....省略其他
        }
    })
    newArr.push(newObj)
})

至此,newArr就可以更新表格的数据了

更新表格数据

// 如果选中的行是最后一行,则全部新增
if (row === dataLen) {
    data.value = data.value.slice(0, dataLen - 1).concat(newArr)
} else { // 更新或新增
    const updateNum = dataLen - row
    const updateArr = newArr.slice(0, updateNum)  // 要更新的数据
    const restArr = newArr.slice(updateNum) // 要新增的数据
    // ....省略其他代码
}

我们是怎么写小程序页面的

新增页面

当我们新增页面时,需要新增一个文件夹和4个文件,还要在app.json中新增页面path,我们觉得有点麻烦。

因此写了一个命令行工具,只要执行如下命令即可完成上面的操作:

// 新增页面
npm run cpa
// 或者新增组件
npm run cpc

此命令主要用nodejs来读写文件

  1. 读取页面模板
  2. 用模板内容生成目录和文件
  3. 修改app.json文件以添加path

html转wxml实时监听程序

开发小程序页面,我们习惯在vscode中开发,然而在vscode写wxml文件emmt不友好,因此,我们还是用html写页面

我们开发了一个htmlwxml的程序,实时监听文件变化生成wxml文件。

这个实时监听程序将html转换成wxml,还可以帮助我们做以下事情:

  1. div标签转换成view标签
  2. span标签转换成text标签
  3. 自动引入全局的wxs文件
  4. 自动引入自定义navbar
  5. 自动引入全局的dialog组件和toast组件

图标

以往,我们使用图标都是引入iconfont链接,但这种方式缺点明显:

  1. 添加了新图标,链接地址要更新
  2. 需要网络加载,图标会出现延迟显示的情况
  3. 考虑将来,将来iconfont网站不知道会发生什么
  4. iconfont项目管理员可能离职

因此,我们采用将图标的svg代码复制到项目仓库,跟随代码一起管理的方式

在项目中,用一个专用文件放置图标的svg文件

然后用一个.wxs文件管理svg文件的路径,类似下面这样

var baseSvgUrl = '/assets/svg-icons/'
module.exports = {
    logo: baseUrl + 'logo.png'
}

使用图标时,我们只需这样

<wxs src="/config/img-urls.wxs" module="imgUrls" />
<image class="w16 h12 mr3" src="{{imgUrls.logo}}"></image>

弹框组件应用

弹框是最频繁使用的组件之一,以声明式使用vant的组件,每次都需要定义如下变量和方法

<van-popup show="{{ show }}" bind:close="onClose">内容</van-popup>
Page({
  data: {
    show: false,
  },

  showPopup() {
    this.setData({ show: true });
  },

  onClose() {
    this.setData({ show: false });
  },
});

若一个页面有多个弹框,需要定义多个更多这样的变量,略显繁琐,所以,我们希望以函数式调用使用弹框,例如:

const res = await popup({
  content: 'calendar',  // 弹框类型
  title: _t('请选择日期'), 
  param: {}, // 传输参数
})
if(res) {
    // 其他逻辑
}

实现方式, 先在app对象上定义一个getPopup方法,调用后返回popup方法

export default function (params) {
  return this.selectComponent('#custom-popup').open(params)
}
// app.js中引入上面的方法
// 在需要popup的页面调用
const popup = app.getPopup.bind(ctx)
// 调用popup
await popup({
....
})

我们是怎么支持国际化的

常规国际化方案

定义语言配置文件,例如:

module.exports = {
    zh_cn: {
        pageTitle: '页面标题'
    },
    en: {
        pageTitle: 'page title'
    },
    ...
}

然后定义一个方法,根据i18n变量读取相应的语言配置

function _t(i18n, key) {
    if(i18n === 'zh_cn') {
        return obj[key]
    }
    ...
}

这样做,不利于代码阅读,因为页面中没有实际的中文,而且如果要通过搜索某个中文定位代码也不是很方便。

为解决上面的问题,我们直接在代码中写中文实现国际化,例如:

<wxs src="./i18n_html/t.wxs" module="_t" />
<div>
    {{_t('新增学员', III)}}
    {{_t('金额{0}', III, [1000])}}
</div>

很明显,上面代码的_t函数是用于切换语言的,那么是怎么切换的呢?同样也是根据语言配置文件

var map = require('./keymap.wxs')  // 引入语言配置文件
module.exports = function(text, i18n,  params) {
  var ret = ''
  if (i18n === 'zh_CN') {
    ret = text
  } else {
    var obj = map[text]
    if (obj && obj[i18n]) {
      ret = obj[i18n]
    } else {
      ret = text
    }
  }
  // ...
  return ret
}

上面的语言配置文件是怎么来的呢?这里我们又用到了实时监听程序了,程序自动为我们生成了配置文件

const map = {}
const reg = /_t\(([^\)]+)\)/gim
const m = htmlStr.match(reg)  // 代码中是否有_t()调用
if(!m) return

m.forEach((code, i) => {
const m = code.match(/[\'\"]([^\'\"]+)[\'\"]/)
if (m && m[1]) {
  map[m[1]] = null
}
})
fs.writeFile(rootPath + 'keymap.js', `export default ${JSON.stringify(map, null, 2)}`, (err) => {})

配置文件结果如下

module.exports = {
  "新增学员": {
      "en": "Add student",
      "zh_HK": "新增學員",
      "zh_TW": "新增學員"
  },
}

配置文件生成之后,接下来就只要翻译了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值