open-index/open-npm huggingface dataset:
npm package: @retailwe/common
Description: Common code such as libs, utils, helper, style, wxs
Latest version: 0.0.12

# common

Common code such as libs, utils, helper, style, wxs

# 使用

## 安装

```shell
yarn add '@retailwe/common' --save
```

## 引入

```javascript
import { isUtils } from '@retailwe/common/utils';

isUtils.isString('abc'); // true
```

# API

[API 文档详情](./docs/globals.md)

# 开发

- 运行：`yarn watch`
- 构建：`yarn build`
- 单元测试：`yarn test`
- 测试报告：`yarn cov`
- 文档：`yarn doc`

# 测试覆盖率

| File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
| --------- | ------- | -------- | ------- | ------- | ----------------- |
| All files | 71.43   | 58.33    | 100     | 71.43   |                   |
| image.ts  | 71.43   | 58.33    | 100     | 71.43   | 20,23             |
npm package: @retailwe/common-libs
Description: RetailWe libs
Latest version: 0.0.32

# common

Common code such as libs, utils, helper, style, wxs

# 使用

## 安装

```shell
yarn add '@retailwe/common-libs' --save-exact
```

## 引入

```javascript
import { status } from '@retailwe/common-libs';

const { Status } = status;

const bootstrapStatus = new Status('bootstrap');
```

# API

[API 文档详情](./docs/globals.md)

# 开发

- 运行：`yarn watch`
- 构建：`yarn build`
- 单元测试：`yarn test`
- 测试报告：`yarn cov`
- 文档：`yarn doc`

# 测试覆盖率

| File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
| --------- | ------- | -------- | ------- | ------- | ----------------- |
| All files | 71.43   | 58.33    | 100     | 71.43   |                   |
| image.ts  | 71.43   | 58.33    | 100     | 71.43   | 20,21             |
npm package: @retailwe/common-libs-async-queue
Description: RetailWe libs
Latest version: 0.0.4

# 异步队列模块

## 需求背景

- 多个模块之间，需要调用相同的异步请求，为了减少重复请求，使用队列来缓存相同的请求

## 设计思路

通过内部维护一个队列 Map，不同的 key 对应的不同的请求，同时对外暴露相关方法来操作这个 Map：

```ts
export default class AsyncQueue {
  private asyncSet: Map<string, Array<Promise<any>>>;

  constructor() {
    this.asyncSet = new Map<string, Array<Promise<any>>>();
  }

  /**
   * 缓存指定 key 的异步请求
   */
  public setPromise(key: string, anyPromise: Promise<any>): void {
    const cur = this.asyncSet.get(key) || [];
    if (!cur) {
      // 初始化 key: []
      this.asyncSet.set(key, []);
    }
    cur.push(anyPromise);
    this.asyncSet.set(key, cur);
  }

  /**
   * 获取指定 key 的异步请求
   */
  public getPromise(key: string): Promise<any> | undefined {
    let result;
    const queue = this.asyncSet.get(key);
    if (queue && queue.length) {
      result = queue.pop();
    }
    return result;
  }

  /**
   * cachePromise 缓存请求
   * @param key 唯一索引 Key
   * @param fn  异步请求
   */
  public cachePromise<T>(key: string, fn: () => Promise<T>): () => Promise<T> {
    let cachePromise = this.getPromise<T>(key);
    if (!cachePromise) {
      cachePromise = fn();
      this.setPromise(key, cachePromise);
    }

    // 使用 ! 非空断言
    return () => cachePromise!;
  }

  ....
}

```

**建议项目内部维护唯一单例**

## 使用方式

### 小程序

**建议在 `app.ts` 中的 `globalData` 里面，初始化队列：**

```js
import AsyncQueue from '@retailwe/common-libs-async-queue';

App({
  globalData: {
    asyncQueue: new AsyncQueue()
  }
});
```

**业务层请求缓存封装**

