dc-vue-h5-base
v1.0.113
Published
dckj h5 base
Downloads
7
Readme
东臣H5端基础框架
Vite + Vue3 + TypeScript + Pinia
规范说明
- 文件命名规则
- Vue 文件采用驼峰大写命名,例如:
HelloWorld.vue
- js/ts 文件采用小写命名,例如:
hello-world.js
assets/images
下的图片文件,采用小写中划线格式,例如:hello-world.png
- Vue 文件采用驼峰大写命名,例如:
- 目录分包规则
src/view
下的目录按照业务模块来进行划分,例如:system/uesr
- 增改查分包分别使用
Add.vue
、Update.vue
、Detail.vue
, 审批、生成使用Approval.vue
、Generate.vue
。
例如:system/uesr
目录:Manage.vue
表示管理页,一般是表格的页面Add.vue
表示新增用户Update.vue
表示修改用户(可以和新增合并)Detail.vue
表示查看用户详情用户
- 路由说明
- 路由的可以不写
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页面放在home
的children
里面
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;
}
常见问题
使用 BaseFormItem,文件上传校验问题,已经上传了还是提示不能为空。
设置校验触发时机为trigger: 'blur'
使用 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>
使用 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 } })
使用 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: '可填' }] } })
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() }