npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

dc-vue-h5-base

v1.0.113

Published

dckj h5 base

Downloads

7

Readme

东臣H5端基础框架

Vite + Vue3 + TypeScript + Pinia

规范说明

  1. 文件命名规则
    • Vue 文件采用驼峰大写命名,例如:HelloWorld.vue
    • js/ts 文件采用小写命名,例如:hello-world.js
    • assets/images下的图片文件,采用小写中划线格式,例如:hello-world.png
  2. 目录分包规则
    • src/view下的目录按照业务模块来进行划分,例如:system/uesr
    • 增改查分包分别使用Add.vueUpdate.vueDetail.vue, 审批、生成使用Approval.vueGenerate.vue
      例如:system/uesr目录:
      • Manage.vue 表示管理页,一般是表格的页面
      • Add.vue 表示新增用户
      • Update.vue 表示修改用户(可以和新增合并)
      • Detail.vue 表示查看用户详情用户
  3. 路由说明
    • 路由的可以不写name,path必写,对应菜单的菜单地址:linkUrl字段,path建议带上前缀,例如:/account/user/system/menu

使用技术

( 推荐使用pnpm进行包管理 )
npm install -g pnpm

( 安装husky )
husky install

( 设置统一换行符 )
git config --global core.autocrlf true

  • Vite
  • Vue3
  • TypeScript
  • Pinia
  • VueRouter
  • Eslint
  • Ant Design Vue

框架说明

网络请求

默认写法

import {PostRequestModel, GetRequestModel, PutRequestModel, DeleteRequestModel} from '@/utils/model/request-model';
import {PageModel} from "@/utils/model/result-model";
import {defaultConfig} from "@/http/json-config";
import {FactorVo} from "@/entity/eess/FactorVo";
import {FactorMapVo} from "@/entity/eess/FactorMapVo";

// 当前模块通用请求前缀
const MODULE_API_PREFIX = '/system/factor';

export default {
    page: (query?: {}) => new PostRequestModel<PageModel<FactorVo>>(`${MODULE_API_PREFIX}/page`, query).request(),
    formSelect: (query?: {}) => new PostRequestModel<Array<FactorVo>>(`${MODULE_API_PREFIX}/all`, query).request(),
    map: (query?: {}) => new PostRequestModel<Array<FactorMapVo>>(`${MODULE_API_PREFIX}/map`, query).request(),
    getById: (id: string) => new GetRequestModel<FactorVo>(`${MODULE_API_PREFIX}/${id}`, {}).request(),
    add: (data: {}) => new PostRequestModel<FactorVo>(`${MODULE_API_PREFIX}/`, data).request(),
    update: (id: string, data: {}) => new PutRequestModel<FactorVo>(`${MODULE_API_PREFIX}/${id}`, data).request(),
    delete: (id: string) => new DeleteRequestModel<boolean>(`${MODULE_API_PREFIX}/${id}`, {}).request(),
}

修改配置(配置需要继承已有的配置信息)

import {PostRequestModel, GetRequestModel, PutRequestModel, DeleteRequestModel} from '@/utils/model/request-model';
import {PageModel} from "@/utils/model/result-model";
import {defaultConfig} from "@/http/json-config";
import {FactorVo} from "@/entity/eess/FactorVo";
import {FactorMapVo} from "@/entity/eess/FactorMapVo";
import {getDefaultConfig} from '@/http/config/custom-config'

// 当前模块通用请求前缀
const MODULE_API_PREFIX = '/system/factor';

const defaultConfig = getDefaultConfig<any>()
// 修改配置中的值
// defaultConfig。。。

