tiangong-doc tiangong-doc
首页
  • 天宫体系

    • 前端架构
    • 后端架构
    • 扩展功能
    • 打包部署
    • 更新日志
    • 辅助开发平台 (opens new window)
    • 权限平台部署包下载 (opens new window)
  • 基础组件

    • 工作流引擎(待整理)
    • 跨境加签客户端(待整理)
    • 权限平台(待整理)
  • 数字化工具

    • 数字魔方
    • 数据采集
    • 智能导入
    • 数字员工
    • 规则引擎(开发中)
    • 消息引擎(待开发)
    • 智能导出(待开发)
  • 业务算法

    • 南京业务体检(待整理)
    • 智慧长江(待整理)
  • 识别模型

    • 空箱识别(待整理)
    • 智能客服(待整理)
    • OCR识别(待整理)
    • 车牌识别(待整理)
    • 超市停留识别(待整理)
    • 危险品识别(待开发)
    • 超长车识别(待开发)
    • 车辆靠台(待开发)
    • 车辆占用(待开发)
  • 算法模型(待整理)
  • 风控模型(待整理)
  • 海关规则(待整理)
  • 危险品知识(待整理)
  • 组件设计文档(待整理)
  • 数据服务

    • 商品归类服务(待开发)
    • RECP贸易服务(待开发)
    • 商品价格服务(待开发)
    • 业务申报解答(待开发)
    • 报关单状态查询(待开发)
  • 常用开发工具

    • BASE64编解码工具 (opens new window)
    • MD5编码工具 (opens new window)
    • AES/DES加解密 (opens new window)
    • JWT解码工具 (opens new window)
    • ASCII编解码工具 (opens new window)
    • Unicode编解码工具 (opens new window)
    • UTF-8编解码工具 (opens new window)
    • 字符串编解码工具 (opens new window)
    • 通用进制转换工具 (opens new window)
    • 浮点数十进制转换 (opens new window)
    • 在线JSON解析 (opens new window)
    • 在线JS代码格式化工具 (opens new window)
    • CSS可视化工具 (opens new window)
    • XML格式化工具
    • SQL压缩/格式化工具 (opens new window)
    • JSON/XML在线转换 (opens new window)
    • JSON/YAML在线转换 (opens new window)
    • IP地址查询 (opens new window)
    • HTTP在线接口测试 (opens new window)
    • UUID在线生成器 (opens new window)
    • 随机数生成器 (opens new window)
    • 在线文本比对 (opens new window)
  • 研发标准规范(待整理)
  • 国产化适配