```ts
// service 使用
const mockService = async (key: string): Promise<any> => {
  const testPromise = queue.getPromise(key);
  if (testPromise) return testPromise;

  const tempPromise = asyncFn(key);
  queue.setPromise(key, tempPromise);
  const val = await tempPromise;
  return new Promise(resolve => resolve(val));
};

// 单元测试
test('asnycQueue test1', async t => {
  const key1 = 'asyncQueue1';
  const promiseAll = await Promise.all([mockService(key1), mockService(key1)]);
  t.is(queue.getQueue(key1)?.length, 0);
  t.is(JSON.stringify(promiseAll), JSON.stringify([1, 1]));
});

// 通过 cachePromise 直接缓存一个 () => Promise<any> 函数
test.serial('test cachePromise', async t => {
  const key = 'cachePromise';
  const fn1 = queue.cachePromise<number>(key, () => mockPromiseFn(1));
  t.is(queue.getQueue(key)?.length, 1);
  t.is(await fn1(), 1);
  const fn2 = queue.cachePromise<number>(key, () => mockPromiseFn(2));
  // 这里会使用上一次队列的缓存，所以第二次的结果是上一次的结果
  t.is(await fn2(), 1);
  // cache 相同 key, 队列数不变，
  t.is(queue.getQueue(key)?.length, 0);
  // 队列 map.size + 1
  t.is(queue.getQueues().size, 3);
});
```
npm package: @retailwe/common-libs-event
Description: RetailWe libs
Latest version: 0.0.4

# Event 事件订阅

## 安装

```shell
yarn add '@retailwe/common-libs-event' --save-exact
```

## 使用

```javascript
import Event from '@retailwe/common-libs-event';

const event = new Event();
const callback = (msg) => {...};

// 监听
event.on('event_name', callback);

// 监听一次
event.once('event_name', callback);

// 触发
event.emit('event_name', msg);

// 移除指定事件的特定监听器
event.off('event_name', callback);

// 移除指定事件的所有监听器
event.offAll('event_name');
```

## 单元测试覆盖率

[单元测试覆盖率](cov/index.html)
npm package: @retailwe/common-libs-listpage
Description: RetailWe libs
Latest version: 0.0.3

# Listpage 数据列表

## Feature

1. 满足常用「数据列表分页」的业务场景
1. 支持分页
1. 支持多个数据列表
1. 自动捕捉下拉重载：onPullDownRefresh
1. 自动捕捉上拉加载：onReachBottom
1. 自带请求锁，防止帕金森氏手抖用户
1. 简单优雅的 API

## 安装

```shell
yarn add '@retailwe/common-libs-listpage' --save-exact
```

## 快速使用

### Step1 - 配置初始化

```javascrpit
import { listPageDecorator } from '@retailwe/common-libs-listpage';

@wxPage()
@listPageDecorator({
  // 自动下拉刷新（要自行在 json 文件配置：enablePullDownRefresh）
  enabledPullDownRefresh: true,

  // 自动触底翻页
  enabledReachBottom: true,

  // 是否自动加载第一页
  autoLoadFirstPage: true,

  // 会把列表数据注入到 this.data.listPage = {
  //   loadingMore: boolean,
  //   activeListName: 'works', // 默认第一个
  //   works: {
  //       totalPage: number,
  //       data: [...], // fetchPageData 返回来的数据
  //       hasMore: boolean,
  //       currentPage: number,
  //   },
  //   goods: {...}, // 同 works
  // }
  dataDomain: 'listPage',

  // 列表数据配置
  lists: [
    // 我的作品
    {
      name: 'works',
      pageSize: 20,
      fetchPageData({ theHost, pageNo, pageSize }) {
         return xxxAPI({
           pageNo,
           pageSize,
           keywords,
         }).then(data => {
           const { list, totalCount } = data;
           theHost.setData({ totalCount });

           // 更新 totalPage
           this.setTotalPage(Math.ceil( totalCount / pageSize ));
           return list;
         });
       },
    },

    // 商品
    {
      name: 'goods',
      pageSize: 20,
      fetchPageData({ theHost, pageNo, pageSize }) {...},
    },
  ],
})
export default class extends BasePage {...}
```

### Step2 - 渲染列表

```html
<view class="content">
  <wr-videos videos="{{ listPage.works.data }}"></wr-videos>
  <wr-goods goods="{{ listPage.goods.data }}" />
</view>
```

## Active List

