组件结构
html
<template><!-- 在Vue2.x template 内必须有且仅有一个根节点 --><div><!-- 内容... --></div></template>
TypeScript
<script lang='ts'>import { Component, Vue } from 'vue-property-decorator'@Component({name: 'ElementName', //组件名称components: {} //此组件使用其他组件的注册地点})export default class extends Vue {// ...}</script>
css/scss
<style lang='scss' scoped> /*scoped 防止组件样式污染全局*//* ... */.a{.b{.c{color:red;}}}</style>
vue指令(官网)
https://cn.vuejs.org/v2/api/#%E6%8C%87%E4%BB%A4
数据
成员变量 和 get set 函数
<template><!-- 在Vue2.x template 内必须有且仅有一个根节点 --><div><!-- 内容... -->name:{{name}}{{greeting}}<input v-model="greeting" /></div></template><script lang='ts'>import { Component, Vue } from 'vue-property-decorator'@Component({name: 'ElementName', //组件名称components: {} //此组件使用其他组件的注册地点})export default class extends Vue {// 成员变量// [修饰符] 成员变量[:类型] = 值// 成员变量 有初始化值的时候会被监控其值的变化private name:string = "Guquanlengyue"// get set 函数名可以一致 一般成对出现// get 函数:get <函数名>(){}// 函数必须有返回值// 函数名直接当变量来使用// 在调用取值的时候触发函数,返回函数结果// 若函数内部使用的变量发生改变 会触发函数,维护函数结果最新get greeting(){return "helloworld,"+this.name}// set 函数:set <函数名>(){}// 在尝试给greeting赋值的时候触发// 尝试赋的值会通过函数参数传入set greeting(name:string){this.name = `${name}!`;}}</script>
@Watch 监听
<template><!-- 在Vue2.x template 内必须有且仅有一个根节点 --><div></div></template><script lang='ts'>import { Component, Vue, Watch } from 'vue-property-decorator'@Component({name: 'ElementName', //组件名称components: {} //此组件使用其他组件的注册地点})export default class extends Vue {private name:string = "Guquanlengyue"// @Watch(path:string,option:{ immediate?: true, deep?: true }|undefined) 监听装饰器// path: 成员变量名or get函数名// immediate: 是否在Vue class 初始化完成的时候立即执行被装饰的函数// deep: 是否深度监听path指定对象的所有子对象// 若deep:false 只有当对象的引用地址改变才会触发被装饰的函数// 若deep:true 对象的子对象引用地址改变就会触发被装饰函数@Watch("name",{ immediate: true, deep: true })private doSomething(newValue,oldValue){// do something ...}}</script>
函数
生命周期与生命周期函数
自定义组件
组件调用
<template><!-- 在Vue2.x template 内必须有且仅有一个根节点 --><div><!--<组件名 [[参数]...]>[组件插槽填充]</组件名>--></div></template><script lang='ts'>import <组件名> from "path/to/component"import { Component, Vue, Watch } from 'vue-property-decorator'@Component({name: 'ElementName', //组件名称components: {[<组件名>...]} //此组件使用其他组件的注册地点})export default class extends Vue {}</script>
组件接收参数
<script lang='ts'>import { Component, Vue, Prop } from 'vue-property-decorator'@Component({name: 'ElementName', //组件名称components: {} //此组件使用其他组件的注册地点})export default class extends Vue {// required: 是否一定需要这个参数, 若required:true, 调用组件不给该参数赋值是,会报错// type: 指定参数类型 不能使用自定义类型// default: 参数默认值@Prop({ required: true, type: String, default: 'helloworld' }) propName?: string@Prop({ required: true, type: Array, default: ()=>{return ['list1','list2']} }) propList?: List<string>@Prop({ required: true, type: Object, default: ()=>{return {name:'xiaoming',age:'6'}} }) propName?: {name:string,age:string}}</script>
组件抛出结果
子组件
<template><!-- 在Vue2.x template 内必须有且仅有一个根节点 --><div><div @click='handleClick'> ...假装有内容 </div></div></template><script lang='ts'>import { Component, Vue, Prop } from 'vue-property-decorator'@Component({name: 'chird', //组件名称components: {} //此组件使用其他组件的注册地点})export default class extends Vue {handleClick(){// $emit('抛出的事件名', 可以带上需要的参数)this.$emit('eventName', [someArg[,someArgs]])}}</script>
父组件
<template><!-- 在Vue2.x template 内必须有且仅有一个根节点 --><div><chird @eventName='handleChirdEvent'> ...假装有内容 </chird></div></template><script lang='ts'>import { Component, Vue, Prop } from 'vue-property-decorator'import chird from './chird.vue'@Component({name: 'parent', //组件名称components: {chird} //此组件使用其他组件的注册地点})export default class extends Vue {// 在父组件中捕获子组件抛出的事件handleChirdEvent(args){// ....}}</script>
项目文件结构
sadais-cabinet-console ├── LICENSE // 最外层都是配置相关的文件 ├── README-zh.md ├── README.md ├── babel.config.js
├── cypress.json
├── gulpfile.js
├── jest.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── check-token.js
│ ├── favicon.ico
│ ├── favicon.png
│ ├── img
│ ├── index.html
│ ├── manifest.json
│ └── robots.txt
├── src // 平时开发关注最多的文件
│ ├── App.vue
│ ├── api // 与服务端请求数据的api文件夹
│ ├── typings // 类型文件 配合api使用
│ ├── assets // 静态文件存放文件夹
│ ├── components // 公用组件文件夹
│ ├── consts // 基础常量文件夹
│ ├── directives
│ ├── filters // 自定义模板过滤器文件夹
│ ├── icons
│ ├── lang // 国际化翻译相关
│ ├── layout // 全局布局文件夹
│ ├── main.ts
│ ├── mixin
│ ├── permission.ts
│ ├── pwa
│ ├── router // 路由文件
│ ├── settings.ts
│ ├── shims-tsx.d.ts
│ ├── shims.d.ts
│ ├── store // 常用存储在storage里的数据
│ ├── styles
│ ├── utils // 工具类文件夹
│ └── views // 各页面存放文件夹
├── tests
│ └── unit
├── tsconfig.json
└── vue.config.js
与服务端进行数据交互
1、新建API
// @/typings/modules/bannerts/*** banner信息*/export interface IBanner {/** 名称 */name?: string/** 图片地址 */pic?: string/** 消息跳转值 */jumpVal?: string/** 排序 */sort?: number/** 跳转类型 */jumpType?: number/** 栏目(activity活动index首页顶部) */column?: string/** Banner有效开始时间 */startTime?: string/** Banner有效结束时间 */endTime?: string/** 是否上架(1是0否) */isUse?: number/** 内容 */content?: string/** 记录ID */id?: string/** 创建时间 */createDate?: string}
// @/api/banner.tsimport { IBanner } from '@/typings/modules/banner'import request from '@/utils/request'/*** 加载列表* @param {Number} pageno 当前页* @param {Number} pagesize 每页大小*/export async function apiQueryBaseList(pageno: Number, pagesize: Number) {const { data } = await request.get<HttpResponse>('/api/ca/cabinet/v1/banner/findlist', {params: { pageno, pagesize } // aprams:{参数名:参数值[,参数名:参数值]}})return data}/*** 创建/更新* @param {Object} params 对象信息*/export async function apiSaveOrUpdate(params: IBanner) {const { data } = await request.post<HttpResponse>('/api/ca/cabinet/v1/banner/saveorupdate',params)return data}
2、使用API
需要临时缓存数据状态
// @/store/modules/banner.tsimport { apiDelete, apiQueryList } from '@/api/banner'import consts from '@/consts'import store from '@/store'import { SearchCondition, IBanner, DefaultBanner } from '@/typings/modules/banner'import dayjs from 'dayjs'import { Notification } from 'element-ui'import { Action, getModule, Module, Mutation, VuexModule } from 'vuex-module-decorators'import cloneDeep from 'lodash/cloneDeep'@Module({ dynamic: true, store, name: 'banner', namespaced: true })class Banner extends VuexModule implements IBannerState {@Mutationpublic CHANGE_STATE(payload: { key: string; value: any }) {const { key, value } = payloadconst _this = this as anyif (Object.prototype.hasOwnProperty.call(_this, key)) {_this[key] = value}}@Actionpublic async handleQuery(condition?: SearchCondition) {condition = cloneDeep(condition)...this.CHANGE_STATE({ key: 'loading', value: true })const params = {pageNo: condition.pageNo,pageSize: condition.pageSize,...}const { data, head: { ret, msg } = { ret: 1, msg: '列表加载失败' } } = await apiQueryList(params)if (ret === consts.RET_CODE.SUCCESS) {this.CHANGE_STATE({ key: 'searchResult', value: data })}this.CHANGE_STATE({ key: 'loading', value: false })}}
// @/views/banner/list.vue<script lang='ts'>import { Component, Vue } from 'vue-property-decorator'import { formatJson } from '@/utils'import { exportJson2Excel } from '@/utils/excel'import Pagination from '@/components/Pagination/index.vue' // secondary package based on el-paginationimport { IBanner, SearchCondition } from '@/typings/modules/banner'import { BannerModule } from '@/store/modules/banner'@Component({name: 'BannerList',components: {Pagination}})export default class extends Vue {// ...// 当这个页面创建时created() {// 如果 BannerModule 对象缓存中有数据, 则跳过加载数据if (BannerModule.searchResult.result.length) returnthis.handleQuery()}private handleQuery(page?: { pageNo: number; pageSize: number }) {if (page) {// 重置页码page.pageNo && (this.form.pageNo = page.pageNo)page.pageSize && (this.form.pageSize = page.pageSize)}BannerModule.handleQuery(this.form)}}</script>
不需要缓存数据状态
// @/views/banner/list.vue<script lang='ts'>import { Component, Vue } from 'vue-property-decorator'import { formatJson } from '@/utils'import { exportJson2Excel } from '@/utils/excel'import Pagination from '@/components/Pagination/index.vue' // secondary package based on el-paginationimport { IBanner, SearchCondition } from '@/typings/modules/banner'import { apiDelete, apiQueryList } from '@/api/banner'@Component({name: 'BannerList',components: {Pagination}})export default class extends Vue {// ...// 当这个页面创建时created() {this.handleQuery()}private handleQuery(page?: { pageNo: number; pageSize: number }) {if (page) {// 重置页码page.pageNo && (this.form.pageNo = page.pageNo)page.pageSize && (this.form.pageSize = page.pageSize)}const { data, head: { ret, msg } = { ret: 1, msg: '列表加载失败' } } = await apiQueryList(params)// ...}}</script>
路由
目录结构
router
├── index.ts
└── modules
├── banner.ts
├── cabinet-product.ts
├── cabinet.ts
├── distribution.ts
├── hire-record.ts
├── order.ts
├── permission.ts
├── product.ts
├── statistic.ts
├── system-config.ts
└── user-info.ts
svg组件生成
- 将svg图片文件放到
@/assets/img/ - 控制台内项目根目录下运行命令
npm run svg - svg组件将会生成到
@/assets/components目录下侧边栏配置

路由文件内容与页面信息关系
当且仅当 只有一个可显示子菜单时, 直接使用子菜单的名字和目标路由 ```typescript // 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 hidden: true // (默认 false)
//当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 redirect: ‘noRedirect’
// 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式—如组件页面
// 只有一个时,会将那个子路由当做根路由显示在侧边栏—如引导页面
// 若你想不管路由下面的 children 声明的个数都显示你的根路由
// 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
alwaysShow: true
component: ()=>import(‘permission/index’), // 使用的组件
name: ‘router-name’ // 设定路由的名字,一定要填写不然使用
// 当路由设置了该属性,则会高亮相对应的侧边栏。 // 这在某些场景非常有用,比如:一个文章的列表页路由为:/article/list // 点击文章进入文章详情页,这时候路由为/article/1,但你想在侧边栏高亮文章列表的路由,就可以进行如下设置 activeMenu: ‘/article/list’ }
<a name="JWeaD"></a>### 正式使用这个路由<br />必须要在 `@/router/index.ts`内引入并要添加在 `asyncRoutes` 内才能在侧边栏正式生效. 且 `asyncRoutes` 与侧边栏循序有关.<a name="LFnSb"></a>### 页面跳转与参数携带```typescriptprivate handleEdit(row?: IBanner) {// 使用组件方式编辑this.showEdit = truelet query = {}if (row && row.id) {query = {id: row.id}}this.$router.push({ path: '/banner/edit', query })// this.$router.push({name:'BannerEdit',query})}