首页
  • 天宫体系

    • 前端架构
    • 后端架构
    • 扩展功能
    • 打包部署
    • 更新日志
    • 辅助开发平台 (opens new window)
    • 权限平台部署包下载 (opens new window)
  • 基础组件

    • 工作流引擎(待整理)
    • 跨境加签客户端(待整理)
    • 权限平台(待整理)
  • 数字化工具

    • 数字魔方
    • 数据采集
    • 智能导入
    • 数字员工
    • 规则引擎(开发中)
    • 消息引擎(待开发)
    • 智能导出(待开发)
  • 业务算法

    • 南京业务体检(待整理)
    • 智慧长江(待整理)
  • 识别模型

    • 空箱识别(待整理)
    • 智能客服(待整理)
    • OCR识别(待整理)
    • 车牌识别(待整理)
    • 超市停留识别(待整理)
    • 危险品识别(待开发)
    • 超长车识别(待开发)
    • 车辆靠台(待开发)
    • 车辆占用(待开发)
  • 算法模型(待整理)
  • 风控模型(待整理)
  • 海关规则(待整理)
  • 危险品知识(待整理)
  • 组件设计文档(待整理)
  • 数据服务

    • 商品归类服务(待开发)
    • RECP贸易服务(待开发)
    • 商品价格服务(待开发)
    • 业务申报解答(待开发)
    • 报关单状态查询(待开发)
  • 常用开发工具

    • BASE64编解码工具 (opens new window)
    • MD5编码工具 (opens new window)
    • AES/DES加解密 (opens new window)
    • JWT解码工具 (opens new window)
    • ASCII编解码工具 (opens new window)
    • Unicode编解码工具 (opens new window)
    • UTF-8编解码工具 (opens new window)
    • 字符串编解码工具 (opens new window)
    • 通用进制转换工具 (opens new window)
    • 浮点数十进制转换 (opens new window)
    • 在线JSON解析 (opens new window)
    • 在线JS代码格式化工具 (opens new window)
    • CSS可视化工具 (opens new window)
    • XML格式化工具
    • SQL压缩/格式化工具 (opens new window)
    • JSON/XML在线转换 (opens new window)
    • JSON/YAML在线转换 (opens new window)
    • IP地址查询 (opens new window)
    • HTTP在线接口测试 (opens new window)
    • UUID在线生成器 (opens new window)
    • 随机数生成器 (opens new window)
    • 在线文本比对 (opens new window)
  • 研发标准规范(待整理)
  • 国产化适配
  • 前端框架 V1.0.0
    • 框架一览
    • 简介
    • 1.下拉框组件 <Eci-select />
    • 2.输入框组件 <Eci-input />
    • 3.按钮组件 <Eci-button />
    • 4.表格组件(带分页功能) <Eci-table />
    • 5.导入组件 <ImportComGlobal />
    • 6.导出组件 <ExcelExportTask />
    • 7.图片上传 <ImagesUpload />
    • 1.函数节流
    • 2.函数防抖
    • 3.文件大小换算
    • 4.颜色转换
    • 5.邮箱正则校验
    • 6.手机号正则校验
    • 7.JSON字符串格式校验
    • 8.时间戳转换成日期格式
    • 9.日期格式化
    • 10.控件回车键跳到下一步
    • 11.indexedDB 前端数据库
    • 背景简介
    • eci-cli 前端框架项目构建步骤
    • 可配置查询 demo
      • 1.查询条件
      • 2.查询结果
      • 3.表单(表头表体)
  • 其他
2023-07-12
目录

前端框架 V1.0.0

# 框架简介

# 框架一览

  • 功能阐述

image

  • 目录结构

image

# 简介

  • 采用的vue3 + vite + element-plus技术架构

  • vue版本 3.2.45, element-plus版本 2.2.26,nodejs版本 16.17.0,npm版本 8.19.3

  • axios 基于 promise 的http请求封装

  • 全局状态管理vuex,集中式存储管理应用的组件状态

  • indexedDB前端数据库,支持大量数据前端缓存

  • websocket,即时接收通信信息

  • draggable拖拽,工作台、查询条件,查询结果,表单等自定义拖拽排版

  • i18n国际化,支持多语言之间的切换

  • 框架中预留了两种类型查询页demo(1.支持可配置的,2.常规的)

  • 前端搭建私有化 npm 库:eci-tools 工具类,eci-cli 前端脚手架

# 组件封装

  • 基于element-plus UI库的二次封装,根据用户场景,用户习惯,做功能的优化与拓展

  • 根据业务需求进行功能性的整合封装,便于开发使用和代码维护

# 1.下拉框组件 <Eci-select />

<el-select-v2
    v-model="value" // 绑定值
    :options="options" // 数据
    placeholder="Please select" // 输入提示
    size="large" // 尺寸
    :disabled="disabled" // 显隐
    placement="top" // 显示位置
    filterable // 输入过滤
    clearable // 清空
    multiple // 多选
    collapse-tags // 多选tags
    allow-create // 创建新项目
    default-first-option // 默认选中第一条
    :value-key="value.code + '|' + value.name" // 自定义绑定值
  >
  	<template #default="{ item }">
      <span style="margin-right: 8px">{{ item.code }}</span>
      <span style="color: var(--el-text-color-secondary); font-size: 13px">{{ item.name }}</span>
    </template>
