1.0.113 • Published 1 year ago

dc-vue-h5-base v1.0.113

Weekly downloads
-
License
MIT
Repository
gitlab
Last release
1 year ago

东臣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()
    }
1.0.109

1 year ago

1.0.108

1 year ago

1.0.110

1 year ago

1.0.112

1 year ago

1.0.111

1 year ago

1.0.113

1 year ago

1.0.107

1 year ago

1.0.106

1 year ago

1.0.100

1 year ago

1.0.105

1 year ago

1.0.104

1 year ago

1.0.91

2 years ago

1.0.95

2 years ago

1.0.94

2 years ago

1.0.93

2 years ago

1.0.92

2 years ago

1.0.97

2 years ago

1.0.96

2 years ago

1.0.88

2 years ago

1.0.87

2 years ago

1.0.89

2 years ago

1.0.84

2 years ago

1.0.83

2 years ago

1.0.82

2 years ago

1.0.81

2 years ago

1.0.80

2 years ago

1.0.79

2 years ago

1.0.78

2 years ago

1.0.77

2 years ago

1.0.76

2 years ago

1.0.75

2 years ago

1.0.74

2 years ago

1.0.73

2 years ago

1.0.71

2 years ago

1.0.69

2 years ago

1.0.66

2 years ago

1.0.64

2 years ago

1.0.62

2 years ago

1.0.60

2 years ago

1.0.53

2 years ago

1.0.58

2 years ago

1.0.57

2 years ago

1.0.54

2 years ago