在多列表下，当触发 `onPullDownRefresh` 和 `onReachBottom` ，就会面临到底加载哪一个列表的问题。
这种场景多数见于一个页面，有多个 tab 切换，每个 tab 对应了可以分页的数据。

listPage 当然对种问题的有优雅的解决方案的。

于是「Active List」 的概念被引入了，它的默认值是 `options.lists` 中数组第一个列表。

我们可以通过 `listPage.setActiveList(name)` 来改变当前 Active List：

首先假设我们定义了以下这个页面：

```javascript
onTabChange(e) {
   // 当前的 tab name
   const { name } = e.detail;
   await this.listPage.setActiveList(name);
   this.loadFirstPage();
}
```

## 获取 `MultiList` 和 `List` 实例

MultiList 和 List API，请查看对应文档。

```javascript
onLoad() {
  const listPage: MultiList = this.listPage;
  const list: List = listPage.getList('works');
}
```

## 单元测试覆盖率

[单元测试覆盖率](cov/index.html)
npm package: @retailwe/common-libs-logger
Description: RetailWe libs
Latest version: 0.0.3

# Logger 日志

## 安装

```shell
yarn add '@retailwe/common-libs-logger' --save-exact
```

## 使用

```javascript
import Logger from '@retailwe/common-libs-logger';

const logger: any = new Logger({ prefix: 'retailwe-modules-status' });

logger.info('status:success', 'something success');
logger.warn('status:success', 'something success');
logger.error('status:success', 'something success');
logger.debug('status:success', 'something success');
```

## 单元测试覆盖率

[单元测试覆盖率](cov/index.html)
npm package: @retailwe/common-libs-navigator
Description: RetailWe libs
Latest version: 0.0.3

# Navigator 导航器

## Feature

1. 防抖
2. 最大页面栈处理
3. Tabbar 页面处理

## 安装

```shell
yarn add '@retailwe/common-libs-navigator' --save-exact
```

## 使用 `gotoPage`

**定义：**

```javascript
function gotoPage(path, query, options);
```

**使用：**

```javascript
import Navigator from '@retailwe/common-libs-navigator';

const navigator = new Navigator();

navigator.gotoPage('/pages/home/index', { name: 'retailwe' });
```

**策略说明**

1. 防抖：短时间多次调用，只触发一次
2. 最大页面栈处理：
   1.  目标页面存在页面栈中，页面出栈只到目标页面处于栈顶（`wx.navigateBack`）
   2. 目标页面不在页面栈中，出栈第一个页面，压入目标页面（`wx.redirect`）
3. Tabbar 页面处理：清除页面栈，压入目标页面（`wx.switchTab`）

**显式 Redirect**

```javascript
navigator.gotoPage(
  '/pages/home/index',
  { name: 'retailwe' },
  { redirect: true }
);
```

**Tabbar 页面跳转**

由于目前还没有比较成熟的判断跳转页面是 Tabbar 页面的方式，需要调用时显式传递

```javascript
import { Navigator, PathType } from '@retailwe/common-libs-navigator';

const navigator = new Navigator();

navigator.gotoPage({
  path: '/pages/home/index',
  type: PathType.TAB
});
```

## 结合 RouterMap

```javascript
import { PathType, Navigator } from '@retailwe/common-libs-navigator';

// 维护路由映射表
const routes = {
  home: {
    path: '/pages/home/index',
    type: PathType.TAB
  },
  log: {
    path: '/pages/log/index',
    type: PathType.NORMAL
  }
};

const navigator = new Navigator();

navigator.gotoPage(routes.home);
```

## 单元测试覆盖率

[单元测试覆盖率](cov/index.html)
npm package: @retailwe/common-libs-status
Description: RetailWe libs
Latest version: 0.0.4

# Status 状态机

在原生的小程序启动流程中，因为无论 app，page，component 的生命周期钩子函数，都不支持异步阻塞。

但是实际项目中，我们有大量的类似的实际场景需要满足：登录态的初始化，全局数据的初始化等等。

Status 基于事件驱动，来满足这一类的需求。