export default {
    page: (query?: {}) => new PostRequestModel<PageModel<FactorVo>>(`${MODULE_API_PREFIX}/page`, query, defaultConfig).request(),
    formSelect: (query?: {}) => new PostRequestModel<Array<FactorVo>>(`${MODULE_API_PREFIX}/all`, query, defaultConfig).request(),
    map: (query?: {}) => new PostRequestModel<Array<FactorMapVo>>(`${MODULE_API_PREFIX}/map`, query, defaultConfig).request(),
    getById: (id: string) => new GetRequestModel<FactorVo>(`${MODULE_API_PREFIX}/${id}`, {}, defaultConfig).request(),
    add: (data: {}) => new PostRequestModel<FactorVo>(`${MODULE_API_PREFIX}/`, data, defaultConfig).request(),
    update: (id: string, data: {}) => new PutRequestModel<FactorVo>(`${MODULE_API_PREFIX}/${id}`, data, defaultConfig).request(),
    delete: (id: string) => new DeleteRequestModel<boolean>(`${MODULE_API_PREFIX}/${id}`, {}, defaultConfig).request(),
}

目录结构

使用tree命令生成

├─api   api接口
│  ├─account      账户模块的接口(部门、个人信息、单位组织、角色信息、用户模块)
│  ├─dynamic      动态表单
│  ├─login        登录接口模块
│  ├─system       系统信息(数据字典、数据字典类型、菜单信息、消息模块、系统参数)
│  └─system-log   系统日志的接口(备份、文件备份、文件上传、消息、操作、版本)
├─assets          资源文件
│  ├─font         字体文件
│  ├─images       图片资源
│  │  ├─index     首页的图标
│  │  └─login     登录的一些图标
│  └─scss         样式文件
├─components      组件模块
│  ├─form
│  └─form-item    输入框基础组件
│      └─props
├─entity          实体类
│  ├─account      账户相关的实体类(部门、个人信息、单位组织、角色信息、用户模块)
│  ├─common       公共实体类
│  ├─dynamic      动态表单
│  ├─login        登录接口模块
│  ├─system       系统信息(数据字典、数据字典类型、菜单信息、消息模块、系统参数)
│  └─system-log   系统日志的接口(备份、文件备份、文件上传、消息、操作、版本)
├─http            网络模块
│  └─config       配置信息
├─router          路由模块
│  └─modules
├─store           存储模块
├─utils           工具类
└─views           基础页面
    ├─account     
    ├─exception   异常界面(404/500/403)
    ├─index       首页信息
    ├─login       登录模块
    │  └─component登录模块的一些组件
    └─system      系统模块(结果界面)

组件使用

Iconfont 使用

首先需要替换掉本地的iconfont.ttf,src/assets/font/iconfont.ttf


<template>
	<van-icon class-prefix="iconfont" name="wechat"/>
</template>

<style>
/*这句建议在app.vue引用(全局)*/
@import "~@/assets/font/iconfont.css";

.iconfont {
	font-size: 50px;
	color: var(--van-primary-color);
}

.iconfont-wechat:before {
	content: '\e73b';
}
</style>

主题颜色定制

默认主题颜色使用theme.css,如果需要修改主题,配置如下

基础颜色,如需修改vant组件颜色,请前往Vant组件进行查看