</el-select-v2>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 使用el-select-v2组件,支持大量数据的渲染,减少浏览器负担,提升性能
  • 继承原有功能(v-model绑定值,filterable是否可筛选, clearable是否清空,multiple是否多选,collapse-tags多选标签,disabled是否禁用,allow-create是否创建新项目,placement下拉框出现的位置,等等)
  • 支持输入检索,默认选中第一条(default-first-option : filterable 设置为true时)
  • 支持下拉框回车事件,焦点后移(focustypes,focusNext)
  • 选值类型 (code + '|' + name)

# 2.输入框组件 <Eci-input />

<el-input
    v-model="input" // 绑定值
    placeholder="Please input"
    show-password // 显示隐藏的密码框
    clearable // 是否允许清空
    disabled // 是否禁用
    maxlength="10" // 最大长度
    minlength="2" // 最短长度
/>
1
2
3
4
5
6
7
8
9
  • 继承原有功能(v-model绑定值,maxlength 最大长度,minlength 最小长度,clearable是否清空,disabled是否禁用,show-password 是否显示隐藏的密码框等等)
  • 支持输入校验,是否纯数字,是否纯字母,是否数字+字母组合等
  • 支持是否数字框输入 Number (最大值,最小值,小数点保留位数,小数点是否四舍五入,controls-position控制按钮位置,controls-position控制按钮显隐)

# 3.按钮组件 <Eci-button />

<el-button
    type="primary" // 样式:primary,success,info,warning,danger
    plain // 边框 & 背景
    link // 链接
    size // 大小
    :loading="loadingFlag" // 加载状态
    :icon="iconName" // 支持按钮带图标
    @click="singleClick" // 触发单击事件,统一做函数节流/防抖处理
>
    {{ buttonName }} // 支持传入按钮名称
</el-button>
1
2
3
4
5
6
7
8
9
10
11
  • 继承原有功能(type按钮状态颜色样式,plain 边框或背景,link 链接,size 大小等等)
  • 是否带icon,支持传入icon 的名称显示
  • loading 加载状态控制 布尔

# 4.表格组件(带分页功能) <Eci-table />

<el-table :data="tableData" style="width: 100%">
    <slot></solt> // 传入的<el-table-column>内容
</el-table>
<el-pagination
    @size-change="handleSizeChange" // 改变页数
    @current-change="handleCurrentChange" // 改变页码
    :current-page="paging.pageNum" // 当前页数
    :page-sizes="paging.SECH_PAGE_SIZES" // 当前页数选择 数组 [10,20,50,100]
    :page-size="paging.pageSize" // 每页条数
    layout="total, sizes, prev, pager, next, jumper"
    background // 背景色样式
    :total="tableDataObj.total" // 总条数
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 使用插槽,接收表格列内容(slot, el-table-column)
  • 带分页功能,分页功能支持自定义(显示隐藏,样式,大小等)

# 5.导入组件 <ImportComGlobal />

  • 维护对应导入传入的业务类型
  • 页面使用 $openImportCom('业务类型')