![](https://camo.githubusercontent.com/d36dba16d4c0739b0b45ba59ff298f39e17c22e8/68747470733a2f2f696d672e797a63646e2e636e2f7075626c69635f66696c65732f323031392f30322f32362f65376137663164646234313835303765626431663761343530363365333831342e706e67)

一个状态机，有四种状态：

- 未开始：un start （为状态机的初始态）
- 进行中：ing
- 成功：success
- 失败：fail

实例的 api 主要分三类：

- 状态改变
- 状态监听
- 状态判断

## 安装

```shell
yarn add '@retailwe/common-libs-status' --save-exact
```

## 使用

```typescript

import { Status } from '@retailwe/common-libs-status';

// on app.js
App({
    status: {
       bootstrap: new Status('bootstrap');
    },

    onLaunch() {
        API
            .fetch('xxx')
            .then(data => this.globalData = data)

            // 把状态机设置为 success
            .then(() => this.status.bootstrap.success());
    },
});


// on page.js
Page({
    onLoad() {
        getApp()
            .status.bootstrap

            // 监听一次成功状态事件
            .onceSuccess(() => {
                const globalData = getApp().globalData;
                if (globaleData) this.initPage(globalData);
            });
    },
});
```

## 单元测试覆盖率

[单元测试覆盖率](cov/index.html)
npm package: @retailwe/common-libs-storage
Description: RetailWe libs
Latest version: 0.0.5

# Storage 增强存储

## Feature

1.  版本隔离
2.  环境隔离
3.  过期控制
4.  支持内存缓存
5.  同步/异步 API

## 安装

```shell
npm install @retailwe/common-libs-storage
```

## Quick Useage

**设置与获取**

```javascript
const { Storage } frmo '@retailwe/common-libs-storage'
const myStorage = new Storage();

await storage.set('name', 'jc');

console.log(await storage.get('name'));  // jc
```

**移除**

```javascript
await storage.set('name', 'jc');
await storage.remove('name');

console.log(await storage.get('name')); // undefinded
```

**清除所有数据**

```javascript
await storage.set('name', 'jc');
await storage.set('age', 18);
await storage.clear();

console.log(await storage.get('name')); // undefinded
console.log(await storage.get('age')); // undefinded
```

## 隔离与过期

```javascript
const { storage } frmo '@retailwe/common-libs'
const myStorage = new storage.Storage({
  // 版本隔离
  appVersion: '0.0.1',
  // 环境隔离
  env: 'prod',
  // 过期时间 (单位ms)
  expires: 60 * 1000
});
```

## 内存缓存

为了进一步优化读取速度与安全，支持开启建立内存缓存，实现机制如下：

- 读：优先访问内存，其次到 wx.storage，默认增量式写入。
- 写：同时写入内存与 wx.storage

```javascript
const { storage } frmo '@retailwe/common-libs'
const myStorage = new storage.Storage({
  enableMemoryCache: true,
});
```

**全量同步 wx.storage 到内存**

满足在小程序启动之后，希望全量初始化缓存信息到内存，绕过增量式写入

```javascript
const { storage } frmo '@retailwe/common-libs'
const myStorage = new storage.Storage({
  enableMemoryCache: true,
});

await myStorage.fillMemoryCache();
```

## 单元测试覆盖率

[单元测试覆盖率](cov/index.html)
npm package: @retailwe/common-utils
Description: Common code such as libs, utils, helper, style, wxs
Latest version: 0.0.4

# common

Common code such as libs, utils, helper, style, wxs

# 使用

## 安装

```shell
yarn add '@retailwe/common-utils' --save-exact
```

## 引入

```javascript
import {
  time,
  rpx,
  object,
  is,
  image,
  path,
  money,
  wx
} from '@retailwe/common-utils';

is.isString('abc'); // true
money.f2y(10000); // 100.00
```

# API

[API 文档详情](./docs/globals.md)

# 开发

- 运行：`yarn watch`
- 构建：`yarn build`
- 单元测试：`yarn test`
- 测试报告：`yarn cov`
- 文档：`yarn doc`

# 测试覆盖率

| File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
| --------- | ------- | -------- | ------- | ------- | ----------------- |
| All files | 71.43   | 58.33    | 100     | 71.43   |                   |
| image.ts  | 71.43   | 58.33    | 100     | 71.43   | 20,23             |