:root {
    /* Color Palette */
    --van-black: #000;
    --van-white: #fff;
    --van-gray-1: #fafafa;
    --van-gray-2: #f2f3f5;
    --van-gray-3: #ebedf0;
    --van-gray-4: #dcdee0;
    --van-gray-5: #c8c9cc;
    --van-gray-6: #999999;
    --van-gray-7: #666666;
    --van-gray-8: #333333;
    --van-blue: #1989fa;
    --van-orange-dark: #ed6a0c;
    --van-orange-light: #fffbe8;
    --van-green: #00b578;
    --van-orange: #ff6a2a;
    --van-red: #e63633;

    /*Gradient Colors */
    --van-gradient-red: linear-gradient(to right, #ff6034, #ee0a24);
    --van-gradient-orange: linear-gradient(to right, #ffd01e, #ff8917);

    /*Component Colors */
    --van-primary-color: #428ffc;
    --van-success-color: var(--van-green);
    --van-danger-color: var(--van-red);
    --van-warning-color: var(--van-orange);
    --van-text-color: var(--van-gray-8);
    --van-text-color-2: var(--van-gray-6);
    --van-text-color-3: var(--van-gray-5);
    --van-text-link-color: #576b95;
    --van-active-color: var(--van-gray-2);
    --van-active-opacity: 0.6;
    --van-disabled-opacity: 0.5;
    --van-background-color: var(--van-gray-1);
    --van-background-color-light: var(--van-white);

    /* Padding */
    --van-padding-base: 4px;
    --van-padding-xs: 8px;
    --van-padding-sm: 12px;
    --van-padding-md: 16px;
    --van-padding-lg: 24px;
    --van-padding-xl: 32px;

    /*Font */
    --van-font-size-xs: 10px;
    --van-font-size-sm: 12px;
    --van-font-size-md: 14px;
    --van-font-size-lg: 16px;
    --van-font-weight-bold: 500;
    --van-line-height-xs: 14px;
    --van-line-height-sm: 18px;
    --van-line-height-md: 20px;
    --van-line-height-lg: 22px;
    --van-base-font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
    Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB',
    'Microsoft Yahei', sans-serif;
    --van-price-integer-font-family: Avenir-Heavy, PingFang SC, Helvetica Neue,
    Arial, sans-serif;

    /*Animation */
    --van-animation-duration-base: 0.3s;
    --van-animation-duration-fast: 0.2s;
    --van-animation-timing-function-enter: ease-out;
    --van-animation-timing-function-leave: ease-in;

    /*Border */
    --van-border-color: var(--van-gray-3);
    --van-border-width-base: 1px;
    --van-border-radius-sm: 2px;
    --van-border-radius-md: 4px;
    --van-border-radius-lg: 8px;
    --van-border-radius-max: 999px;

    /* --------------------------------------------- */
    /* 浙里办主题 */
    --zlb-space-s1: 4px;
    --zlb-space-s2: 8px;
    --zlb-space-s3: 12px;
    --zlb-space-s4: 16px;

    /* 圆角*/
    --zlb-radius-r1: 2px;
    --zlb-radius-r2: 4px;
    --zlb-radius-r3: 6px;
    --zlb-radius-r4: 8px;

    /* --------------------------------------------- */
    /* login */
    --login-background-color: #248c86;

    /* --------------------------------------------- */
    /* checkbox */
    --van-checkbox-size: 16px;
    --van-checkbox-border-color: #d9d9d9;
    --van-checkbox-checked-icon-color: #428ffc;

    /* --------------------------------------------- */
    /* tabbar */
    --van-tabbar-item-text-color: #353b45;
    --van-tabbar-item-active-color: #428ffc;

    /*  Button  */
    --van-button-border-radius: 8px;

    /* --------------------------------------------- */
    /*  Result  */
    --zlb-result-color-success: var(--van-green);
    --zlb-result-color-fail: var(--van-orange);
    --zlb-result-color-error: var(--van-red);
    --zlb-result-color-wait: #9fccff;
}
// 本地主题,覆盖
import './assets/scss/theme.css'

首页配置

重写tabs.ts文件

export interface TabItem {
    /**
     * 标题
     */
    label: string,
    /**
     * 图标
     */
    icon: string,
    /**
     * 激活图标
     */
    activeIcon: string,
    /**
     * 路由地址
     */
    router: string
}

并且把路由配置index.ts

tab页面放在homechildren里面

const route = {
    path: '/',
    redirect: '/home',
    name: 'home',
    component: () => import('@dc/vue-h5-base/src/views/index/Index.vue'),
    children: [
        {
            path: '/home',
            component: () => import( '@/views/index/Home.vue')
        },
        {
            path: '/district',
            component: () => import( '@/views/index/District.vue')
        },
        {
            path: '/housekeeper',
            component: () => import( '@/views/index/Housekeeper.vue')
        },
        {
            path: '/mine',
            component: () => import( '@/views/index/Mine.vue')
        }
    ]
}

全局样式

/*公共样式*/
.page {
    height: 100vh;
    width: 100vw;
    display: flex;
    flex-direction: column;
    overflow: hidden scroll;
    background: var(--van-gray-1);
    box-sizing: border-box;
}

/*占位*/
.spacer {
    flex: 1 1 auto;
}

.spacer-2 {
    flex: 2 1 auto;
}

常见问题

  1. 使用 BaseFormItem,文件上传校验问题,已经上传了还是提示不能为空。
    设置校验触发时机为trigger: 'blur'

  2. 使用 BaseFormItem,比如使用选择框,我即想要label又想要value,我要怎么写?
    推荐使用update:extraValue方法进行实现, 可以使用extraNames指定返回字段

    <template>
        <base-form-item v-for="item of fields" :key="item.name" v-model:value="formState[item.name]" v-bind="item" @update:extraValue="extraValueChange" />
    </template>
      
    <script setup lang="ts">
    import fields from '/src/views/index/fields'
    import { reactive } from 'vue'
    import BaseFormItem from 'src/views/components/form-item/BaseFormItem.vue'
    import { ExtraProps } from '/src/components/form-item/field-props'
    import { BaseFormDTO } from '/src/entity/common/base'
      
    const formState = reactive<BaseFormDTO>({})
      
    function extraValueChange(name: string, extra: ExtraProps) {
        // 需要注意的是赋值以后和 onFinish 里面拿不到这个赋值, 两个不是一个对象
      
        // 这样赋值
        Object.assign(formState, extra)
      
        // 这样赋值,返回的key可以使用extraNames
        Object.keys(extra).forEach(key => {
            formState[key] = extra[key]
        })
      
        // 或者通过 name 找到, 然后手动赋值
        // fields.find(value => value.name == name)
        if (name == 'upload-images') {
            //  手动进行操作或者判断
        }
      
        console.log(extra)
    }
    </script>
  3. 使用 BaseFormItem,如何设置自动完成(AutoComplete)/下拉项(Select)/级联项(Cascader)/自动完成(AutoComplete)值?
    首先,需要对fields设置可观察(ref/reactive,例如:

    // 先设置可观察,否则界面不会及时更新
    const fieldList = reactive(fields)
    // 当界面挂载后进行数据处理
    onMounted(async () => {
        // 通过name字段进行查找,这个比较健壮一点,对typeValue没填时进行了处理
        const find = fieldList.find((value: FieldProps) => value.name == 'select')
        if (find) {
            const typeValue = find?.typeValue as SelectProps
            const { data } = await requestMethod.getListRootCode('building_trade_tag')
            if (typeValue) typeValue.options = data
            // fieldNames 用来指定label和value对应的字段名称
            else find.typeValue = { options: data, fieldNames: { label: 'name', value: 'id', children: 'children' } }
        }
       
        // 如果你在`fields`中给`select`设置了`typeValue`,那么这里可以更简单点,使用这段的前提是在`fields`中给`select`设置了`typeValue`
        const typeValue = fieldList.find((value: FieldProps) => value.name == 'select')?.typeValue as SelectProps
        if (typeValue) {
            const { data } = await requestMethod.getListRootCode('building_trade_tag')
            typeValue.options = data
        }
    })
  4. 使用 BaseFormItem,怎么设置文件上传默认值?
    (参考第 3 点)

    // 先设置可观察,否则界面不会及时更新
    onMounted(async () => {
        if (!props.id) return
      
        const typeValue = fieldList.find((value: FieldProps) => value.name == 'upload')?.typeValue as UploadProps
        if (typeValue && props.id) {
            const { data } = await requestMehod.getById(props.id)
            typeValue.fileList = [{ id: data.imageId, uploadPath: data.imageUrl, oldFileName: '可填' }]
        }
    })
  5. Api 请求,怎么设置application/x-www-form-urlencoded

     update: (data: QueryType) => {
        const defaultConfig = getDefaultConfig<AccountUser>()
        defaultConfig.contentType = 'application/x-www-form-urlencoded'
        return new PutRequestModel<AccountUser>('/accountUser/updateMyPass', data, defaultConfig).request()
    }