// 导入统一函数
const openImportCom = (bizType, platNo) => {
    if (!platNo && platNo !== undefined) {
        customElement.warning('请保存表头')
        return
    }
    let options = {
        bizType: importMap[bizType],  // 对应导入配置的业务类型
        asUse: 1,
        bizNo: platNo || '',
        sysCode: window.global.importSys, // 对应配置平台系统代码
        other: { _CentreImportConfigDBID: window.global.importSys, "Authorization": localStorage.get('gw_token') }, //对应配置平台系统代码
    }
    let option = JSON.stringify(options)
    const exStr = Base64.encode(option) // 加密
    let url = import.meta.env.VITE_IMPORT_URL
    if (bizType === 'HZQD') {
        url = import.meta.env.VITE_UPLOADURL_URL
    }
    url += "/" + exStr + "&strToken=" + localStorage.get('gw_userinfo').ssoToken

    window.open(url, '_blank')
    // window.open(url, "导入数据", "left=0")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 6.导出组件 <ExcelExportTask />

  • 组件包含导出按钮
  • 页面引用 <ExcelExportTask :excelData="exportData" />,传入导出数据exportData
// 导出到excel
const downloadExl = (json, downName) => {
        let keyMap = []; // 获取键
        for (let k in json[0]) {
            keyMap.push(k);
        }
        let tmpdata = []; // 用来保存转换好的json
        json.map((v, i) => keyMap.map((k, j) => Object.assign({},
            {
                v: v[k],
                position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
            }))).reduce((prev, next) => prev.concat(next)).forEach(function (v) { tmpdata[v.position] = { v: v.v } }
        )
        let outputPos = Object.keys(tmpdata) // 设置区域,比如表格从A1到D10
        let tmpWB = {
            SheetNames: ["mySheet"], // 保存的表标题
            Sheets: {
                mySheet: Object.assign({}, tmpdata, // 内容
                    {
                        "!ref": outputPos[0] + ":" + outputPos[outputPos.length - 1], // 设置填充区域
                    }
                )
            }
        }
        let tmpDown = new Blob(
            [
                s2ab(
                    XLSX.write(
                        tmpWB,
                        {
                            bookType: "xlsx",
                            bookSST: false,
                            type: "binary",
                        } // 这里的数据是用来定义导出的格式类型
                    )
                ),
            ],
            { type: '' }
        ) // 创建二进制对象写入转换好的字节流
        if ("msSaveOrOpenBlob" in window.navigator) {
            window.navigator.msSaveOrOpenBlob(tmpDown, downName + ".xlsx")
        } else {
            var href = URL.createObjectURL(tmpDown) // 创建对象超链接
            state.outFile.download = downName + ".xlsx" // 下载名称
            state.outFile.href = href // 绑定a标签
            state.outFile.click() // 模拟点击实现下载
            setTimeout(function () {
                URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()来释放这个object URL
            }, 100);
            state.loading = false
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 7.图片上传 <ImagesUpload />

  • isOpenAliyunUpload: true, // windows.global 是否开启阿里云图片上传
  • 页面使用,传入按钮名称,组件返回图片url
// 页面调用
<ImagesUpload
    btnNames="上传签名"
    @getImageUploadUrl="getImageUploadUrl($event, 'signUrl')"
/>
1
2
3
4
5

# 工具类封装(npm库)

采用面向对象的以及纯函数的设计思想,将单一的功能设计成纯函数,纯函数具有行为一致,无副作用,适合统一的工具方法封装。结合主工具类,以及入口工厂函数,方便后期做工具库的配置以及扩展。

# 1.函数节流

/**
 * 创建并返回一个像节流阀一样的函数,当重复调用函数的时候,最多每隔delay毫秒调用一次该函数
 * @author Jerome.luo
 * @date 2023/02/03
 * @param fn 执行函数, delay 时间间隔
 * @returns {Function}
 */
const throttle = (fn, delay) => {
    var timer = null
    var timeStamp = new Date()
    return function() {
        var context = this //获取函数所在作用域this
        var args = arguments  //取得传入参数
        if (new Date() - timeStamp > delay) {
            timeStamp = new Date()
            timer = setTimeout(function(){
                fn.apply(context, args)
            }, delay)
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2.函数防抖

/**
 * 防反跳。fn函数在最后一次调用时刻的delay毫秒之后执行!
 * @author Jerome.luo
 * @date 2023/02/03
 * @param fn 执行函数 delay 时间间隔
 * @param isImmediate 为true,debounce会在delay时间间隔的开始时立即调用这个函数
 * @returns { Function }
 */
const debounce = (fn, delay, isImmediate) => {
    var timer = null  //初始化timer,作为计时清除依据
    return function() {
        var context = this  //获取函数所在作用域this
        var args = arguments  //取得传入参数
        clearTimeout(timer)
        if(isImmediate && timer === null) {
            //时间间隔外立即执行
            fn.apply(context,args)
            timer = 0
            return
        }
        timer = setTimeout(function() {
            fn.apply(context,args)
            timer = null
        }, delay)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 3.文件大小换算

/**
 * 获取文件大小,换算成kb单位
 * @author Jerome.luo
 * @date 2023/02/03
 * @params num 文件总大小
 * @return { Function }
 */
const getFileSize = (num) => {
    let m = num / (1024 * 1024) < 1 ? ((num / 1024).toFixed(2) + 'kb') : ((num / (1024 * 1024)).toFixed(2) + 'M')
    return m
}
1
2
3
4
5
6
7
8
9
10
11

# 4.颜色转换

/**
 * 将hex颜色 转成 rgb
 * @author Jerome.luo
 * @date 2023/02/03
 * @params hex 当前输入的值, opacity 透明度
 * @return { Object }
 */
const hexToRgba = (hex, opacity) => {
    let RGBA = 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' +
        parseInt('0x' + hex.slice(3, 5)) + ',' + parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')'
    return {
        red: parseInt('0x' + hex.slice(1, 3)),
        green: parseInt('0x' + hex.slice(3, 5)),
        blue: parseInt('0x' + hex.slice(5, 7)),
        rgba: RGBA
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.邮箱正则校验

/**
 * 邮箱正则校验
 * @author Jerome.luo
 * @date 2023/02/03
 * @params str 输入的邮箱值 value
 * @return { Boolean }
 */
const isEmail = (str) => {
    // debugger
    // var reg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/
    var reg = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/
    return reg.test(str)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 6.手机号正则校验

/**
 * 手机号正则校验
 * @author Jerome.luo
 * @date 2023/02/03
 * @params str 输入的手机号码 value
 * @return { Boolean }
 */
const isMobPhoneNo = (str) => {
    var reg = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/
    return reg.test(str)
}
1
2
3
4
5
6
7
8
9
10
11

# 7.JSON字符串格式校验

/**
 * 判断数据是否为JSON字符串格式
 * @author Jerome.luo
 * @date 2023/02/03
 * @params str 字符串数据
 * @return { Boolean }
 */
const isJSON = (str) => {
    if (typeof str == 'string') {
        try {
            var obj = JSON.parse(str)
            if (typeof obj == 'object' && obj) {
                return true
            } else {
                return false
            }
        } catch (e) {
            console.log('error:' + str + '!!!' + e)
            return false
        }
    } else {
        console.log('It is not a string!')
        return false
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 8.时间戳转换成日期格式

/**
 * 将时间戳转换成日期格式
 * @author Jerome.luo
 * @date 2023/02/03
 * @params timestamp 时间戳
 * @return { date }
 */
const timestampToTime = (timestamp) => {
    var date = new Date(timestamp) // 时间戳为10位需 * 1000,时间戳为13位的话不需乘1000
    var Y = date.getFullYear() + '-'
    var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
    var D = date.getDate() + ' '
    var h = date.getHours() + ':'
    var m = date.getMinutes() + ':'
    var s = date.getSeconds()
    return Y + M + D + h + m + s
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 9.日期格式化

/**
 * 去掉部分列表里日期的时分秒
 * @author Jerome.luo
 * @date 2023/02/03
 * @params time 时间数据,支持日期连接符</ . ->
 * @return { date }
 */
const deleteTimeSfm = (time)=>{ // 去掉部分列表里日期的时分秒
    let Newtime = '', reg = null
    if (time) {
        if (time.includes('-')) {
            reg = /\d{4}-\d{1,2}-\d{1,2}/g
        } else {
            reg = /\d{4}.\d{1,2}.\d{1,2}/g                    
        }
        Newtime = reg.exec(time)
    }
    return Newtime && Newtime.length ? Newtime[0] : time
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 10.控件回车键跳到下一步

/**
 * 控件回车键,焦点后移(目前只支持:el-input, el-select, el-input-number, 放大镜)
 * @author Jerome.luo
 * @date 2022/11/10
 * @params $event: 当前控件dom对象, focustypes dom绑定的nanoid()
 * @use 页面使用(控件添加)::focustypes="nanoid()" @keyup.native.enter="$Tools.focusNext($event)"
 * @use 放大镜:加在输入框的组件内
 * @return { Function }
 */
const focusNext = ($event) => {
    // 输入框获取focustypes
    const inputId = $event.target.attributes['focustypes'] && $event.target.attributes['focustypes'].value
    // 下拉框获取focustypes
    const selectId = $event.target.offsetParent.offsetParent.attributes['focustypes'] && $event.target.offsetParent.offsetParent.attributes['focustypes'].value
    // 放大镜获取focustypes
    const mgfId = $event.target.parentElement.offsetParent.firstElementChild.attributes['focustypes'] && $event.target.parentElement.offsetParent.firstElementChild.attributes['focustypes'].value

    // 获取所有受控的组件
    const enterRefArr = $event.target && $event.target.form ? $event.target.form.elements : []
    let allEl = []
    for (let i = 0; i < enterRefArr.length; i++) { // 过滤input后,用于获取焦点和查找使用。(date 日期控件 排除)
        if (enterRefArr[i].className.includes('el-input__inner') &&
           (!enterRefArr[i].offsetParent || (enterRefArr[i].offsetParent && !enterRefArr[i].offsetParent.className.includes('el-date-editor'))) &&
            !enterRefArr[i].disabled
        ) {
            allEl.push(enterRefArr[i])
        }
    }
    // 查找当前控件的索引值
    let indexs = allEl.findIndex((item, index) => {
        if (inputId) { // 输入框
            return item.attributes['focustypes'] &&
                   item.attributes['focustypes'].value === inputId
        } else if (selectId) { // 下拉框
            return item.offsetParent && item.offsetParent.offsetParent &&
                   item.offsetParent.offsetParent.attributes['focustypes'] &&
                   item.offsetParent.offsetParent.attributes['focustypes'].value === selectId
        } else { // 放大镜
            return item.offsetParent && item.offsetParent.offsetParent &&
                   item.offsetParent.offsetParent.firstElementChild.attributes['focustypes'] &&
                   item.offsetParent.offsetParent.firstElementChild.attributes['focustypes'].value === mgfId
        }
    })

    // 执行判断下一个控件是否有值
    let newNum = indexs // 存储索引,便于while循环
    if (indexs === -1 || (allEl.length === (indexs + 1))) return // 如果当前控件是最后一个, 或者未绑定当前dom节点
    for (let i = 0; i < allEl.length; i++) {
        if (!allEl[indexs + 1].value) { // 下一个没值时,直接获取焦点
            setTimeout(() => { // 延迟操作速度
                allEl[indexs + 1].focus()
            }, 200)
            return
        } else {
            // while: 如果下一个有值时,继续循环
            while (newNum + 1 !== allEl.length && allEl[newNum + 1].value) newNum++
            if (newNum + 1 !== allEl.length) {
                setTimeout(() => { // 延迟操作速度
                    allEl[newNum + 1].focus()
                }, 200)
                return
            } else {
                newNum = 0
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# 11.indexedDB 前端数据库

/**
 * 打开数据库 indexedDB
 * @author Jerome.luo
 * @date 2023/02/03
 * @params dbName 数据名称,storeName 表名称,version 版本号
 * @return { promise }
 */
const openDB = (dbName, storeName, version = initVersion) => {
    return new Promise((resolve, reject) => {
        let indexedDB = window.indexedDB
        let db
        const request = indexedDB.open(dbName, version)
        request.onsuccess = function(event) {
            db = event.target.result // 数据库对象
            resolve(db)
        }
        request.onerror = function(event) {
            reject(event)
        }
        request.onupgradeneeded = function(event) {
            // 数据库创建或升级的时候会触发
            console.log('onupgradeneeded')
            db = event.target.result // 数据库对象
            let objectStore
            if (!db.objectStoreNames.contains(storeName)) {
                db.createObjectStore(FIRST_LEVEL_CACHE, { keyPath: 'key' }) // 创建表
            }
        }
    })
}

/**
 * 新增数据 indexedDB
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库,storeName 表名称,version 版本号
 * @return { promise }
 */
const addData = (db, storeName, data, version = initVersion) => {
    return new Promise((resolve, reject) => {
        let request = db.transaction([storeName], 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
        .objectStore(storeName) // 仓库对象
        .add(data)
        request.onsuccess = function(event) {
            resolve(event)
        }
        request.onerror = function(event) {
            reject(event)
            throw new Error(event.target.error)
        }
    })
}

/**
 * 通过主键读取数据
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库,storeName 表名称,version 版本号
 * @return { promise }
 */
export async function getDataByKey(db, storeName, key, version = initVersion) {
    return new Promise((resolve, reject) => {
        let transaction = db.transaction([storeName]) // 事务
        let objectStore = transaction.objectStore(storeName) // 仓库对象
        let request = objectStore.get(key)
        request.onerror = function(event) {
            reject(event)
        }
        request.onsuccess = function(event) {
            resolve(request.result)
        }
    })
}

/**
 * 通过游标读取数据
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库,storeName 表名称,version 版本号
 * @return { promise }
 */
const cursorGetData = (db, storeName, version = initVersion) => {
    let list = []
    let store = db.transaction(storeName, 'readwrite') // 事务
        .objectStore(storeName) // 仓库对象
    let request = store.openCursor() // 指针对象
    return new Promise((resolve, reject) => {
        request.onsuccess = function(e) {
            let cursor = e.target.result
            if (cursor) {
                // 必须要检查
                list.push(cursor.value)
                cursor.continue() // 遍历了存储对象中的所有内容
            } else {
                resolve(list)
            }
        }
        request.onerror = function(e) {
            reject(e)
        }
    })
}

/**
 * 通过索引和游标查询记录
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库,storeName 表名称,version 版本号
 * @return { promise }
 */
const cursorGetDataByIndex = (db, storeName, indexName, indexValue) => {
    let list = []
    let store = db.transaction(storeName, 'readwrite').objectStore(storeName) // 仓库对象
    let request = store.index(indexName) // 索引对象
        .openCursor(IDBKeyRange.only(indexValue)) // 指针对象
    return new Promise((resolve, reject) => {
        request.onsuccess = function(e) {
            let cursor = e.target.result
            if (cursor) {
                list.push(cursor.value)
                cursor.continue() // 遍历了存储对象中的所有内容
            } else {
                resolve(list)
            }
        }
        request.onerror = function(ev) {
            reject(ev)
        }
    })
}

/**
 * 更新数据
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库,storeName 表名称,version 版本号
 * @return { promise }
 */
const updateDB = (db, storeName, data, version = initVersion) => {
    let request = db.transaction([storeName], 'readwrite') // 事务对象
        .objectStore(storeName) // 仓库对象
        .put(data)
    return new Promise((resolve, reject) => {
        request.onsuccess = function(ev) {
            resolve(ev)
        }
        request.onerror = function(ev) {
            resolve(ev)
        }
    })
}

/**
 * 删除数据
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库,storeName 表名称,version 版本号
 * @return { promise }
 */
const deleteDB = (db, storeName, key, version = initVersion) => {
    let request = db.transaction([storeName], 'readwrite').objectStore(storeName).delete(key)
    return new Promise((resolve, reject) => {
        request.onsuccess = function(ev) {
            resolve(ev)
        }
        request.onerror = function(ev) {
            resolve(ev)
        }
    })
}

/**
 * 删除数据库
 * @author Jerome.luo
 * @date 2023/02/03
 * @params dbName 数据库名称
 * @return { promise }
 */
const deleteDBAll = (dbName) => {
    let deleteRequest = window.indexedDB.deleteDatabase(dbName)
    return new Promise((resolve, reject) => {
        deleteRequest.onerror = function(event) {
            console.log('删除失败')
        }
        deleteRequest.onsuccess = function(event) {
            console.log('删除成功')
        }
    })
}

/**
 * 关闭数据库
 * @author Jerome.luo
 * @date 2023/02/03
 * @params db 数据库
 * @return { promise }
 */
const closeDB = (db) => {
    db.close()
    console.log('数据库已关闭')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

后期根据业务需要,不断添加与完善。

# 前端脚手架(npm库) eci-cli

# 背景简介

  • 前端有很多优秀的脚手架资源例如Vue-cli,create-react-app等,这些脚手架提供了简单的基本配置,以及部分定制化的内容,根据用户的选择去整合相关开发资源依赖,兼容处理,工程化配置,以及完整的环境配置,基本提供了完整的功能;

  • 这些主流的脚手架也具有一定局限性,没法根据公司自己的技术盏选型去做深度定制化内容开发,官方出于满足大众化的需求提供的项目模板过于简单,而企业级教手架则需要在上层进行二次封装或者完善才能满足真正的项目需求。所以我们结合主流脚手架的搭建方案开发我们内部定制化的脚手架eci-cli;

  • 关于eci-cli, 采用Vue3 + vite + element-plus 等工程的自动化构建,支持Vuex状态管理库,aixos请求等。采用vite为资源整合打包工具,需具有丰富的中文注释,能提供真正的开箱即用功能;

  • eci-cli 脚手架上架后,长期维护优化并升级

# eci-cli 前端框架项目构建步骤

* 需要提前安装好node环境

1.全局安装eci-cli脚手架

cnpm i eci-cli -g
或
npm i eci-cli -g
1
2
3

2.初始化项目,输入命令

eci-cli init
或
eci init 
1
2
3

3.按提示输入或根据需要选择(项目名,项目描述,版本号,项目类型web & mob)

image

4.项目下载完成

cd 项目名 // 切换到项目下
npm install // 安装依赖(后续安装依赖集成到脚手架中,可省略此命令,直接运行项目了)
npm run dev // 依赖安装完成,运行项目
1
2
3

# 项目实战

# 可配置查询 demo

1.支持筛选条件显示隐藏,排序,name修改的控制
2.支持查询结果字段显示隐藏,字段显示排序,name修改,数据排序,字段是否允许勾选导出,导出模板设置的控制
3.支持表单(表头,表体)控件只读,必填,label修改,默认值的控制
1
2
3
  • 代码文件夹copy
  • 文件夹结构与命名规范:业务代码mati,detl 详情组件,hooks 业务js代码,mitn 编辑组件,sech 查询组件

image

# 1.查询条件

  • 代码结构, <筛选配置,筛选控件,筛选配置弹窗,js,css>
  • 编写自己需要的筛选条件控件
  • 输入框、下拉框、日期、放大镜控件使用
  • 配置entity.queryField筛选条件拼接参数:
    data-query="a.platNo|l"
    
    1
  • 筛选配置组件传入业务类型 bizType
  • 配置默认排序数组 defaultSort
  • 筛选条件useFormFilter.js 传入业务类型

代码结构 image

控件类型 image

# 2.查询结果

  • 代码结构, <筛选组件,按钮,查询表格,分页,表格配置弹窗,js,css>
  • 表格内容,有特殊显示要求,单独写
  • 表格配置组件传入业务类型 bizType
  • 配置默认排序数组 defaultSort
  • 查询结果useFormSech.js 传入业务类型
  • 配置查询api接口,例如:
    initCfg.searchSource = '$$api_mat_selectPageList'
    
    1

image

# 3.表单(表头表体)

  • 代码结构,<按钮区域,表单form,其他业务功能组件,js,css>
  • 编写表单form的控件内容,UI组件同筛选条件
  • 绑定字段,==v-model="formParams.copGNo"==
  • 设置控件label,$t 国际化
    :label="$t('gw_material_list_cop_g_no')"
    
    1
  • 绑定控件配置功能,传入字段和参数(固定),自定义指令
    v-formConfig="{ column: 'copGNo', ...headFormData }"
    
    1
  • 绑定控件回车键跳转下一步函数(固定写法)
    :ref="enterRef"
    @keyup.native.enter="$Tools.focusNext($event, enterRefArr)"
    
    1
    2
  • 表头表单useFormHeader.js 传入业务类型
  • entity = {} 罗列表单所有字段,数据初始化和数据回填

image

上次更新: 2024/2/27 15:35:04
最近更新
01
EciReport模板导出_Core
09-10
02
EciReport模板导出
09-09
03
Core权限平台接口
07-25
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式